# Janua Compact LLM context index for this repository. ## Read first - `AGENTS.md` — canonical agent operating instructions. - `README.md` — human/product overview when present. - `ECOSYSTEM.md` — MADFAM ecosystem and Enclii deployment context when present. - `llms-full.txt` — full context map and durable operating contract. - `packages/cli/README.md` — `@janua/cli` consumer provisioning (`janua provision *`). - `docs/PP_3B_STAGING_PIPELINE.md` — staging/prod pipeline, ops bootstrap, OAuth provisioning. - `examples/consumer-bootstrap/` — `janua.client.yaml` template for consumer repos. - `docs/EMAIL_SENDER_POLICY.md` — who our email comes from. THE RULE (a client display name never pairs with a MADFAM address), the vCTO gate, and how a tenant sends on its own provider account. - `docs/MADFAM_EMAIL_INTEGRATION.md` — the internal email API surface: the two send endpoints (`POST /send` raw, `POST /send-template` from the registry), the template registry, the naive-vs-Jinja render split, and sender resolution. The registry itself is `EMAIL_TEMPLATES` + `TEMPLATE_FILENAMES` in `apps/api/app/routers/v1/email.py`, and `GET /templates` returns it live. - `apps/api/app/mcp/README.md` — the janua **MCP server** (P1 pilot slice): the transactional-email endpoints exposed as typed MCP tools, generated from janua's OpenAPI, calling the HTTP API behind the same `X-Internal-API-Key` gate (no back door). How to run it (`python -m app.mcp`), the generator mapping, the auth model, and the drift guard tying tools to the schema. - `docs/architecture/SILENT_SSO_SESSION.md` — one browser session across the estate: the `janua_sso` cookie, `prompt=none`, and the hosted hop for hosts outside the cookie domain. - `docs/runbooks/oauth-shared-state-redis.md` — the hub for OAuth consent state (Redis only; `503` + `Retry-After` when Redis is down, `403` only for a real rejection), first-party clients, the account chooser, what each sign-out / password change / password reset / `DELETE /sessions` revokes, RFC 7009 `/oauth/revoke` (client authentication required), passkeys, and readiness that reports Redis and the database without gating on them. - `docs/runbooks/resend-domain-onboarding.md` — onboarding a client sending domain, and migrating a client to their own Resend account. - `docs/runbooks/resend-email-events.md` — the signed Resend webhook receiver, the per-app events feed, the never-track-token-links rule, and first-party open/click measurement on per-tenant tracking hosts. - `docs/runbooks/secrets/resend-webhook-secret-rotation.md` — the per-account webhook signing secret: Vault → ExternalSecret → env, rotating it without exposing it, and reading 404 vs 401. - `docs/reference/ISSUER_AND_JWKS.md` — the token verification contract other services link to: issuer selection (`JANUA_CUSTOM_DOMAIN` else `API_BASE_URL`), JWKS at `/.well-known/jwks.json` with one RS256 key, `kid` rotation as a hard cut, and `aud` per token type (ID token = `client_id`; access/service token = the client's registered audience). - `docs/runbooks/data-subject-request-access-audit.md` — data-subject request exports are readable only by the subject or a platform admin (others get the unknown-id 404), and the read-only audit `apps/api/scripts/audit_data_subject_request_access.py`. - `docs/runbooks/ALEMBIC_CONVERGENCE.md` — production migrations are applied by hand and recorded in `apps/api/alembic/PROD_ALEMBIC_STATE.json`, which the promote guard reads. ## Current state worth knowing (2026-09-07) - Magic links to a host **outside** `COOKIE_DOMAIN` take a **hosted hop**: janua renders a branded interstitial and forwards `?token=` to the product's `redirect_url`. The product must land that on a route that completes a session from an access token, using `@madfam/janua-next@0.3.0` or later. - The magic-link callback `GET` renders and **spends nothing**; only `POST` exchanges. Mail scanners no longer burn links. - Sending identity is **per tenant** (`app/services/sender_binding.py`), not one global From line. A vCTO client tenant can send from its own domain on **its own Resend account**, keyed by the env var its binding names in `credential_ref`. A missing tenant credential degrades the sender to the platform From line; it never blocks a sign-in link. - A tenant display name is never paired with an address on MADFAM's domain. ## Sessions, revocation and readiness (2026-10-05) - Revocation reads fail closed: while Redis cannot answer, refresh, session check, the OIDC refresh grant, introspection, userinfo, passkeys and social sign-in answer `503` + `Retry-After`. JWKS and discovery never touch Redis. - Every revocation goes through `AuthService.revoke_sessions` (row flags, refresh-token family, access JTI). `/auth/refresh` also refuses a refresh token whose `sessions` row is not live. Password reset revokes every session; password change keeps the current one; `sign-out-all` signs out the accounts held in this browser only. - Most routes (`get_current_user`) do not read the revocation list: a revoked session's access token works until `exp`. Owner ruling pending. - `GET /api/v1/health/ready` answers `200` with `"status": "degraded"` when Redis or the database is down; alerting must read the body. Health bodies carry status fields only. - Passkey binary values are base64url; use the SDK's `WebAuthnHelper` or `base64UrlToArrayBuffer` / `arrayBufferToBase64Url`, never bare `atob`. ## Email events and measurement (2026-09-26) - Resend webhooks land on `POST /api/v1/email/webhooks/resend/{cuenta}` (one slug per Resend account: `platform` plus one per tenant account, mapped in `WEBHOOK_SECRET_SETTINGS`, `app/services/email_events.py`), verified Svix-style with `RESEND_WEBHOOK_SECRET_`. Secret unset: 404. Bad signature: 401 «Invalid webhook signature». Stored: 200 `{"status":"stored"}`. - Janua measures opens/clicks itself (Resend tracking stays OFF) only for mail sent with `track_engagement: true` that carries no token link. Pixel `/e/o/{token}.gif`, click `/e/c/{token}/{i}`, on the tenant's tracking host. The setting a binding names in `tracking_host_setting` is that tenant's on-switch (production sets a host on the tenant's own domain). - A tracking host serves only `/e/o/*` and `/e/c/*`; everything else is 404 and websockets are closed (1008) by `TrackingHostScopeMiddleware`. - Production migrations are applied by hand; `PROD_ALEMBIC_STATE.json` records them (at `019_email_first_party_engagement` since 2026-09-25). ## Build and CI facts (2026-10-01) - GitHub-hosted jobs run on `ubuntu-24.04` (#675); ARC jobs fall back to it. - `@janua/typescript-sdk` >= 0.1.5 starts no timers or sockets without a browser `window`, so a `JanuaClient` per server render is safe; earlier 0.1.x leaked one interval per server render (pass `autoRefreshTokens: false` on the server there). SDKs publish via `publish-sdks.yml` (tag `-v`, `pnpm publish`); publish `typescript-sdk` and `ui` before framework SDKs. - `docker-publish.yml` does not rebuild the **docs** image on `pnpm-lock.yaml` or `packages/**` changes. Ship docs with `gh workflow run docker-publish.yml` (a dispatch rebuilds all five services). - Pending work (pnpm 10 in the app images, the skip sweep, the skipped auth e2e tests, the audit-log merge window and more) lives in one list: AGENTS.md → «Known gaps / Backlog». ## Production operations Routine production operations are Enclii-first. Use Enclii web, API, or CLI before raw infrastructure tools. Direct `kubectl`, `helm`, SSH, provider CLIs, `docker exec`, or direct container access is bootstrap/break-glass only.