1---2name: architecture-review3description: Use this skill to evaluate proposed architecture changes against VoxBento's design principles.4---56# Skill: Architecture Review78> Use this skill to evaluate proposed architecture changes against VoxBento's design principles.910---1112## Fundamental Architecture Principles13141. **Browser-first media.** Audio never touches the Python process. Browser → WHIP → MediaMTX → WHEP → listener browser.152. **FastAPI is coordination only.** Routes, WebSocket signalling, auth, admin. No audio processing.163. **Jitsi is monitoring only.** Interpreters watch/hear the floor session. It is not the ingest path.174. **Single publisher per channel.** Enforced at two layers: Python (`BoothRegistry.set_active_interpreter`) and MediaMTX (`overridePublisher: yes`).185. **In-memory booth state.** `BoothRegistry` is module-level in `portal/booth_state.py`. No external state store yet (see TD-03 in `TECHNICAL_DEBT_REPORT.md`).1920---2122## Component Responsibilities (strict boundaries)2324| Component | Does | Does NOT do |25|---|---|---|26| FastAPI portal | Routes, auth, admin, WS coordination, DB queries | Audio processing, transcoding, media relay |27| MediaMTX | WHIP ingest, WHEP playback, RTSP for ffmpeg | Auth, coordination, UI |28| Jitsi Meet | Floor session monitoring (receive-only iframe) | Audio ingest, interpreter publishing |29| Browser JS | WebRTC/WHIP, WebSocket, Jitsi iframe, mic meter | Server-side logic, DB access |30| ffmpeg (spawned) | PCM extraction from RTSP for transcription | Anything else |31| Transcription providers | Text from PCM audio | Media relay, broadcast, DB write |3233---3435## Evaluating Proposed Changes3637### Does this introduce a new dependency?38- Check if it's already in `pyproject.toml`.39- Ask: can this be done with existing FastAPI/SQLAlchemy/httpx capabilities?40- New Python dependencies → run `uv add {pkg}` (not pip); never edit `uv.lock` manually.4142### Does this add server-side audio processing?43- **STOP.** Audio must not flow through the Python process.44- Route audio directly: browser → MediaMTX → listener.45- If transcription is needed: use the existing `portal/transcription/` subsystem.4647### Does this add a frontend framework?48- **STOP.** Frontend is plain ES modules. No Vue, React, jQuery, build step.49- See `.github/instructions/js.instructions.md` for JavaScript conventions.5051### Does this require a new DB column?52- Add to `portal/models.py` with proper `Mapped` typing.53- Create an Alembic migration in `alembic/versions/`.54- For SQLite compatibility: use `batch_alter_table` in the migration (see migration 008 as reference).5556### Does this change the WebSocket protocol?57- The protocol is used by `static/js/interpreter-booth.js` — both files must change together.58- New message types must be handled in `portal/websockets/manager.py` `ws_booth` loop + `_handle_*` function.59- Role enforcement: role is always from `session.granted_role`, never from client `data['role']`.6061### Does this change auth?62- Three separate token types exist (`session_token`, `user_token`, `admin_token`). Do not conflate.63- All tokens use HS256 with `settings.effective_jwt_secret`.64- WebSocket auth: cookies are read at connect time and stored in `Session.granted_role`.6566---6768## Red Flags in Architecture Proposals6970| Proposal | Risk |71|---|---|72| "Add a WebSocket message to send audio data" | Violates browser-first principle |73| "Use Redis for real-time booth state" | Valid but requires careful migration of `BoothRegistry` |74| "Add a REST endpoint that returns the JWT secret" | Security violation |75| "Encode the role in the WebSocket join message and trust it" | Violates role trust model |76| "Store all session data in a cookie" | Risk of cookie size limits + replay attacks |77| "Add Vue for the admin panel" | Violates no-framework constraint |78| "Use aiortc for SFU" | Explicitly forbidden in invariants |79| "Proxy WHIP through FastAPI" | Breaks browser-first media architecture |8081---8283## Good Architecture Patterns in This Codebase8485- **`safe_redirect(url)`** — validates redirects to prevent open redirect attacks. Use it for all redirects.86- **`_ensure_mediamtx_path(channel_id)`** — creates alwaysAvailable paths. Call before returning WHIP URL.87- **`asyncio.Lock` in `BoothRegistry`** — all booth mutations serialized. Prevents race conditions.88- **Lazy DB engine init** — `_get_engine()` in `portal/database.py` defers connection until first use.89- **`from __future__ import annotations`** — deferred evaluation prevents circular import issues.90- **Fernet encryption for API keys** — SHA-256 derived key, supports key rotation via `MultiFernet`.