25 Commits

Author SHA1 Message Date
itsrubberduck
2b792eb57f refactor(split): decouple app from website in preparation for the repo split
Phase 0 of the OpenSquawk repo separation. Everything happens inside the
monorepo so that the split itself becomes a mechanical path filter — filtering
first and repairing afterwards would leave two broken repos at once.

AUTH_MODE (0.1)
  New server/utils/authMode.ts, session.ts and jwt.ts (the latter extracted
  from auth.ts). requireUserSession now resolves in three steps: the app's own
  session cookie, an app-minted bearer token, then the website access token.
  Only the last one is transitional; it is marked PHASE 1 and disappears with
  the User collection. The app's session is its own JWT in a host-only cookie
  plus a short-lived bearer, so the existing Authorization call sites are
  unchanged.

  AUTH_MODE defaults to 'sso', not 'open' as the plan proposed: while the admin
  and editor surface still lives here, an unset variable would otherwise serve
  it to everyone as a local admin. requireAdmin additionally refuses in open
  mode. Both are one-line removals in Phase 1 and marked as such.

SSO handoff (0.2)
  Issuer: /api/service/auth/sso/{authorize,exchange}. Codes are stored as
  SHA-256 hashes with a TTL index and claimed by a single atomic update, so
  concurrent redemption cannot succeed twice. redirect_uri is matched against
  SSO_REDIRECT_ORIGINS by exact origin — a prefix check would accept
  app.opensquawk.de.evil.tld. There is no default and no wildcard: an empty
  allowlist disables the handoff rather than opening a redirector.
  Consumer: /api/auth/sso/callback plus app/pages/auth/callback.vue. The
  browser only ever carries the code; it is redeemed server-to-server.

Hardcoded values and leaks (0.3)
  Hotjar ID, the dome-light webhook URL and the bug-report recipient were
  compiled in. All three are env-gated and off by default now, so a foreign
  instance cannot ship analytics, cockpit telemetry or its users' bug reports
  to us. Setting HOTJAR_ID, DOME_LIGHT_WEBHOOK_URL and BUG_REPORT_NOTIFY_EMAIL
  restores the current behaviour on opensquawk.de.

Two databases, no shared Mongo (0.6)
  AppUser mirrors an identity locally. Its _id is deliberately the SSO subject,
  i.e. the website's User._id, so every existing LearnProfile, PilotProfile and
  BridgeToken reference keeps resolving without a migration.
  telemetry.ts mirrors records to the hosted service only when TELEMETRY_URL
  and SERVICE_SECRET are both set — the self-host default is that nothing ever
  leaves the instance. It writes locally first, buffers with a bound, drops on
  overflow and never blocks the request path.
  /api/service/user-deleted purges the app's half on account deletion. Unlike
  telemetry this is deliberately loud: the admin delete aborts with the user
  intact if the purge fails, because their id is the only handle for retrying.
  ?force=true overrides it and says so in the response.

Also here
  /api/service/analytics/product-session was an unauthenticated public write
  endpoint; it moves to /api/analytics/product-session behind the auth guard.
  The bridge no longer populates against User but resolves through the mirror,
  backfilling missing rows so live bridges never have to re-pair.
  .claude/worktrees was tracked and would have reached the public repo.

scripts/split-paths.txt carries the filter list, verified by
scripts/verify-split-paths.mjs: every path exists, nothing website-only is
kept, and no kept file imports a dropped one. That check found real gaps —
tests/ cannot be taken wholesale, and two shared modules were missing. Ten
remaining edges are allowlisted, each annotated PHASE 1 in the code.

Open item, flagged and not resolved: flightlabTelemetryStore is an in-process
singleton written by the bridge (app) and read by FlightLab (website). Two
repos means two processes, so that read breaks regardless of which side it
lands on. FlightLab needs an HTTP path in Phase 2/3.

Verified: 609 tests pass, vue-tsc clean. Ran against two throwaway local
MongoDBs: open mode reaches /classroom and /live-atc with no login and
persists progress; the full SSO loop works and the mirror _id matches the
website User._id; lookalike origins, code reuse, forged codes and wrong
service secrets are all rejected; ingest is idempotent on bug-report code;
deletion purges all five collections; and with the app unreachable the admin
delete fails 502 with the user still present.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 18:46:34 +02:00
itsrubberduck
70260cac1e feat(tts): dedicated ATIS broadcast voice on Speaches
The ATIS tag now resolves to en_GB-alba-medium — outside both voice pools —
instead of sharing 'verse' with the user's own readback voice.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 11:22:44 +02:00
itsrubberduck
2a8875a7f3 feat(tts): map logical pool voices to distinct Piper speakers on Speaches
Until now the Speaches branch sent every request to the single env-configured
model (de_DE-thorsten — a German voice for English radio), so the voice pool
had no audible effect. A server-side registry now resolves the logical voice
ids to disjoint controller/pilot Piper speakers; unknown ids keep the env
fallback and the cache key follows the resolved pair.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 10:45:49 +02:00
leubeem
0b7345ced7 feat(server): per-user AI usage tracking, cost alerting, and endpoint hardening
Usage tracking:
- new UsageEvent collection records every STT/TTS/LLM call per user with
  provider, model, volume (audio seconds, characters, tokens) and an
  estimated USD cost; self-hosted providers (Speaches/Piper) and cache
  hits record at $0
- pricing table for whisper-1, tts-1, gpt-5-nano & co. in server/utils/usage.ts
- weekly KPI mail gains an "AI-Nutzung & Kosten" section: weekly and
  rolling 30-day cost, per-kind breakdown, top 5 users by cost
