NAV
curl CLI Browser JS NodeJS Python Go

Introduction

Latest BrowserJS version: v4 (Update Guide)

Welcome to the Skynet SDK API reference docs!

To learn more about Skynet, see the Skynet support docs.

For developer guides, see the Skynet developer docs.

Languages

We have SDK support for several languages:

Full documentation for the Skynet HTTP API can be found in the Sia API Documentation.

Tutorial

We have a detailed tutorial for Creating Your First Webapp On Skynet on our tech blog. Check it out if you are just starting out!

See also our developer docs for more guides.

A Note About Language Differences

Though we tried to keep the SDKs as similar to each other as possible in usage, differences between the languages -- the idiosyncracies and best practices of each -- resulted in differences between the SDKs. We've noted them where necessary throughout the documentation.

Function Parameters

In most of the SDKs the additional options may be left out of the function calls, in which case the default options are used. In Go the options must always be passed in, as the language does not support optional function parameters.

Case

Note that the casing of functions and their parameters differs between the languages:

Language Case
Javascript camelCase
Python snake_case
Go PascalCase

For consistency throughout this document, functions and their parameters are documented using camelCase.

Standard Responses

Functions will respond with the desired content on success and with errors or exceptions on failure. The error messages may contain HTTP status codes (see the Sia Docs).

Functions will fail differently in each SDK depending on the language:

Language Failure Mode
Javascript Exceptions are raised and must be caught with a try-catch block.
Python Same as above.
Go Errors are returned and must be checked for explicitly.

Using The Shell

function skynet() {
  curl -X POST "https://siasky.net/skynet/skyfile" -F "[email protected]$1" \
    | jq ".skylink" | xargs -I _ echo "https://siasky.net/_"
}

Our shell examples present only simple curl calls. You may find it helpful to write wrappers around these in Bash, or the shell language of your choice, to make them more reusable. To the right we have provided a simple example which uses jq.

Getting Started

Making Your First API Call

curl -L -X POST "https://siasky.net/skynet/skyfile/<siapath>" -F '[email protected]'
skynet upload "./image.jpg"
import { SkynetClient } from "skynet-js";

const client = new SkynetClient();

// Assume we have a file from an input form.

async function uploadExample() {
  try {
    const { skylink } = await client.uploadFile(file);
    console.log(`Upload successful, skylink: ${skylink}`);
  } catch (error) {
    console.log(error)
  }
}
const { SkynetClient } = require('@skynetlabs/skynet-nodejs');

const client = new SkynetClient();

(async () => {
    const skylink = await client.uploadFile("./image.jpg");
    console.log(`Upload successful, skylink: ${skylink}`);
})();
import siaskynet as skynet

client = skynet.SkynetClient()

skylink = client.upload_file("image.jpg")
print("Upload successful, skylink: " + skylink)
package main

import (
    "fmt"
    skynet "github.com/SkynetLabs/go-skynet/v2"
)

var client = skynet.New()

func main() {
    skylink, err := client.UploadFile("./image.jpg", skynet.DefaultUploadOptions)
    if err != nil {
        panic("Unable to upload: " + err.Error())
    }
    fmt.Printf("Upload successful, skylink: %v\n", skylink)
}

