feat(auth): add optional admin PIN for all API writes

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>
This commit is contained in:
larssand
2026-09-24 22:14:58 +02:00
parent bcd6e12a6c
commit 9016ce02e4
20 changed files with 627 additions and 8 deletions

View File

@@ -97,7 +97,7 @@ export function renderGuideView({ dom, t }) {
<div data-guide-panel="run">
<div class="guide-section-grid mt-16">
${renderGuidePanel("guide.race_day_title", ["guide.race_day_1", "guide.race_day_2", "guide.race_day_3", "guide.race_day_4", "guide.race_day_5", "guide.race_day_6", "guide.race_day_7", "guide.race_day_8"], "", { t })}
${renderGuidePanel("guide.race_day_title", ["guide.race_day_1", "guide.race_day_2", "guide.race_day_3", "guide.race_day_4", "guide.race_day_5", "guide.race_day_6", "guide.race_day_7", "guide.race_day_8", "guide.race_day_9"], "", { t })}
${renderGuidePanel("guide.manage_steps_title", ["guide.manage_steps_1", "guide.manage_steps_2", "guide.manage_steps_3", "guide.manage_steps_4", "guide.manage_steps_5", "guide.manage_steps_6", "guide.manage_steps_7", "guide.manage_steps_8", "guide.manage_steps_9"], "", { t })}
</div>