Skip to content

F151: Lens mint-endpoint + read-only write-guard#4

Merged
cbroberg merged 1 commit into
mainfrom
feat/f151-lens-mint-endpoint
Jun 7, 2026
Merged

F151: Lens mint-endpoint + read-only write-guard#4
cbroberg merged 1 commit into
mainfrom
feat/f151-lens-mint-endpoint

Conversation

@cbroberg
Copy link
Copy Markdown
Contributor

@cbroberg cbroberg commented Jun 7, 2026

What

Implements the fleet mintEndpoint standard (cardmem F098.1 / F074.13) for cms (epic cms-F151, story cms-F151.1). Lens mints a short-lived, read-only session on demand to capture authed surfaces — no long-lived prod cookie rotting on disk.

How (cms = custom JWT / jose HS256)

  • POST /api/lens-sessionAuthorization: Bearer <LENS_MINT_SECRET> → 401 on missing/wrong. On valid bearer, signs a ~10-min cms-session JWT for lens@webhouse.app (role admin, claim lens:true) with CMS_JWT_SECRET — the same cookie shape cms's own getSession validates (avoids the upmetrics false-green). Returns a Playwright storageState. Cookie domain from the Host header (never 0.0.0.0). GET → 405.
  • proxy.ts/api/lens-session is public (it mints the session, so it predates the cookie); write-guard rejects any mutating method (POST/PUT/PATCH/DELETE) from a lens:true session with 403. This is the read-only boundary — a no-op for every real user.
  • require-role.tsgetSiteRole returns the JWT role for sub "lens" (like dev/service tokens) so the lens principal renders every surface (it has no team membership).

Rollout (after merge)

  1. openssl rand -hex 32LENS_MINT_SECRET: Fly secret on webhouse-app + gitignored .lens/mint-secret (same value).
  2. Deploy webhouse-app.
  3. Runtime-verify on prod: bad bearer → 401; good bearer → 200 + storageState; decode cookie → lens:true + ~10-min exp; write attempt with the minted cookie → 403.
  4. Manifest switch to mintEndpoint is deferred for cms's localhost Lens (needs the secret in the dev server's env + a PM2 restart — not done unprompted; won't regress the current green storageState run).

Verification

  • tsc --noEmit: no new errors (my files clean; only the 12 pre-existing mcp/webhook-dispatch errors remain).

Plan-doc: docs/features/F151-lens-mint-endpoint.md (on main via cardmem). Contract: broberg-ai/cardmem/docs/LENS-MINT-ENDPOINT.md.

🤖 Generated with Claude Code

…ard (F151)

Fleet mintEndpoint standard (cardmem F098.1 / F074.13): Lens mints a
short-lived, read-only session on demand to capture authed surfaces — no
long-lived prod cookie rotting on disk. cms uses custom JWT (jose/HS256),
cookie cms-session, so the mint signs the same cookie shape cms's own
getSession validates.

- src/app/api/lens-session/route.ts: POST mints a ~10-min cms-session JWT for
  lens@webhouse.app (role admin, claim lens:true) when Bearer == LENS_MINT_SECRET
  (else 401); returns a Playwright storageState. Cookie domain from Host header
  (never 0.0.0.0). GET → 405.
- proxy.ts: /api/lens-session is public (it mints the session); write-guard
  rejects any mutating method (POST/PUT/PATCH/DELETE) from a lens:true session
  with 403 — the read-only boundary (no-op for all real users).
- require-role.ts: getSiteRole returns the JWT role for sub "lens" (like
  dev/service tokens) so the lens principal renders every surface.

Plan-doc: docs/features/F151-lens-mint-endpoint.md (on main via cardmem).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@cbroberg cbroberg merged commit a041001 into main Jun 7, 2026
3 of 4 checks passed
@cbroberg cbroberg deleted the feat/f151-lens-mint-endpoint branch June 7, 2026 12:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant