A Local Test-User Door, Locked Five Ways
Owner ruling 2026-10-04 (option A) — a dev-only /dev/sign-in door signs an agent or the owner in as one of two seeded test users without WorkOS's hosted page. It exists only in a dev build, answers only localhost, mints one-hour tokens, is honoured only by a deployment holding DEV_LOGIN_JWKS, and only for user_dev_ subjects.
In plain terms: a side entrance for practice. On a developer's own
computer, /dev/sign-in hands out a one-hour pass for one of two pretend
clients, so the app can be checked end to end without WorkOS's sign-in page.
The live site has no such entrance, and the live database would not honour
its passes.
Decision
Owner ruling, 2026-10-04 (option A).
- The door.
http://localhost:3000/dev/sign-inoffers two test users. Choosing one sets a one-hour cookie holding a signed token (a JWT); while it is held the app hands Convex that token instead of WorkOS's, and the account menu's sign-out (or the door's own button) clears it. - The test users (
bun run seed:test-users, idempotent):user_dev_testerowns a Workspace, the Test Matter Project, the Sample Advice Document (two Modules with Blocks) and two Libraries with guides and tags, Tester's Precedents and Tester's Private Bank.user_dev_guesthas a Builder seat on Test Matter and a seat on Tester's Precedents only, so Library access is your own seat can be checked by hand. - The locks. If any cannot be kept, the door is withdrawn, not weakened.
- Dev build only:
src/start.tsloads the door behindimport.meta.env.DEV; a production build contains none of its code (checked 2026-10-04 by building and searching the output). - Localhost only: any other host name is refused; a POST from another site is refused.
- Test users only: the door signs only the subjects in
convex-dev/devLogin.ts, andconvex/callerIdentity.tstreats a door token for any subject not startinguser_dev_, or auser_dev_subject on any other issuer's token, as nobody. Every read of the caller's identity goes through that one function. - One hour: token and cookie both expire after 3600 seconds.
- Dev deployment only:
convex/auth.config.tsadds the door's issuer only whenDEV_LOGIN_JWKSis set. It is set on the dev deployment (Staging) and never on production, which therefore rejects every door token.
- Dev build only:
One-time setup
bun run dev-login:keyswrites the private key to.env.local(never committed) and prints the next line.- Against the dev deployment only:
bunx convex env set DEV_LOGIN_JWKS "$(bun scripts/dev-login-keys.mjs --print-jwks)". bun run seed:test-users, then restartbun run dev.
Status
- Tested (
convex/devLogin.convex.test.ts,convex/callerIdentity.test.ts): no dev issuer withoutDEV_LOGIN_JWKS; a door token foruser_xyzis nobody; the guest cannot search or insert from the private Library while the tester can; the seed is idempotent. Breaking the lock fails the tests. - Checked live on the dev deployment (2026-10-04): tokens minted by the door were accepted for both test users; the guest's search listed only Tester's Precedents and an insert from the private Library was refused; a tampered token was rejected.
- Not yet seen in a browser end to end. The cloud sandbox's proxy does not carry WebSocket connections, so the app's live data never loaded there. The first laptop run should open Sample Advice and the Add dialog as each test user.
Delete Is a Folio Fact, Not a Placement
A deleted Folio is omitted from the live canvas and from every newly sealed schedule, keeps its live pin so Put back restores its position, stays exactly as it was in every older sealed edition, and is excluded from ordinary search and discovery by default. Delete is never Remove.
Content Writes Receive One Server-Constructed Authority
Why content mutations resolve their actor and capabilities from a server-held Document session instead of accepting createdBy from the client