- quota alert mail when rolling 30-day cost exceeds USAGE_ALERT_USD
  (default $5), at most once per calendar month (UsageAlertDelivery)

Hardening:
- /api/atc/say now requires an authenticated session (middleware
  exemption removed); useFlightLabAudio sends the bearer token
- /api/service/tools/latency requires auth (was a public LLM endpoint)
- per-user rate limits: PTT 20/min, say 60/min, latency 5/min
- cron endpoints (waitlist-drip, weekly-kpi-report) require a shared
  secret via ?secret= or x-cron-secret (CRON_SECRET, falls back to
  KPI_CRON_SECRET); allowed with a warning while unset so existing
  deployments keep working
- PTT records the actual transcribed audio duration for billing accuracy

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 23:17:03 +02:00
leubeem
0907e011fd feat(atc): accept preNormalized flag on /api/atc/say
Clients that already ran the full radiotelephony normalizer (see
normalizeATCText) pass preNormalized: true so the server skips
normalizeATC — double-normalizing corrupts the text, e.g. expandAirports
spells the city name "MAIN" letter-by-letter.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 23:12:53 +02:00
itsrubberduck
22f50aa403 feat(atis): clock-locked ATIS loop tied to tuned frequency
Real ATIS broadcasts run continuously; tuning in mid-broadcast should
drop the pilot into the current spoken position, then loop. The frontend
now generates the full announcement once via TTS, plays it as a looping
HTMLAudioElement, and seeks to ((Date.now() - lastUpdated) / duration)
on metadata-load so all clients tuned to the same ATIS hear it phase-
synced. The loop starts/stops automatically with frequency tuning and
restarts on info-letter change. say.post.ts now caches tag=atis like
tag=flightlab to avoid re-synthesizing identical announcements.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 14:43:48 +02:00
itsrubberduck
6b96b295e1 change default settings 2026-02-18 18:05:12 +01:00
itsrubberduck
bf4ce3abdf typescript 2026-02-17 18:19:55 +01:00
itsrubberduck
46f95baf9e fix(classroom): audio speed slider now actually changes playback speed with pitch correction
- Fix client: playbackRate was set to 1 for non-native-speed providers (Speaches/Piper),
  making the speed slider ineffective in the main Pizzicato audio path
- Fix server: pass speed parameter to Speaches TTS API
- Add pitch-preserving playback via MediaElementSourceNode when rate != 1,
  routing through the same Web Audio effects chain (radio filters, distortion, etc.)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 01:01:02 +01:00
itsrubberduck
d4190c9d95 dont log users id on transmission 2026-02-13 18:44:34 +01:00
Remi
fbec5c4830 Add session timeline logging and admin sessions view 2025-09-21 23:08:10 +02:00
Remi
d6c87d3378 Enhance admin transmissions with detailed LLM and TTS context 2025-09-20 12:42:21 +02:00
Remi
440aef2f62 Fix remaining German comment 2025-09-20 09:46:34 +02:00
Remi
6dbfe07d2b Persist learn progress per user 2025-09-18 23:30:55 +02:00
Remi
7a44d67844 Harden runtime config and input validation 2025-09-18 14:03:25 +02:00
itsrubberduck
0ae44bf89a resolve merge conflict 2025-09-16 17:44:29 +02:00
Remi
19f253f53e Implement authentication, waitlist, and logging upgrades 2025-09-16 17:28:34 +02:00
itsrubberduck
4f1257863c feat(api): extend ATC TTS endpoint with flexible output formats & Speaches support
- say.post.ts:
  - added AudioFmt type, format helpers (mime/ext), Speaches TTS integration
  - routing: Speaches > Piper > OpenAI fallback
  - supports dynamic formats (, ===============================================================================
flac - Command-line FLAC encoder/decoder version 1.5.0
Copyright (C) 2000-2009  Josh Coalson
Copyright (C) 2011-2025  Xiph.Org Foundation

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
===============================================================================

This is the short help; for all options use 'flac --help'; for more explanation
and examples please consult the manual. This manual is often distributed
alongside the program as a man page or an HTML file. It can also be found
online at https://xiph.org/flac/documentation_tools_flac.html

To encode:
  flac [-#] [INPUTFILE [...]]

  -# is -0 (fastest compression) to -8 (highest compression); -5 is the default

To decode:
  flac -d [INPUTFILE [...]]

To test:
  flac -t [INPUTFILE [...]], , ) or smallest
  - uses VOICE_ID and SPEACHES_BASE_URL from ENV
  - response returns correct mime, extension & base64
  - meta extended with modelUsed and format
  - ports & defaults configurable via ENV
2025-09-16 16:42:01 +02:00
itsrubberduck
a7eebf0baf refactor: update OpenAI TTS integration and cleanup imports
- index.vue: comment out cockpit simulator image
- learn.vue: remove unused imports (useRadioTTS, learnModules)
- atc/say.post.ts & utils/normalize.ts: rename openaiOld → normalize, adjust TTS calls, skip ensureDir/writeFile
- communicationsEngine.ts: fix atcDecisionTree import path
2025-09-16 16:14:12 +02:00
itsrubberduck
f72d5b22b8 new version of pm using decision tree 2025-09-16 12:23:32 +02:00
itsrubberduck
01c93fe4db remove writefile 2025-09-15 23:09:05 +02:00
itsrubberduck
2a446f8a61 USE_PIPER 2025-09-15 22:43:07 +02:00
itsrubberduck
27e0507bbd tts tut jetz 2025-09-15 11:58:11 +02:00
itsrubberduck
d4ce409641 claude änderungen 2025-09-15 11:53:50 +02:00
itsrubberduck
426a91e71f iwas was nich tut dazutun 2025-09-15 11:20:30 +02:00