Build / Node guide

Run a Stellarium
node.

A node is your piece of the Stellarium network. It hosts your APIs, learns about everyone else's, and forwards requests to the right place. This guide takes you from an empty folder to a running node in a few steps.

Step 01

Get the
prerequisites.

You will need three things before you start:

  • Node.js 20 or newer.
  • Fastify 5. You create the Fastify server yourself and hand it to the node.
  • RequestScript. It provides the Resource type that describes the APIs your node hosts.

Then install the node along with the packages your app imports directly:

Terminal
npm install stellarium-ts fastify requestscript

Step 02

Register a resource
and start.

A resource is an object that RequestScript programs can call. Its path and name together make its full name, for example com.example.Weather. Each function lists its parameter types and return type, and its exec function runs right here in your process.

Call register before start. When you call start, the node creates its SQLite database, mounts the HTTP API, and starts listening.

server.ts
import Fastify from "fastify";
import { StellariumNode } from "stellarium-ts";
import type { Resource } from "requestscript";

const weather: StellariumResource = {
  metadata: {},
  path: "com.example",
  name: "Weather",
  functions: [
    {
      name: "temperature",
      parameters: [{ name: "city", type: "string" }],
      returnType: "int32",
      exec: async (args) => {
        const city = args.find((arg) => arg.name === "city")?.value;
        return city === "Oslo" ? 12 : 20;
      },
    },
  ],
};

const node = new StellariumNode();
await node.register(weather);

const fastify = Fastify({ logger: true });
await node.start(fastify, {
  port: 3000,
  // Omit this to run standalone. Set it to copy peers and resources
  // from a node that is already up.
  startingPeer: "http://127.0.0.1:3001",
});

Node options

OptionDefaultWhat it does
port3000The port Fastify listens on.
startingPeernoneThe address of a peer to join through. Leave off the /v1 at the end.

Environment variables

The node loads a .env file from your working directory automatically, so you can put these settings there.

VariableWhat it does
BASE_URLThe public address of this node. Peers send resource calls to {BASE_URL}/v1/run/resource, so set it to something like http://127.0.0.1:3000. Don't include /v1.
SIGNING_PRIVATE_KEYYour ED25519 private key, base64url encoded. The node uses it to sign what it publishes to peers.
SIGNING_PUBLIC_KEYThe matching ED25519 public key, base64url encoded. Peers use it to check your signatures. It must pair with the private key.
API_KEYA secret your clients must send as Authorization: Bearer <API_KEY> to run requests on this node.
OPERATOR_URLOnly needed if you host priced resources. The operator address your node sends payment vouchers to.
.env
BASE_URL=http://127.0.0.1:3000
SIGNING_PRIVATE_KEY=<your base64url private key>
SIGNING_PUBLIC_KEY=<your base64url public key>
API_KEY=<a secret your clients send to this node>

Don't have signing keys yet? Run this script to generate a pair:

Terminal
node --input-type=module -e '
import { generateKeyPairSync } from "node:crypto";

const { publicKey, privateKey } = generateKeyPairSync("ed25519", {
  publicKeyEncoding: { type: "spki", format: "der" },
  privateKeyEncoding: { type: "pkcs8", format: "der" },
});

console.log(JSON.stringify({
  publicKey: publicKey.toString("base64url"),
  privateKey: privateKey.toString("base64url"),
}, null, 2));
'

The node keeps the peers and resources it learns about in a file called requestscript.db in your working directory. It is created the first time the node starts.

Step 03

Join a
network.

A node with no startingPeer runs on its own. To join an existing network, set startingPeer to the address of a node that is already running. Once your node is listening, it does four things:

01

Copies the peer list

It calls GET /v1/peers on the starting peer. If your node has no peers yet, it saves that list. If it already has peers, it keeps its own list.

02

Copies the resource list

It calls GET /v1/resources and saves each resource along with the address of the node that hosts it.

03

Introduces itself

It tells every peer in the network that it exists.

04

Shares its own resources

It tells the network about the resources you registered, so others can call them.

After that, requests just work. If a program asks for a resource registered on your node, your exec function runs locally. If it asks for one hosted somewhere else, your node forwards the request to that peer and hands back its answer.

Step 04

Talk to your
node over HTTP.

Every node exposes a small HTTP API under /v1.

RouteBodyWhat it does
GET /v1/noneHealth check. Responds with { "hello": "world" }.
GET /v1/peersnoneLists the peers this node knows about.
GET /v1/resourcesnoneLists resources hosted here and resources learned from peers. Function code always stays on the node that hosts it.
POST /v1/run{ "source": "..." }Runs one RequestScript request. Requires your API_KEY.

Run your first request

Send the script as the source field of a JSON body to /v1/run, with your API_KEY as a bearer token. The script must be a request.

Terminal
curl -X POST http://127.0.0.1:3000/v1/run \
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer $API_KEY" \
  -d '{"source": "request GetTemperature {\n  const weather: com.example.Weather\n  const reading = weather.temperature(city: \"Oslo\")\n  return reading.value\n}"}'

If it works, you get back a 200 with the result:

Response
{ "returnValue": 12 }

A missing or wrong API key returns a 401. If the script isn't a request, you get a 400 with { "error": "Invalid script" }. Anything else that goes wrong returns a 500. A resource call that fails does not fail the request: it comes back as an error value your script can check.

To learn more about writing scripts, read the RequestScript docs. For the full reference, see the stellarium-ts README.

Got a node running? Come say hi.

Meet the community →