# Node Pairing and Approval This document defines how nodes become trusted members of Spiderweb. ## Pairing Modes - Invite-based pairing: - Operator creates an invite token (`control.node_invite_create`). - Node redeems invite (`control.node_join`) and receives node credentials. - Manual approval pairing: - Node submits a pending request (`control.node_join_request`). - Operator reviews queue (`control.node_join_pending_list`). - Operator approves (`control.node_join_approve`) or denies (`control.node_join_deny`). Both modes result in the same node identity material: - `node_id` - `node_secret` - `lease_token` - `lease_expires_at_ms` ## Control Operations ### `control.node_join_request` Request payload: ```json { "node_name": "desktop-west", "fs_url": "ws://10.0.0.8:18891/v2/fs", "platform": { "os": "linux", "arch": "amd64", "runtime_kind": "native" } } ``` Response payload includes: - `request_id` - `node_name` - `fs_url` - `platform` - `requested_at_ms` ### `control.node_join_pending_list` Request payload may be `{}`. Response payload: ```json { "pending": [ { "request_id": "pending-join-1", "node_name": "desktop-west", "fs_url": "ws://10.0.0.8:18891/v2/fs", "platform": { "os": "linux", "arch": "amd64", "runtime_kind": "native" }, "requested_at_ms": 1739900000000 } ] } ``` ### `control.node_join_approve` Request payload: ```json { "request_id": "pending-join-1", "lease_ttl_ms": 900000 } ``` Response is the same join credential envelope as `control.node_join`. ### `control.node_join_deny` Request payload: ```json { "request_id": "pending-join-1" } ``` Response payload: ```json { "denied": true, "request_id": "pending-join-1" } ``` ## Auth and Role Expectations - Approval queue operations are admin-only. - Approval queue operations require operator-scope auth token if configured. - `control.node_join_request` is intentionally non-admin to allow unpaired join proposals. ## Acheron namespace projection Operator Surface Privileged agents can manage pairing through Namespace without direct control RPC calls: - Read pending requests: `/debug/pairing/pending.json` - Approve request: write JSON payload to `/debug/pairing/control/approve.json` - Deny request: write JSON payload to `/debug/pairing/control/deny.json` - Refresh queue snapshot: write any payload to `/debug/pairing/control/refresh` - Create invite token: write optional payload to `/debug/pairing/invites/control/create.json` - Refresh invite snapshot: write any payload to `/debug/pairing/invites/control/refresh` - Read active invites: `/debug/pairing/invites/active.json` - Inspect queue outcomes: `/debug/pairing/last_result.json` and `/debug/pairing/last_error.json` - Inspect invite outcomes: `/debug/pairing/invites/last_result.json` and `/debug/pairing/invites/last_error.json` ## Node Daemon Loop (`spiderweb-fs-node`) `spiderweb-fs-node` can now run as a pairing/lease daemon when `--control-url` is set. Behavior: - Loads persisted node state from `--state-file` (default `.spiderweb-fs-node-state.json`). - If not paired: - `--pair-mode invite`: calls `control.node_join` using `--invite-token`. - `--pair-mode request`: calls `control.node_join_request`, then retries `control.node_join_approve`. - Once paired, uses `node_secret` as FS session auth token (unless `--auth-token` is explicitly provided). - Runs a background lease refresh loop via `control.node_lease_refresh`. - Publishes node capability/service metadata via `control.node_service_upsert`: - FS provider enabled by default. - terminal providers from `--terminal-id`. - labels from `--label`. - Persists updated lease fields (`lease_token`, `lease_expires_at_ms`) after each successful refresh. - Uses reconnect backoff for both pairing retries and lease refresh failures. Key flags: - `--control-url <ws://host:port/>` - `--control-auth-token <token>` (or `SPIDERWEB_AUTH_TOKEN`) - `--pair-mode invite|request` - `--invite-token <token>` (required for invite mode) - `--node-name <name>` - `--fs-url <ws://host:port/v2/fs>` (advertised node URL) - `--state-file <path>` - `--lease-ttl-ms <ms>` - `--refresh-interval-ms <ms>` - `--reconnect-backoff-ms <ms>` - `--reconnect-backoff-max-ms <ms>` - `--no-fs-service` (disable default FS service advertisement) - `--terminal-id <id>` (repeatable) - `--label <key=value>` (repeatable)