refactor!: unify local and hosted into a single deployment flow#348
Merged
Conversation
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
The first protected command now registers the identity and blocks on a browser email+password registration + claim (first account = admin), matching the single deployment flow. Also drop the removed `mode` field from the daemon health block. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Entire-Checkpoint: 68b4ca3c1fc0
The guide had drifted well beyond the deployment-flow change:
- Commands moved under the `provider` and `connections` groups
(`provider list`, `provider inspect`, `provider register|remove|revoke`,
`connections inspect`, `connections set-default`). Update every invocation.
- Remove the documented `get`, `export`, `--show-secret`, and `--json`
commands/flags — none exist. `connections inspect` is always redacted
and there is no human-table mode; output is always JSON (`{"v": 1, ...}`).
- Add sections for `scan`, `profile`, and `daemon restart`/`logs`.
- Note admin-only operations (register/revoke) and JSON-shaped expected
output throughout; fix the `daemon status` health block (no `mode`).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Entire-Checkpoint: b68c1ae5d66c
beubax
approved these changes
May 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Collapses the local/hosted deployment-mode split into one flow, identical for every deployment — the former hosted flow. There is no longer a deployment mode.
New flow:
authsome initregisters an Identity and the daemon returns a browser claim URL → the user registers (or logs in) with email + password (first Principal created becomes admin, rest are users) → that Principal confirms the claim → PoP-authenticated calls are then authorized.Why
Two code paths doubled the test surface, scattered
if get_deployment_mode() == "hosted"branches across five files, and meant "local and hosted behave the same" was asserted in prose (ADR 0006) but never enforced in code — free to drift. The syntheticlocal@authsome.internalprincipal also let a second local identity silently inherit the admin principal.Changes
AUTHSOME_DEPLOYMENT_MODE,get_deployment_mode(),LOCAL_PRINCIPAL_EMAIL, theLocal*/Hosted*resolver + bootstrap classes, and theAuthService(deployment_mode=...)parameter.OwnershipResolverandIdentityBootstrapServicebecome single concrete (claim-based) classes.HealthResponse.modefield.ensure_identity_readywas already mode-agnostic; it now prints the claim URL to stderr for headless use.Net −183 lines across 21 files.
Existing local installs have an Identity registered with no accepted claim and data under the
local@authsome.internalprincipal/vault. After upgrading, those identities are rejected until the user registers a Principal (email+password) and claims the Identity. A vault-rebind migration is possible but intentionally out of scope here.Test plan
uv run pytest— 352 passeduv run ruff check/ruff format— cleanuv run ty check src/— cleanpre-commit run --all-files— passed/auth/register→ confirm → PoPwhoami).src/ortests/.🤖 Generated with Claude Code