This file is the source of truth for how to work with this repository. Any agent, LLM, or contributor working on this project must read and follow these instructions.
This is a centralized OAuth / authentication service used by multiple products.
These documents define what we're building and how:
Docs/brief.md— The full build brief. Defines the product, features, security model, constraints, and task breakdown. This is the single source of truth for what we're building.Docs/techstack.md— The tech stack and project structure. Defines technology choices (Node.js, React, Tailwind, PostgreSQL + Prisma), folder layout (/API,/Auth), and environment variables. This is the single source of truth for how it's built.Docs/Auth/architecture-api.md— API architecture. Defines the layered structure (routes → middleware → services → Prisma), directory layout, error handling patterns, and file organization rules for/API.Docs/Auth/architecture-auth.md— Auth window architecture. Defines the React component structure, theming system, i18n approach, and auth flow navigation for/Auth.Docs/Admin/architecture-admin.md— Admin panel architecture. Defines the React CSR structure, auth boundary, quality gate, and component/data-layer rules for/Admin.Docs/Admin/README.md— Admin template baseline. Identifies the existing HTML templates that the React admin app must translate rather than redesign.Docs/Requirements/roles-and-acl.md— Role and admin-auth requirements. Definessystem_admin,/internal/admin/*auth boundaries, and org/team role semantics.Docs/deploy.md— Deployment to Google Cloud Run (build, deploy, env vars, service config).Docs/deployment.md— How the whole product estate deploys: UOA on Cloud Run, every consuming product on the shared Hetzner host, and how to tell when a change is onmainbut not live. Read this before concluding a UOA change "does not work" — a capability only takes effect once the product redeploys and serves a new config JWT.Docs/api-2.0-implementation-plan.md— Branch-specific implementation plan forapi-2.0. Read this as the execution guide when working on that branch.
Before making any architectural, design, or implementation decisions, read all documents above in full.
If you are working on branch api-2.0, read Docs/api-2.0-implementation-plan.md before implementation work.
- Do not remove or rewrite content from
Docs/brief.mdunless explicitly instructed - Clarifications and additions are welcome; deletions are not
- If something in the brief seems wrong, flag it — don't silently change it
- All implementation decisions must align with
Docs/brief.md - If the brief doesn't cover something, ask before assuming
- If you spot a contradiction, raise it rather than picking a side
- No code file longer than 500 lines. Documentation is exempt, but all
.ts,.tsx,.js,.jsxfiles must stay under 500 lines. Split if approaching the limit. - Keep the codebase reusable and clean. This is the hard rule. Prefer one React component per file, but small helper components that only exist to support a parent can live in the same file if it makes sense. If a component is reusable, it gets its own file.
- Keep it simple — no over-engineering
- No premature abstractions
- No features beyond what the brief specifies
- Tailwind-only for UI — no other CSS frameworks
- Stateless where possible
- Follow the architecture docs:
architecture-api.mdfor API,architecture-auth.mdfor the auth window,architecture-admin.mdfor the admin panel
- Shared secret never in code, only in environment variables
- No email enumeration — ever
- All auth errors are generic to the user
- Only provider-verified emails accepted from social logins
- All config JWTs must be verified before trust
- Confidential
/auth/tokenassertions must be RS256, short-lived, audience-bound, and verified through the source config's same-host JWKS. Always re-resolve the current user and source-domain role; when an assertion includes team context, re-resolve its current org/team membership before issue. After those checks, atomically consume the source-domainjtionce through the assertion's accepted expiry plus clock tolerance before signing. - Confidential resource tokens use the shared RS256 resource-token signer and
/oauth/jwks.json, carry only the non-secret source domain asazp, and must never contain the per-domain hash bearer credential. The signing key does not enable the optional public OAuth profile; that profile requires its explicit gate. - The user access token is HS256 over
SHARED_SECRETby default, which is also the domain-hash secret and refresh-token pepper and therefore can never be published for relying parties to verify. SettingUSER_ACCESS_TOKEN_PRIVATE_JWK+USER_ACCESS_TOKEN_PUBLIC_JWKS_JSONswitches issuance to RS256 with the verification key atGET /oauth/jwks.json; identical claims/iss/aud/TTL, so it is a drop-in. Verification accepts both algorithms with strictly separate key material per branch — never one key class for the other. Relying-party verification is defence in depth and never replaces UOA'stokenVersionrevocation check. SeeDocs/Auth/access-token-verification.md. - Billing relying-party
X-UOA-Actorassertions must name the exact endpoint they are presented to:aud = ${PUBLIC_BASE_URL}${request path}, verified per endpoint after the signature, refused withBILLING_ACTOR_AUDIENCE_MISMATCH. One constant audience for every endpoint is the legacy shape and is accepted only whileBILLING_ACTOR_AUDIENCE_MODE=warn(the transition default), which logs every use. SeeDocs/Auth/billing-actor-assertions.md. - Avatar uploads are raster-only (PNG/JPEG/WebP), magic-byte sniffed, and size-capped; SVG uploads are rejected. Provider avatar fetches are HTTPS-only, SSRF-guarded, and fail closed to the generated image. Generated SVGs are server-authored only and served with
nosniffplus a restrictive CSP. SeeDocs/Auth/avatars.md.
GET /apimust always return the full endpoint schema for all routes (method, path, description, auth, query, body, response)GET /is the public holding page linking to/admin,/llm, and/apiGET /llmmust always return Markdown instructions for LLMs and human integrators, and must link/apifor machine-readable JSON- When adding, removing, or changing any endpoint, update both
API/src/routes/root/index.tsandAPI/src/routes/root/llm.ts /apiis the machine-readable contract for the API;/llmis the readable integration guide. They must never fall out of sync
These are explicitly out of scope unless noted otherwise (see brief section 20):
- Admin dashboard is now in scope; see
Docs/Admin/ - Avatar upload/storage and generated fallbacks are now in scope; see
Docs/Auth/avatars.md(supersedes "no local avatar storage") - No per-client OAuth secrets
- No user-visible error specificity
- No unsigned configs accepted
- Refresh tokens are now implemented; see
Docs/Auth/long-lived-tokens.md - No backup codes for 2FA
CLAUDE.md — This file (project instructions)
AGENTS.md — Agent onboarding instructions
Docs/
brief.md — Full build brief (the spec)
techstack.md — Tech stack and project structure
Auth/
architecture-api.md — API architecture (/API)
architecture-auth.md — Auth window architecture (/Auth)
Admin/
architecture-admin.md — Admin panel architecture (/Admin)
README.md — Admin template baseline
Requirements/
roles-and-acl.md — Roles, ACL, and system-admin auth requirements
API/ — Node.js auth server (see Docs/Auth/architecture-api.md)
Auth/ — React auth UI (see Docs/Auth/architecture-auth.md)
Admin/ — Admin panel UI
- Read
Docs/brief.mdbefore doing anything - Read this file (
CLAUDE.md) for working rules - On branch
api-2.0, readDocs/api-2.0-implementation-plan.mdbefore implementation work - When in doubt, ask — don't assume
- Commit messages should be clear and describe the "why"
- Don't create files unless necessary — prefer editing existing ones