The daemon branched on AUTHSOME_DEPLOYMENT_MODE and ran two parallel implementations of nearly every ownership concept. "Local and hosted behave the same" was asserted in prose (ADR 0006) but never enforced in code, so the paths were free to drift, and the synthetic local principal let a second local identity silently inherit the admin principal. Collapse to one flow, identical for every deployment: authsome init registers an identity and gets back a browser claim URL; the user registers email+password (first principal becomes admin); that principal confirms the claim; PoP calls are then authorized. - Remove AUTHSOME_DEPLOYMENT_MODE, get_deployment_mode(), LOCAL_PRINCIPAL_EMAIL, the Local*/Hosted* resolver and bootstrap classes, and the AuthService(deployment_mode=...) parameter. - OwnershipResolver and IdentityBootstrapService become single concrete classes (the former hosted, claim-based implementations). - Admin gating is purely role-based: non-admin principals are blocked in every deployment (previously implicitly allowed in local mode). - The server UI always requires a hosted browser session; remove the vestigial HealthResponse.mode field. - CLI ensure_identity_ready was already mode-agnostic; it now prints the claim URL to stderr for headless use. Add ADR 0007 recording the decision; amend ADR 0006 and CONTEXT.md. BREAKING CHANGE: existing local installs have an unclaimed identity under local@authsome.internal and are rejected until the user registers a principal (email+password) and claims the identity. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Entire-Checkpoint: d31cf246f6f2
2.1 KiB
Principal roles: admin / user, first-created principal is admin
Principals need an authorization tier to gate deployment-level operations (audit log access, provider registration/deletion, cross-vault credential revocation) from per-principal operations (own connections, claim accept/reject). We store a role column (admin | user) directly on the principals table. The first Principal created on a server is assigned admin; all subsequent Principals receive user. Role assignment is immutable at creation time (mutation is deferred to a future milestone).
Considered options
Environment variable (AUTHSOME_ADMIN_PRINCIPALS) — the prior approach. Rejected because it requires knowing the PrincipalId before the server starts, cannot be changed without a restart, and is invisible to the UI and route layer.
Separate principal_roles table — considered for future extensibility (multiple roles per principal). Rejected as premature: the role model is binary and a join adds complexity without benefit today.
Default admin account created at server init — would require deciding on credentials before any real user exists. Rejected in favour of first-user-becomes-admin: the first principal to register is admin, all subsequent principals are users. (Originally justified as "zero-config" for an implicit local principal; that implicit-principal path was later removed when local and hosted were unified into a single registration + claim flow — see ADR 0007. First-principal-is-admin remains the rule, now reached the same way in every deployment.)
Consequences
AUTHSOME_ADMIN_PRINCIPALSenv var andis_admin_principal()are removed entirely.- Admin enforcement at the route level uses a
get_admin_auth_serviceFastAPI dependency (parallel toget_protected_auth_service) that raisesHTTP 403for non-admin principals. - Admin-only routes:
GET /audit/events,POST /providers,DELETE /providers/{provider},POST /connections/{provider}/revoke. - Schema migration:
ALTER TABLE principals ADD COLUMN role TEXT NOT NULL DEFAULT 'user', followed by setting the earliest-created principal to'admin'.