Skip to content

feat(auth): capability-tiered trust — agent-rooted KB self-signup vs human-anchored custody (US-26.7.1)#303

Merged
mkreyman merged 1 commit into
masterfrom
feat/capability-tiered-trust
Jul 4, 2026
Merged

feat(auth): capability-tiered trust — agent-rooted KB self-signup vs human-anchored custody (US-26.7.1)#303
mkreyman merged 1 commit into
masterfrom
feat/capability-tiered-trust

Conversation

@mkreyman

@mkreyman mkreyman commented Jul 4, 2026

Copy link
Copy Markdown
Owner

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):

  • KB tier (agent-rooted): a lightweight, rate-limited POST /api/v1/signup (+ MCP signup tool) 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).
  • Custody tier (human-anchored): the work-breakdown / chain-of-custody surface stays behind a new RequireHumanAnchor plug, keyed on tenant.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)

  • Spec review (pre-implementation): found 5 critical tier-gate bypasses in the story itself — a missing :create_in_project alias, 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.
  • Full enhanced review (post-implementation): team (security/architecture/correctness/completeness) + adversarial + VCA. No custody bypass survived. 10 confirmed findings (1 high / 4 med / 5 low), all fixed in-branch — no deferrals — including a pre-existing audit-key-rotation orphan bug (keypair-mismatch) the review surfaced.

Notable hardening

  • Transactional secret safety: external Fly secret writes now compensate on mid-Multi failure (delete for signup/bootstrap, restore-old-key for rotate), with a durable retry OrphanedSecretCleanupWorker + telemetry when compensation itself fails. Process-dictionary bridge removed.
  • DB-level CHECK constraints backstop trust_tier and the superadmin↔tenant_id IS NULL invariant.

mix precommit green: 3718 tests, 0 failures; credo --strict clean; dialyzer passed.

…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.
@mkreyman mkreyman merged commit 6fe2717 into master Jul 4, 2026
10 checks passed
@mkreyman mkreyman deleted the feat/capability-tiered-trust branch July 4, 2026 06:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant