Doctrine

ORA-2026-0115 — Credential grant registry discipline

credentialsregistryworkspaceoauthdwdsecurity-debt

ORA-2026-0115 — Credential grant registry discipline

Rule

Every external credential grant — Workspace DWD, OAuth client, IAM role, API key, vendor portal access, Stripe restricted key, Slack app install, GitHub App installation — MUST have a corresponding row in the fleet credential registry. A grant without a registry row is a security-debt violation; the registry, not the admin console, is the source of truth for what is granted, why, to whom, with what scopes, by whom, and last-validated when.

The registry lives at /Users/chadbarlow/Desktop/fleet/registries/credentials.md (markdown table — fleet-greppable, easy to audit). Promote to a heartwood-db table only when query patterns demand structured access; markdown is the default for fleet-discoverable surfaces because every seat can Read it without a connector.

Storage-format follow-on (peer-review note): Gemini reviewer (2026-05-01) flagged that a single 9-column markdown table risks (a) horizontal width hostile to CLI reads at scale and (b) git-blame collisions on concurrent writes. Counter-proposal: directory of files (registries/credentials/<credential_id>.md) with YAML frontmatter for structured fields + markdown body for audit trail. This is a valid future shape — file as FLT-0636-followon-storage-shape ticket if the registry crosses ~25 rows or experiences a write collision, whichever fires first. For now (single-digit-row registry, single-writer fleet writer-of-record), the markdown table holds.

Required fields per row

FieldMeaningExample
whatExact identifier (client_id / key fingerprint / role ARN / app ID)108547392145678901234 (21-digit SA client_id)
whereWhich system the grant lives inadmin.heartwoodcustombuilders.com → Security → DWD
whyLiteral use-site (which pipeline / surface / feature requires it)gmail-financial-pipeline edge function impersonating admin@HCB
scopesExplicit scope list — no +N More placeholdersgmail.readonly, gmail.metadata, gmail.modify, gmail.compose, calendar, drive, admin.directory.user.readonly, admin.directory.group.readonly
ownerWho created it, when, with what authorizationChad 2026-04-29 per FLT-0630 directive
last_validatedDate last verified against use-site (drift detection)2026-04-29
stateACTIVE / STALE_SINCE: <date> / UNDER_INVESTIGATION / DELETED <date>ACTIVE
provider_domainPer ORA-2026-0075 — keychain service= fieldworkspace.google.com
account_labelPer ORA-2026-0075 — keychain account= fieldHCB_WORKSPACE_INTEGRATION_SA

Why

HCB Gmail 403-trio incident, 2026-04-29. Three service accounts had Domain-Wide Delegation on the heartwoodcustombuilders.com Workspace: orbit-mcp-runtime (101150455469870397475), claude_1 (107312379978944480251), Data Migration (New) (111745453960285227386). The fleet had zero visibility into which surface used which SA, what scopes each held beyond truncated UI display (+3 More), who created Data Migration (New) (which carried gmail.modify + gmail.insert write scopes — high blast radius), or whether any were orphaned. When admin@HCB pipeline 403'd, the fleet spent hours decomposing which-grant-is-broken instead of querying a registry.

The diagnostic fork even reached the wrong root cause ("DWD missing for admin@HCB") because the registry didn't exist to disconfirm. The actual root cause was Google's super-admin SA-impersonation block — a structural fact discoverable in 30 seconds with a registry, but invisible without one.

Chad framing (2026-04-29): "part of the problem is lack of understanding and discipline and record for what settings are already set on what accounts and for what purpose."

How to apply

New grant

  • Same turn as the grant: file the registry row. No grant lives undocumented past the turn that created it.
  • Registry row is part of the FOLLOW-ONS line on the DONE post that authorized the grant. Untied grants are equivalent to dormant capabilities (ORA-2026-0036) but with negative blast radius — a live attack surface with no documented owner.

Existing grant (archaeology)

  • Boy-Scout rule applies: the seat that discovers an undocumented grant fills the row before continuing. No "I'll file it later" — context decay makes later more expensive than now.
  • If the use-site cannot be named after a 5-minute audit, the row is filed state: UNDER_INVESTIGATION with the discovering seat's id and a follow-on ticket to identify or tombstone the grant.

Drift detection

  • last_validated <90 days rows surface to STRAT shepherd as drift candidates each tick.
  • A row that has been UNDER_INVESTIGATION for >14 days advances on the tombstone ladder (ORA-2026-0029 / Standing Directive §11): rename use-site label to _orphan_<original>, file deletion ticket as CRITICAL.

Cross-Workspace asymmetry

  • When the same SA is granted on multiple Workspaces with different scope sets (e.g., orbit-mcp-runtime had 5 scopes on HCB vs. 3 on camberzero), the registry MUST list both grants and call out the asymmetry. Asymmetric scope sets are a registry signal — usually means one Workspace was migrated without the other.

"+N More" UI placeholders

  • Forbidden in the registry. The registry expands what the admin console truncates. If the admin console shows +3 More, the seat filing the row clicks through to enumerate every scope explicitly.

