How Oche resolves principals today and how to upgrade to Neon JWT RLS.
- API key -
x-oche-ownerheader or?key=(WebSocket). Sources:- Built-in demo keys (
demo-key-a,demo-key-b) → seed owners A/B OCHE_API_KEYSJSON in Worker env (local:.oche-keys.json)- Hashed rows in
api_keystable (created via/auth/keysUI)
- Built-in demo keys (
- JWT -
Authorization: Bearer <token>or?token=(WebSocket). Minted byPOST /auth/tokenusingOCHE_JWT_SECRET(HS256, 1h TTL). - RLS - Worker still calls
withPrincipal()→SET LOCAL app.current_owner. Policies unchanged; JWT/API key only affects which owner id is set.
AuthProviderexchanges the selected venue key for a JWT on load/switch.OwnerSwitchertoggles demo-key-a / demo-key-b (RLS demo)./settings/keys- create/revoke hashed API keys (plaintext shown once).
| Variable | Where | Purpose |
|---|---|---|
OCHE_JWT_SECRET |
.env, Wrangler secret (staging/prod) |
Sign/verify JWTs |
OCHE_API_KEYS |
apps/api/.dev.vars |
Extra key → owner map (local) |
Generate local keys: npm run keys:generate
Replace the GUC stand-in so Postgres reads the subject from a verified JWT:
- Enable Neon RLS on the project.
- Forward the user JWT on the DB connection (Hyperdrive +
Authorizationor Neon Auth). - Swap Drizzle policies to
authenticatedRole+authUid(ownerId)- see docs/examples/neon-jwt-rls.sql. - Drop
withPrincipal()GUC; Worker verifies OIDC/JWT once, DB enforces viaauth.user_id().
Alternatives: Clerk / Cloudflare Access at the edge → same pattern (verify JWT, pass sub to RLS).
- RLS.md - policy model
.cursor/rules/34-auth.mdc- agent rules