mirror of
https://github.com/OpenSquawk/OpenSquawk
synced 2026-08-05 00:46:00 +08:00
docs: document the self-hosted app stack
This commit is contained in:
115
CLAUDE.md
115
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
|
||||
```
|
||||
|
||||
71
README.md
Normal file
71
README.md
Normal file
@@ -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).
|
||||
Reference in New Issue
Block a user