Three features from this session, full server suite green (535/535).
TOTP 2FA (#100) — backend shipped without a UI; add it:
- Login: mfa_required -> 6-digit challenge (recovery codes accepted) -> /totp/verify.
- Settings > Account: enable (QR + confirm -> recovery codes once), regenerate,
disable; SSO accounts see "managed by your identity provider".
- /totp/setup returns a server-rendered qr_data_url (bundled qrcode dep). keyuri
folds the request Host into the issuer so multi-instance accounts are
distinguishable in the authenticator app.
Email verification on signup — hosted HARD-block / self-host SOFT-nudge:
- email_verified column; existing users asked on first login (SSO + platform
admins grandfathered); single-use 24h tokens (SHA-256 hashed).
- Gate engages only when email is configured (never locks out a no-mail instance).
GET /verify-email + POST /resend-verification (generic, no account enumeration).
- Client: "confirm your email" flow + resend, verified/error toasts, self-host
banner; onAuthSuccess refuses a tokenless response (defensive).
Tizen SSSP URL-Launcher install — Fusion-style one-URL native install:
- Server hosts /tizen/sssp_config.xml (dynamic <size>, always matches the served
.wgt) + /tizen/ScreenTinker.wgt + a human landing. lib/wgt-cache.js resolves the
signed .wgt (/data mount wins, mirroring the APK).
- build-wgt.sh also emits a static sssp_config.xml for CDN hosting.
- Retail panels require a Samsung Partner cert; dev-mode is SDB self-signed only.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The subprocess-booting test suites hand-picked fixed ports in a cramped ~3955-4021 range, and
156-schedule-read-path deviated to a RANDOM port (3900 + rand%90) that overlapped those fixed
ports. Under CI load two servers could race on the same port, surfacing as flaky "no such table:
devices" / "FOREIGN KEY constraint failed" (a server answering a request against a half-migrated
or wrong DB). It's environmental — the suites pass locally and in isolation.
Fix: a shared test/helpers/free-port.js (bind :0 on loopback, read the OS-assigned port, release)
called in before() so every suite gets a guaranteed-unique ephemeral port — concurrent suites can
no longer collide, and no one has to hand-assign ports.
- Codemod converted 30 suites: const PORT = <fixed|random> -> let PORT (+ BASE) assigned via
`PORT = await freePort()` at the top of before().
- 3 hand-fixed (different structure): 148-eviction-storm (lowercase `base`), boot-health (no
before() — allocates PORT + a throwaway SEED_PORT inside the test, replacing the hardcoded
3894), totp-keyrotation (no before() — allocates at the test start before bootServer()).
No fixed 39xx/40xx ports remain. Full server suite 435/435; the 4 hand-touched suites pass in
isolation. Pure test-infra change — no app code touched.
Review caught the encrypted TOTP secret riding in the login/verify response body:
issueSession receives a SELECT * user row and only destructured out password_hash, so
totp_secret_enc (and the internal replay counter totp_last_step) leaked. Encrypted, so
not catastrophic, but it regresses the API work's "secrets never in responses" rule.
Strip both in issueSession (covers /login and /totp/verify); add an assertion that a
verify response carries no totp_secret_enc.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Unit: the mfa_pending BITE (db-injected so removing the rejection goes red), lockout, replay,
recovery-hash, decrypt-null graceful. Integration: enrollment, login->mfa_required, route-level
bite, recovery single-use, API-token bypass, verify lockout. Key-rotation: enroll under key A,
reboot under key B -> recovery still works, TOTP fails cleanly (no 500).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>