auth-lite-v2 — running in production, port 7772
This site is auth-lite-v2, the reference host of goappfw v2 — identity, tenancy, files, audit, a typed data plane, and an MCP endpoint for agents, all compiled into one Go process and mounted under this domain. No sidecars. No frontend deploy. Sign up below and a fresh tenant appears inside it. Try it: create a workspace, write a post, then point the same token at another tenant and watch it bounce.
Four doors into the same process — three for people, one for agents. Every one of them is served by the binary above: nothing behind a CDN, nothing deployed separately.
Sign up (a brand-new tenant + you), log in with a password or an emailed code, work with your own data. The same token against another tenant is denied — 403, by design.
→ 02 Admin UIThe operator surface: collections, a cross-tenant records browser, tenant lifecycle, IAM grants, files, logs, and the audit stream. Sign in with the admin token.
→ 03 MCP endpoint for agentsPoint Claude Code, Claude Desktop, or Cursor at /mcp and the agent gets this instance's typed tools — collections, records, tenants, storage — over the admin credential. The admin's MCP page builds the client config for you.
→ 04 OpenAPI documentEvery collection route this instance serves, generated from the live registry — it cannot drift from the data plane.
→
Present a Bearer JWT and an X-Tenant-ID; the framework verifies the token into a
tenant-bound actor and rejects any mismatch. Writes and reads go through the same routes the portal uses.
# create a record as tenant <tid> (JWT-bound: the token's Attrs["tenant"] must match)
curl -X POST /api/employees -H "X-Tenant-ID: $tid" \
-H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
-d '{"firstName":"Ada","email":"ada@x.test","role":"eng"}'
# list — same tenant binding
curl /api/employees -H "X-Tenant-ID: $tid" -H "Authorization: Bearer <token>"
Or — act as a tenant with the admin token (D-18): it is admitted on the data plane as a
tenant-impersonating identity, audited as <admin> as <tenant>.
# the admin token assumes <tid> (no JWT needed; audited). No X-Tenant-ID → 400.
curl -X POST /api/employees -H "X-Tenant-ID: $tid" \
-H "Authorization: Bearer $APPFW_ADMIN_TOKEN" -H "Content-Type: application/json" \
-d '{"firstName":"Ada","email":"ada@x.test"}'
The guarantees this example exists to demonstrate.
The data plane verifies a presented Bearer into a tenant-bound rbac.Actor; a mismatched tenant is denied (403). The admin surface admits the admin token or an admin-role JWT.
The portal's signup/signin (go-auth store + bcrypt) mints a JWT the framework then verifies — real production posture, not a dev shortcut. Users are tenant-local: credentials live inside each workspace's own DB, so login names the workspace before it names the user.
RequireTenantMatch binds every data-plane call to the token's tenant. The portal ships a live probe you can run with your own token to watch the 403 fire. The admin's records browser is the sanctioned cross-tenant surface — and its writes go through the normal data plane, audited.
The MCP tools at /mcp are generated from the same registry the REST plane serves, so an agent can never reach a collection the API doesn't expose. The endpoint is admin-gated (an unauthenticated call is a 401) and every tool call lands in the audit stream like any other write — no side door.
off in prod
The example's /api/dev/mint endpoint is disabled here (APPFW_DEV_MINT unset). Present a real token minted by your own login path over the same HMAC secret — or sign up for one.