The SDKs are set up to be as simple as possible. Despite the many options for configuration, most users will be able to get started with a single API call. In the example on the right, we upload the file image.jpg to the default Skynet portal (which in Browser JS is the portal running the code and in other SDKs is https://siasky.net).

Note: If you are testing or running a Browser JS app locally, you will either have to run a Skynet portal locally or initialize the Skynet Client to connect to an existing portal such as https://siasky.net. See Portal Selection below.

Skynet Client

The above examples also demonstrate the use of the Skynet client. This client is required to make any API calls. It sets the portal that will be used for all requests. It can also be initialized with custom connection options that will be applied in all calls, unless calling an API method with the same options, which then take precedent and override what was set in client initialization.

Please see Using The Skynet Client for more information.

Portal Selection

In order to use a specific portal, you will need to use it as the first argument in client creation. Please see Basic Portal Selection for more information.

Setting Additional Options

Each SDK function also accepts additional options. These vary depending on the endpoint and are documented alongside each function.

Common Options

import { SkynetClient } from "skynet-js";

// Set an upload progress tracker.
const onUploadProgress = (progress, { loaded, total }) => {
  console.info(`Progress ${Math.round(progress * 100)}%`);
};

// Initialize the client.
const client = new SkynetClient("https://siasky.net", { onUploadProgress });

async function uploadExample() {
  try {
    const skylink = await client.uploadFile(file);
  } catch (error) {
    console.log(error);
  }
}

Every function accepts the following common options:

Option Description Default
APIKey The API password used for authentication. ""
customUserAgent Allows changing the User Agent, as some portals may reject user agents that are not Sia-Agent for security reasons. ""
onUploadProgress Optional callback to track upload progress. undefined
onDownloadProgress Optional callback to track download progress. undefined
timeout_seconds (Python-only) The timeout in seconds. ""

Useful Constants

Here are some constants exported by the SDKs which may be of use in applications.

Constant Description Default
defaultSkynetPortalUrl The default Skynet portal to use, if one is not provided. "https://siasky.net"
uriSkynetPrefix The Skynet URI prefix. "sia://" (Browser JS: "sia:")

Using The Skynet Client

The Skynet Client is required to make any API calls. It sets the portal that will be used for all requests. It can also be initialized with custom connection options that will be applied in all calls, unless calling an API method with the same options (which then take precedent and override what was set in client initialization).

Basic Portal Selection

No client available.
No client available.
import { SkynetClient } from "skynet-js";

// Or SkynetClient() without arguments to use the default portal.
const client = new SkynetClient("https://some-other-portal.xyz");
const { SkynetClient } = require('@skynetlabs/skynet-nodejs');

// Or SkynetClient() without arguments to use the default portal.
const client = new SkynetClient("https://some-other-portal.xyz");
import siaskynet as skynet

# Or SkynetClient() without arguments to use the default portal.
client = skynet.SkynetClient("https://some-other-portal.xyz")
package main

import (
    "fmt"
    skynet "github.com/SkynetLabs/go-skynet/v2"
)

var client = skynet.NewCustom("https://some-other-portal.xyz", skynet.Options{})

To make any API calls you will need to first create a new client with the desired portal. The client can be initialized without arguments to let the client choose it for you (see below) or the portal can be specified. See the code example on the right.

Default Portal Selection

The default portal used is https://siasky.net for all SDKs except Browser JS, which tries to use the portal which is running the sky app. The default portal is accessible through the exported function, defaultPortalUrl, and no configuration is required to use it. Having a reasonable choice already selected keeps friction for new developers low.

In the future the default selection will be smarter and there will be more options for default portal selection, such as configuration files. Please see this issue.

Setting Connection Options

No client available.
No client available.
import { SkynetClient } from "skynet-js";

const client = new SkynetClient("", { skynetApiKey: "foobar" });
const { SkynetClient } = require('@skynetlabs/skynet-nodejs');

const client = new SkynetClient("", { skynetApiKey: "foobar" });
import siaskynet as skynet

client = skynet.SkynetClient("", { "skynet_api_key": "foobar" })
package main

import (
    "fmt"
    skynet "github.com/SkynetLabs/go-skynet/v2"
)

var client = skynet.NewCustom("", skynet.Options{ SkynetAPIKey: "foobar" })

Apart from setting a persistent portal, client initialization also allows you to choose persistent connection settings. These will be applied on every subsequent API call using the client, unless the setting is overridden in a particular call.

For example, in the code samples we initialize a client with a Skynet API key that is applied on every request made by the client. See API Authentication.

Please also see Setting Additional Options.

Uploading To Skynet

Uploading A File

curl -L -X POST "https://siasky.net/skynet/skyfile/<siapath>" -F '[email protected]'
skynet upload "./image.jpg"
import { SkynetClient } from "skynet-js";

const client = new SkynetClient();

// NOTE: This example is different from the other SDKs because we cannot just
// take a path to a local file.

async function uploadExample() {
  try {
    const { skylink } = await client.uploadFile(file);
  } catch (error) {
    console.log(error)
  }
}
const { SkynetClient } = require('@skynetlabs/skynet-nodejs');

const client = new SkynetClient();

(async () => {
    const skylink = await client.uploadFile("./image.jpg");
    console.log(`Upload successful, skylink: ${skylink}`);
})();
import siaskynet as skynet

client = skynet.SkynetClient()

skylink = client.upload_file("image.jpg")
print("Upload successful, skylink: " + skylink)
package main

import (
    "fmt"
    skynet "github.com/SkynetLabs/go-skynet/v2"
)

var client = skynet.New()

func main() {
    skylink, err := client.UploadFile("./image.jpg", skynet.DefaultUploadOptions)
    if err != nil {
        panic("Unable to upload: " + err.Error())
    }
    fmt.Printf("Upload successful, skylink: %v\n", skylink)
}

Uploading a file to Skynet can be done through a Skynet portal or your local siad instance.

Parameters

Field Description
path The local path where the file to upload may be found.

Browser JS:

Field Description
file The File object returned from an input form.

Additional Options

Note that you can set common options for individual method calls as well, including onUploadProgress.

Field Description Default
portalFileFieldName The field name for files on the portal. Usually should not need to be changed. "file"
portalDirectoryFileFieldName The field name for directories on the portal. Usually should not need to be changed. "files[]"
customFilename Custom filename. This is the filename that will be returned when downloading the file in a browser. ""
customDirname Custom dirname. If this is empty, the base name of the directory being uploaded will be used by default. ""
onUploadProgress Optional callback to track upload progress. See Setting Additional Options. undefined

Response

{
  "skylink": "CABAB_1Dt0FJsxqsu_J4TodNCbCGvtFf1Uys_3EgzOlTcg",
  "merkleroot": "QAf9Q7dBSbMarLvyeE6HTQmwhr7RX9VMrP9xIMzpU3I",
  "bitfield": 2048
}
Successfully uploaded file! Skylink: sia://CABAB_1Dt0FJsxqsu_J4TodNCbCGvtFf1Uys_3EgzOlTcg
"sia://CABAB_1Dt0FJsxqsu_J4TodNCbCGvtFf1Uys_3EgzOlTcg"
"sia://CABAB_1Dt0FJsxqsu_J4TodNCbCGvtFf1Uys_3EgzOlTcg"
"sia://CABAB_1Dt0FJsxqsu_J4TodNCbCGvtFf1Uys_3EgzOlTcg"
"sia://CABAB_1Dt0FJsxqsu_J4TodNCbCGvtFf1Uys_3EgzOlTcg"
Field Description
skylink This is the skylink that can be used when downloading to retrieve the file that has been uploaded. It is a 46-character base64 encoded string that consists of the merkle root, offset, fetch size, and Skylink version which can be used to access the content.
merkleroot This is the hash that is encoded into the skylink.
bitfield This is the bitfield that gets encoded into the skylink. The bitfield contains a version, an offset and a length in a heavily compressed and optimized format.

Uploading A Directory

curl -L -X POST "https://siasky.net/skynet/<siapath>?filename=images" -F 'files[][email protected]/images/image1.png' -F 'files[][email protected]/images/image2.png'
skynet upload "./images"
import { getRelativeFilePath, getRootDirectory, SkynetClient } from "skynet-js";

const client = new SkynetClient();

// Assume we have a list of files from an input form.

async function uploadDirectoryExample() {
  try {
    // Get the directory name from the list of files.
    // Can also be named manually, i.e. if you build the files yourself
    // instead of getting them from an input form.
    const filename = getRootDirectory(files[0]);

    // Use reduce to build the map of files indexed by filepaths
    // (relative from the directory).
    const directory = files.reduce((accumulator, file) => {
      const path = getRelativeFilePath(file);

      return { ...accumulator, [path]: file };
    }, {});

    const { skylink } = await client.uploadDirectory(directory, filename);
  } catch (error) {
    console.log(error);
  }
}
const { SkynetClient } = require('@skynetlabs/skynet-nodejs');

const client = new SkynetClient();

(async () => {
    const skylink = await client.uploadDirectory("./images");
    console.log(`Upload successful, skylink: ${skylink}`);
})();
import siaskynet as skynet

client = skynet.SkynetClient()

skylink = client.upload_directory("./images")
print("Upload successful, skylink: " + skylink)
package main

import (
    "fmt"
    skynet "github.com/SkynetLabs/go-skynet/v2"
)

var client = skynet.New()

func main() {
    skylink, err := client.UploadDirectory("./images", skynet.DefaultUploadOptions)
    if err != nil {
        panic("Unable to upload: " + err.Error())
    }
    fmt.Printf("Upload successful, skylink: %v\n", skylink)
}

It is possible to upload a directory as a single piece of content. Doing this will allow you to address your content under one skylink, and access the files by their path. This is especially useful for webapps.

For example, let's say you upload a web app with the following simple structure:

src
|-- favicon.ico
|-- index.html
|-- css
    |-- main.css
|-- js
    |-- app.js

The four files can be accessed as follows:

[portal url]/[skylink]/favicon.ico
[portal url]/[skylink]/index.html
[portal url]/[skylink]/css/main.css
[portal url]/[skylink]/js/app.js

Parameters

Field Description
path The local path where the directory to upload may be found.

Browser JS:

Field Description
directory Object containing Files from an input form to upload, indexed by their path strings.
filename The name of the directory.

Additional Options

See Uploading A File.

Response

{
  "skylink": "EAAV-eT8wBIF1EPgT6WQkWWsb3mYyEO1xz9iFueK5zCtqg",
  "merkleroot": "QAf9Q7dBSbMarLvyeE6HTQmwhr7RX9VMrP9xIMzpU3I",
  "bitfield": 2048
}
Successfully uploaded directory! Skylink: sia://EAAV-eT8wBIF1EPgT6WQkWWsb3mYyEO1xz9iFueK5zCtqg
"sia://EAAV-eT8wBIF1EPgT6WQkWWsb3mYyEO1xz9iFueK5zCtqg"
"sia://EAAV-eT8wBIF1EPgT6WQkWWsb3mYyEO1xz9iFueK5zCtqg"
"sia://EAAV-eT8wBIF1EPgT6WQkWWsb3mYyEO1xz9iFueK5zCtqg"
"sia://EAAV-eT8wBIF1EPgT6WQkWWsb3mYyEO1xz9iFueK5zCtqg"

See Uploading A File.

Downloading From Skynet

Downloading A File

curl -A "Sia-Agent" "https://siasky.net/CABAB_1Dt0FJsxqsu_J4TodNCbCGvtFf1Uys_3EgzOlTcg" -o dst.jpg
skynet download "XABvi7JtJbQSMAcDwnUnmp2FKDPjg8_tTTFP4BwMSxVdEg" "./dst.jpg"
import { SkynetClient } from "skynet-js";

const client = new SkynetClient();
const skylink = "XABvi7JtJbQSMAcDwnUnmp2FKDPjg8_tTTFP4BwMSxVdEg";

(async () => {
  try {
    await client.downloadFile(skylink);
    // Or client.openFile(skylink) to open it in a new browser tab.
  } catch (error) {
    console.log(error);
  }
}();
const { SkynetClient } = require('@skynetlabs/skynet-nodejs');

const client = new SkynetClient();
const skylink = "XABvi7JtJbQSMAcDwnUnmp2FKDPjg8_tTTFP4BwMSxVdEg";

(async () => {
    await client.downloadFile("./dst.jpg", skylink);
    console.log('Download successful');
})();
import siaskynet as skynet

client = skynet.SkynetClient()
skylink = "XABvi7JtJbQSMAcDwnUnmp2FKDPjg8_tTTFP4BwMSxVdEg"

client.download_file("./dst.jpg", skylink)
print("Download successful")
package main

import (
    "fmt"
    skynet "github.com/SkynetLabs/go-skynet/v2"
)

const skylink = "XABvi7JtJbQSMAcDwnUnmp2FKDPjg8_tTTFP4BwMSxVdEg"
var client = skynet.New()

func main() {
    err := client.DownloadFile("./dst.go", skylink, skynet.DefaultDownloadOptions)
    if err != nil {
        panic("Something went wrong, please try again.\nError: " + err.Error())
    }
    fmt.Println("Download successful")
}

This function downloads a skylink using HTTP streaming. The call blocks until the data is received. There is a 30s default timeout applied to downloading a skylink. If the data can not be found within this 30s time constraint, a 404 error will be returned. This timeout is configurable.

Parameters (non-BrowserJS)

Field Description
path The local path where the file should be downloaded to.
skylink The skylink that should be downloaded. The skylink can contain an optional path.
onDownloadProgress Optional callback to track download progress. See Setting Additional Options.

Parameters (BrowserJS)

Field Description
skylink The skylink that should be downloaded. The skylink can contain an optional path.

Additional Options (non-BrowserJS)

None.

Additional Options (BrowserJS)

Field Description Default
path The path to use after the skylink. See the next section. ""

Response

Empty on success.

Downloading A File From An Uploaded Directory

curl -A "Sia-Agent" "https://siasky.net/XABvi7JtJbQSMAcDwnUnmp2FKDPjg8_tTTFP4BwMSxVdEg/dir2/file3" -o dst.jpg
skynet download "XABvi7JtJbQSMAcDwnUnmp2FKDPjg8_tTTFP4BwMSxVdEg/dir2/file3" "./dst.jpg"
import { SkynetClient } from "skynet-js";

const client = new SkynetClient();

// Using the skylink.
(async () => {
  try {
    const skylink = "XABvi7JtJbQSMAcDwnUnmp2FKDPjg8_tTTFP4BwMSxVdEg/dir2/file3";
    await client.downloadFile(skylink);
  } catch (error) {
    console.log(error);
  }
})();

// Using the path option.
(async () => {
  try {
    const skylink = "XABvi7JtJbQSMAcDwnUnmp2FKDPjg8_tTTFP4BwMSxVdEg";
    client.downloadFile(skylink, { path: "dir2/file3" });
  } catch (error) {
    console.log(error);
  }
})();
const { SkynetClient } = require('@skynetlabs/skynet-nodejs');

const client = new SkynetClient();

// Using the skylink.
(async () => {
  const skylink = "XABvi7JtJbQSMAcDwnUnmp2FKDPjg8_tTTFP4BwMSxVdEg/dir2/file3";
  await client.downloadFile("./dst.jpg", skylink);
  console.log('Download successful');
})();
import siaskynet as skynet

client = skynet.SkynetClient()

# Using the skylink.
skylink = "XABvi7JtJbQSMAcDwnUnmp2FKDPjg8_tTTFP4BwMSxVdEg/dir2/file3"
client.download_file("./dst.jpg", skylink)
print("Download successful")
package main

import (
    "fmt"
    skynet "github.com/SkynetLabs/go-skynet/v2"
)

var client = skynet.New()

func main() {
    // Using the skylink.
    skylink := "XABvi7JtJbQSMAcDwnUnmp2FKDPjg8_tTTFP4BwMSxVdEg/dir2/file3"
    opts := skynet.DefaultDownloadOptions
    err := client.DownloadFile("./dst.go", skylink, opts)
    if err != nil {
        panic("Something went wrong, please try again.\nError: " + err.Error())
    }
    fmt.Println("Download successful")
}

It is possible to download files from uploaded directories if their paths relative to the uploaded directory are known. There are two ways to do this.

The skylink being passed in can contain an optional path. This path can specify a directory or a particular file. If specified, only that file or directory will be returned. The examples here use the directory structure from Uploading A Directory to illustrate this.

The Path Additional Parameter (BrowserJS)

There is a caveat to the above approach: the skylink is used as-is and any special characters in the appended path are not encoded. We recommend using the additional option path to let the SDK properly encode it for you.

The path option also is easier to use if you would otherwise have to manually append the path to the skylink.

Getting Metadata

curl -I -A "Sia-Agent" "https://siasky.net/CABAB_1Dt0FJsxqsu_J4TodNCbCGvtFf1Uys_3EgzOlTcg"
# NOTE: this function has not yet been implemented for this SDK.

skynet metadata "XABvi7JtJbQSMAcDwnUnmp2FKDPjg8_tTTFP4BwMSxVdEg"
import { SkynetClient } from "skynet-js";

const client = new SkynetClient();
const skylink = "XABvi7JtJbQSMAcDwnUnmp2FKDPjg8_tTTFP4BwMSxVdEg";

async metadataExample() {
  try {
    const { metadata, contentType, skylink } = await client.getMetadata(skylink);
  } catch (error) {
    console.log(error);
  }
}
// NOTE: this function has not yet been implemented for this SDK.

const { SkynetClient } = require('@skynetlabs/skynet-nodejs');

const client = new SkynetClient();
const skylink = "XABvi7JtJbQSMAcDwnUnmp2FKDPjg8_tTTFP4BwMSxVdEg";

(async () => {
    const md = await client.getMetadata(skylink);
    console.log(`Get metadata successful: ${md}`);
})();
import siaskynet as skynet

client = skynet.SkynetClient()
skylink = "XABvi7JtJbQSMAcDwnUnmp2FKDPjg8_tTTFP4BwMSxVdEg"

md = client.get_metadata(skylink)
// NOTE: this function has not yet been implemented for this SDK.

package main

import (
    "fmt"
    skynet "github.com/SkynetLabs/go-skynet/v2"
)

const skylink = "XABvi7JtJbQSMAcDwnUnmp2FKDPjg8_tTTFP4BwMSxVdEg"
var client = skynet.New()

func main() {
    md, err := client.Metadata(skylink, skynet.DefaultMetadataOptions)
    if err != nil {
        panic("Something went wrong, please try again.\nError: " + err.Error())
    }
    fmt.Printf("Get metadata successful, metadata: %+v\n", md)
}

It is possible to get metadata about a file or directory without fetching the entire content. These API calls will perform a HEAD request that fetches the headers for the given skylink. These headers are identical to the ones that would be returned if the request had been a GET request.

Parameters

Field Description
skylink The skylink that should be downloaded. The skylink can contain an optional path. This path can specify a directory or a particular file. If specified, only that file or directory will be returned.

Additional Options

See Downloading A File.

Response

{
  "mode":     640,
  "filename": "folder",
  "subfiles": {
    "folder/file1.txt": {
      "mode":         640,
      "filename":     "folder/file1.txt",
      "contenttype":  "text/plain",
      "offset":       0,
      "len":          6
    }
  }
}
{
  "mode":     640,
  "filename": "folder",
  "subfiles": {
    "folder/file1.txt": {
      "mode":         640,
      "filename":     "folder/file1.txt",
      "contenttype":  "text/plain",
      "offset":       0,
      "len":          6
    }
  }
}
{
  "mode":     640,
  "filename": "folder",
  "subfiles": {
    "folder/file1.txt": {
      "mode":         640,
      "filename":     "folder/file1.txt",
      "contenttype":  "text/plain",
      "offset":       0,
      "len":          6
    }
  }
}
{
  "mode":     640,
  "filename": "folder",
  "subfiles": {
    "folder/file1.txt": {
      "mode":         640,
      "filename":     "folder/file1.txt",
      "contenttype":  "text/plain",
      "offset":       0,
      "len":          6
    }
  }
}
{
  "mode":     640,
  "filename": "folder",
  "subfiles": {
    "folder/file1.txt": {
      "mode":         640,
      "filename":     "folder/file1.txt",
      "contenttype":  "text/plain",
      "offset":       0,
      "len":          6
    }
  }
}
{
  "mode":     640,
  "filename": "folder",
  "subfiles": {
    "folder/file1.txt": {
      "mode":         640,
      "filename":     "folder/file1.txt",
      "contenttype":  "text/plain",
      "offset":       0,
      "len":          6
    }
  }
}
Field Description
skylink The skylink that should be downloaded. The skylink can contain an optional path. This path can specify a directory or a particular file. If specified, only that file or directory will be returned.
contentType String representing the file's content type.
metadata Object returned in the skynet-metadata header when accessing the file.

SkyDB

SkyDB is a framework that allows Skynet users to create decentralized accounts and store mutable files in those accounts. It is built on top of the Registry, where every SkyDB entry in the Registry is a skylink that contains the full data.

Getting Data From SkyDB

# There are no API endpoints for SkyDB -- you must use the registry directly.
import { SkynetClient, genKeyPairFromSeed } from "skynet-js";

const client = new SkynetClient();
const { publicKey } = genKeyPairFromSeed("this seed should be fairly long for security");

const dataKey = "myApp";

async function getJSONExample() {
  const { data, dataLink } = await client.db.getJSON(publicKey, dataKey);
}
const { SkynetClient, genKeyPairFromSeed } = require("@skynetlabs/skynet-nodejs");

const client = new SkynetClient();
const { publicKey } = genKeyPairFromSeed("this seed should be fairly long for security");

const dataKey = "myApp";

(async () => {
  const { data, dataLink } = await client.db.getJSON(publicKey, dataKey);
})();

Method

getJSON

Parameters

Field Type Description
publicKey string User's public key as a hex-encoded string. Can be generated with the genKeyPairFromSeed function.
dataKey string The key of the data to fetch for the given user.

Response

{
  data: {
    example: "This is some example JSON data."
  },
  dataLink: "sia://CABAB_1Dt0FJsxqsu_J4TodNCbCGvtFf1Uys_3EgzOlTcg"
}
{
  data: {
    example: "This is some example JSON data."
  },
  dataLink: "sia://CABAB_1Dt0FJsxqsu_J4TodNCbCGvtFf1Uys_3EgzOlTcg"
}

Setting Data On SkyDB

# There are no API endpoints for SkyDB -- you must use the registry directly.
import { SkynetClient, genKeyPairFromSeed } from "skynet-js";

const client = new SkynetClient();
const { privateKey } = genKeyPairFromSeed("this seed should be fairly long for security");

const dataKey = "myApp";
const json = { example: "This is some example JSON data." };

async function setJSONExample() {
  await client.db.setJSON(privateKey, dataKey, json);
}
const { SkynetClient, genKeyPairFromSeed } = require("@skynetlabs/skynet-nodejs");

const client = new SkynetClient();
const { publicKey } = genKeyPairFromSeed("this seed should be fairly long for security");

const dataKey = "myApp";
const json = { example: "This is some example JSON data." };

(async () => {
  await client.db.setJSON(privateKey, dataKey, json);
})();

Method

setJSON

Parameters

Field Type Description
privateKey string User's private key as a hex-encoded string. Can be generated with the genKeyPairFromSeed function.
dataKey string The key of the data to fetch for the given user.
json object The JSON object to set for the given private key and data key.

Response

Empty on success.

Registry

The registry allows getting and setting registry entries with a user's key and a data key (e.g. the name of an app).

Registry entries contain data (currently capped at 113 bytes), the data key, and a revision number. The revision number increases every time the registry entry is changed. The latest revision number, plus at least one, is required when calling setEntry. Previous revisions are not accessible once an entry has been overwritten with a higher revision number.

Getting Data From The Registry

curl -X GET -G -A "Sia-Agent" -u "":<apipassword> -d "publickey=ed25519%3Ab4f9e43178222cf33bd4432dc1eca49499397ecd1f7de23b568f3fa1e72e5c7c" -d "datakey=79c05b4b67764ad99a7976a7d2fb1cfce4f196ea217ef0356af042cb5492bd5d" "https://siasky.net/skynet/registry"
import { SkynetClient, genKeyPairFromSeed } from "skynet-js";

const client = new SkynetClient();
const { publicKey } = genKeyPairFromSeed("this seed should be fairly long for security");

const dataKey = "foo";

async function getEntryExample() {
  const { entry, signature } = await client.registry.getEntry(publicKey, dataKey);
}
const { SkynetClient, genKeyPairFromSeed } = require("@skynetlabs/skynet-nodejs");

const client = new SkynetClient();
const { publicKey } = genKeyPairFromSeed("this seed should be fairly long for security");

const dataKey = "foo";

(async () => {
  const { entry, signature } = await client.registry.getEntry(publicKey, dataKey);
})();

Method

getEntry

Parameters

Field Type Description
publicKey string User's public key as a hex-encoded string. Can be generated with the genKeyPairFromSeed function.
dataKey string The key of the data to fetch for the given user.
customOptions Object Custom options to pass into this method. See below.

Optional Parameters

Field Description Default
endpointGetEntry The relative URL path of the portal endpoint to contact. /skynet/registry
hashedDataKeyHex Whether the data key is already hashed and in hex format. If not, we hash the data key. false

Response

{
  entry: {
    datakey: "foo",
    data: Uint8Array([ 98, 97, 114 ]), // corresponds to string "bar"
    revision: BigInt(0)
  },
  signature: "788dddf5232807611557a3dc0fa5f34012c2650526ba91d55411a2b04ba56164"
}
{
  entry: {
    datakey: "foo",
    data: Uint8Array([ 98, 97, 114 ]), // corresponds to string "bar"
    revision: BigInt(0)
  },
  signature: "788dddf5232807611557a3dc0fa5f34012c2650526ba91d55411a2b04ba56164"
}

Setting Data On The Registry

curl -L -X POST -d '{"publickey":{"algorithm":"ed25519","key":[180,249,228,49,120,34,44,243,59,212,67,45,193,236,164,148,153,57,126,205,31,125,226,59,86,143,63,161,231,46,92,124]},"datakey":"79c05b4b67764ad99a7976a7d2fb1cfce4f196ea217ef0356af042cb5492bd5d","revision":7,"data":[65,65,65,118,74,68,105,95,101,102,75,76,101,99,83,65,112,78,56,110,89,113,84,110,122,83,106,76,116,102,66,55,80,99,69,110,81,112,75,75,101,79,88,100,54,119],"signature":[2,105,4,22,162,190,49,191,180,70,89,91,4,177,81,87,79,175,225,40,224,69,173,193,113,227,225,106,48,121,221,1,119,92,253,115,198,90,142,167,2,108,245,249,217,99,112,174,87,117,213,5,105,162,191,242,129,103,244,126,136,68,33,11]}' "https://siasky.net/skynet/registry"
import { SkynetClient, genKeyPairFromSeed, stringToUint8ArrayUtf8 } from "skynet-js";

const client = new SkynetClient();
const { privateKey } = genKeyPairFromSeed("this seed should be fairly long for security");

const dataKey = "foo";
const data = stringToUint8ArrayUtf8("bar");
const revision = BigInt(0);
const entry = { dataKey, data, revision };

async function setEntryExample() {
  await client.registry.setEntry(privateKey, entry);
}
const { SkynetClient, genKeyPairFromSeed, stringToUint8ArrayUtf8 } = require("@skynetlabs/skynet-nodejs");

const client = new SkynetClient();
const { privateKey } = genKeyPairFromSeed("this seed should be fairly long for security");

const dataKey = "foo";
const data = stringToUint8ArrayUtf8("bar");
const revision = BigInt(0);
const entry = { dataKey, data, revision };

(async () => {
  await client.registry.setEntry(privateKey, entry);
})();

Method

setEntry

Parameters

Field Type Description
privateKey string User's private key as a hex-encoded string. Can be generated with the genKeyPairFromSeed function or with PKI in the node-forge library on NPM. Should be kept secret.
entry RegistryEntry The registry entry to set. See below.
customOptions Object Custom options to pass into this method. See below.

Optional Parameters

Field Description Default
endpointSetEntry The relative URL path of the portal endpoint to contact. /skynet/registry
hashedDataKeyHex Whether the data key is already hashed and in hex format. If not, we hash the data key. false

RegistryEntry

This object corresponds to a versioned entry in the registry.

Field Type Description
datakey string The key of the data for the given entry.
data string The data for this entry. Capped at 113 bytes, but can be a skylink or an HNS domain.
revision number The revision number of this entry. It must be at least 1 more than the latest revision number, or 0 if the entry doesn't exist.

Response

Empty on success.

Getting The Entry URL

import { SkynetClient, genKeyPairFromSeed } from "skynet-js";

const client = new SkynetClient();
const { publicKey } = genKeyPairFromSeed("this seed should be fairly long for security");

const dataKey = "foo";

async function getEntryUrlExample() {
  const url = await client.registry.getEntryUrl(publicKey, dataKey);
}
const { SkynetClient, genKeyPairFromSeed } = require("@skynetlabs/skynet-nodejs");

const client = new SkynetClient();
const { publicKey } = genKeyPairFromSeed("this seed should be fairly long for security");

const dataKey = "foo";

(async () => {
  const url = await client.registry.getEntryUrl(publicKey, dataKey);
})();

Method

getEntryUrl

Parameters

See Getting Data From The Registry.

Response

"https://siasky.net/skynet/registry?publickey=ed25519%3Ac1197e1275fbf570d21dde01a00af83ed4a743d1884e4a09cebce0dd21ae254c&datakey=7c96a0537ab2aaac9cfe0eca217732f4e10791625b4ab4c17e4d91c8078713b9"
"https://siasky.net/skynet/registry?publickey=ed25519%3Ac1197e1275fbf570d21dde01a00af83ed4a743d1884e4a09cebce0dd21ae254c&datakey=7c96a0537ab2aaac9cfe0eca217732f4e10791625b4ab4c17e4d91c8078713b9"

MySky

MySky is a decentralized identity protocol that gives users their own empire of data within Skynet. Everything that happens inside of that empire is under the control of the user, but it can also be visited by anyone else and shared around freely.

MySky provides a standard for declaring data in a way that makes it easy for anyone in the world to discover that data. At the same time, it provides standards for protecting that data, ensuring only data which is meant to be found is discoverable by others.

MySky also provides simple yet powerful tooling that enables applications to make use of the same data. For example, the profile information of a user like their preferred name and avatar can be placed in a shared folder, and then safely used and updated by all of the user’s applications. This tooling extends to any type of data.

See this blog post for more information!

Initializing MySky

import { SkynetClient } from "skynet-js";

const client = new SkynetClient();
const hostApp = "host-app.hns";

async function mySkyExample() {
  try {
    // Initialize MySky.
    const mySky = await client.loadMySky(hostApp);
  } catch (error) {
    console.log(error)
  }
}

MySky can be initialized using the client. This creates the MySky iframe which connects to the MySky handshake domain and takes care of operations such as logging in silently, signing registry entries, getting the user ID, and logging out.

Parameters

Field Description
skappDomain (Optional) The data domain for the skapp. MySky will automatically request permissions for this domain.

Additional Options

Field Description Default
dev Whether to run MySky in dev mode (all requested permissions are granted by default, but production MySky data is not accessible). false
debug Whether to run MySky and all DACs with debugging messages enabled. DACs will run with the ?debug=true query parameter set which they can use for their own purposes. false

Adding DACs

import { SkynetClient } from "skynet-js";
import { ContentRecordDAC } from "@skynetlabs/content-record-library";

const client = new SkynetClient();
const hostApp = "host-app.hns";

async function loadDacsExample() {
  try {
    const mySky = await client.loadMySky(hostApp);

    // Initialize DAC, auto-adding permissions.
    const dac = new ContentRecordDAC()
    await mySky.loadDacs(dac);
  } catch (error) {
    console.log(error)
  }
}

This method loads the given DACs. They must have been instantiated previously using the DAC library's constructor, which creates the iframe for the DAC. DACs must also be loaded before they are ready to use.

Loading a DAC automatically adds its requested permissions to the permissions list, so we do not need to add permissions for it manually. The user may have to approve the DAC's permissions if it requests nonstandard permissions.

For an example of a DAC and its usage, see the content-record DAC library provided by SkynetLabs.

Method

mySky.loadDacs

Parameters

Field Type Description
...dacs ...DacLibrary[] One or more DACs. If you have an array, you can pass it in like ...dacsArray.

Adding Custom Permissions

import { Permission, PermCategory, PermType, SkynetClient } from "skynet-js";

const client = new SkynetClient();

async function addPermissionsExample() {
  try {
    const mySky = await client.loadMySky();

    // Add additional needed permissions before checkLogin.
    // Can be Permissions object or list of Permissions objects
    await mySky.addPermissions(new Permission("requestor.hns", "domain.hns/path", PermCategory.Hidden, PermType.Write));
  } catch (error) {
    console.log(error)
  }
}

This method lets you add one or more custom permissions, which are then requested on login and must be approved by the user. It is useful to call this if you want access to e.g. the data domain of a skapp other than your own.

Note that the requestor must be set manually in the Permission object. For example, if you know that your skapp lives at requestor.hns, the requestor should be "requestor.hns".

You can request permission for either the entire domain or just a specific path. For more information about valid paths please consult MySky Paths.

Method

mySky.addPermissions

Parameters

Field Type Description
...permissions ...Permission[] One or more permissions. If you have an array, you can pass it in like ...permsArray.

Permission

This object corresponds to a permission for a given requestor domain for the given path.

Field Type Description
requestor string The requestor domain.
path string The path to grant permission for. Can be a directory or a root domain to grant permissions to all subpaths.
category PermCategory The permission category (see below).
permType PermType The permission type (see below).

PermCategory

Permission category enum. All values are numbers. If you're not using Typescript, you can use the provided constant instead of the enum value.

Value Description Constant
PermCategory.Discoverable Permission for discoverable files. PermDiscoverable
PermCategory.Hidden Permission for hidden files. PermHidden
PermCategory.LegacySkyID Permission for legacy SkyID files. PermLegacySkyID

PermType

Permission type enum. All values are numbers. If you're not using Typescript, you can use the provided constant instead of the enum value.

Value Description Constant
PermType.Read Permission for reading. PermRead
PermType.Write Permission for writing. PermWrite

MySky Paths

When calling methods to write or get data, a path must be passed in. Paths can point to a file or a directory; examples include skyfeed.hns/path/to/file.json and riftapp.hns/some/directory.

All data lives at a certain path. Likewise, all paths have associated permissions. So a skapp cannot access a path, and its data, if it doesn't have permission with MySky to do so.

Path format

Paths have a specific format that they must follow. All paths must start with a data domain, which usually is the domain of a skapp on Skynet, such as riftapp.hns. Note: this should not include the portal, such as riftapp.hns.siasky.net. Use await client.extractDomain(url) if you have a full URL and need the domain.

The path may just be a data domain, for example when a skapp is requesting permissions for all files at a domain. The path may also contain a path to a file or directory after the data domain, with each path element separated by a forward slash.

Examples

Valid paths include:

"app.hns"
"app.hns/"
"app.hns/path/file.json"
"app.hns/path/"
"app.hns/path"

Invalid paths include:

"/app.hns"
"/app.hns/path/"
"/"
""

Logging In

In order to actually use MySky the user will need to be logged in to a MySky account. Most of the time, the user should already be logged in -- a single sign-in should log a user into all MySky-compatible apps across the Skynet ecosystem.

For that reason, your app should first attempt a silent login. If that fails, you can initiate a popup login, where the user has the chance to enter his login details or sign up. Note that this must be done only on button click, or the popup window will be blocked by most popup blockers!

Check Login (Silent Login)

import { SkynetClient } from "skynet-js";

const client = new SkynetClient();
const hostApp = "host-app.hns";

async function checkLoginExample() {
  try {
    const mySky = await client.loadMySky(hostApp);

    // Try to login silently, requesting permissions for host-app.hns.
    const loggedIn = await mySky.checkLogin();
  } catch (error) {
    console.log(error)
  }
}

Makes an initial silent login attempt with the previously set permissions.

Method

mySky.checkLogin

Parameters

None. Any requested permissions should be set earlier through mySky.addPermissions.

Response

true

Request Login Access (Popup Login)

import { SkynetClient } from "skynet-js";

const client = new SkynetClient();
const hostApp = "host-app.hns";

async function requestLoginAccessExample() {
  try {
    const mySky = await client.loadMySky(hostApp);

    const loggedIn = await mySky.checkLogin();

    // Add button action for login.
    if (!loggedIn) {
      document
        .getElementByID("login-button")
        .addEventListener("click", (e) => {
          mySky.requestLoginAccess();
        });
    }
  } catch (error) {
    console.log(error);
  }
}

Popup login method to call if the silent login failed.

Method

mySky.requestLoginAccess

Parameters

None. Any requested permissions should be set earlier through mySky.addPermissions.

true

Logout

async function logoutExample() {
  try {
    await mySky.logout();
  } catch (error) {
    console.log(error)
  }
}

Logout method that can be called if the user is logged in. Note that the MySky instance cannot be used to access data until the user logs in again.

Method

mySky.logout

Parameters

None.

Getting And Setting User Data

Once a user is logged in, the app will be able to get and set data in the user's filesystem, as long as the app has permissions for the filepath it is trying to access as well as for the type of data (Discoverable or Hidden) being read or written.

Getting Discoverable JSON

// Assume we have a logged-in mysky instance from above

async function getJSONExample() {
  try {
    // Get discoverable JSON data from the given path.
    const { data, dataLink } = await mySky.getJSON("app.hns/path/file.json");
  } catch (error) {
    console.log(error)
  }
}

This method gets discoverable JSON. It does not require permissions because all discoverable data can be read publicly.

Method

mySky.getJSON

Parameters

Field Type Description
path string The data path.
customOptions Object Custom options to pass into this method. See below.

Optional Parameters

Field Description Default
cachedDataLink The latest known data link. If the data link at the registry entry is the same, then skip downloading the file again. undefined
endpointDownload The relative URL path of the portal endpoint to contact for downloads. /
endpointGetEntry The relative URL path of the portal endpoint to contact for the registry. /skynet/registry

Response

{
  data: {
    example: "This is some example JSON data."
  },
  dataLink: "sia://CABAB_1Dt0FJsxqsu_J4TodNCbCGvtFf1Uys_3EgzOlTcg"
}

Setting Discoverable JSON

// Assume we have a logged-in mysky instance from above

async function setJSONExample() {
  try {
    // Set discoverable JSON data at the given path. The return type is the same as getJSON.
    const { data, dataLink } = await mySky.setJSON("app.hns/path/file.json", { message: "hello" });
  } catch (error) {
    console.log(error)
  }
}

This method sets discoverable JSON. It requires Discoverable Write permissions for the given path.

Method

mySky.setJSON

Parameters

Field Type Description
path string The data path.
json Object The data to set.
customOptions Object Custom options to pass into this method. See below.

Optional Parameters

Field Description Default
endpointGetEntry The relative URL path of the portal endpoint to contact. /skynet/registry
endpointSetEntry The relative URL path of the portal endpoint to contact. /skynet/registry

Response

{
  data: {
    example: "This is some example JSON data."
  },
  dataLink: "sia://CABAB_1Dt0FJsxqsu_J4TodNCbCGvtFf1Uys_3EgzOlTcg"
}

Getting Encrypted JSON

// Assume we have a logged-in mysky instance from above

async function getJSONEncryptedExample() {
  try {
    // Get encrypted JSON data from the given path.
    const { data } = await mySky.getJSONEncrypted("app.hns/path/file.json");
  } catch (error) {
    console.log(error)
  }
}

This method gets encrypted JSON. It requires Hidden Read permissions for the given path.

Method

mySky.getJSONEncrypted

Parameters

Field Type Description
path string The data path.
customOptions Object Custom options to pass into this method. See below.

Optional Parameters

Field Description Default
cachedDataLink The latest known data link. If the data link at the registry entry is the same, then skip downloading the file again. undefined
endpointDownload The relative URL path of the portal endpoint to contact for downloads. /
endpointGetEntry The relative URL path of the portal endpoint to contact. /skynet/registry

Response

{
  data: {
    example: "This is some example JSON data."
  }
}

Setting Encrypted JSON

// Assume we have a logged-in mysky instance from above

async function setJSONEncryptedExample() {
  try {
    // Set encrypted JSON data at the given path. The return type is the same as getJSONEncrypted.
    const { data } = await mySky.setJSONEncrypted("app.hns/path/file.json", { message: "hello" });
  } catch (error) {
    console.log(error)
  }
}

This method sets encrypted JSON. It requires Hidden Write permissions for the given path.

Method

mySky.setJSONEncrypted

Parameters

Field Type Description
path string The data path.
json Object The data to set.
customOptions Object Custom options to pass into this method. See below.

Optional Parameters

Field Description Default
endpointGetEntry The relative URL path of the portal endpoint to contact. /skynet/registry
endpointSetEntry The relative URL path of the portal endpoint to contact. /skynet/registry

Response

{
  data: {
    example: "This is some example JSON data."
  }
}

Crypto Utilities

This page documents cryptographic utilities for generating public and private keys as well as secure seeds. Private keys should, as the name implies, be kept private. The seeds should also be handled carefully as they can be used to derive public/private key pairs.

Generating Key Pairs And Seeds

import { genKeyPairAndSeed } from "skynet-js";

const { publicKey, privateKey, seed } = genKeyPairAndSeed();

If you do not yet have a seed, you can generate one with this function and it will also generate an associated key pair. The seed is generated using strong, cryptographic-grade randomness. It is recommended that you only store the seed for later use and re-derive the key pair with genKeyPairFromSeed, and that you store it securely.

Function

genKeyPairAndSeed

Parameters

None

Response

{
  publicKey: "f8a7da8324fabb9d57bb32c59c48d4ba304d08ee5f1297a46836cf841da71c80",
  privateKey: "c404ff07fba961000dfb25ece7477f45b109b50a5169a45f3fb239343002c1cff8a7da8324fabb9d57bb32c59c48d4ba304d08ee5f1297a46836cf841da71c80",
  seed: "c1197e1275fbf570d21dde01a00af83ed4a743d1884e4a09cebce0dd21ae254c"
}

Generating Key Pairs From Seeds

import { genKeyPairFromSeed } from "skynet-js";

const { publicKey, privateKey } = genKeyPairFromSeed("this seed should be fairly long for security");

If you already have a seed (e.g. from a previous call to genKeyPairAndSeed) you can deterministically derive the same keypair.

Function

genKeyPairFromSeed

Parameters

Field Type Description
seed string The seed that should be used to generate a deterministic keypair. Can be a long and secure passphrase.

Response

{
  publicKey: "f8a7da8324fabb9d57bb32c59c48d4ba304d08ee5f1297a46836cf841da71c80",
  privateKey: "c404ff07fba961000dfb25ece7477f45b109b50a5169a45f3fb239343002c1cff8a7da8324fabb9d57bb32c59c48d4ba304d08ee5f1297a46836cf841da71c80",
}

Deriving Child Seeds

import { deriveChildSeed, genKeyPairAndSeed } from "skynet-js";

const { publicKey, privateKey, masterSeed } = genKeyPairAndSeed();
const childSeed = deriveChildSeed(masterSeed, "foo");

This function can be used to derive a child seed from a given master seed and subseed. For example, the master seed can be a long and secure passphrase while the subseed can be the name of an application.

Function

deriveChildSeed

Parameters

Field Type Description
masterSeed string The master seed that should be used to generate a deterministic keypair. Can be a long and secure passphrase.
seed string The subseed that, in combination with the master seed, results in a deterministic keypair.

Response

"f79cd29b92124c80a662f3085bba98955f7defbaed6e58ea891b901dc99aafc0"

Handshake

Handshake is a protocol which allows the creation of update-able content with persistent links, backed by the Skynet infrastructure. For more information on using Handshake with Skynet, please see this blog post.

The SDKs contain support for downloading from Handshake domains as well as for resolving Handshake domains to retrieve the underlying skylinks.

Downloading Handshake Files

curl -A "Sia-Agent" "https://siasky.net/hns/doesn"
# NOTE: this function has not yet been implemented for this SDK.

skynet hns download "doesn" "./dst.html"
import { SkynetClient } from "skynet-js";

const client = new SkynetClient();
const domain = "doesn";

try {
  client.downloadFileHns(domain);
  // Or client.openFileHns(domain) to open it in a new browser tab.
} catch (error) {
  console.log(error);
}
# NOTE: this function has not yet been implemented for this SDK.

const { SkynetClient } = require('@skynetlabs/skynet-nodejs');

const client = new SkynetClient();
const domain = "doesn";

(async () => {
    await client.downloadFileHns("./dst.html", domain);
    console.log('Handshake download successful');
})();
# NOTE: this function has not yet been implemented for this SDK.

import siaskynet as skynet

client = skynet.SkynetClient()
domain = "doesn"

client.download_file_hns("./dst.html", domain)
print("Handshake download successful")
// NOTE: this function has not yet been implemented for this SDK.

package main

import (
    "fmt"
    skynet "github.com/SkynetLabs/go-skynet/v2"
)

var client = skynet.New()

func main() {
  const domain = "doesn"

    err := client.DownloadFileHns("./dst.html", domain, skynet.DefaultDownloadHnsOptions)
    if err != nil {
        panic("Something went wrong, please try again.\nError: " + err.Error())
    }
    fmt.Println("Handshake download successful")
}

This function downloads a file from a given Handshake domain on the portal's /hns endpoint. To give an example, the full URL of the Sia-controlled Handshake domain doesn is https://siasky.net/hns/doesn.

Parameters

Field Description
path The local path where the file should be downloaded to.
domain The Handshake domain that should be downloaded.

Browser JS:

Field Description
domain The Handshake domain that should be downloaded.

Response

Empty on success.

Resolving Handshake Domains

curl -A "Sia-Agent" "https://siasky.net/hnsres/doesn"
# NOTE: this function has not yet been implemented for this SDK.

skynet hns resolve "doesn"
import { SkynetClient } from "skynet-js";

const client = new SkynetClient();
const domain = "doesn";

try {
  const data = client.resolveHns(domain);
} catch (error) {
  console.log(error);
}
# NOTE: this function has not yet been implemented for this SDK.

const { SkynetClient } = require('@skynetlabs/skynet-nodejs');

const client = new SkynetClient();
const domain = "doesn";

(async () => {
    const data = await client.resolveHns(domain);
    console.log('Handshake resolve successful');
})();
# NOTE: this function has not yet been implemented for this SDK.

import siaskynet as skynet

client = skynet.SkynetClient()
domain = "doesn"

data = client.resolve_hns(domain)
print("Handshake resolve successful")
// NOTE: this function has not yet been implemented for this SDK.

package main

import (
    "fmt"
    skynet "github.com/SkynetLabs/go-skynet/v2"
)

const domain = "doesn"
var client = skynet.New()

func main() {
    err := client.ResolveHns(domain, skynet.DefaultResolveHnsOptions)
    if err != nil {
        panic("Something went wrong, please try again.\nError: " + err.Error())
    }
    fmt.Println("Handshake resolve successful")
}

This function resolves a given Handshake domain and returns its TXT record. In the context of Skynet, this should contain a skylink field. For example, a request to https://siasky.net/hnsres/doesn returns only the data {"skylink":"sia://IAC6CkhNYuWZqMVr1gob1B6tPg4MrBGRzTaDvAIAeu9A9w"}.

Parameters

Field Description
domain The Handshake domain that should be resolved.

Response

{"skylink":"sia://IAC6CkhNYuWZqMVr1gob1B6tPg4MrBGRzTaDvAIAeu9A9w"}
Coming Soon
{"skylink":"sia://IAC6CkhNYuWZqMVr1gob1B6tPg4MrBGRzTaDvAIAeu9A9w"}
{"skylink":"sia://IAC6CkhNYuWZqMVr1gob1B6tPg4MrBGRzTaDvAIAeu9A9w"}
{"skylink":"sia://IAC6CkhNYuWZqMVr1gob1B6tPg4MrBGRzTaDvAIAeu9A9w"}
{"skylink":"sia://IAC6CkhNYuWZqMVr1gob1B6tPg4MrBGRzTaDvAIAeu9A9w"}

The full TXT record containing the skylink is returned.

Browser JS API

The following are some methods and utilities that only make sense in the browser, and thus are only provided by the Browser JS SDK.

Opening A File

import { SkynetClient } from "skynet-js";

const client = new SkynetClient();
const skylink = "XABvi7JtJbQSMAcDwnUnmp2FKDPjg8_tTTFP4BwMSxVdEg";

(async () => {
  try {
    await client.openFile(skylink);
  } catch (error) {
    console.log(error);
  }
})();

Use the client to open a skylink in a new browser tab. Browsers support opening natively only limited file extensions like .html or .jpg and will fallback to downloading the file.

Parameters

See Downloading A File.

Response

Empty on success.

Loading A File's Contents

import { SkynetClient } from "skynet-js";

const client = new SkynetClient();
const skylink = "AACJjVpOpsZ6c9PBwOYvxTtDc_nmrGxTTEomHDBEEfvRhA"; // JSON File

try {
  const { data, contentType, metadata, skylink } = await client.getFileContent(skylink);
  console.log(data.fruit) // prints 'Apple'
} catch (error) {
  console.log(error);
}

Use the client to load a skylink's content for use by application.

Parameters

Field Description
skylink The skylink that should be downloaded. The skylink can contain an optional path.

Additional Options

See Downloading A File.

Response

{
  data: { fruit: 'Apple', size: 'Large', color: 'Red' },
  contentType: 'application/json',
  metadata: {
    filename: 'example_1.json',
    length: 65,
    subfiles: { 'example_1.json': [Object] }
  },
  skylink: 'sia:AACJjVpOpsZ6c9PBwOYvxTtDc_nmrGxTTEomHDBEEfvRhA'
}
Field Description
data Data returned in the response body when doing a GET request for the skylink.
skylink This is the skylink that can be used when downloading to retrieve the file that has been uploaded. It is a 46-character base64 encoded string that consists of the merkle root, offset, fetch size, and Skylink version which can be used to access the content.
contentType String representing the file's content type.
metadata Object returned in the skynet-metadata header when accessing the file

Getting The Download URL

import { SkynetClient } from "skynet-js";

const client = new SkynetClient();
const skylink = "XABvi7JtJbQSMAcDwnUnmp2FKDPjg8_tTTFP4BwMSxVdEg";

(async () => {
  try {
    const url = await client.getSkylinkUrl(skylink);
  } catch (error) {
    console.log(error);
  }
})();

Use the client to generate a direct skylink url.

Parameters

See Downloading A File.

Additional Options

Field Description Default
download Option to include download directive in the url that will force a download when used. false

Response

"https://siasky.net/XABvi7JtJbQSMAcDwnUnmp2FKDPjg8_tTTFP4BwMSxVdEg"
Field Description
url The URL for the given skylink on the client portal.

Getting A Handshake URL

import { SkynetClient } from "skynet-js";

const client = new SkynetClient();
const domain = "doesn";

(async () {
  try {
    const url = await client.getHnsUrl(domain);
  } catch (error) {
    console.log(error);
  }
}();

Use the client to generate a direct Handshake url from a Handshake domain.

Parameters

See Downloading Handshake Files.

Additional Options

Field Description Default
download Option to include download directive in the url that will force a download when used. false

Response

"https://siasky.net/hns/doesn"
Field Description
url The URL for the given Handshake domain on the client portal.

Getting A Handshake Resolver URL

import { SkynetClient } from "skynet-js";

const client = new SkynetClient();
const domain = "doesn";

(async () {
  try {
    const url = await client.getHnsresUrl(domain);
  } catch (error) {
    console.log(error);
  }
})();

Use the client to generate a direct Handshake Resolver url from a Handshake domain.

Parameters

See Resolving Handshake Domains.

Additional Options

Field Description Default
download Option to include download directive in the url that will force a download when used. false

Response

"https://siasky.net/hnsres/doesn"
Field Description
url The URL for the given Handshake Resolver domain on the client portal.
import { parseSkylink } from "skynet-js";

const client = new SkynetClient();
const uri = "sia://XABvi7JtJbQSMAcDwnUnmp2FKDPjg8_tTTFP4BwMSxVdEg";

try {
  const skylink = parseSkylink(uri);
} catch (error) {
  console.log(error);
}

Extract a skylink from a string.

Parameters

Field Description
string The string to extract a skylink from.

Currently supported string types are:

Response

"CABAB_1Dt0FJsxqsu_J4TodNCbCGvtFf1Uys_3EgzOlTcg"
Field Description
skylink See Uploading A File.

API Authentication

Skynet Portal Authentication

curl -X POST -H "Skynet-Api-Key: <token>" "https://skynetfree.net/skynet/skyfile"
skynet upload image.jpg --skynet-api-key foobar
import { SkynetClient } from "skynet-js";

const client = new SkynetClient();

async function authenticationExample() {
  const { skylink } = await client.uploadFile(file, { skynetApiKey: "foobar" });
}
const { SkynetClient } = require('@skynetlabs/skynet-nodejs');

const client = new SkynetClient();

(async () => {
    const skylink = await client.uploadFile("./image.jpg", { skynetApiKey: "foobar" });
    console.log(`Upload successful, skylink: ${skylink}`);
})();
import siaskynet as skynet

client = skynet.SkynetClient()

skylink = client.upload_file("image.jpg", { "skynet_api_key": "foobar" })
print("Upload successful, skylink: " + skylink)
package main

import (
    "fmt"
    skynet "github.com/SkynetLabs/go-skynet/v2"
)

var client = skynet.New()

func main() {
    opts := skynet.DefaultUploadOptions
    opts.SkynetAPIKey = "foobar"
    skylink, err := client.UploadFile("./image.jpg", opts)
    if err != nil {
        panic("Unable to upload: " + err.Error())
    }
    fmt.Printf("Upload successful, skylink: %v\n", skylink)
}

Certain Skynet portals are only available to signed-in or paid users. To access these programmatically, you will need an API key. You can get an API key for your user and pass it either to individual calls or to the Skynet client.

Warning: Be careful not to leak your API key as it will allow malicious attackers to compromise your account!

Obtaining a Skynet API Key

Instructions

Local Node Authentication

curl -X POST -A "Sia-Agent" --user "":"foobar" \
  "https://siasky.net/skynet/skyfile" -F '[email protected]'
skynet upload "./image.jpg" --api-key "foobar" --custom-user-agent "Sia-Agent"
import { SkynetClient } from "skynet-js";

const client = new SkynetClient();

async function authenticationExample() {
  try {
    const { skylink } = await client.uploadFile(
      file,
      { APIKey: "foobar", customUserAgent: "Sia-Agent" }
    );
  } catch (error) {
    console.log(error);
  }
}
const { SkynetClient } = require('@skynetlabs/skynet-nodejs');

const client = new SkynetClient();

(async () => {
    const skylink = await client.uploadFile(
    "./image.jpg",
    { APIKey: "foobar", customUserAgent: "Sia-Agent" }
  );
    console.log(`Upload successful, skylink: ${skylink}`);
})();
import siaskynet as skynet

client = skynet.SkynetClient();

skylink = client.upload_file(
  "image.jpg",
  {"api_key": "foobar", "custom_user_agent": "Sia-Agent"}
)
print("Upload successful, skylink: " + skylink)
package main

import (
    "fmt"
    skynet "github.com/SkynetLabs/go-skynet/v2"
)

var client = skynet.New()

func main() {
    opts := skynet.DefaultUploadOptions
    opts.APIKey = "foobar"
  opts.CustomUserAgent = "Sia-Agent"
    skylink, err := client.UploadFile("./image.jpg", opts)
    if err != nil {
        panic("Unable to upload: " + err.Error())
    }
    fmt.Printf("Upload successful, skylink: %v\n", skylink)
}

When making requests to a locally-running Skynet node (skyd instance), you may authenticate yourself by setting the APIKey custom option. You would have to set the portal to localhost.

Setting The User Agent

The node may also require that certain sensitive requests contain a custom user agent header, usually Sia-Agent. This is for security purposes, as otherwise a malicious website could make requests to your local portal on your behalf and steal coins.

We want this to be an opt-in for now, so Sia-Agent is not currently the default. You may change the user agent header by setting the customUserAgent custom option. See Setting Additional Options.

More Information

For more information about authentication on local skyd instances please see the Sia Docs.

Updating From v3 (BrowserJS)

Changelog

Table of Contents

skynet-js version v4 is a breaking change coming from v3. In addition to some major changes such as adding MySky and Data Access Controllers (DACs), this change contains many quality-of-life changes. These include making some non-async methods async, removing revision from SkyDB, and changing the return types of the SkyDB methods.

We understand that updating your code to accommodate breaking changes is inconvenient, so we put together this guide which we hope will be helpful.

Upgrading BrowserJS

npm install skynet-js

To upgrade to v4 (see right):

Note that just npm install will not work. This is by design: you must explicitly opt-in to new major versions as they contain breaking changes.

Installing a DAC

To use MySky to its fullest potential you may additionally want to install a DAC. These are used through libraries which can be installed and imported from like regular libraries.

npm install @skynetlabs/content-record-library

For example, to install SkynetLabs's official Content Record DAC (see right):

Usage instructions can be found here.

Breaking Changes

const { data, revision } = await client.db.getJSON(publicKey, dataKey);

=>

const { data, dataLink } = await client.db.getJSON(publicKey, dataKey);

Revision numbers have been removed from SkyDB and the return value of getJSON changed according. See right.

Required code changes

Please do a search for getJSON in your code and double-check that you are not using the returned revision, and remove it if you are. You no longer need to pass revision into setJSON (see below).

SkyDB setJSON no longer accepts an optional revision number

await client.db.setJSON(privateKey, dataKey, json, revision, customOptions);

=>

await client.db.setJSON(privateKey, dataKey, json, customOptions);

Note: setJSON also has been changed to have the same return type as getJSON.

Required code changes

Please do a search for setJSON in your code and double-check that you are not passing in revision, and remove it if you are. You no longer need to pass revision into setJSON.

getSkylinkUrl, downloadFile, openFile, and the HNS equivalents are all now async

const skylinkUrl = client.getSkylinkUrl(skylink);
downloadFile(skylink);
openFile(skylink);

=>

const skylinkUrl = await client.getSkylinkUrl(skylink);
await downloadFile(skylink);
await openFile(skylink);
// ... same with HNS versions of these methods.

The Skynet Client now silently fetches the portal API URL the first time it is needed (you can trigger this manually with the new client.initPortalUrl()). This means that all methods that take skylinks or operate on them internally must be async.

Required code changes

Please do a search for affected functions in your code and make sure you prepend async if you expect the functions to be blocking.

portalUrl is now an async method instead of a variable

const portalUrl = client.portalUrl;

=>

const portalUrl = await client.portalUrl();

In line with the above change, client.portalUrl is now an async method as it will make a request for the portal API URL the first time it is called.

Required code changes

Please do a search for where you may be using client.portalUrl in your code and make the necessary changes, as shown on the right.

Renamed RegistryEntry datakey to dataKey for consistency

const dataKey = "abcd";
const entry = { datakey: dataKey, data, revision };

=>

const dataKey = "abcd";
const entry = { dataKey, data, revision };

The RegistryEntry "datakey" field has been renamed to "dataKey" for consistency with the rest of the codebase.

Required code changes

Please do a search for any instance of datakey (case-sensitive) in your code and replace it with dataKey.

The getEntry timeout option has been removed as it no longer has an effect

await client.registry.getEntry(publicKey, dataKey, { timeout: 10 });

=>

await client.registry.getEntry(publicKey, dataKey);

The timeout optional option has been removed.

Required code changes

Please do a search for "timeout" in your code and remove it if using as an option to getEntry. You will get a runtime error if you don't make the change.

getFileContent and getFileContentHns no longer return metadata

const { skylink, metadata } = await client.getFileContent(skylink);

=>

const { skylink } = await client.getFileContent(skylink);
const { metadata } = await client.getMetadata(skylink);

The metadata was too large to be returned in headers. It now has its own method and can only be obtained with getMetadata.

Required code changes

The metadata is no longer returned from these methods. Use the dedicated getMetadata method to get the metadata. See right.

getMetadata no longer returns contentType

The getMetadata method no longer returns a separate contentType field as it is now a part of the metadata itself.

Registry entries now take data that is type Uint8Array instead of string

const entry = RegistryEntry { dataKey, data: "hello", revision };
await client.registry.setEntry(privateKey, entry);
const { entry } = client.registry.getEntry(publicKey, dataKey);
const data = entry.data; // Previously, this was a string.

=>

import { stringToUint8ArrayUtf8, uint8ArrayToStringUtf8 } from "skynet-js";

const entry = RegistryEntry { dataKey, data: stringToUint8ArrayUtf8("hello"), revision };
await client.registry.setEntry(privateKey, entry);
const { entry } = client.registry.getEntry(publicKey, dataKey);
const data = uint8ArrayToStringUtf8(entry.data); // Now, this is a Uint8Array

The type of RegistryEntry.data has changed. In order to maintain compatibility, please make sure you convert any string data to Uint8Array before calling setEntry, and that you convert Uint8Array back to a string on the returned data after calling getEntry.

You should still be able to read entries set using older versions of the SDK, including SkyDB entries, but older versions may no longer be able to read entries set using the new version.

Required code changes

Please use the provided conversion functions stringToUint8ArrayUtf8 and uint8ArrayToStringUtf8 if you wish to continue using strings instead of raw bytes when working with entry data. See right.

Please be aware of this change when working with any skylinks returned from the API.

Previously, some skylinks returned from SkyDB did not contain either the sia: or the sia:// prefix. Please be aware that they now start with the new prefix (see above).