79 lines
9.5 KiB
Markdown
79 lines
9.5 KiB
Markdown
# 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-101’s 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 review iterated through role inheritability, hash-complete migration ordering, all reachable runtime DDL entrypoints, installer DSN persistence, and platform-bootstrap ownership; each finding was incorporated into the final frozen contract/DAG. The last security review found no new KBN-101 vulnerability; its sole low finding is the pre-existing unstaged Mosaic session-lock metadata, which is excluded from this commit.
|
||
- Commit: `82ce3252df38a687c50485f8d048b53ca8db5989` (`docs(#771): freeze database runtime role split`).
|
||
- Pre-push queue guard: `ci-queue-wait.sh --purpose push -B main` returned `state=unknown` without failure. The push hook ran repository `pnpm typecheck`, `pnpm lint`, and `pnpm format:check`: PASS.
|
||
- Pushed branch `docs/771-kbn101-db-role-split` at the exact commit above; no PR was opened, merged, or closed. `web1:mosaic-100` received the handoff with head, decisions, DAG, and validation.
|
||
- Awaiting independent security/Ultron review.
|