The Invariant
One token, no shared secrets. Provisioning carries only a single-use bootstrap token; the Ed25519 keypair is generated on the device at first boot and the private key never travels the wire.
The Contract
| In | Out |
|---|---|
bt_* single-use claim token (AXIOM_BOOTSTRAP_TOKEN env) |
edge-credentials.json (0600): tenant_id, node_id, api_key, node_privkey |
AXIOM_SYNC_URL pointing at the Control Plane |
a syncing, signed edge node |
The identity resolution runs inside the edge process itself — no separate installer: env vars first, persisted credentials second, bootstrap claim third. Re-running with existing credentials preserves identity; re-claiming with a fresh bt_* rotates the API key and the node keypair (the old key dies instantly).
The Flow
- Token — ops issues a
bt_*bootstrap token from the admin console (POST /admin/tenants/:id/bootstrap-tokens), TTL-bound and single-use. - First boot — the edge reads
AXIOM_BOOTSTRAP_TOKEN+AXIOM_SYNC_URL, generates the Ed25519 pair on-device. - Claim —
POST /api/v1/bootstrap/claimwithtoken+node_pubkey(+ optionalhw_metaaudit fields) →{ tenant_id, node_id, api_key, scope: "sync" }. - Persist — credentials land in
edge-credentials.json(0600); the token is spent and cannot be reused. - Sync — the node starts draining the outbox, signed batches, heartbeat on every call.
Payload Example
// POST /api/v1/bootstrap/claim — sent once, at first boot
{ "token": "bt_<48 hex>",
"node_pubkey": "<base64 SPKI DER>", // generated on-device
"hw_meta": { "hostname": "linea-3", "arch": "x86_64",
"mac_hash": "sha256…", "cpu_model": "…" } } // self-reported audit data
// → 200 { tenant_id, node_id, api_key, scope: "sync" }
Known Failures
| Symptom | Cause → fix |
|---|---|
Token non valido, scaduto o già utilizzato (403) |
Token spent/expired (TTL) → issue a fresh bt_* from the admin console |
EDGE_CAP_EXCEEDED |
Contract cap on claimed nodes reached → raise max_edges or decommission a node |
| Node won’t start syncing | AXIOM_SYNC_URL/AXIOM_BOOTSTRAP_TOKEN missing and no persisted credentials — provision the env or copy edge-credentials.json |
EDGE_SIGNATURE_REQUIRED after re-claim |
The new pubkey is registered — unsigned calls are now fail-closed, restart the edge so it picks up the new identity |