Patrick Fanella c313eed706 Merge pull request #87 from subculture-collective/feat/twitch-bot
feat(twitch): wire tmi.js IRC bot and connect to court API
2026-03-01 10:59:09 -06:00
2026-02-28 19:51:49 -06:00
2026-02-28 19:51:49 -06:00
2026-02-27 20:29:37 +00:00
2026-02-26 15:02:24 -06:00

JuryRigged

CI

JuryRigged is a real-time, multi-agent courtroom simulation. An Express API orchestrates agent dialogue across deterministic phases, streams live events via Server-Sent Events (SSE), and supports jury voting for verdict and sentence outcomes.

This repository is standalone and does not require subcult-corp at runtime.

Highlights

  • Multi-agent role orchestration (judge, prosecutor, defense, witnesses, bailiff)
  • Strict, forward-only phase progression:
    • case_promptopeningswitness_examevidence_revealclosingsverdict_votesentence_votefinal_ruling
    • Optional skip: witness_examclosings
  • Live per-session SSE stream (/api/court/sessions/:id/stream)
  • Jury voting APIs with phase gating + anti-spam/rate-limiting
  • Main viewer UI (public/) and React operator dashboard (/operator)
  • In-memory or Postgres-backed persistence (auto-selected by DATABASE_URL)
  • Optional broadcast hook integration (noop or obs) for production workflows
  • Ace Attorneystyle renderer (Phase 7): PixiJS overlay with camera presets, character poses, dialogue typewriter, effects engine, and evidence presentation
  • Structured case file: immutable case context with witness roster, evidence inventory, and charge sheet generated at session start
  • Audience interaction: /press and /present API endpoints + Twitch chat commands (!press, !present, !objection)
  • Render directives: backend-inferred visual cues (camera, pose, face, effects) streamed alongside dialogue turns
  • NDJSON recording and deterministic replay: record live sessions to NDJSON, replay at configurable speed

Tech Stack

  • Node.js + TypeScript
  • Express (API + static serving)
  • React + Vite (operator dashboard)
  • Postgres (optional durable store)

Quick Start (Local)

1) Install dependencies

npm install

2) Configure environment

cp .env.example .env

For a zero-dependency local run:

  • leave OPENROUTER_API_KEY empty (mock dialogue fallback)
  • leave DATABASE_URL empty (in-memory session store)

3) Start the API server

npm run dev

Default local URL: http://localhost:3000 (from .env.example).

4) Build operator dashboard assets

npm run build:dashboard

Then open:

  • Main app: http://localhost:3000
  • Operator dashboard: http://localhost:3000/operator

The API serves /operator from dist/dashboard. If you havent built it yet, /operator will return a helpful 404 message.

Dashboard Dev Mode (Hot Reload)

Run the dashboard separately while API dev server is running:

npm run dev:dashboard
  • Dashboard dev URL: http://localhost:3001/operator/
  • API proxy target in Vite is http://localhost:3000

If your API is not on port 3000, update vite.config.ts proxy settings.

Docker Compose (API + Postgres)

The compose stack includes:

  • api (JuryRigged server)
  • db (Postgres 16)

Start:

npm run docker:up

Stop:

npm run docker:down

Container behavior:

  • API runs on container port 3001
  • Host mapping defaults to ${API_HOST_PORT:-3001}
  • Migrations run automatically on container startup (npm run migrate:dist)
  • TRUST_PROXY defaults to 1 in compose so IP-based rate limits remain accurate behind one proxy hop

Default compose endpoints:

  • App + API: http://localhost:${API_HOST_PORT:-3001}
  • Operator dashboard: http://localhost:${API_HOST_PORT:-3001}/operator

Configuration

Copy .env.example and tune as needed.

Core runtime

Variable Purpose
PORT API port for local non-Docker runs (default in .env.example: 3000)
TRUST_PROXY Express proxy trust setting (true, false, hop count like 1, or CIDR/subnet list) used for accurate client IP detection behind reverse proxies
OPENROUTER_API_KEY Required for live LLM calls; empty enables deterministic mock fallback
LLM_MODEL OpenRouter model identifier
LLM_MOCK Force mock mode (true/false)
DATABASE_URL Enables Postgres-backed durable store; omit for in-memory
LOG_LEVEL debug, info, warn, error

Voting + moderation safety

Variable Purpose
VERDICT_VOTE_WINDOW_MS Verdict poll window duration
SENTENCE_VOTE_WINDOW_MS Sentence poll window duration
VOTE_SPAM_MAX_VOTES_PER_WINDOW Vote rate cap per window
VOTE_SPAM_WINDOW_MS Rate-limit window size
VOTE_SPAM_DUPLICATE_WINDOW_MS Duplicate-vote suppression window

Witness / token controls

Variable Purpose
WITNESS_MAX_TOKENS Max witness response tokens before truncation
WITNESS_MAX_SECONDS Max witness response duration
WITNESS_TOKENS_PER_SECOND Duration↔token heuristic
WITNESS_TRUNCATION_MARKER Marker appended after truncation
JUDGE_RECAP_CADENCE Emit recap every N witness cycles
ROLE_MAX_TOKENS_* Per-role token budget overrides
TOKEN_COST_PER_1K_USD Cost estimation coefficient

Broadcast integration

