mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 13:01:00 +00:00
A step-up elevation minted from the account password satisfied the fresh-factor routes. That is a second-factor bypass: a stolen token plus a known password could rotate the recovery codes — which take no factor in the body — and then spend one of them to disable the second factor outright. The web has never allowed that chain, because a password login does not stamp mfaVerifiedAt. The method is now an authorisation input. Only totp, webauthn, and passkey satisfy a fresh-factor gate, which is exactly the set of ceremonies the web stamps a session for; password reaches what a plain cookie session reaches and stops. The mint gains TOTP and security-key arms so a TOTP-only account can still manage its factor natively, and the response says which reach it bought. The gate also returns a discriminated context. session.id meant a session row on one arm and a token row on the other, and the disable route passed the token id to a session-scoped query — excluding nothing, so it deleted every browser session and revoked the caller's own device login. The transport is now in the type and the compiler refuses the confusion. Redemption splits into validate-then-commit so a 429, a 422, or a wrong code no longer burns a proof against a five-per-fifteen-minute mint ceiling. The claim is UPDATE ... RETURNING, so the statement that decides is the one that reports. The mint is transactional behind a partial unique index. Sign-out-everywhere and both password-reset paths clear elevations. The status read drops to plain requireAuth — it carries no credential material — and every mutation stays gated.
prisma/
The database layer. HealthLog runs PostgreSQL 16 via Prisma 7. This directory holds the single schema file and the forward-only migration history; the generated client is emitted elsewhere.
Contents
schema.prisma— 61 models, one file.cuid()primary keys,snake_casecolumns via@map(...), encrypted columns mostly suffixed*Encrypted(search the file; there are 24 encrypted column references). Thegenerator clientblock emits to../src/generated/prisma(imported as@/generated/prisma).migrations/— numbered, forward-only SQL migrations (0001_…upward).migration_lock.tomlpins the provider topostgresql.
Migration workflow
# author a migration from a schema edit
pnpm dlx prisma migrate dev --name <short_snake_case_name>
# regenerate the client after any schema change
pnpm dlx prisma generate
Number a new migration with the next free prefix. The production entrypoint runs prisma migrate deploy on boot, so every migration must apply cleanly forward — there is no down-migration path. Always include the migration file in the same change as the schema edit.
Conventions
- Encryption is fail-closed:
*Encryptedcolumns are written through@/lib/crypto(AES-256-GCM, versioned key ids); never store plaintext in an encrypted column. Key rotation isscripts/rotate-encryption-key.ts+docs/ops/encryption-key-rotation.md. - The generated client under
src/generated/prismais build output — do not edit it. - Schema-touching changes that affect the API surface must regenerate
docs/api/openapi.yaml(see../CLAUDE.md).
Related: ../src/lib/README.md (db.ts, rollups/), ../docs/ops/ (migrations + backup runbooks).