Per-guild puppet allocation & multiplexing #46

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

Roadmap: Stage 3 of the path to hosted bridging (see #43). The load-bearing refactor for sharing one bot pool across many Discord servers.

A single Discord bot maintains an independent voice gateway connection per guild, so one bot can sit in voice in many guilds at once (this is how public music bots scale). Our code doesn't exploit this: PuppetSlot.busy is a process-global AtomicBool (crates/discord/src/puppet.rs:202) with no guild qualifier, and ChannelCtx carries a single guild_id/channel_id. So if one guild's channel claims all N bots, every other guild starves — an artificial cap imposed by our allocator, not by Discord.

The SDK layer is already capable: each bot's songbird manager keys Calls by guild (manager.get_or_insert(guild_id)), so concurrent per-guild Calls are standard.

Scope:

  • (a) Allocation unit per-(bot, guild): replace the global busy flag with per-guild assignment state. Contained to the allocator + tests — moderate.
  • (b) Per-bot runtime multiplexing (the hard part): one bot running capture (SSRC->user attribution) and playback for several guilds at once, all audio routing keyed by guild. The capture/anchor path is the real refactor.

Spike the per-guild concurrent capture first to de-risk before committing to (b). Precondition for multi-tenant hosting (#43). See the "multi-guild scaling finding" in docs/nether-voicebridge-public-instance-feasibility.md.

**Roadmap: Stage 3 of the path to hosted bridging (see #43). The load-bearing refactor for sharing one bot pool across many Discord servers.** A single Discord bot maintains an independent voice gateway connection **per guild**, so one bot can sit in voice in many guilds at once (this is how public music bots scale). Our code doesn't exploit this: `PuppetSlot.busy` is a **process-global `AtomicBool`** (`crates/discord/src/puppet.rs:202`) with no guild qualifier, and `ChannelCtx` carries a single `guild_id`/`channel_id`. So if one guild's channel claims all N bots, every other guild starves — an artificial cap imposed by our allocator, not by Discord. The SDK layer is already capable: each bot's songbird manager keys Calls by guild (`manager.get_or_insert(guild_id)`), so concurrent per-guild Calls are standard. Scope: - **(a)** Allocation unit per-`(bot, guild)`: replace the global `busy` flag with per-guild assignment state. Contained to the allocator + tests — moderate. - **(b)** Per-bot runtime **multiplexing** (the hard part): one bot running **capture** (SSRC->user attribution) **and** playback for several guilds at once, all audio routing keyed by guild. The capture/anchor path is the real refactor. **Spike the per-guild concurrent capture first** to de-risk before committing to (b). Precondition for multi-tenant hosting (#43). See the "multi-guild scaling finding" 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 01:24:09 +00:00
Author
Collaborator

Code shipped in v0.3.0-alpha.5 — allocation unit is now (bot, guild): sessions/health keyed under the alloc lock, a per-guild GuildConn command loop per bot, same-guild-only steals; 34 tests cover the allocator including the steal paths. The same-guild live regression passed (multi-hour prod call, no allocation misbehavior), and alpha.5→alpha.7 have been running on it in prod since.

Leaving open for the one unproven claim — the actual cross-guild multiplexing (one bot holding live voice in two guilds at once). The live spike needs a bot invited to a second guild with humans on both sides; scheduled to run today. Will close on a passing spike.

**Code shipped in v0.3.0-alpha.5** — allocation unit is now `(bot, guild)`: sessions/health keyed under the alloc lock, a per-guild `GuildConn` command loop per bot, same-guild-only steals; 34 tests cover the allocator including the steal paths. The **same-guild live regression passed** (multi-hour prod call, no allocation misbehavior), and alpha.5→alpha.7 have been running on it in prod since. Leaving open for the one unproven claim — the actual *cross-guild* multiplexing (one bot holding live voice in two guilds at once). The live spike needs a bot invited to a second guild with humans on both sides; scheduled to run today. Will close on a passing spike.
Author
Collaborator

Cross-guild multiplexing live test — PASSED (2026-07-05)

The last unproven #46 claim — one bot holding live Discord voice in two guilds simultaneously — is now verified live on prod (v0.3.0-alpha.7, no code changes).

Setup: tenant bridge via the #45 SIGHUP path — "Multiguild Test": Discord Auto Test 01 (guild 1523348036040790087) ↔ !iICkB3Qu8tQemzFqDl:nether.im (unencrypted, auto-detected). Pool bot idx 0 ("Voice Bridge 01") was the ONLY bot invited to guild 2, so allocation had to land on the same bot serving guild 1 (1217620951211049080, Purple Voice).

Evidence (journal, UTC):

17:43:44 puppet joined voice and assigned idx=0 bridge=Multiguild Test
17:43:44 anchor capture active — Discord→Matrix demux from this puppet idx=0 bridge=Multiguild Test
17:44:06 puppet joined voice and assigned idx=0 bridge=Purple Voice
17:44:06 anchor capture active — Discord→Matrix demux from this puppet idx=0 bridge=Purple Voice
  • Same idx=0, two guilds, concurrent capture anchors for ~12 minutes (both bridges voicetick-healthy through 17:55+).
  • Zero puppet left voice and released / released puppet / dropped unexpectedly / rejoin failed / BecomeAnchor with no engagement / no puppet available lines across the whole window (17:43 → 17:55), confirmed by a live log watchdog and a full-journal sweep.
  • Audio confirmed by ear in all four directions (guild-1 Discord ↔ Matrix and guild-2 Discord ↔ Matrix, simultaneously) by @dark + tester.
  • Robustness: tester left + rejoined the guild-2 channel at 17:46 → only guild-2-scoped ghost release/re-assign (track republished in ~3 s, pump healthy: frames_in=250 frames_out=500 jitter_dropped=0), no puppet release, guild-1 session untouched — release_in_one_guild_preserves_other_guild_session semantics observed live.

No steals occurred (single-bot guild 2 by design), so nothing to add to #24.

Closing — #46 is fully proven: same-guild regression (alpha.5) + cross-guild multiplexing (this test).

## Cross-guild multiplexing live test — PASSED (2026-07-05) The last unproven #46 claim — **one bot holding live Discord voice in two guilds simultaneously** — is now verified live on prod (v0.3.0-alpha.7, no code changes). **Setup:** tenant bridge via the #45 SIGHUP path — "Multiguild Test": Discord `Auto Test 01` (guild `1523348036040790087`) ↔ `!iICkB3Qu8tQemzFqDl:nether.im` (unencrypted, auto-detected). Pool bot **idx 0 ("Voice Bridge 01")** was the ONLY bot invited to guild 2, so allocation had to land on the same bot serving guild 1 (`1217620951211049080`, Purple Voice). **Evidence (journal, UTC):** ``` 17:43:44 puppet joined voice and assigned idx=0 bridge=Multiguild Test 17:43:44 anchor capture active — Discord→Matrix demux from this puppet idx=0 bridge=Multiguild Test 17:44:06 puppet joined voice and assigned idx=0 bridge=Purple Voice 17:44:06 anchor capture active — Discord→Matrix demux from this puppet idx=0 bridge=Purple Voice ``` - **Same `idx=0`, two guilds, concurrent capture anchors for ~12 minutes** (both bridges voicetick-healthy through 17:55+). - **Zero** `puppet left voice and released` / `released puppet` / `dropped unexpectedly` / `rejoin failed` / `BecomeAnchor with no engagement` / `no puppet available` lines across the whole window (17:43 → 17:55), confirmed by a live log watchdog and a full-journal sweep. - **Audio confirmed by ear in all four directions** (guild-1 Discord ↔ Matrix and guild-2 Discord ↔ Matrix, simultaneously) by @dark + tester. - **Robustness:** tester left + rejoined the guild-2 channel at 17:46 → only guild-2-scoped ghost release/re-assign (track republished in ~3 s, pump healthy: `frames_in=250 frames_out=500 jitter_dropped=0`), **no puppet release**, guild-1 session untouched — `release_in_one_guild_preserves_other_guild_session` semantics observed live. No steals occurred (single-bot guild 2 by design), so nothing to add to #24. Closing — #46 is fully proven: same-guild regression (alpha.5) + cross-guild multiplexing (this test).
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#46
No description provided.