Variable Purpose
BROADCAST_PROVIDER noop or obs
OBS_WEBSOCKET_URL OBS WebSocket endpoint
OBS_WEBSOCKET_PASSWORD OBS auth password (optional but recommended)

See docs/broadcast-integration.md for setup details.

Twitch integration (Phase 7)

Variable Purpose
TWITCH_CHANNEL Twitch channel to monitor for audience commands
TWITCH_BOT_TOKEN OAuth token for the Twitch bot account
TWITCH_CLIENT_ID Twitch application client ID

When any Twitch variable is unset, the adapter runs in noop mode (no connection).

Replay + recording (Phase 7)

Variable Purpose
RECORDINGS_DIR Directory for NDJSON session recordings (default: recordings/)
REPLAY_FILE Path to NDJSON file to replay instead of live orchestration
REPLAY_SPEED Playback speed multiplier (1 = real-time, 4 = 4×)

API at a Glance

  • GET /api/health
  • GET /api/metrics (Prometheus-format telemetry)
  • GET /api/court/sessions
  • GET /api/court/sessions/:id
  • POST /api/court/sessions
  • POST /api/court/sessions/:id/vote
  • POST /api/court/sessions/:id/press (Phase 7 — audience press)
  • POST /api/court/sessions/:id/present (Phase 7 — present evidence)
  • POST /api/court/sessions/:id/phase
  • GET /api/court/sessions/:id/stream (SSE)

Full schemas, error codes, and event contracts: docs/api.md.

Development Commands

npm scripts

Command Description
npm run dev Start API in watch mode (src/server.ts)
npm run dev:dashboard Start Vite dashboard dev server
npm run build Compile TS to dist/ + build dashboard
npm run build:dashboard Build dashboard only
npm run start Run compiled server (dist/server.js)
npm run migrate Run migrations from source (tsx)
npm run record:sse Record SSE session fixture
npm run migrate:dist Run migrations from compiled output
npm test Run Node test suite
npm run test:ops Run ops config tests
npm run smoke:staging Run staging smoke script

SSE fixture record + replay

Record a live SSE stream to a fixture file:

npm run record:sse -- --session <SESSION_ID>

Defaults:

  • base URL: http://127.0.0.1:${PORT}
  • output: public/fixtures/sse-<SESSION_ID>-<timestamp>.json

Optional flags:

  • --base <url>
  • --out <absolute-or-relative-path>
  • --max-events <number>
  • --duration-ms <number>

Replay a fixture in the browser overlay by adding a query param:

http://localhost:3000/?replayFixture=/fixtures/<fixture-file>.json

When fixture replay mode is enabled, live SSE is disabled and recorded events are replayed with their captured offsets.

Server-side NDJSON recording and replay (Phase 7)

During normal live runs, session events are recorded to NDJSON files in recordings/<SESSION_ID>.ndjson (override with RECORDINGS_DIR).

Start server replay mode from an existing NDJSON file:

npm run dev -- --replay recordings/<SESSION_ID>.ndjson --speed 4

Equivalent environment-variable mode:

REPLAY_FILE=recordings/<SESSION_ID>.ndjson REPLAY_SPEED=4 npm run dev

Notes:

  • --speed / REPLAY_SPEED controls playback rate (1 = real-time, 4 = 4× faster).
  • In replay mode, orchestration is disabled and SSE emits recorded events with captured inter-event timing.
  • Existing viewer and operator UIs connect to replay mode using the same SSE endpoint.

Make targets

Makefile mirrors common workflows (make dev, make test, make ci, make docker-up, etc.).

Local CI Parity

Run before opening a PR:

npm run lint
npm run build
npm test

Repository Layout

  • src/ — server, orchestrator, store, moderation, broadcast, Twitch adapter, tests
  • public/ — viewer UI + overlay
  • public/renderer/ — modular PixiJS renderer (stage, layers, camera, dialogue, effects)
  • public/assets/ — placeholder-first assets (backgrounds, characters, UI, fonts, SFX)
  • dashboard/ — operator dashboard (React + Vite)
  • db/migrations/ — SQL schema migrations
  • docs/ — architecture, API, moderation, ops runbooks
  • ops/ — alert thresholds + runtime health dashboard artifacts

Documentation Map

Document Description
docs/ADR-001-juryrigged-architecture.md Core architectural decisions and invariants
docs/architecture.md System components and phase sequencing
docs/api.md REST + SSE contracts and schemas
docs/coding-conventions.md Team coding style and maintainability conventions
docs/operator-runbook.md Operator procedures and incident response
docs/ops-runbook.md Staging deploy path, SLI/alert definitions
docs/moderation-playbook.md Moderation policy and handling
docs/event-taxonomy.md Event taxonomy and payload expectations
docs/broadcast-integration.md OBS/broadcast automation + Twitch integration
docs/phase5-6-implementation-plan.md Roadmap implementation plan

Notes

  • Migrations run automatically when using Postgres-backed storage.
  • When DATABASE_URL is not set, sessions are non-durable and not recoverable after restart.
  • On restart with Postgres, interrupted running sessions are recovered and resumed.
Description
No description provided
Readme 434 MiB
Languages
TypeScript 82.1%
JavaScript 14.3%
HTML 2.7%
Shell 0.4%
Makefile 0.3%