Files
gibbz/README.md
developer 2b950e4953
Build and Publish / Metadata (push) Successful in 4s
Build and Publish / BuildAndDeployAmd64 (push) Successful in 14s
eventsourced-ai live check live check
refactor: port backend and frontend to TypeScript classes (bun + vite)
Backend (src/server, runs on Bun):
- GibbzHttpServer (health, /api/ice, /api/rooms, static, keepalive tuning)
- SignalingServer with Peer/Room/RoomRegistry classes and typed messages
- IceConfigProvider (env STUN/TURN + coturn HMAC ephemeral creds)
- StaticFileServer (dist/ with public/ fallback, traversal guard)
- Tracing (OpenTelemetry SDK), AppConfig; createApp() factory for tests

Frontend (src/client, built by Vite):
- VoiceApp orchestrator implementing VoiceAppContext
- WebRtcManager + PeerSession (perfect negotiation, pc rebuild, quality monitor)
- SignalingClient + ReconnectController, AudioManager, SpatialAudio,
  Microphone/ScreenShare controllers, ScreensUi/RosterUi/SpatialMap/
  SpeakingMonitor, Settings/Welcome dialogs, LayoutManager, MatrixChat

Tooling: bun scripts (dev/start/test/typecheck), vite.config.ts root
src/client with publicDir public/static, vitest-config for CI-less runs,
bun-based Dockerfile (oven/bun:1), Makefile + README updated, legacy
server.js/tracing.js/test.js/public js removed.

Verified locally: tsc --noEmit clean, vitest run 6/6 green, vite build ok,
server smoke-tested serving dist + signaling endpoints under Node. Local
bun binary segfaults (SIGILL) on this host, so 'bun test' itself was not
executable here; tests are vitest-API compatible which bun test maps.
2026-09-24 07:43:04 +00:00

3.8 KiB
Raw Permalink Blame History

gibbz voice — POC

Discord-clone proof of concept: peer-to-peer WebRTC voice chat + screen sharing with position-based spatial audio and pluggable STUN/TURN.

TypeScript everywhere: the backend runs on Bun, the client is built with Vite, and both are organized into classes.

Layout

src/server/          Bun (Node-compatible) WebSocket/HTTP backend
  index.ts           entrypoint + createApp() factory (used by tests)
  http-server.ts     GibbzHttpServer — health probes, /api/ice, /api/rooms, static
  signaling.ts       Peer / Room / RoomRegistry / SignalingServer (ws)
  ice.ts             IceConfigProvider — env-driven STUN/TURN + HMAC TURN creds
  static-files.ts    StaticFileServer — dist/ (or public/) with traversal guard
  tracing.ts         Tracing — OpenTelemetry SDK setup
  config.ts          AppConfig — env + static dir resolution

src/client/          Vite root (index.html, style.css)
  app.ts             VoiceApp — orchestrator implementing VoiceAppContext
  context.ts         the subsystem-facing application interface
  state.ts           AppState — shared cross-cutting voice state
  webrtc/            WebRtcManager, PeerSession, SignalingClient, ReconnectController
  media/             AudioManager (mic chain), SpatialAudio, Microphone-, ScreenShareController
  ui/                roster/screens/spatial-map renderers, settings, welcome, layout
  chat/              MatrixChat (OAuth2/OIDC text channel)

public/static/       PWA assets served verbatim (icons, manifest, service worker)
tests/               bun test / vitest compatible suite (tests/server.test.ts)

Develop

bun install
bun run dev:server      # backend on :8080 (bun --watch)
bun run dev             # vite dev server for the client
bun test                # tests (vitest-compatible; `bun run test:node` = vitest run)
bun run typecheck       # tsc --noEmit
bun run build           # production client bundle into dist/
bun run start           # serve dist/ + signaling (bun src/server/index.ts)

The server also runs under Node 22.18+/23+ (node src/server/index.ts) since it only uses erasable TypeScript, but Bun is the supported runtime (the Dockerfile is oven/bun:1). A bun.lock is generated on first bun install.

What works

  • Mesh WebRTC voice (perfect negotiation; lowest peer id initiates), full pc-rebuild recovery on ICE failure with TURN-relay fallback
  • Screen share with audio (mic is spatialized, screen audio plays direct), spotlight/grid tiles and pop-out windows
  • Spatial audio: drag your avatar on the plane; remote mics run through a PannerNode (HRTF, inverse distance). Positions sync over signaling.
  • Hi-Fi rooms (Code, Gaming): stereo Opus 256 kbps, echo cancellation off
  • Custom STUN/TURN: gear icon → JSON override in localStorage, applied on next join. Server default comes from env (below).
  • Mute (M), deafen (D), per-peer volume, speaking indicators, quality diagnostics (loss/jitter/RTT), auto-reconnect with countdown banner
  • Matrix-backed text channel (OAuth2/OIDC against matrix.k6n.net)

ICE configuration (server side)

Env var Meaning
STUN_URLS comma-separated stun: urls
TURN_URLS comma-separated turn:/turns: urls
TURN_USERNAME / TURN_CREDENTIAL static TURN creds
TURN_SECRET coturn static-auth-secret → ephemeral HMAC creds
ICE_TRANSPORT_POLICY WebRTC ICE policy: all, relay, or none
ICE_SERVERS_JSON full {"iceServers":[...]} override, wins over the above
PORT / HOST listen address (default 8080 / 0.0.0.0)

POC shortcuts (productionize later)

  • No auth, rooms are in-memory, signaling is plain ws behind the ingress.
  • No media server: N² mesh — fine for small rooms, needs SFU beyond ~6–8 participants.
  • Text channels are a single live Matrix room; no history pagination.