Commands Server

Run nodes in different servers via Cap’n Proto RPC.

Quick start

Use spo CLI to generate a simple config with your API key and run below command inside flow-backend repository:

$ spo run all-cmds-server

Your servers will be used to run nodes in your flows.

Example output:

🚀 > spo run all-cmds-server                                                                                                                                                                                                                                                   09:56:56 AM
generated _data/all-cmds-server.jsonc
$ cargo build --bin all-cmds-server
warning: use of deprecated trait `solana_program::program_error::PrintProgramError`: Use `ToStr` instead with `solana_msg::msg!` or any other logging
   --> vendor/mpl-token-metadata/src/generated/errors/mpl_token_metadata.rs:624:37
    |
624 | impl solana_program::program_error::PrintProgramError for MplTokenMetadataError {
    |                                     ^^^^^^^^^^^^^^^^^
    |
    = note: `#[warn(deprecated)]` on by default

warning: `mpl-token-metadata` (lib) generated 1 warning
    Finished `dev` profile [optimized] target(s) in 0.36s
$ target/debug/all-cmds-server _data/all-cmds-server.jsonc
2025-10-13T02:57:04.677085Z  INFO command_rpc::command_side::command_server: using public key: c279c19826915ed6c303bd0895e236d0d6d6950f562195a14edaf3a7bf29708b
2025-10-13T02:57:08.470314Z  INFO command_rpc::command_side::command_server: joined 12b5b06f4837f4998f24186fc013a066c5e00f56750bc72028a53f3d0c8335cd
2025-10-13T02:57:08.470341Z  INFO command_rpc::command_side::command_server: connection type Some(Direct(34.169.65.3:1536))
2025-10-13T03:04:33.779756Z  INFO command_rpc::command_side::command_trait: ran generate_keypair 190.430925ms
2025-10-13T03:04:34.806439Z  INFO command_rpc::command_side::command_trait: ran wallet 190.432655ms
2025-10-13T03:04:34.806837Z  INFO command_rpc::command_side::command_trait: ran const 190.776821ms
2025-10-13T03:04:34.806965Z  INFO command_rpc::command_side::command_trait: ran generate_keypair 190.8822ms

What happened:

  • The CLI generated _data/all-cmds-server.jsonc because it didn't exist

  • Build the binary with cargo build --bin all-cmds-server

  • Running all-cmds-server with generated generated config: target/debug/all-cmds-server _data/all-cmds-server.jsonc

  • When you start your flow, nodes will be automatically run on your node server:

    2025-10-13T03:04:33.779756Z  INFO command_rpc::command_side::command_trait: ran generate_keypair 190.430925ms
    2025-10-13T03:04:34.806439Z  INFO command_rpc::command_side::command_trait: ran wallet 190.432655ms
    2025-10-13T03:04:34.806837Z  INFO command_rpc::command_side::command_trait: ran const 190.776821ms
    2025-10-13T03:04:34.806965Z  INFO command_rpc::command_side::command_trait: ran generate_keypair 190.8822ms

To stop the server, press CTRL+C.

Configuration

Config files are written in JSONC format. Config schema: https://schema.spaceoperator.com/command-server-config.schema.json , you can include this schema in the file to use IDE auto-complete (not required).

Top-Level Properties

The root of the configuration is a JSON object with the following properties:


flow_server

  • Type: array of FlowServerConfig objects

  • Required: No

  • Description: A list of flow servers the command server should connect to. This is the primary way to define upstream servers.

  • Default: If this property is not specified, the server will default to connecting to our main serverhttps://dev-api.spaceoperator.com/. The default value is:

    [
      {
        "url": "https://dev-api.spaceoperator.com/"
      }
    ]

secret_key

  • Type: string

  • Required: No

  • Description: The Iroh secret key that determines the server's unique node ID. Providing a key ensures the server has the same identity across restarts. If not specified, a new key (and thus a new node ID) will be generated every time the server starts.


apikey

  • Type: string

  • Required: No

  • Description: A global API key used to authenticate with all servers defined in the flow_server list. This is a convenient way to set authentication if all your flow servers share the same key. This key will be overridden by any apikey specified within an individual FlowServerConfig object.


Object Definitions

These are the definitions of complex objects that can be used within the configuration.

FlowServerConfig

Each item in the flow_server array is a FlowServerConfig object. It defines a single flow server connection and can be specified in one of two ways: by public URL (FlowServerUrl) or by direct node address (FlowServerAddress).

It also has one optional common property:

  • apikey (string): An API key specific to this server entry. If provided, it overrides the global apikey for this specific connection.

Connection Methods

A FlowServerConfig object must match one of the following structures:

  1. FlowServerUrl: Connect via a public URL.

  2. FlowServerAddress: Connect directly via a node's specific address details.

FlowServerUrl

This is the simplest and most common way to connect to a flow server.

Property
Type
Description

url

string (uri)

The public URL of the flow-server API.

Example:

{
  "url": "https://dev-api.spaceoperator.com/"
}

FlowServerAddress

This method is used for connecting directly to a node, bypassing a public-facing URL. This is an advanced option typically used when you know the node's direct network address details.

Property
Type
Description
Required

node_id

string

The unique Iroh node ID of the flow server.

Yes

relay_url

string (uri)

The URL of the relay server used to help establish the connection.

Yes

direct_addresses

array of string

An optional list of IP addresses (e.g., 192.168.1.10) of the node.

No

Example:

{
  "$schema": "https://schema.spaceoperator.com/command-server-config.schema.json",
  "node_id": "b3af5dca5c351e1e...",
  "relay_url": "https://use1-1.relay.n0.iroh.iroh.link./",
  "direct_addresses": [
    "10.0.0.5"
  ]
}

Full Configuration Examples

Specifying a Secret Key and a Custom Server

This configuration provides a persistent identity for the command server and connects it to a production flow server.

{
  "$schema": "https://schema.spaceoperator.com/command-server-config.schema.json",
  "secret_key": "my-super-secret-persistent-key-string",
  "flow_server": [
    {
      "url": "https://dev-api.spaceoperator.com/"
    }
  ]
}

Using a Global API Key for Multiple Servers

{
  "$schema": "https://schema.spaceoperator.com/command-server-config.schema.json",
  "secret_key": "my-super-secret-persistent-key-string",
  "apikey": "global-api-key-for-all-servers",
  "flow_server": [
    {
      "url": "https://dev-api.spaceoperator.com/"
    },
    {
      "url": "http://localhost:8080/"
    }
  ]
}

Using Per-Server API Keys

This example demonstrates how a specific server's apikey overrides the global one.

{
  "$schema": "https://schema.spaceoperator.com/command-server-config.schema.json",
  "secret_key": "my-super-secret-persistent-key-string",
  "flow_server": [
    {
      "url": "https://dev-api.spaceoperator.com/",
      "apikey": "<API key for spaceoperator>"
    },
    {
      "url": "http://api.mysite.com/"
      "apikey": "<API key for your self hosted instance>"
    }
  ]
}

Using the Advanced FlowServerAddress Connection

This configuration connects to one server via its URL and to another via its direct node address details.

{
  "$schema": "https://schema.spaceoperator.com/command-server-config.schema.json",
  "secret_key": "my-super-secret-persistent-key-string",
  "flow_server": [
    {
      "url": "https://dev-api.spaceoperator.com/",
      "apikey": "<API key>"
    },
    {
      "node_id": "b3af5dca5c351e1e0000ffffffffffffffffffffffffffff",
      "relay_url": "https://use1-1.relay.n0.iroh.iroh.link./",
      "direct_addresses": ["192.168.1.50"],
      "apikey": "internal-node-key"
    }
  ]
}

Last updated

Was this helpful?