Runtime bridge provisioning (supervisor + tenant persistence) #45

Closed
opened 2026-06-27 16:46:54 +00:00 by robocub · 1 comment
Collaborator

Roadmap: Stage 2 of the path to hosted, configure-without-install bridging (see #43).

Today the bridge spawns all of its bridges once at startup from the TOML config (the for (idx, bridge) loop in crates/bridge/src/main.rs), SharedPool::register_channel is only ever called at startup, the config is read-only serde with no write-back, and the only runtime lifecycle is SIGINT/SIGTERM shutdown. So provisioning a new tenant currently means editing TOML and restarting.

Hosting needs to add and remove a bridge while running:

  • A bridge supervisor owning the running bridge tasks, with spawn_bridge(cfg) / stop_bridge(id), reusing each bridge's existing per-bridge CancellationToken tree for clean teardown.
  • A runtime unregister_channel counterpart to register_channel (the pool already hands back a lifetime-free Arc<ChannelView>, so the seam is half there).
  • A tenant-state persistence layer so tenants survive a restart — a small SQLite or flat store is sufficient; this does not require Postgres (#10).
  • Runtime validation reusing the existing Config::validate invariants (unique store_path, unique device tuple) when adding a tenant.

Precondition for the management bot (#4) and the public instance (#43). Background analysis in docs/nether-voicebridge-public-instance-feasibility.md.

**Roadmap: Stage 2 of the path to hosted, configure-without-install bridging (see #43).** Today the bridge spawns all of its bridges **once at startup** from the TOML config (the `for (idx, bridge)` loop in `crates/bridge/src/main.rs`), `SharedPool::register_channel` is only ever called at startup, the config is read-only `serde` with no write-back, and the only runtime lifecycle is SIGINT/SIGTERM shutdown. So provisioning a new tenant currently means editing TOML and restarting. Hosting needs to **add and remove a bridge while running**: - A bridge **supervisor** owning the running bridge tasks, with `spawn_bridge(cfg)` / `stop_bridge(id)`, reusing each bridge's existing per-bridge `CancellationToken` tree for clean teardown. - A runtime `unregister_channel` counterpart to `register_channel` (the pool already hands back a lifetime-free `Arc<ChannelView>`, so the seam is half there). - A **tenant-state persistence** layer so tenants survive a restart — a small SQLite or flat store is sufficient; this does **not** require Postgres (#10). - Runtime validation reusing the existing `Config::validate` invariants (unique `store_path`, unique device tuple) when adding a tenant. Precondition for the management bot (#4) and the public instance (#43). Background analysis in `docs/nether-voicebridge-public-instance-feasibility.md`.
dark added this to the Roadmap project 2026-06-27 16:48:04 +00:00
robocub referenced this issue from a commit 2026-07-05 03:47:25 +00:00
Author
Collaborator

Shipped in v0.3.0-alpha.7 (master e7a0275) and deployed to prod 2026-07-05. Staging acceptance had passed all six criteria with Purple Voice as a live tenant; post-deploy verification on prod: all 4 static bridges up with reused crypto device ids, keys re-delivered <3s into a live call, --check-tenants pre-flight working against the prod data dir (no tenants file yet — static-only until runtime tenants are added). Closing.

Shipped in **v0.3.0-alpha.7** (master `e7a0275`) and deployed to prod 2026-07-05. Staging acceptance had passed all six criteria with Purple Voice as a live tenant; post-deploy verification on prod: all 4 static bridges up with reused crypto device ids, keys re-delivered <3s into a live call, `--check-tenants` pre-flight working against the prod data dir (no tenants file yet — static-only until runtime tenants are added). Closing.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
dark/nether-voicebridge#45
No description provided.