From cd2b2820d2a4d34aac16c48b833a53c95a12659c Mon Sep 17 00:00:00 2001 From: itsrubberduck Date: Mon, 27 Jul 2026 19:30:44 +0200 Subject: [PATCH] docs: document the self-hosted app stack --- CLAUDE.md | 115 +++++++++++++++++++++++++++++------------------------- README.md | 71 +++++++++++++++++++++++++++++++++ 2 files changed, 133 insertions(+), 53 deletions(-) create mode 100644 README.md diff --git a/CLAUDE.md b/CLAUDE.md index 1d30d19..91bc7f1 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,63 +1,72 @@ -# OpenSquawk - Project Guide +# OpenSquawk App — Project Guide + +## Scope + +This repository is the self-hostable OpenSquawk application. It contains the +browser UI and its H3 API. It does not own the authoritative Live ATC decision +state; that belongs to the separate `OpenSquawk-LiveATC-api` service. ## Architecture -- **Nuxt 4** (Vue 3 SFC) frontend in `/app` -- **H3 server** handlers in `/server` -- **Shared types/utils** in `/shared` -- MongoDB models in `/server/models` -- **Python backend** — a separate repo, `OpenSquawk-LiveATC-api`, owns the authoritative - Live ATC (`/live-atc`, formerly `/pm` — legacy path now redirects) session state and routing decisions. It runs as its own service - (default `http://127.0.0.1:8000`). This Nuxt app owns STT, TTS, client audio, auth, - the flow editor, and account/admin features. -> The LLM decision routing that used to live in this repo (`routeDecision()`, -> `/api/llm/decide`, a Nuxt `/api/decision-flows/runtime` route) has been removed. -> Decision routing now happens in the Python backend. Don't reintroduce it here. +- Nuxt 4 and Vue 3 frontend in `/app` +- H3 API handlers in `/server` +- Shared types, data, and state-machine utilities in `/shared` +- MongoDB models in `/server/models` +- Python decision backend at `NUXT_PUBLIC_RADIO_BACKEND_URL` + +`AUTH_MODE=open` is the default. It creates one stable local `AppUser` identity +and requires no login. `AUTH_MODE=sso` is optional and exchanges a one-time code +with the configured `NUXT_PUBLIC_AUTH_ISSUER`; the app then owns its session. ## Key Files -- `/app/composables/useRadioBackend.ts` — Typed client for the Python backend REST API - (`createSession`, `transmit`, `deleteSession`, `fetchFlows`). This is how `/live-atc` talks - to the decision engine. -- `/shared/utils/communicationsEngine.ts` — Local state-machine composable used by `/live-atc`. - It mirrors the flow for cursor tracking and TTS scheduling; the **Python backend owns the - authoritative state**. `fetchRuntimeTree()` loads the flow definition from the backend's - `/api/decision-flows/runtime`. -- `/server/services/decisionFlowService.ts` — Builds/edits MongoDB-backed decision trees. - Used **only by the flow editor** (`/server/api/editor/flows*`), not by `/live-atc`. -- `/server/utils/openai.ts` — `getOpenAIClient()` only. Used for TTS (`/api/atc/say`) and - STT (`/api/atc/ptt`). It is **not** a decision router anymore. -- `/app/pages/live-atc.vue` — Live ATC page (speech-to-text, PTT, text input). -- `/app/pages/classroom.vue` — Classroom learning mode (separate system, does NOT use - communicationsEngine). -## Live ATC Flow (/live-atc) -1. `startMonitoring()` → `fetchRuntimeTree('icao_atc_decision_tree', radioBackendUrl)` - loads the flow definition from the Python backend into the local engine (cursor / TTS). -2. `startMonitoring()` → `radioBackend.createSession(...)` creates the authoritative - server-side session and stores `backendSessionId`. -3. User input (PTT or text) → `handlePilotTransmission()`. - PTT audio is transcribed first via Nuxt `POST /api/atc/ptt` (Whisper). -4. `radioBackend.transmit(backendSessionId, transcript)` → the Python backend runs regex - routing, readback evaluation, side effects, and flow chaining; it returns - `next_state_id`, `controller_say_template`, `auto_advanced_states`, `flags`, - and `session_complete`. -5. `moveToSilent(stateId)` is called for each auto-advanced state and the final state — - advances the local cursor without re-triggering auto-transitions. -6. `scheduleControllerSpeech(...)` speaks the ATC reply via TTS (`/api/atc/say`). +- `/app/pages/index.vue` — mode selector and application entry point +- `/app/pages/live-atc.vue` — Live ATC UI +- `/app/pages/classroom.vue` — classroom learning mode +- `/app/composables/useRadioBackend.ts` — typed Python-backend client +- `/shared/utils/communicationsEngine.ts` — local cursor, variables, logs, and + TTS scheduling for Live ATC +- `/server/utils/auth.ts` — app-user resolution and request authentication +- `/server/utils/authMode.ts` — open/SSO identity selection and local user +- `/server/utils/session.ts` — app-owned cookie and bearer tokens +- `/server/utils/telemetry.ts` — opt-in telemetry mirror; disabled when + `TELEMETRY_URL` is empty +- `/server/utils/openai.ts` — OpenAI-compatible client for speech services -## Decision Tree States -States have `role: 'pilot' | 'atc' | 'system'`. The local engine handles two template -schemas transparently (via `stateSayTpl()` / `stateUtteranceTpl()`): -- Legacy (MongoDB editor): `say_tpl`, `utterance_tpl`, vars as `{var}` -- Python backend YAML: `say_template`, `expected_pilot_template`, vars as `{{var}}` +## Live ATC Flow -Both `{{var}}` and `{var}` are rendered by `renderTpl()`. Transition fields seen across -schemas: `next`, `ok_next`, `bad_next`, `timer_next`, `auto_transitions`. +1. The frontend loads a runtime flow from `OpenSquawk-LiveATC-api`. +2. It creates an authoritative radio session in the Python backend. +3. PTT audio is transcribed through `POST /api/atc/ptt`; text input skips STT. +4. The transmission is sent to the Python session. +5. The returned state, auto-advanced states, variables, and flags update the + local communications engine. +6. The controller response is spoken through `POST /api/atc/say`. -## Environment Variables -- `NUXT_PUBLIC_RADIO_BACKEND_URL` — URL of the Python backend (default `http://127.0.0.1:8000`) +The Python backend owns routing and authoritative session state. Do not add a +second routing implementation to the Nuxt application. -## Commands -- `yarn dev` — dev server (Nuxt) -- `yarn test` — runs the `tsx --test` suite (`tests/`, `server/`, `shared/`) -- Python backend: see `OpenSquawk-LiveATC-api/README.md` +## Decision State Compatibility + +The local engine accepts both supported template conventions: + +- `say_tpl` and `utterance_tpl` +- `say_template` and `expected_pilot_template` + +It renders both `{variable}` and `{{variable}}`. Supported transition fields +include `next`, `ok_next`, `bad_next`, `timer_next`, and `auto_transitions`. + +## Local Commands + +```bash +yarn install +yarn dev +yarn test +yarn typecheck +``` + +Start `OpenSquawk-LiveATC-api` separately with: + +```bash +poetry run uvicorn main:app --reload +``` diff --git a/README.md b/README.md new file mode 100644 index 0000000..399a744 --- /dev/null +++ b/README.md @@ -0,0 +1,71 @@ +# Self-host OpenSquawk + +OpenSquawk is a browser-based aviation radio training application. It provides +Live ATC scenarios with spoken controller responses, a classroom learning mode, +and optional simulator/cockpit bridge integration. + +This repository contains the Nuxt application and its H3 API. Live ATC routing +and authoritative session state are provided by the separate +[`OpenSquawk-LiveATC-api`](https://github.com/OpenSquawk/OpenSquawk-LiveATC-api) +service. + +## Requirements + +- Node.js 22 and Yarn 4 +- MongoDB +- Python 3.12 and Poetry for `OpenSquawk-LiveATC-api` +- An OpenAI-compatible service for cloud STT/TTS, or a configured local speech + provider + +The simulator/cockpit bridge is optional. + +## Start locally + +Start MongoDB, then prepare the Nuxt application: + +```bash +corepack enable +cp .env.example .env +yarn install +``` + +Generate strong values for `JWT_SECRET`, `APP_JWT_SECRET`, and +`SERVICE_SECRET`. Keep `AUTH_MODE=open` for a self-hosted instance without a +login. Configure the speech provider variables required by your setup. + +In a separate checkout of `OpenSquawk-LiveATC-api`, start the decision backend: + +```bash +poetry install +poetry run uvicorn main:app --reload +``` + +It listens on `http://127.0.0.1:8000` by default. If it runs elsewhere, set +`NUXT_PUBLIC_RADIO_BACKEND_URL` accordingly. + +Start the Nuxt application: + +```bash +AUTH_MODE=open yarn dev +``` + +The application is then available at `http://localhost:3000`. + +## Configuration + +All documented variables and self-hosting defaults are in +[`.env.example`](./.env.example). MongoDB stores user state, learning progress, +bug reports, and transmission logs locally. Telemetry forwarding is disabled +when `TELEMETRY_URL` is empty. + +## Services + +- `OpenSquawk-LiveATC-api`: decision flows, routing, and Live ATC session state +- MongoDB: application identities, progress, reports, and logs +- OpenAI-compatible or local speech service: transcription and controller audio +- Optional bridge: simulator and cockpit integration + +## License + +OpenSquawk is licensed under the +[GNU Affero General Public License v3.0 only](./LICENSE).