ADR-0007: Identity through the git provider, sessions and RBAC ​
Status: accepted (2026-10-12)
Decision ​
- Users sign in with the git provider. For GitHub this is the user authorization flow of the configured GitHub App (
/login/oauth/authorizewithstateand PKCE S256; token exchange at/login/oauth/access_token). There are no local passwords, TOTP or separate identity service. Generic OIDC (with Dex as optional broker) is specified in ADR-0013. - Identities live in the system-only
userstable, unique per provider, host and external id. Sessions live in the system-onlyauth_sessionstable: only SHA-256 hashes of the session and CSRF tokens are stored; the GitHub user token is stored encrypted and the session never outlives it (8 h). Refresh tokens are not stored. - The session cookie is opaque,
HttpOnly,Securein production,SameSite=Lax, rotated at login. State-changing requests require the CSRF token header and, when present, anOriginequal toPUBLIC_URL. - Amendment: bearer tokens are accepted on
/mcponly. Routes markedauth: 'bearer'authenticate with a personal MCP access token in theAuthorizationheader, ignore cookies, need no CSRF token and still require anOriginequal toPUBLIC_URLwhen one is sent. Cookie routes never accept a bearer principal. Token format, grant derivation and freshness are specified in ADR-0018. - Every route declares a
Permissionin its route config; the app refuses to start otherwise.isAllowed(permission, grants)in@quavon/shared-typesis the single permission matrix. - Grants are derived from GitHub and cached in tenant-scoped
tenant_memberships/repo_memberships: org owners and members of the tenant's configured admin team are tenant admins; repo admin/maintain → maintainer, write → reviewer, read → reader. Repository roles only count for members of the installation's organisation, so read access to a public repository grants nothing. - Instance administration (GitHub Apps, OAuth settings, instance settings, instance audit) requires
instance_admin. The first GitHub login that carries a valid setup session claims it; afterwards it is granted by an instance admin or the CLI. - Break-glass: a CLI command creates a single-use, short-lived link (
setup_tokens.purpose = 'break_glass'). The resulting session has no user, is limited to instance administration and expires after one hour. - Every mutation writes an
audit_logentry. Instance-level actions usetenant_id = null, which forms its own hash chain. - Secrets entered in the UI (GitHub App credentials, LLM keys) are encrypted with
@quavon/cryptobound to<table>:<id>, returned only masked, and never logged.
Consequences ​
- Role changes on GitHub reach Quavon within the cache TTL; mutations re-check grants that are older than a short threshold.