İçeriğe geç

ADR-021: Database Roles And Agent Isolation

Geliştirme · 0.0.0-dev

Yayın

Doküman
0.0.0-dev
Uygulama
0.0.0

Bu sayfa

Uygulama
0.0.0
  • Status: Accepted
  • Date: 2026-08-18
  • Roadmap task: F1-031
  • Decision owners: database platform and execution security maintainers

Münderecat needs migration, runtime, curation, audit and legacy-reading capabilities with materially different privilege boundaries. A single local administrator credential would collapse those boundaries and would also let parallel agents mutate shared state. The bootstrap plan further forbids issuing write-agent credentials until F1-033 certifies the complete execution chain.

ADR-015 selected Supabase CLI as the canonical local topology, but recorded that Docker Desktop can ignore the bridge’s requested loopback binding and publish CLI-managed ports on wildcard interfaces. Real corpus data or writable credentials cannot be admitted while that condition is only a warning.

PostgreSQL references:

The migration creates eight idempotent, NOLOGIN, NOINHERIT capability roles: legacy_reader, agent_local_owner, ci_migrator, app_runtime, curation_executor, prod_migrator, auditor and break_glass_owner.

All are NOSUPERUSER, NOCREATEDB, NOCREATEROLE, NOREPLICATION and NOBYPASSRLS. PostgreSQL 17 records the non-superuser role creator with ADMIN TRUE, SET FALSE, INHERIT FALSE; the live gate permits only this non-exercisable administrative edge and rejects every usable or external membership. It also requires that no role can become the migration creator, which closes transitive control of that administrative edge. The human-only break-glass capability has no login or repository issuance path; any incident-scoped elevation remains an external two-approval operation. Read-only roles set default_transaction_read_only=on.

Database and public-schema grants are explicit. app_runtime and curation_executor receive no direct table privilege and no routine execute in this foundation migration. Future core/RPC migrations must name their grants. Default privileges for all migration-owner capabilities remove PUBLIC table, sequence, routine and type access, preventing a newly created object from silently widening the runtime surface.

config/db-access-policy.yml is the machine-readable intent. A static verifier checks its closed schema and security invariants; a live verifier compares pg_roles, role settings, database/schema capabilities, memberships and public ACLs after migration replay.

Every write-capable packet gets a separate Supabase CLI project and Docker network, not merely another database in a shared mutable cluster. Its identity and three-port allocation are deterministic from packet ID; occupied ports fail before startup. Its 256-bit random password is not deterministic and is never placed in argv, stdout, Git or execution evidence.

The lifecycle order is fixed:

  1. Validate packet, registered worktree, active lease and holder identity.
  2. Validate the F1-033 trusted readiness receipt from Git common metadata.
  3. Reserve a packet-unique identity and free port set.
  4. Materialize canonical config and migrations outside the repository.
  5. Create the packet network and start its Supabase CLI stack.
  6. Discover every container carrying the packet’s Supabase project label, then verify all published bindings and non-loopback reachability.
  7. Create the packet login, grant only agent_local_owner membership and make that login owner of the disposable database/public schema.
  8. Write a mode-0600 credential file as the final step.

Any failure before credential issuance stops the stack without backup, removes its network and deletes generated state. If cleanup itself fails, state is kept as cleanup_required; an active container is never hidden by deleting its record.

Canonical and packet startup both discover the complete Supabase project by Docker label, require loopback-only bindings for every published port and run a negative TCP probe from each non-loopback IPv4 and IPv6 host address. The API gateway and PostgreSQL container must exist and publish their exact expected container-port/host-port mappings with no extra published mapping; internal containers may publish none. Wildcard publication anywhere in the project is a hard failure followed by cleanup. There is no warning-only or environment-variable bypass.

On Docker Desktop versions that cannot honor this boundary, local database startup remains unavailable. This is preferable to treating a known local credential and future corpus API as LAN-safe. Linux/CI hosts that honor the binding continue to use the pinned Supabase CLI topology.

F1-031 implements the provisioning mechanism but does not activate it. A real start requires a hash-bound receipt in the existing protected trusted-receipt store for the committed F1-033 readiness certificate on main. Candidate worktree content and an ordinary file in Git metadata cannot substitute for that receipt. F1-033 owns receipt issuance after its independent verification.

The local operating-system account and its filesystem permissions are the operator trust boundary. MUNDERECAT_ACTOR_ID is an audit/lease identity, not cryptographic authentication by itself. Credential issuance additionally requires an operator-owned repository and Git common directory, the protected F1-033 receipt, and the active registered worktree/lease checks. Multi-user or remote-agent execution requires a separate authenticated broker and is outside this local bootstrap contract.

  • Runtime, curation, migration, audit and emergency capabilities are distinct and inspectable.
  • New objects do not acquire accidental public write or execute grants.
  • Parallel write packets cannot share project, network, ports, login, password or mutable database state.
  • Credential material exists only after all authority and exposure gates pass.
  • Deterministic tests exercise failure ordering without repeatedly starting Docker; CI performs one real migration replay and live ACL comparison.
  • Each active write packet consumes a complete minimal Supabase stack.
  • Docker Desktop installations with wildcard-only CLI publication fail closed until their runtime provides a verifiable loopback binding.
  • Future schema migrations must explicitly grant each approved runtime read or RPC execute capability.
  • A failed cleanup requires visible operator action instead of silently discarding state.

Global roles, extensions, server settings and resource pressure would remain shared. One privileged or faulty migration could affect every agent, so this does not satisfy the plan’s isolation contract.

This is simpler but makes policy names documentary only and lets application or curation paths bypass ownership, RLS and grant boundaries. It was rejected.

ADR-015’s initial empty-stack warning was sufficient only before real data and credentials. Continuing after detection would turn a known exposure into an accepted operating mode, so startup now fails closed.

Repeated test execution by an AI reviewer adds latency without independent judgment. The lead records deterministic evidence once. Reviewers inspect the final PostgreSQL privilege semantics and lifecycle security boundary only.

  • Supabase CLI gains a supported host binding option that is verified on every target runtime.
  • F1-033 changes the trusted receipt schema or integration authority.
  • Application schema work identifies a smaller runtime or curation capability that should become a separate role.
  • Production platform credential issuance or managed Supabase role constraints require a provider-specific adapter.