mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-13 22:32:04 +00:00
feat(admin): replace the key backup step with a recovery kit
The step confirmed one key, the active application key. A restore needs more: off-host copies are sealed under BACKUP_ENCRYPTION_KEY, a copy written before a rotation needs the retired application key it was written under, and every session, token and share link hangs off API_TOKEN_HMAC_KEY. An operator could do everything the step asked and still be unable to read a single backup. The recovery kit lists every key a restore of this server needs, by id and fingerprint, never key material: - each id in ENCRYPTION_KEYS, plus any retired id a stored copy (innerKeyIds) or an off-host object inside the bucket's retention (OffhostBackupKeyUse) still lists, also once the server dropped it; - BACKUP_ENCRYPTION_KEY and each BACKUP_ENCRYPTION_PREVIOUS_KEYS entry, named by the id their objects record; - API_TOKEN_HMAC_KEY. Confirming records the set the admin was shown and refuses a stale one with 409 recovery.kit.stale. The kit is due again as soon as a key it lists is not in that set (a rotation, a new backup key, a re-keyed install); a key that merely drops out does not make it due. "Check my copy" now checks any key in the kit. Migration 0487 carries a confirmation given under the old step forward as a kit holding that key, so that part stays confirmed, and drops the four app_settings columns. It also creates the two tables the readiness evaluation uses (restore_drill_outcomes, recovery_readiness_notices). The routes move to /api/admin/recovery-kit; the reader of the keys the backups still need moves out of the encryption status route into src/lib/recovery/backup-key-needs.ts so the kit and the status view share one definition.
This commit is contained in:
1 parent
953c64f7a4
commit
d3c672cc3f
56 files changed
+3042
-1611
No files matched your search
@@ -5,8 +5,8 @@
|
||||
"entries": {
|
||||
"app/api/admin/ai-settings/route.ts": 1,
|
||||
"app/api/admin/drain-per-sample-cumulative/route.ts": 1,
|
||||
"app/api/admin/encryption/key-backup/verify/route.ts": 1,
|
||||
"app/api/admin/notifications/reminder-check/route.ts": 1,
|
||||
"app/api/admin/recovery-kit/verify/route.ts": 1,
|
||||
"app/api/admin/rollups/recompute/route.ts": 1,
|
||||
"app/api/admin/users/[id]/reset-password/route.ts": 1,
|
||||
"app/api/ai/test/route.ts": 1,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$comment": "Production files over 800 lines and their length. Maintained by src/__tests__/source-size-ratchet.test.ts.",
|
||||
"count": 86,
|
||||
"total": 96631,
|
||||
"total": 96611,
|
||||
"entries": {
|
||||
"app/medications/page-client.tsx": 839,
|
||||
"components/charts/mood-chart.tsx": 1166,
|
||||
@@ -67,7 +67,7 @@
|
||||
"lib/jobs/coach-nudge.ts": 1084,
|
||||
"lib/jobs/insight-pregenerate.ts": 1189,
|
||||
"lib/jobs/medication-intake-import.ts": 819,
|
||||
"lib/jobs/offhost-backup.ts": 1147,
|
||||
"lib/jobs/offhost-backup.ts": 1127,
|
||||
"lib/jobs/reaction-line.ts": 824,
|
||||
"lib/jobs/reminder/medication-reminder-check.ts": 816,
|
||||
"lib/managed-profiles/handover.ts": 986,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$comment": "Production functions over 200 lines, keyed \"file :: name\", and their length. Maintained by src/__tests__/source-size-ratchet.test.ts.",
|
||||
"count": 348,
|
||||
"total": 112838,
|
||||
"count": 347,
|
||||
"total": 112600,
|
||||
"entries": {
|
||||
"app/achievements/page.tsx :: AchievementsPage": 206,
|
||||
"app/api/admin/backups/upload/route.ts :: apiHandler(arg)": 314,
|
||||
@@ -59,7 +59,6 @@
|
||||
"components/admin/glitchtip-section.tsx :: GlitchtipSection": 233,
|
||||
"components/admin/host-metrics-chart.tsx :: HostMetricsChart": 232,
|
||||
"components/admin/invite-tokens-section.tsx :: InviteTokensSection": 525,
|
||||
"components/admin/key-backup-card.tsx :: KeyBackupCard": 238,
|
||||
"components/admin/login-overview-section.tsx :: LoginOverviewSection": 503,
|
||||
"components/admin/pulse-day-mean-card.tsx :: PulseDayMeanCard": 305,
|
||||
"components/admin/reminders-section.tsx :: RemindersSection": 309,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$comment": "Comment lines that open with a release version, per file under src/. Maintained by src/__tests__/version-stamp-ratchet.test.ts.",
|
||||
"count": 2897,
|
||||
"total": 9249,
|
||||
"total": 9248,
|
||||
"entries": {
|
||||
"__tests__/account-payload-consumer-guard.test.ts": 2,
|
||||
"__tests__/acting-account-boundary-guard.test.ts": 7,
|
||||
@@ -2703,7 +2703,7 @@
|
||||
"lib/queries/use-medication-compliance-summary.ts": 3,
|
||||
"lib/queries/use-medication-list-layout.ts": 3,
|
||||
"lib/queries/use-profile-claim.ts": 4,
|
||||
"lib/query-keys/admin.ts": 13,
|
||||
"lib/query-keys/admin.ts": 12,
|
||||
"lib/query-keys/ai-runs.ts": 1,
|
||||
"lib/query-keys/auth.ts": 9,
|
||||
"lib/query-keys/coach.ts": 13,
|
||||
|
||||
@@ -242,18 +242,6 @@ const UNPUBLISHED: Readonly<Record<string, Exemption>> = {
|
||||
kind: "adminConsole",
|
||||
methods: ["POST"],
|
||||
},
|
||||
"/api/admin/encryption/key-backup": {
|
||||
kind: "adminConsole",
|
||||
methods: ["GET"],
|
||||
},
|
||||
"/api/admin/encryption/key-backup/confirm": {
|
||||
kind: "adminConsole",
|
||||
methods: ["POST"],
|
||||
},
|
||||
"/api/admin/encryption/key-backup/verify": {
|
||||
kind: "adminConsole",
|
||||
methods: ["POST"],
|
||||
},
|
||||
"/api/admin/encryption/rotate": { kind: "adminConsole", methods: ["POST"] },
|
||||
"/api/admin/encryption/status": { kind: "adminConsole", methods: ["GET"] },
|
||||
"/api/admin/host-metrics": { kind: "adminConsole", methods: ["GET"] },
|
||||
@@ -276,6 +264,19 @@ const UNPUBLISHED: Readonly<Record<string, Exemption>> = {
|
||||
"/api/admin/provider-health": { kind: "adminConsole", methods: ["GET"] },
|
||||
"/api/admin/notifications/health": { kind: "adminConsole", methods: ["GET"] },
|
||||
"/api/admin/notifications/test": { kind: "adminConsole", methods: ["POST"] },
|
||||
"/api/admin/recovery-kit": { kind: "adminConsole", methods: ["GET"] },
|
||||
"/api/admin/recovery-kit/confirm": {
|
||||
kind: "adminConsole",
|
||||
methods: ["POST"],
|
||||
},
|
||||
"/api/admin/recovery-kit/verify": {
|
||||
kind: "adminConsole",
|
||||
methods: ["POST"],
|
||||
},
|
||||
"/api/admin/recovery-readiness": {
|
||||
kind: "adminConsole",
|
||||
methods: ["GET"],
|
||||
},
|
||||
"/api/admin/rollups/recompute": { kind: "adminConsole", methods: ["POST"] },
|
||||
"/api/admin/settings": { kind: "adminConsole", methods: ["GET", "PUT"] },
|
||||
"/api/admin/settings/assistant-flags": {
|
||||
|
||||
Reference in new issue
Block a user