feat(auth): capability-tiered trust — agent-rooted KB self-signup vs human-anchored custody (US-26.7.1)#303
Merged
Conversation
…an-anchored custody (US-26.7.1) Adds a KB-tier self-signup path (POST /api/v1/signup, no WebAuthn ceremony) alongside the existing human-anchored WebAuthn signup, gated by a new trust_tier column on tenants. The RequireHumanAnchor plug blocks the work-breakdown / chain-of-custody surface for agent-rooted tenants while leaving the full knowledge-wiki surface open, keyed on tenant trust_tier (never role) so a self-minted higher-role key cannot bypass the gate. - Migration + Tenant schema/changesets set trust_tier programmatically, never via cast; existing tenants with an enrolled authenticator are backfilled to human_anchored. DB CHECK constraints enforce the trust_tier value set and the superadmin-iff-null-tenant api_keys invariant (L2). - Loopctl.Tenants.self_signup/1 shares the keypair/genesis-audit Multi with signup/1 via a with_secret_compensation/3 helper (lexical secret name, no process dictionary): the external Fly secret write is deleted if any later Multi step fails or raises. A failed compensation is never swallowed - it emits [:loopctl,:secrets,:orphan_cleanup_failed] telemetry and enqueues a durable OrphanedSecretCleanupWorker retry. The same machinery is extended to bootstrap_audit_key/1 (delete) and rotate_audit_key/2 (restore the OLD key so the rolled-back OLD public key still verifies). - RequireHumanAnchor is applied to every work-breakdown/chain-of-custody controller action (stories, epics, dependencies, dispatch, orchestrator state, tokens, bulk ops, cap recovery, project import, artifact reports, skill definitions), verified against each controller's real action atoms, with a default-deny router-walk test as the completeness backstop. - Public signup rate-limit counts oversized requests and uses a proxy-aware, non-shared per-IP bucket. MCP server gains a public signup tool; discovery doc, OpenAPI spec, and chain-of-custody-v2.md document the two-tier model.
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.
US-26.7.1 — Capability-Tiered Trust
Attaches trust to the capability, not to tenant genesis, resolving the two-use-case conflict (supervised code workflow vs. agent-native KB):
POST /api/v1/signup(+ MCPsignuptool) mints a tenant with no human/WebAuthn ceremony. It gets the full knowledge surface immediately (ingest/search/curate, article CRUD, BYO-LLM config, own agent identity).RequireHumanAnchorplug, keyed ontenant.trust_tier(orthogonal to role, so a self-minted higher-role key can't escape it). Existing WebAuthn-signed tenants are unaffected.This does not weaken L0 for the ops that depend on it — it scopes where the boundary sits. The 409 self-custody guards and dispatch-lineage checks are independent of L0 and remain intact. The opt-in WebAuthn upgrade flow (agent-rooted → human-anchored) is a signed-off fast-follow (US-26.7.2).
Two-stage review (both ran before merge)
:create_in_projectalias, whole un-enumerated custody controllers (Bulk/CapRecovery/ImportExport), and action-atom-vs-prose mismatches that compile to silent no-op guards. Fixed in the spec, plus added a default-deny router-walk guard test so any future custody route fails closed.Notable hardening
Multifailure (delete for signup/bootstrap, restore-old-key for rotate), with a durable retryOrphanedSecretCleanupWorker+ telemetry when compensation itself fails. Process-dictionary bridge removed.trust_tierand the superadmin↔tenant_id IS NULLinvariant.mix precommitgreen: 3718 tests, 0 failures; credo --strict clean; dialyzer passed.