rename: bridges/valves → tunnels (one term across types + API + UI)
CI / validate (push) Successful in 7s
CI / docker (push) Failing after 5s

API shape:
  GET /api/connections → GET /api/tunnels
  body: {"connections": […]} → {"tunnels": […]}

Type rename (package stays "bridge" — internal):
  Valve         → Listener
  clientBridge  → tunnel
  ConnSnapshot  → TunnelSnapshot

Log messages mirror the new vocab ("listener open/close", "tunnel
open/idle evict/forward failed"). UI header is now "Active tunnels"
and the empty state reads "no active tunnels".

server-manager's dashboard polls /infra/svc-proxy/api/tunnels and
shows "N tunnels" on the svc-proxy infra card.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-10 18:48:17 +02:00
parent 7fbe2555fb
commit 41a7e39754
5 changed files with 163 additions and 160 deletions
+8 -8
View File
@@ -1,20 +1,20 @@
# svc-proxy
Standalone UDP "valve" for [Simple Voice Chat](https://github.com/henkelmax/simple-voice-chat). Per-server public UDP port → backend voice address. Routes read from Postgres via `LISTEN`/`NOTIFY`, same pattern as `mc-router`.
Standalone UDP tunnel for [Simple Voice Chat](https://github.com/henkelmax/simple-voice-chat). Per-server public UDP port → backend voice address. Routes read from Postgres via `LISTEN`/`NOTIFY`, same pattern as `mc-router`.
## What it does
Each MC server in the automc fleet runs SVC on its own UDP port inside its container (default 24454). svc-proxy exposes a **public** UDP port per server and bridges client traffic to the backend. SVC's own `SecretPacket` is configured per backend to advertise the public proxy hostname + the assigned proxy port, so the client connects directly to the proxy — no MITM, no plugin-channel sniffing.
Each MC server in the automc fleet runs SVC on its own UDP port inside its container (default 24454). svc-proxy exposes a **public** UDP port per server and tunnels client traffic to the backend. SVC's own `SecretPacket` is configured per backend to advertise the public proxy hostname + the assigned proxy port, so the client connects directly to the proxy — no MITM, no plugin-channel sniffing.
```
SVC client ──UDP──► svc-proxy.timemachine.center:24455
├── (per-server valve)
├── (per-server tunnel)
└──UDP──► mc-gtnh:24454 (backend SVC)
```
The proxy is **opaque** to the SVC payload — it can read the cleartext outer header (magic byte + player UUID) but the AES-GCM body stays end-to-end. Source-address bridges (one ephemeral upstream socket per client `SocketAddress`) survive NAT rebinds within the idle TTL.
The proxy is **opaque** to the SVC payload — it can read the cleartext outer header (magic byte + player UUID) but the AES-GCM body stays end-to-end. Per-client tunnels (one ephemeral upstream socket per client `SocketAddress`) survive NAT rebinds within the idle TTL.
## pg schema
@@ -51,7 +51,7 @@ UPDATE servers
NOTIFY automc_routes_changed;
```
svc-proxy logs `valve open: :24455 → mc-gtnh:24454 (gtnh)` and is ready.
svc-proxy logs `listener open: :24455 → mc-gtnh:24454 (gtnh)` and is ready.
To retire a server's voice routing:
@@ -60,7 +60,7 @@ UPDATE servers SET voice_address = NULL, voice_proxy_port = NULL WHERE name = 'g
NOTIFY automc_routes_changed;
```
svc-proxy logs `valve close: :24455 (gtnh)`. In-flight bridges are torn down.
svc-proxy logs `listener close: :24455 (gtnh)`. In-flight tunnels are torn down.
## Backend-side configuration
@@ -81,8 +81,8 @@ svc-proxy is the equivalent for the mc-router shape: pure UDP data plane, pg-dri
## Limitations
- No replay protection at the proxy layer (SVC's AES-GCM is the only freshness guarantee — same as upstream).
- No client rate-limiting (SVC's plugin-channel rate limit covers TCP setup; UDP audio relies on Opus payload caps + the wrapper's `BRIDGE_IDLE_TTL` to bound per-source sockets).
- Bridge ephemeral upstream sockets aren't pooled — one syscall per concurrent client. Fine up to a few thousand concurrent voice users on a single proxy host.
- No client rate-limiting (SVC's plugin-channel rate limit covers TCP setup; UDP audio relies on Opus payload caps + the `BRIDGE_IDLE_TTL` env to bound per-tunnel sockets).
- Per-tunnel ephemeral upstream sockets aren't pooled — one syscall per concurrent client. Fine up to a few thousand concurrent voice tunnels on a single proxy host.
## Related