219 files, 2.0 MB, untracked in souveraine/docs and existing nowhere else. The volume is at 100% with no snapshots.
8.4 KiB
| task_id | title | status | priority | phase | created | references | |||||
|---|---|---|---|---|---|---|---|---|---|---|---|
| node-connection-manager-001 | Node & Connection Manager — Local, Remote, Bootstrap | scoping | high | 3.0 | 2026-05-14 |
|
Task: Node & Connection Manager
The Question
Right now souveraine starts and goes straight into the TUI or CLI depending
on flags. There's no "where do you want to connect?" step. Running remotely
requires knowing the URL and token ahead of time through config. There is no
discovery, no pairing flow, no way to say "I'm setting up a new instance."
The goal: souveraine loads and presents you with a connection chooser.
Local, Connect to a remote, Bootstrap a new remote. The substrate knows where
it is in the topology at all times.
What already exists
| Component | Status | Where |
|---|---|---|
SeedId (Ed25519 keypair) |
✅ Landed | src/core/identity/seed.rs |
souveraine identity show/sign/verify |
✅ Landed | src/cli/commands.rs |
| Per-agent seeds | ✅ Landed | server/agents/{uuid}/seed/ |
| Instance registry + uptime | ✅ Landed | SQLite agent_instances table |
Auth middleware (require_token) |
✅ Landed | src/api/auth.rs — but only wired to memory routes |
LocalBackend |
✅ Landed | In-process, no network |
RemoteBackend |
✅ Landed | HTTP/SSE, requires manual config |
souveraine.toml bifrost + server config |
✅ Landed | URL, port, token fields exist |
What's missing
| Gap | Why it matters |
|---|---|
| No "where to connect?" startup flow | First-run or flag-day: user sees nothing, has to know --local or souveraine.example.toml exists |
| Auth middleware not on agent/conversation routes | Remote backend can't safely expose agent update/delete or conversation read/inject |
| No node identity advertisement | A remote souveraine instance has no way to say "I am souveraine@hostname, trust anchor is X" |
| No pairing/bootstrapping flow | Setting up a new remote requires manual SCP/scp of tokens |
| No connection health/status | The TUI shows agent state but not backend connection state |
souveraine.example.toml not wired |
File exists at repo root but nothing reads it for guided setup |
Flow: The startup experience
$ souveraine
╭─ Souveraine ─────────────────────────────╮
│ │
│ Where do you want to connect? │
│ │
│ ◉ Local (in-process, no server needed) │
│ ○ Connect to a remote instance │
│ ○ Bootstrap a new remote instance │
│ │
│ [Enter to select, ↑↓ to navigate] │
│ │
│ Souveraine v0.1.0 │
│ Seed: glyphlike · No remotes configured │
╰───────────────────────────────────────────╯
If Local is chosen (or single-node is the only option), it proceeds to the
existing TUI/CLI. If Connect is chosen, it prompts for URL + token (or
discovers via mDNS / known hosts). If Bootstrap is chosen, it SSH'es into
a remote machine and runs souveraine init + returns the connection info.
Phases
Phase 1: Startup chooser (TUI-only, 1-2 days)
Before entering the Screen loop, App::new() or main.rs shows a connection
chooser as a ratatui screen. Three options:
- Local — immediately creates
LocalBackend, proceeds to Welcome screen. Default when no remote config exists. - Connect to remote — inline form: URL, API token. Validates by hitting
/health. On success, stores in config + createsRemoteBackend. - Bootstrap new remote — SSH host, SSH user (optional, defaults to
current user), SSH key path (optional, defaults to
~/.ssh/id_ed25519). Runssouveraine initon the remote via SSH, captures the seed pubkey + API token, stores connection info. This is the "one-command setup" flow.
The chooser is skipped when --local, --remote <url>, or a saved default
connection is set. It only shows when ambiguous.
Files:
src/ui/connection_chooser.rs— new, the ratatui chooser screensrc/main.rs— edit, route to chooser before App init unless flags/savedsrc/ui/app.rs— the chooser populatesconfig.remotebefore App buildssouveraine.example.toml— already exists, used as config template
Phase 2: Auth middleware expansion (2-3 days)
Move agent update/delete and conversation routes behind require_token:
PATCH /v1/agents/:id— agent owner onlyDELETE /v1/agents/:id— agent owner onlyGET /v1/conversations/:id— conversation's agent owner onlyPOST /v1/conversations/:id/messages— conversation's agent owner onlyGET /v1/agentsandPOST /v1/agentsstay open (listing, bootstrap)
Conversation auth needs a new middleware wrapper that resolves agent_id from
the conversation lookup, then delegates to the existing require_token.
Files:
src/api/mod.rs— route groups with.route_layer()src/api/auth.rs— newrequire_conversation_tokenwrappersrc/backend/remote.rs— send the token header on API calls
Phase 3: Node identity advertisement (1 day)
A GET /v1/node endpoint that returns the instance's seed pubkey, version,
agent list, and connection hint:
{
"id": "did:key:z6Mk...",
"version": "0.1.0",
"agents": ["ani", "sam"],
"hostname": "onyx.local",
"public_url": "https://souveraine.onyx.local:8484"
}
The connection chooser's "Connect" flow hits this endpoint to validate the URL and pre-fill the instance name.
Files:
src/api/handlers.rs— newget_node_infohandlersrc/api/mod.rs— wire toGET /v1/node(public, no auth)
Phase 4: Connection manager (2-3 days)
Persistent connection profiles stored in config or a new JSON file at
~/.souveraine/connections/. Each profile has:
[connections.onyx]
url = "https://souveraine.onyx.local:8484"
token = "souv_..."
default = true
last_seen = "2026-05-14T18:30:00Z"
The chooser reads these and shows them as quick-select options below "Connect to a remote." Pressing Enter on a saved connection skips the form.
Files:
src/core/config.rs— addconnectionsfield toConsciousnessConfigsrc/ui/connection_chooser.rs— show saved connections as selectable itemssrc/backend/remote.rs— accept connection profile, not manual URL+token
Phase 5: Bootstrap via SSH (2-3 days)
The "Bootstrap new remote" flow:
- Prompt for SSH target (
user@host), key path, install path ssh user@host "curl -sfL https://souveraine.sh | bash"or scp the binaryssh user@host "souveraine init --seed-from <local-seed-pubkey>"- Read back the remote's
/v1/nodeto get its seed pubkey - Read back the remote's API token from
~/.souveraine/server/token - Store the connection profile locally
- Return to the chooser with the new connection selected
SSH is the bootstrap transport; once configured, all communication goes through the HTTP/SSE API with bearer token auth.
Files:
src/core/ssh.rsorsrc/cli/ssh.rs— new, SSH command executionsrc/ui/connection_chooser.rs— add Bootstrap flow state machine
Non-goals
- mDNS / automatic discovery — typed-in URLs and SSH bootstrap only. No bonjour/avahi. Discovery can be layered on top later.
- WebSocket relay server — not yet. Phase 5 only establishes a point-to-point connection. A relay for NAT traversal is future work.
- Multi-hop federation (A→B→C) — star topology only. One local node connects to one remote node.
- Remote agent creation — agents are created on the local instance and synced. The remote is a read/submit surface, not a creation surface.
Build sequence
- ✅ SeedId, per-agent seeds, instance registry, CLI commands
- Connection chooser screen (
Phase 1) — pure TUI, no SSH - Auth middleware expansion (
Phase 2) — secure remote API - Node identity endpoint (
Phase 3) —GET /v1/node - Connection profiles (
Phase 4) — saved connections in config - SSH bootstrap (
Phase 5) — one-command remote setup - Connection health in TUI — status indicator in header/footer