The installer now fetches one male and one female Piper voice per language (Swedish
NST/Lisa, English Ryan/Lessac), with genders confirmed by measuring each voice's pitch
rather than guessed from the name. Settings -> Audio gets a voice selector per language
with its own Test button that always plays the Piper voice. Male is preselected for both.
The integration test now runs its server with an empty home directory, so voices
installed on the developer's own machine can't leak into it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Announcements can use Piper, a free local neural TTS, instead of the browser's system
voice. No account, no paid service, and it keeps working with no internet at the track.
- npm run tts:install (or Settings -> Audio -> Install) downloads the standalone Piper
release for Linux/Windows/macOS plus Swedish and English voices into piper/ once
- server/tts.js generates each line once, one at a time, and caches it as WAV under
data/tts/; a new line takes ~0.3 s with the standalone build
- src/piper_speech.js plays it and falls back to the browser voice if Piper is missing,
errors, or takes over 2.5 s, so no announcement is lost and timing is never held up
- "Pre-generate phrases and driver names" renders every fixed phrase and name in both
languages before race day
Browser voice stays the default. Generation routes fall under the admin PIN.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Off by default. Once a PIN is set in Settings -> System & backup, every non-GET /api/*
request (plus GET /api/ammc/config) needs a signed 30-day HttpOnly cookie, which also
closes the unauthenticated /api/update/run restart trigger. A 401 opens a PIN dialog
and the pending save is retried after login. Reads stay open because the public overlay
hydrates through GET /api/state. Optional RC_ADMIN_ALLOWED_CIDRS restricts the same
routes by client address.
The PIN hash and signing secret live in data/, outside the app_state blob, so they
never leak through GET /api/state, exports or backups.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Closes both open "Documentation maintenance" roadmap items. Used a research-only
Explore pass to get a concrete diff against the actual code before editing, rather
than guessing what was stale.
Documented: src/track_layouts_logic.js (previously unmentioned anywhere),
buildEventSchedule, the Settings/Event Manager tab layouts, new state fields
(raceConfig.scheduleStartAt/scheduleGapMin, Session.trackLayoutId, top-level
trackLayouts/audioEventLog), a real SQLite table contract section, a full
field-by-field Settings shape, several previously-undocumented backend routes,
and the complete list of files tests/browser_smoke.js actually invokes.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>