FAQ

Questions worth answering first

The common questions, then a four-command guide to connecting a network. Everything here is reachable from the CLI — no operation needs a browser.

Do I need BatonCloud to run Baton?
No. Baton is open source and Apache-2.0 — the node model, the API, the CLI verbs and the snapshot format are the same wherever a node runs. With this service entirely unreachable every local capability keeps working; only public directory browsing degrades.
What am I actually paying for?
Operations, never capability. A host we provision and keep patched, uptime we keep, backups we take and retain, a relay we run. Your agent's identity, runtime, attach, snapshots and migration are Baton itself — the same on any node, your laptop included — so they are not on the price list.
What is a node?
A Baton node we run for you: its own microVM — a kernel each, not a slice of a shared one — at 2 vCPU · 2 GiB · 25 GB SSD. It stays on so your agent can answer at 3am, and pauses to almost nothing while idle, which is why a resident seat costs a few dollars.
Can you read my agents' work?
No. This service is not in the message path. It helps two networks find each other and then gets out of the way — traffic goes directly between them. We hold public keys, a signed endpoint record, and whatever you publish to the directory.
When can I actually buy?
We are in early access. The buy flow is built, but checkout is off and nothing is charged today — provisioning a node is not shipped yet, and we do not sell what does not run. Add a node to get in line; you will be first when it opens.
What happens to my networks if this service goes away?
Networks that can already address each other keep federating. A registry outage is a discovery outage — it cannot break connections that already exist, and it cannot let anyone impersonate your network, because your identity is a keypair we do not hold half of.
Guide

Connect a network in four commands

Everything BatonCloud does is reachable from the CLI. There is no operation here that needs a browser, and the panel never calls this service directly — it asks your local CLI, and the CLI talks to us.

1 · Point Baton at a deployment

The endpoint is configuration with a default, not a constant. That is what makes BatonCloud a provider rather than the provider.

export BATON_CLOUD_URL=https://dev.batoncloud.org
baton cloud status --output json

Status is three values, never two: connected, not_registered, unreachable. Unreachable is not the same as unregistered — the first says we do not know, the second would send you to register something you may already have.

2 · Register the network

Two credentials are involved and they prove different things. A browser sign-in proves who may claim a name; a signature from your control plane proves which network is being registered. Neither substitutes for the other.

baton cloud register            # identity only, no hosted name
baton cloud register my-team    # identity + claim a hosted name

The name is optional on purpose. Registering an identity and claiming a name are separable acts — a network that already has nike-agents.dev resolves through DNS and never needs a hosted name, but still registers so that later signatures are checkable.

3 · How a hosted name is written

A hosted network name is a label, not a hostname. It becomes the first half of an address local-part:

hosted        network  nike@batoncloud.org
              agent    nike.coder@batoncloud.org    network folds into the local-part

own domain    network  agents.nike.com              the network IS the domain
              agent    coder@agents.nike.com

So the same word network lives in two different places depending on which tier you are in. In the second tier we are not involved in your naming at all — you prove control of the domain with a TXT record and BatonCloud never learns the name. Three rules follow from the address being something a person has to say out loud and write down:

  • No dots in a network name. The dot separates network from agent; a dot inside the name would make a.b.c@batoncloud.org ambiguous with no rule to choose a reading.
  • Case carries no meaning. Names are lowercased, so two names differing only by case are one name.
  • No +. It already means sub-addressing in an email address.

4 · Authorise a provider

Two keypairs. Each side stores only the other's public half, so a breach on either side cannot impersonate the other. There is no shared secret anywhere in this model and no route that accepts one.

  Your network                        BatonCloud
  SK_A / PK_A                         SK_C / PK_C

  A → Cloud   SK_A signs  ─────────>  verify PK_A    "I am Network A"
  Cloud → A   verify PK_C <─────────  SK_C signs     "I am the Cloud A authorised"

Create a binding in the dashboard, copy the passkey it mints, and install it locally:

baton integrations add --passkey-file ./passkey.pem \
  --provider baton-cloud --scopes skill.install
The passkey is a public key, and it is not masked anywhere. It has a friendly name because a person has to move it by hand. That does not make it a secret — and hiding it would teach you to treat it as one. What you must do is compare the fingerprint shown here with the one your panel shows. Two base64 strings look alike; fingerprints are how anyone catches a substituted key.

5 · Authenticate the CLI with a network key

For reads that act as the network rather than as your account, exchange a signed attestation for a short-lived token:

POST /api/auth/network/token
  { "network_id": "net_…", "payload": "<base64>", "signature": "<base64>" }

payload, newline-separated:
  cloud-token
  <network_id>
  <audience>              e.g. https://dev.batoncloud.org
  <RFC3339 UTC timestamp>
  <uuid nonce>

→ { access_token, token_type: "Bearer", expires_in: 900, scope: "network" }

This token cannot claim a name or touch billing. That limit is mechanical rather than a matter of good behaviour: the token carries no account identity at all, so the routes that need one cannot load it. A network key proves what a network is — never what its owner is entitled to claim.

Errors

Every failure has the same three fields, and the third is written for a person.

{ "code": "CLOCK_SKEW",
  "message": "the timestamp is 612s away from this service's clock",
  "remediation": "Check this machine's clock — this is not a key problem." }

CLOCK_SKEW is deliberately its own code and never shares one with a bad signature. When a clock has drifted every request fails, and an operator told "invalid signature" will spend the outage investigating a key that was never broken.

The full route list, request shapes and status codes are in the OpenAPI document.