Refs #25. This closes the simultaneous-join race. The issue's other gap, a token fetched earlier and used up to an hour later, would need connect-time enforcement (LiveKit participant_joined webhooks) and stays open. Production change: the guard runs on LXC 151 and needs reinstalling and restarting after merge.
voice-limit-guard checked the limit against the users LiveKit already had connected. Tokens issued but not yet connected (the 1–2 s before the LiveKit connect) weren't counted, so two users pressing Join at the same moment could both get into a 1-person channel.
Change
SlotReservations: each admitted user gets a 30 s reservation, counted like a present user until LiveKit lists them, or it expires.
The check for a room runs under a per-room lock, so two requests can't both read "0/1".
Fail-open on errors is unchanged.
Verified
Unit tests: the second simultaneous join is blocked; the same user on another device is still allowed; reservations expire and clear once present; locks are per room. 47/47 pass, ruff clean.
Real guard against the local LiveKit + Synapse. Voice Lounge max_users: 1, two users' /sfu/get fired simultaneously:
currently running guard (old code): [200, 200], both admitted;
this branch, started on :8072 with the same env: [200, 403], logged blocked @aud_bob…: 1/1.
To deploy (LXC 151): reinstall per the README (install -D -m644 …/voice-limit-guard.py /opt/voice-limit-guard/), then systemctl restart voice-limit-guard.
Refs #25. This closes the simultaneous-join race. The issue's other gap, a token fetched earlier and used up to an hour later, would need connect-time enforcement (LiveKit `participant_joined` webhooks) and stays open. **Production change:** the guard runs on LXC 151 and needs reinstalling and restarting after merge.
`voice-limit-guard` checked the limit against the users LiveKit **already had connected**. Tokens issued but not yet connected (the 1–2 s before the LiveKit connect) weren't counted, so two users pressing Join at the same moment could both get into a 1-person channel.
**Change**
- `SlotReservations`: each admitted user gets a **30 s reservation**, counted like a present user until LiveKit lists them, or it expires.
- The check for a room runs under a **per-room lock**, so two requests can't both read "0/1".
- Fail-open on errors is unchanged.
**Verified**
- Unit tests: the second simultaneous join is blocked; the same user on another device is still allowed; reservations expire and clear once present; locks are per room. 47/47 pass, ruff clean.
- **Real guard against the local LiveKit + Synapse.** Voice Lounge `max_users: 1`, two users' `/sfu/get` fired simultaneously:
- currently running guard (old code): **[200, 200]**, both admitted;
- this branch, started on :8072 with the same env: **[200, 403]**, logged `blocked @aud_bob…: 1/1`.
**To deploy** (LXC 151): reinstall per the README (`install -D -m644 …/voice-limit-guard.py /opt/voice-limit-guard/`), then `systemctl restart voice-limit-guard`.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5
The limit was checked against participants LiveKit already had, so
tokens issued but not yet connected were not counted: two users
pressing Join together could both get into a 1-person channel.
Serialise the check per room and give each admitted user a 30 s
reservation, counted like a present user until LiveKit lists them or
it expires.
Refs #25
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5
jared
merged commit 96d1778bb4 into main2026-10-18 19:46:01 -04:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Refs #25. This closes the simultaneous-join race. The issue's other gap, a token fetched earlier and used up to an hour later, would need connect-time enforcement (LiveKit
participant_joinedwebhooks) and stays open. Production change: the guard runs on LXC 151 and needs reinstalling and restarting after merge.voice-limit-guardchecked the limit against the users LiveKit already had connected. Tokens issued but not yet connected (the 1–2 s before the LiveKit connect) weren't counted, so two users pressing Join at the same moment could both get into a 1-person channel.Change
SlotReservations: each admitted user gets a 30 s reservation, counted like a present user until LiveKit lists them, or it expires.Verified
max_users: 1, two users'/sfu/getfired simultaneously:blocked @aud_bob…: 1/1.To deploy (LXC 151): reinstall per the README (
install -D -m644 …/voice-limit-guard.py /opt/voice-limit-guard/), thensystemctl restart voice-limit-guard.🤖 Generated with Claude Code
https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5