Files
stack/docs/scratchpads/771-kbn101-db-role-split.md
2026-07-15 06:26:03 -05:00

8.7 KiB
Raw Blame History

Scratchpad — KBN-101 DB runtime/migration role split (#771)

  • Branch: docs/771-kbn101-db-role-split
  • Base: main e9c4aa3
  • Scope: planning/documentation only; authorized files are PRD, Native Kanban task/shared/index docs, sitemap, this scratchpad, and the new KBN-101 contract.
  • Explicit exclusions: source/runtime/config/deployment/secret/migration/compose/CI/lock/package/KBN-100 branch edits; no production mutation.

Objective

Freeze an implementation-ready PostgreSQL role/connection split so the Gateway uses a non-owner runtime identity and only a dedicated migration phase uses an owner/migrator identity. Make real deployed-role certification—not synthetic role tests—a serial prerequisite of KBN-100 and KBN-105.

Intake and current-state evidence

  • Mission MVP is active; W3 Native Kanban/SOT is planning-complete. The task state shows KBN-010 as the predecessor and KBN-100 as the current schema slice.
  • Current branch started at e9c4aa3; .mosaic/orchestrator/{mission.json,session.lock} were already runtime-modified and remain untouched.
  • packages/db/src/client.ts, migrate.ts, and drizzle.config.ts resolve one DATABASE_URL (with default fallback). packages/storage/src/adapters/postgres.ts calls runMigrations(this.url).
  • apps/gateway/src/database/database.module.ts calls storageAdapter.migrate() at startup for PostgreSQL; this is the owner-runtime defect to remove in KBN-101 implementation.
  • packages/config/src/mosaic-config.ts, installer wizard, local/federated compose, Portainer test stack, and .woodpecker/ci.yml currently expose one URL. PGlite has an existing explicit local migration path.
  • Current KBN contract requires immutable events/checkpoints/artifacts/evidence, RESTRICT, and KBN-100 generated Drizzle consistency. It did not establish a deployable runtime identity split.

Frozen decisions

  1. DATABASE_URL is the non-owner runtime URL; DATABASE_MIGRATION_URL is migration-only. Both are required in their respective PostgreSQL phases; PGlite is the explicit local exception; migration never falls back to runtime/default/config URL.
  2. PostgreSQL Gateway runtime never auto-runs migration/DDL. Dedicated migrator uses pg_try_advisory_lock(hashtext('mosaic-schema-migration-v1')); replicas only check exact ordered Drizzle-ledger readiness and fail closed.
  3. Roles are non-login mosaic_platform_database_owner, non-login mosaic_schema_owner, login/noinherit mosaic_migrator, non-login mosaic_runtime_capability, and login/inherit mosaic_runtime. Runtime inherits only its capability role with SET/ADMIN denied, has no owner/migrator membership, no unsafe attributes/ownership/DDL authority, and an explicit trusted search path.
  4. Runtime gets mutable DML only as needed, but INSERT/SELECT only on task_events, artifacts, task_checkpoints, task_checkpoint_artifacts, and approval_decision_artifacts. KBN-100 still enforces RESTRICT/no-cascade.
  5. Startup verifies effective role/ownership/attributes/inherited capability/TEMP/function-execute/ledger grants/search path/immutable denials/schema fingerprint without DSN exposure. It also requires authenticated CA/hostname-verified TLS. Stable sanitized errors and redaction rules are required.
  6. N-1 retains single runtime URL only as a non-certified compatibility release; staged role provisioning/migration/runtime deployment then enforces the split. Rollback never injects migration URL into Gateway.
  7. Vault target paths, rotation, deployment injection, CI, installer, compose, Portainer, and observability are separate one-card/one-PR handoffs. The migration-only file manifest includes packages/db/drizzle.config.ts; KBN-101 repairs the known PostgreSQL runner/journal ordering defect and proves a clean database applies every hash once before its foundation certificate. No application migration creates roles/passwords or hardcodes credentials.
  8. KBN-101 foundation merges/certifies first. KBN-100 then rebases, restores Drizzle declaration/snapshot/journal consistency, and bounds procedural immutable-table grant/trigger/backfill work to its own slice. Because those immutable relations do not exist until KBN-100, KBN-101s real deployed-role immutable-operation certificate follows KBN-100 and is the serial gate before KBN-105.

Assumptions

  • standalone and federated are all current PostgreSQL production-like modes; a future PostgreSQL tier inherits this contract unless versioned otherwise.
  • Deployment will support a dedicated migration Job/one-shot command. A target that cannot run it cannot receive production/federated KBN certification.
  • Canonical Vault target paths require deployment-owner verification before provisioning; the planning document does not claim they already exist.

Documentation produced

  • docs/PRD.md: bounded KBN-101 requirements and acceptance criteria.
  • docs/native-kanban-sot/KBN-101-DB-ROLE-SPLIT.md: normative rc.5 implementation, threat, migration/rollback, evidence, and exact file DAG contract.
  • docs/native-kanban-sot/SHARED-CONTRACT.md: rc.5 amendment preserving rc.4.
  • docs/native-kanban-sot/TASKS.md: KBN-101 inserted before and blocks KBN-100; KBN-105 held.
  • Native Kanban index and root sitemap links.

Validation plan

  1. Prettier only for changed Markdown.
  2. Markdown link target/check checks scoped to modified docs.
  3. Strict contract check with pnpm exec tsc --noEmit -p docs/native-kanban-sot/tsconfig.json (the frozen TypeScript contracts remain unchanged).
  4. Diff allowlist proves only authorized documentation files changed, apart from pre-existing Mosaic runtime state.
  5. Independent documentation/security self-review: role escalation, fallback, startup DDL, schema readiness, grants/default privileges, immutable tables, secret leakage, deployment and KBN-100 boundaries.

Review corrections

Independent Codex review found two blockers and security review found two medium defects; all were remediated in the frozen contract:

  1. Split the KBN-101 certificate into a foundation role/schema-boundary certificate (before KBN-100) and real immutable-operation certificate (after KBN-100, before KBN-105). This preserves the requested KBN-100 block without requiring evidence for tables not yet created.
  2. Removed the legacy owner-runtime exception. N-1 compatibility preserves variable/config shape only; the KBN-101 runtime refuses owner/migrator identity and current single-URL installs remain on their previous release until role cutover.
  3. Introduced mosaic_platform_database_owner as a separate non-login platform role. mosaic_schema_owner owns application/ledger schemas only, not the database and has no database CREATE/ALTER/extension authority.
  4. Replaced blocking pg_advisory_lock with pg_try_advisory_lock and the deterministic DATABASE_MIGRATION_LOCKED failure.
  5. Review also flagged active .mosaic/orchestrator state. It was pre-existing launcher state and remains unstaged/uncommitted.
  6. Second review added packages/db/drizzle.config.ts to the migration-only slice, mandates DATABASE_MIGRATION_URL with a missing-variable negative, grants runtime only USAGE plus SELECT on drizzle.__drizzle_migrations, and verifies/revokes its ledger writes.
  7. Security review added DATABASE_TLS_CA_CERT_PATH / DatabaseTlsConfigDto with authenticated TLS and hostname/CA verification in production-like modes, explicit database TEMPORARY revocation/catalog denial testing, and default-PUBLIC function EXECUTE revocation with SECURITY DEFINER prohibited by default.
  8. Final review corrected the runtime login to inherit only its capability role with SET/ADMIN denied, and moved the known hash-complete migration-runner/journal repair into KBN-101-03 before the foundation certificate.
  9. Final manifest review added all live runtime DDL paths (packages/storage/src/tier-detection.ts, Gateway startup, and fleet-backlog) to KBN-101-02, requiring read-only extension probes and no PostgreSQL runtime auto-migration. It also requires KBN-101-04 to stop persisting either DSN into generated .env/mosaic.config.json, using only Vault/deployment references and injected variables.
  10. Provisioning review separated the external privileged platform bootstrap actor from the NOCREATEDB database-owner role and added KBN-101-00. That IaC/bootstrap card owns fresh/existing database role/ownership/grant/Vault transition evidence and is a foundation-certificate dependency.

Results

  • pnpm exec prettier --check on every authorized Markdown file: PASS.
  • Markdown link and whitespace checker on all seven authorized Markdown files: PASS.
  • pnpm exec tsc --noEmit -p docs/native-kanban-sot/tsconfig.json: PASS (frozen strict contracts unchanged).
  • Codex code/security review rerun is required after the above remediation; no source code or unauthorized files were changed.