Anti-patterns this prevents

  • "Why does Workspace X have client_id Y granted? — let me try git-blame archaeology" (current state without registry)
  • Grant proliferation — adding a new grant alongside existing grants without checking if existing ones cover the use-case
  • "Scary to delete because we don't know what uses it" — orphaned grants that compound because removal is too risky without a registry to disconfirm dependence
  • Diagnostic forks reaching wrong root causes because they cannot see what is actually configured
  • "Gate on branch, credential in prod" (ORA-2026-0036 sibling) — provisioning credentials before the gate-and-registry are also live

Relationship to peer doctrines

  • ORA-2026-0044 (names carry contracts): Registry rows MUST use shape names, not mechanism names — hcb-workspace-integration not orbit-mcp-runtime or Data Migration (New). The registry enforces the naming doctrine at grant-creation time.
  • ORA-2026-0075 (credentials carry domain context): Registry rows pin the service=<provider-domain> and account=<PROJECT>_<SCOPE>_<KEY_TYPE> keychain format. The registry is the SSOT index that 0075 mandates.
  • ORA-2026-0036 (credential-gate atomicity): Registry row + activation gate + credential provisioning are a 3-tuple that MUST land in the same merge. A row without a credential is OK (planning); a credential without a row is a violation.
  • feedback_durable_auth_principle.md (candidate ORA): Registry rows include an auth_class: field — platform-bound (durable: SA+DWD, GitHub App, AWS IAM) vs. user-bound (fragile: OAuth refresh token, user PAT). User-bound rows MUST carry a documented exception reason.

Initial registry rows (FLT-0636 closure)

The registry is seeded at FLT-0636 closure with these rows:

whatwhereaccount_labelprovider_domainstatelast_validatednotes
hcb-workspace-integration@hcb-workspace-prod.iam.gserviceaccount.comadmin.heartwoodcustombuilders.com → Security → DWDHCB_WORKSPACE_INTEGRATION_SAworkspace.google.comACTIVE2026-04-29Sole consolidated SA for HCB Workspace. 13-scope DWD grant. Replaces 3 prior SAs. Per project_workspace_sa_consolidation_directive.md.
orbit-mcp-runtime (HCB)DELETED 2026-04-29 from HCB DWD_orphan_orbit-mcp-runtimeworkspace.google.comDELETED 2026-04-29n/aReplaced by hcb-workspace-integration. SA itself disabled with 24h dwell, scheduled for deletion.
claude_1 (HCB)DELETED 2026-04-29 from HCB DWD_orphan_claude_1workspace.google.comDELETED 2026-04-29n/aReplaced by hcb-workspace-integration.
Data Migration (New) (HCB)DELETED 2026-04-29 from HCB DWD_orphan_data_migration_newworkspace.google.comDELETED 2026-04-29n/aReplaced by hcb-workspace-integration. Carried gmail.insert write scope — likely never used in fleet code.
orbit-mcp-runtime (camberzero)admin.camberzero.com → Security → DWDCAMBERZERO_ORBIT_MCP_RUNTIME_SAworkspace.google.comSTALE_SINCE: 2026-04-29n/aPending camberzero Workspace migration. Will be replaced by camberzero-workspace-integration per playbook.
super@heartwoodcustombuilders.comCloud Identity Free / HCB WorkspaceHCB_WORKSPACE_SUPER_ADMINworkspace.google.comACTIVE2026-04-29Break-glass super-admin. 2FA enrolled. Authenticator app + 8 backup codes. Per reference_credentials_hcb_workspace_super_admin.md.

Future credential-related work (camberzero migration, Stripe restricted keys, Slack app installs, GitHub App tokens) extends the registry rather than re-inventing the documentation surface.

Enforcement

  • Code review on any PR that introduces a credential reference (env var, config block, edge function secret): reviewer asks "is the registry row filed?" before approving.
  • Shepherd tick: last_validated <90 days rows surface as drift candidates.
  • Quarterly fleet audit: enumerate every external grant via admin console snapshots, diff against the registry. Untracked grants are filed as UNDER_INVESTIGATION rows.

Operator note

Chad 2026-04-29 — "i would like to consolidate all the settings into one for each account and it should have nothing to do with orbit by name or infra." The directive that closed the proximate HCB consolidation also seeded this registry doctrine: the consolidation surfaces what is true now; the registry guarantees we will remember it tomorrow.

See also

  • feedback_credential_settings_registry.md — origin feedback for this doctrine
  • project_workspace_sa_consolidation_directive.md — HCB migration directive that surfaced the gap
  • reference_workspace_tenant_consolidation_playbook.md — 7-phase playbook for the next Workspace tenant
  • ORA-2026-0036 — credential-gate atomicity (sibling)
  • ORA-2026-0044 — names carry contracts (registry naming)
  • ORA-2026-0075 — credentials carry domain context (registry schema)
  • /Users/chadbarlow/Desktop/fleet/registries/credentials.md — the registry surface itself