docs(#771): close role split rc.8 residuals
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
# KBN-101 — Database Runtime/Migration Role Split
|
||||
|
||||
**Status:** frozen implementation contract; rc.7 residual remediation pending independent exact-head re-review for [#771](https://git.mosaicstack.dev/mosaicstack/stack/issues/771)
|
||||
**Status:** frozen implementation contract; rc.8 exact residual remediation complete, awaiting independent exact-head re-review for [#771](https://git.mosaicstack.dev/mosaicstack/stack/issues/771)
|
||||
|
||||
**Version:** 1.0.0-rc.7
|
||||
**Version:** 1.0.0-rc.8
|
||||
|
||||
**Dependency:** KBN-010 → **KBN-101 foundation** → KBN-100 → **KBN-101 deployed-role certification** → KBN-105
|
||||
**Scope:** PostgreSQL standalone/federated runtime identity, the sole application DDL runner, TLS bootstrap, readiness, deployment handoff, and evidence. This documentation card changes no database, secret, deployment, CI, runtime, migration, or compose artifact.
|
||||
@@ -27,13 +27,18 @@ Current `main` has one `DATABASE_URL` path in `packages/db/src/client.ts`, `migr
|
||||
|
||||
## 2. The sole PostgreSQL DDL control plane
|
||||
|
||||
`mosaic-db-migrator` is the **only application/CI/test command that may connect with DDL authority to PostgreSQL**. It owns, in one `max: 1` PostgreSQL session: migration-DTO parse, TLS/identity/search-path preflight, advisory-lock acquisition, manifest/ledger reconciliation, migration execution, postflight/readiness verification, lock release, and session close. It rejects `DATABASE_URL`-only invocation **before opening a connection or emitting DDL**. The only exception is an external privileged platform/IaC bootstrap actor (§6), which is not an application command, never receives either application URL, and has its separately audited fixed bootstrap artifact.
|
||||
`mosaic-db-migrator` is the **only application/CI/test command that may connect with DDL authority to PostgreSQL**. It owns, in one `max: 1` PostgreSQL session: migration-DTO parse, TLS/identity/search-path preflight, advisory-lock acquisition, manifest/ledger reconciliation, migration execution, postflight/readiness verification, lock release, and session close. It rejects `DATABASE_URL`-only invocation **before opening a connection or emitting DDL**. The only exception is the external privileged platform/IaC bootstrap actor (§4/§7), which is not an application command, never receives either application URL, and executes only the fixed bootstrap artifact.
|
||||
|
||||
**Executable contract (KBN-101-03 exclusive):** `packages/db/package.json` publishes `bin.mosaic-db-migrator`; its executable target is `packages/db/src/cli.ts`. KBN-101-03 also exclusively owns the immutable runner image recipe `docker/db-migrator.Dockerfile`. The CLI imports only private implementation modules `packages/db/src/migrator/runner.ts`, `packages/db/src/migrator/config.dto.ts`, `packages/db/src/migrator/manifest.ts`, `packages/db/src/migrator/identity.ts`, and `packages/db/src/migrator/tls.ts`, plus their tests. Package root exports do **not** expose `runMigrations`; no other programmatic migration API is public. The sole typed programmatic runner is private to the package/CLI boundary and accepts the typed migration DTO, never a URL/string/SQL/schema/role argument.
|
||||
|
||||
The exact user-facing interface is `mosaic-db-migrator --run` and `mosaic-db-migrator --verify`. `--help` is required and is the only discovery mode. Both commands use the fixed packaged migration folder and accept only `DATABASE_MIGRATION_URL`, `DATABASE_TLS_CA_CERT_PATH`, and non-secret `MOSAIC_DATABASE_TIER`/correlation input from the environment. They reject `DATABASE_URL` as fallback and reject URL, SQL, schema, role, database, migration-folder, and identifier arguments in argv. Stable sanitized process exits are: `0` success; `64` configuration; `65` TLS; `66` unsafe identity; `67` ledger/schema; `68` lock contention; `69` migration failure. CI, Compose, Swarm, and each two-gateway migration Job invoke the immutable db-migrator image with exactly `mosaic-db-migrator --run`; their readiness verification invokes exactly `mosaic-db-migrator --verify`. Required tests cover `--help`, argv rejection, runtime-only refusal before connect, migration-only success, each sanitized error/lock exit, and ordered `postgres-a → mosaic-db-migrator-a → gateway-a` plus `postgres-b → mosaic-db-migrator-b → gateway-b` execution.
|
||||
|
||||
The implementation must inventory and close every present and future entrypoint as follows:
|
||||
|
||||
| Current entrypoint | Required rc.7 disposition |
|
||||
| Current entrypoint | Required rc.8 disposition |
|
||||
| ----------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `packages/db/src/migrate.ts:runMigrations()` | Remove its optional URL, `DATABASE_URL`, and default fallback API from public/runtime exports. Its PostgreSQL behavior moves behind `mosaic-db-migrator`; callers cannot invoke it with an arbitrary URL. |
|
||||
| `packages/db/src/index.ts` | `KBN-101-03` removes the public `runMigrations` re-export; only the explicit PGlite-local API remains as separately typed local behavior. A compile/import-negative proves `@mosaicstack/db` cannot directly import `runMigrations`; a `DATABASE_URL`-only direct-library attempt fails before connect. |
|
||||
| `packages/db/drizzle.config.ts` and direct `drizzle-kit migrate` | A database-connecting Drizzle configuration reads only `DATABASE_MIGRATION_URL` through the migration DTO and rejects its absence before connection. Replace direct `drizzle-kit migrate` exposure with `mosaic-db-migrator`. `db:generate` is an offline schema artifact command and must neither resolve nor connect to a URL. |
|
||||
| `pnpm --filter @mosaicstack/db db:migrate` and CI invocation | Make it a thin `mosaic-db-migrator` wrapper; no direct Drizzle migrator invocation remains. CI supplies an isolated disposable migration URL only to that job. |
|
||||
| `db:push` / direct `drizzle-kit push` | Forbidden for standalone, federated, CI production-like, Portainer, and any URL outside a disposable developer database. If retained for local experimentation, a wrapper requires `MOSAIC_DISPOSABLE_DEVELOPER_DB=1`, a locally allowlisted disposable target, `DATABASE_MIGRATION_URL`, verified TLS when PostgreSQL is used, and rejects `DATABASE_URL`, any production-like tier, and every non-allowlisted host/database before connection. It is never a release, repair, or migration procedure. |
|
||||
@@ -46,10 +51,16 @@ The implementation must inventory and close every present and future entrypoint
|
||||
| `docker/init-db.sql` | `KBN-101-00` deletes this duplicate tracked init artifact. `infra/pg-init/01-extensions.sql` is also retired from image-init ownership; neither may remain as a hidden extension authority. The sole extension action is the fixed external bootstrap artifact described in §5/§7, or the runner only when its reviewed implementation card explicitly grants that authority. |
|
||||
| `packages/storage/src/migrate-tier.ts` operator remediation | `KBN-101-07` replaces the raw SQL recommendation with the sanitized instruction `mosaic-db-migrator --run` (secret injection and target selection are deployment-owned; no URL, SQL, or credential is accepted on the command line), or directs the operator to the approved external-bootstrap runbook when extension eligibility has not been met. It never recommends `CREATE EXTENSION`. |
|
||||
| `tools/federation-harness/docker-compose.two-gateways.yml` | Active topology; `KBN-101-05` migrates it rather than retires it. It must contain `postgres-a`, `postgres-b`, `mosaic-db-migrator-a`, `mosaic-db-migrator-b`, `gateway-a`, and `gateway-b`; each database has separate runtime/migrator URL and CA consumers, TLS server material, verified-TLS readiness, and runner-before-Gateway ordering. Its existing plaintext URLs/init mount are forbidden. |
|
||||
| `docs/fleet/backlog-conventions.md` | `KBN-101-07` removes the current automatic first-use PostgreSQL `runMigrations()` claim. It points PostgreSQL operators to the sole runner/readiness sequence and labels only the PGlite routine as local-only. A documentation-route `DATABASE_URL`-only test asserts no first-use/migration instruction is executable before connect. |
|
||||
| `docs/PERFORMANCE.md` | `KBN-101-07` removes direct `drizzle-kit migrate` and Gateway-startup `runMigrations()` instructions. It points to the sole runner/readiness sequence. A documentation-route `DATABASE_URL`-only test asserts neither direct command nor Gateway fallback remains. |
|
||||
| `README.md`, `CLAUDE.md`, `docs/guides/{dev-guide,deployment}.md`, and `docs/federation/SETUP.md` operator instructions | `KBN-101-07` replaces direct `db:migrate`, `db:push`, and `CREATE EXTENSION` production-like instructions with the runner/bootstrap procedure, and labels any remaining disposable-local command as non-production with the §2 guard. Documentation is a DDL entrypoint and cannot advertise a bypass. |
|
||||
| New package scripts, test helpers, setup hooks, CLI commands, installers, CI steps, adapters, or operator docs | A repository check rejects any new PostgreSQL DDL-capable entrypoint or instruction unless it is the dedicated runner or the named external bootstrap artifact. No future script may accept a URL parameter or `DATABASE_URL` as a DDL escape hatch. |
|
||||
|
||||
The runner must run the original migration bytes, including shipped `0009`; no migration command may repair a ledger by manual insertion, adoption, `db:push`, or schema diff. Tests requiring PostgreSQL schema consume a pre-migrated disposable database or invoke this exact runner. `KBN-101-06` owns a finite static inventory containing every row above by exact path plus the existing `infra/pg-init/01-extensions.sql`, `.woodpecker/ci.yml`, `packages/db/{src/migrate.ts,drizzle.config.ts,package.json}`, `packages/db/src/migrate.test.ts`, `packages/storage/src/cli.ts`, `CLAUDE.md`, and `docs/federation/SETUP.md`; it fails on an unclassified current DDL token, URL-consuming setup hook, init mount, or raw operator DDL guidance. The same card owns a `DATABASE_URL`-only denial matrix for **each inventory path** and both `gateway-a`/`gateway-b`: every negative must fail before connection/DDL, including missing migration URL, runtime-only fixture/test, direct Drizzle, `db:push`, init artifact, tier migration guidance, and each harness migration job/Gateway. The `db:push` negatives also cover production-like tier and production-like URL rejection.
|
||||
The runner must run the original migration bytes, including shipped `0009`; no migration command may repair a ledger by manual insertion, adoption, `db:push`, or schema diff. Tests requiring PostgreSQL schema consume a pre-migrated disposable database or invoke this exact runner.
|
||||
|
||||
`KBN-101-06` owns a finite scanner and `DATABASE_URL`-only-before-connect matrix. It inventories by exact path every executable current source/script/package bin, operator document, and deploy manifest—including every §2 row, `packages/db/src/index.ts`, `docs/fleet/backlog-conventions.md`, `docs/PERFORMANCE.md`, `infra/pg-init/01-extensions.sql`, `.woodpecker/ci.yml`, `packages/db/{src/migrate.ts,drizzle.config.ts,package.json}`, `packages/db/src/migrate.test.ts`, `packages/storage/src/cli.ts`, `CLAUDE.md`, and `docs/federation/SETUP.md`. The matrix assigns each row an owner, disposition, and test; `packages/db/src/index.ts` is KBN-101-03/import-compile-negative, the two current documents are KBN-101-07/operator-route negatives, and both gateway pairs are KBN-101-05/06 ordered-Job negatives. It rejects every unclassified hit in scanner classes **executable current source/scripts/package bins**, **operator docs**, and **deploy manifests**.
|
||||
|
||||
The only reviewed allowlists are exact path/category pairs: byte-immutable historical SQL under `packages/db/drizzle/**` (reviewed solely as runner input); PGlite-only routines; negative-test literals; vendored/generated artifacts; and clearly labeled historical review reports. Each allowlist entry records path, category, rationale, reviewer, and expiry/review revision; it cannot mask a current executable or current instruction. An unknown token hit fails. The scanner is a classifier plus path inventory and review—not a naive token scan alone—and therefore never by itself proves DDL authority. For every non-allowlisted inventory row and `gateway-a`/`gateway-b`, the matrix proves `DATABASE_URL`-only, missing migration URL, direct library import, direct Drizzle, `db:push`, init artifact, operator route, runtime-only fixture/test, and each harness migration Job/Gateway fail before connection/DDL as applicable. The `db:push` negatives also cover production-like tier and production-like URL rejection.
|
||||
|
||||
## 3. Exact migration manifest, ledger, and lock
|
||||
|
||||
@@ -97,14 +108,15 @@ Before any preflight that can decide migration state, `mosaic-db-migrator` acqui
|
||||
|
||||
Role creation, passwords, membership, database ownership, certificates, and Vault values are platform/IaC/operator work—not Drizzle/application migrations. Application SQL must not issue credential/role management statements or embed credentials.
|
||||
|
||||
| Role | Attributes and ownership | Membership / session use |
|
||||
| --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `mosaic_platform_database_owner` | `NOLOGIN NOSUPERUSER NOCREATEDB NOCREATEROLE NOREPLICATION NOBYPASSRLS`; platform-only database owner after bootstrap. | Never granted to application roles. |
|
||||
| external platform bootstrap actor | Provider/operator/IaC-controlled privileged identity outside the Mosaic role graph and Vault/application configuration. | Creates/transitions the database and roles, then retires from application use. |
|
||||
| `mosaic_schema_owner` | `NOLOGIN NOSUPERUSER NOCREATEDB NOCREATEROLE NOREPLICATION NOBYPASSRLS`; final ownership is only `mosaic`, `drizzle`, and `mosaic_extensions` schemas/objects. | Never an application login; no database/extension authority except reviewed bootstrap scope. |
|
||||
| `mosaic_migrator` | `LOGIN NOINHERIT NOSUPERUSER NOCREATEDB NOCREATEROLE NOREPLICATION NOBYPASSRLS`. | Only `mosaic_schema_owner`; runner verifies `session_user=mosaic_migrator`, `SET ROLE mosaic_schema_owner`, then `current_user=mosaic_schema_owner`. |
|
||||
| `mosaic_runtime_capability` | `NOLOGIN` and no ownership/administrative attributes. | Holds only named runtime grants. |
|
||||
| `mosaic_runtime` | `LOGIN INHERIT` with no ownership/administrative attributes. | Only `mosaic_runtime_capability WITH INHERIT TRUE, SET FALSE, ADMIN FALSE`; never owner/migrator member. |
|
||||
| Role | Attributes and ownership | Membership / session use |
|
||||
| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `mosaic_platform_database_owner` | `NOLOGIN NOSUPERUSER NOCREATEDB NOCREATEROLE NOREPLICATION NOBYPASSRLS`; platform-only database owner after bootstrap. | Never granted to application roles. |
|
||||
| external platform bootstrap actor | Provider/operator/IaC-controlled privileged identity outside the Mosaic role graph and Vault/application configuration. | Creates/transitions the database and roles, then retires from application use. |
|
||||
| `mosaic_schema_owner` | `NOLOGIN NOSUPERUSER NOCREATEDB NOCREATEROLE NOREPLICATION NOBYPASSRLS`; owns only `mosaic`/`drizzle` schemas and application/ledger objects. It owns the `mosaic_extensions` **schema** but not its extension/member objects. | Never an application login; no database/extension authority. A bootstrap-only schema `CREATE` grant is revoked after extension creation. |
|
||||
| `mosaic_extension_owner` | Dedicated `NOLOGIN NOSUPERUSER NOCREATEDB NOCREATEROLE NOREPLICATION NOBYPASSRLS` extension owner, distinct from platform/schema/migrator/runtime. It owns `vector` and all extension-member objects in `mosaic_extensions`: the explicit exception to the schema-owner object rule. | No application login has membership, `SET ROLE`, credential, or inheritable grant to it. Only the external bootstrap actor may `SET ROLE mosaic_extension_owner`, only during bootstrap. |
|
||||
| `mosaic_migrator` | `LOGIN NOINHERIT NOSUPERUSER NOCREATEDB NOCREATEROLE NOREPLICATION NOBYPASSRLS`. | Only `mosaic_schema_owner`; runner verifies `session_user=mosaic_migrator`, `SET ROLE mosaic_schema_owner`, then `current_user=mosaic_schema_owner`. |
|
||||
| `mosaic_runtime_capability` | `NOLOGIN` and no ownership/administrative attributes. | Holds only named runtime grants. |
|
||||
| `mosaic_runtime` | `LOGIN INHERIT` with no ownership/administrative attributes. | Only `mosaic_runtime_capability WITH INHERIT TRUE, SET FALSE, ADMIN FALSE`; never owner/migrator member. |
|
||||
|
||||
The fixed application/runtime schema is **`mosaic`**. Every application/runtime pooled connection executes and verifies exactly `SET search_path TO pg_catalog, mosaic` before its first application query; connection checkout repeats this after reset/reconnect. Transactional application operations use `SET LOCAL search_path TO pg_catalog, mosaic` and verify it before query execution. `public` and `$user` are forbidden in all runtime paths.
|
||||
|
||||
@@ -112,11 +124,19 @@ The sole runner has one narrowly bounded **legacy-history bootstrap subphase** f
|
||||
|
||||
`KBN-101-03` exclusively owns `packages/db/src/schema.ts`, `packages/db/drizzle/meta/*`, `packages/db/drizzle/meta/_journal.json`, the generated relocation migration, and exact database/Drizzle tests. It freezes one exported `export const mosaic = pgSchema('mosaic')`; every application `pgTable` and every application `pgEnum` must be declared through that export. The generated snapshot/journal and future `db:generate` output must target `mosaic` only; a static declaration/SQL test rejects a default-schema application `pgTable`/`pgEnum`, `public` application declaration, or future generated application DDL outside `mosaic`. Historical `0000` through current SQL and the shipped journal provenance are byte-immutable and execute only in the trusted legacy-`public` subphase; they are never rewritten to claim they originally targeted `mosaic`.
|
||||
|
||||
The selected extension policy is fixed: external bootstrap creates `vector` fresh as `CREATE EXTENSION vector WITH SCHEMA mosaic_extensions`; `mosaic_extensions` is owned by `mosaic_schema_owner`, non-writable by runtime, and grants runtime only `USAGE`/read-only function access proven by catalog. Existing databases may execute `ALTER EXTENSION vector SET SCHEMA mosaic_extensions` only after a catalog eligibility check proves the exact supported `extversion`, `extrelocatable = true`, a complete expected extension-member set, and no dependent object that would change the selected qualification policy; any other version, false relocatability, extra/missing member, or partial prior move fails eligibility closed. `schema.ts` must emit `mosaic_extensions.vector(...)`, and all vector casts/operators/functions in runner, application queries, fixtures, and generated SQL must explicitly qualify `mosaic_extensions` (for example `OPERATOR(mosaic_extensions.<->)`); `mosaic_extensions` is deliberately **not** added to runtime `search_path`.
|
||||
### 4.1 pgvector ownership and supported transition
|
||||
|
||||
The selected extension policy is fixed. Fresh databases: the external bootstrap actor temporarily `SET ROLE mosaic_extension_owner`, creates `vector` as `CREATE EXTENSION vector WITH SCHEMA mosaic_extensions`, validates `pg_extension.extowner`, extension-member ownership/schema/version, then revokes the temporary bootstrap-only schema grant. `mosaic_extensions` remains non-writable by runtime and grants runtime only catalog-proven `USAGE`/read-only function access. Runtime, migrator, and schema owner have no membership in `mosaic_extension_owner` and must be denied `ALTER EXTENSION`, `DROP EXTENSION`, and extension-member `UPDATE`/DDL.
|
||||
|
||||
PostgreSQL has **no supported** `ALTER EXTENSION ... OWNER TO`. No card may invent it, mutate system catalogs, use `DROP ... CASCADE`, or adopt legacy extension ownership. An existing `vector` is eligible for the clean in-place path only when `pg_extension.extowner` already resolves to the approved `mosaic_extension_owner`; after exact supported-version, `extrelocatable=true`, dependency, and complete expected member-set checks, the bootstrap actor may perform the tested `ALTER EXTENSION vector SET SCHEMA mosaic_extensions`. The same postflight verifies `extowner`, each member owner/schema, version, dependency inventory, and denial for runtime/migrator/schema owner.
|
||||
|
||||
A `vector` extension owned by a legacy runtime/single login is **not activated in place**. It fails closed before activation and requires this controlled shadow-database migration: (1) approved backup and read-only inventory; (2) create new database/roles; (3) privileged actor creates vector under `mosaic_extension_owner`; (4) sole runner applies migrations and relocation; (5) copy data with vector dimensions, row counts, checksums, FK, and sequence evidence; (6) quiesce/drain writers; (7) apply and verify final delta; (8) prove role/TLS/readiness; (9) atomically switch connections; and (10) retain the old database read-only for the approved rollback window. Partial/cancel/resume and rollback preserve the old read-only source until the switch; no target unable to shadow-migrate is eligible until separately reviewed. Required tests cover fresh, approved-owner clean existing relocation, legacy-owner shadow path, partial/resume/rollback, N-1, exact `extowner`/member/schema/version assertions, and ALTER/DROP/member-update denials for runtime, migrator, and schema owner.
|
||||
|
||||
`schema.ts` must emit `mosaic_extensions.vector(...)`, and all vector casts/operators/functions in runner, application queries, fixtures, and generated SQL must explicitly qualify `mosaic_extensions` (for example `OPERATOR(mosaic_extensions.<->)`); `mosaic_extensions` is deliberately **not** added to runtime `search_path`.
|
||||
|
||||
Before relocation, the runner records a parameterized catalog inventory and dependency graph. It must classify, in dependency order, application enum/domain/base types; owned and identity sequences; application tables; table columns/defaults; functions/procedures; views/materialized views; extension-owned objects; then triggers, constraints/FKs, indexes, and dependent rules/policies. It moves base types, sequences, tables, and functions as required; OID-bound constraints/indexes follow their owning objects and are catalog-verified rather than recreated blindly. Every object must be expected exactly once and be in either the immutable legacy/bootstrap allowlist, the `mosaic` application allowlist, or the selected `mosaic_extensions` extension membership; unknown, extra, duplicate, cross-schema, dependency-cycle, or partial-resume state fails closed. No raw client-side identifier interpolation is allowed.
|
||||
|
||||
`KBN-101-03` tests clean bootstrap, current-public relocation, interrupted/partial relocation resume, and pre-activation reverse rollback-before-activation; reverse rollback is allowed only before KBN-101-08 and restores the approved backup or the reviewed inverse relocation, never a runtime `search_path` bypass. Its N-1 order is: current legacy release → inactive bootstrap/runner and `mosaic` declarations → catalog relocation and generated-artifact verification → verified non-owner runtime activation. Tests include byte-immutable historical execution, no public application objects/declarations/future SQL, vector type/cast/operator query success under fixed `pg_catalog,mosaic`, and all extension eligibility negatives.
|
||||
`KBN-101-03` tests clean bootstrap, approved-owner existing relocation, legacy-owner shadow migration, interrupted/partial relocation resume, partial/cancel shadow resume, and pre-activation reverse rollback-before-activation; reverse rollback is allowed only before KBN-101-08 and restores the approved backup or the reviewed inverse relocation, never a runtime `search_path` bypass. Its N-1 order is: current legacy release → inactive bootstrap/runner and `mosaic` declarations → catalog relocation/generated-artifact verification or approved shadow migration → verified non-owner runtime activation. Tests include byte-immutable historical execution, no public application objects/declarations/future SQL, vector type/cast/operator query success under fixed `pg_catalog,mosaic`, exact extension-owner/member/schema/version assertions, ALTER/DROP denial for runtime/migrator/schema owner, and all extension eligibility negatives.
|
||||
|
||||
No SQL identifier may come from URL/config/environment/operator input. Catalog comparisons use parameter values. The fixed identifiers above are constants; the external bootstrap artifact alone may use server-side `format('%I', fixed_allowlisted_identifier)` after allowlist validation. Raw client-side interpolation for identifiers, `SET search_path`, database, schema, role, table, or extension names is forbidden. Tests include injection-shaped values, a poisoned pooled-session reset, and transaction `SET LOCAL` restoration negatives.
|
||||
|
||||
@@ -126,7 +146,7 @@ Immutable KBN relations, after KBN-100 creates them, grant runtime only `SELECT,
|
||||
|
||||
## 5. Deployable verified-TLS bootstrap
|
||||
|
||||
`mosaicstack/stack` is the named repository/control plane for KBN-101-00 and KBN-101-05. Those cards exclusively own the current `docker-compose.yml`, `docker-compose.federated.yml`, `deploy/portainer/federated-test.stack.yml`, and `tools/federation-harness/docker-compose.two-gateways.yml`, plus the new `apps/gateway/Dockerfile`, bootstrap renderer/templates, and rendered-config validation tests. The named **Mosaic deployment control plane / Jason** is activation authority; the environment-specific IaC/Vault owner supplies only the approved input secret versions and may not substitute an unreviewed current-repository artifact. The renderer is the only handoff: it reads secret-provider references, validates owners/modes/digests/SANs, writes each output atomically (`mkstemp` on the target tmpfs, `fsync`, `chmod`/`chown`, atomic rename), and records only secret-version identifiers and hashes.
|
||||
`mosaicstack/stack` is the named repository/control plane. Ownership is intentionally non-overlapping: **KBN-101-00 exclusively owns** the versioned external bootstrap interface `infra/pg-bootstrap/roles.sql`, `infra/pg-bootstrap/extensions.sql`, and `infra/pg-bootstrap/README.md`, plus its bootstrap tests—roles, extension-owner transition, and no renderer/deployment manifests. **KBN-101-05 exclusively owns** `tools/db/render-postgres-secrets.ts`, its tests, the current `docker-compose.yml`, `docker-compose.federated.yml`, `deploy/portainer/federated-test.stack.yml`, `tools/federation-harness/docker-compose.two-gateways.yml`, and `apps/gateway/Dockerfile`; it consumes the versioned KBN-101-00 bootstrap interface and owns no bootstrap SQL. The named **Mosaic deployment control plane / Jason** is activation authority; the environment-specific IaC/Vault owner supplies only approved input secret versions and may not substitute an unreviewed current-repository artifact. The KBN-101-05 renderer is the only deployment handoff: it reads secret-provider references, validates owners/modes/digests/SANs, writes each output atomically (`mkstemp` on the target tmpfs, `fsync`, `chmod`/`chown`, atomic rename), and records only secret-version identifiers and hashes.
|
||||
|
||||
`KBN-101-05` changes the Gateway image to fixed non-root `USER 10001:10001`. Gateway CA and Gateway leaf-certificate mounts, and its own Gateway private key only when it terminates its HTTPS listener, must be readable by `10001:10001`; PostgreSQL private keys and migration-only material are never mounted there, and no secret is world-readable. PostgreSQL is not assigned a guessed UID/GID: its image must first be pinned by digest, and an image-inspection plus rendered Compose/Swarm test freezes the image's effective PostgreSQL UID:GID before the renderer selects mount owner/group. A digest, service UID/GID, rendered secret `uid`/`gid`/`mode`, or container `USER` mismatch is a KBN-101-05 failure. Mosaic applications never generate, self-sign, copy, or persist production certificates; the external bootstrap actor receives them only through the deployment secret mechanism and no plaintext development exception exists for production-like modes.
|
||||
|
||||
@@ -171,18 +191,18 @@ Stable sanitized codes are `DATABASE_RUNTIME_URL_REQUIRED`, `DATABASE_MIGRATION_
|
||||
|
||||
Every KBN-101 card remains one PR with exclusive ownership. Cards `00`–`07` may merge only as **prepared, inactive capability**: no current owner-runtime deployment consumes their image/config, and no compatibility switch is exposed to a runtime operator. They must not retain `ALLOW_LEGACY_*`, runtime DDL, `DATABASE_URL` migration fallback, plaintext TLS, direct Drizzle, or test-only bypass flags. Current owner-runtime deployments remain on their known N-1 release until final activation.
|
||||
|
||||
| Card | Depends on | Exact ownership and required result |
|
||||
| ----------------------------------------------------------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `KBN-101-00` platform bootstrap / IaC | contract | `mosaicstack/stack` owns the fixed external bootstrap artifact, its renderer/templates, and rendered validation; environment IaC/Vault is named deployment-owner input only. It creates/transitions roles, `mosaic`/`drizzle`/`mosaic_extensions` ownership, eligible vector bootstrap, Vault-to-secret bindings, service UID/GID declarations, server TLS config, fresh/existing inventory, and reversible evidence. It is not an app migration or runtime command. |
|
||||
| `KBN-101-01` runtime config/DB boundary | 00 | Typed split DTOs, verified TLS config, fixed schema/session verifier, no PostgreSQL Gateway startup migration. |
|
||||
| `KBN-101-02` entrypoint closure | 01 | Owns all paths in §2 except runner/journal: storage, CLI, adapters, Gateway startup, tier probes, fleet backlog, and direct-DDL test closure. |
|
||||
| `KBN-101-03` runner/manifest/schema foundation | 00,01 | Exclusively owns `packages/db/src/schema.ts`, generated snapshots/journal/migration, exact DB tests, `mosaic-db-migrator`, canonical Git-blob/LF manifest v1, fixed lock, journal logical-order repair, runner-only legacy-public bootstrap, `mosaic`/`mosaic_extensions` relocation/eligibility/rollback, ledger/manifest grants, and PostgreSQL runner tests. Shipped `0009` bytes remain unchanged. |
|
||||
| `KBN-101-04` installer/wizard/config | 01 | Production-like config persists no DSN and has no fallback; only non-secret references/injected variables. |
|
||||
| `KBN-101-05` compose/Portainer deployment | 00,01,03 | `mosaicstack/stack` exclusively owns current local/federated Compose, Portainer test stack, the two-gateway harness, bootstrap renderer/templates, gateway `10001:10001`, verified PostgreSQL image UID/GID, secret references/targets, TLS server/client mounts, server configuration, one Job per database, readiness, standalone/federated/Swarm/two-gateway TLS tests, and no plaintext exception. |
|
||||
| `KBN-101-06` CI/test topology | 02,03,05 | Disposable identities and pre-migrated/runner test topology; owns the finite static DDL inventory, every-path `DATABASE_URL`-only denial matrix, runner-prepared persistent pgvector fixture, and repository checks rejecting DDL bypasses/insecure URLs. |
|
||||
| `KBN-101-07` Vault/runbook/observability | 00,04,05,06 | Named control-plane/Vault-bootstrap handoff, activation/rotation/incident/backup/rollback runbook, sanitized observability evidence, and replacement of direct-DDL operator documentation (`README.md`, `CLAUDE.md`, `docs/guides/dev-guide.md`, `docs/guides/deployment.md`, `docs/federation/SETUP.md`, `packages/storage/src/{cli,migrate-tier}.ts`). |
|
||||
| `KBN-101-08` foundation certification and **atomic activation release** | 00…07 | Independent review verifies all prepared cards and terminal-green CI; Mosaic control plane/Jason authorizes one ordered activation: backup → drain/scale zero all N-1 runtime clients → TLS server start → terminate non-TLS sessions and enforce `hostssl`/no `host` rule → verify no plaintext session → roles → runner → verified readiness → rolling non-owner runtime. Any red test/readiness/CI result aborts; no force-on-red or bypass. Remove/disable all temporary compatibility support before this gate. |
|
||||
| `KBN-101-09` post-KBN-100 certification | KBN-100,08 | Real deployed-role immutable INSERT/SELECT and UPDATE/DELETE-denial evidence plus independent security/Ultron approval. |
|
||||
| Card | Depends on | Exact ownership and required result |
|
||||
| ----------------------------------------------------------------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `KBN-101-00` platform bootstrap / IaC | contract | Exclusively owns `infra/pg-bootstrap/roles.sql`, `infra/pg-bootstrap/extensions.sql`, `infra/pg-bootstrap/README.md`, and bootstrap tests: role graph, `mosaic_extension_owner`, fresh/approved-existing/legacy-owner eligibility, shadow migration interface, and bootstrap evidence. It owns no renderer, Compose, Portainer, Swarm, or deployment manifest. |
|
||||
| `KBN-101-01` runtime config/DB boundary | 00 | Typed split DTOs, verified TLS config, fixed schema/session verifier, no PostgreSQL Gateway startup migration. |
|
||||
| `KBN-101-02` entrypoint closure | 01 | Owns all §2 runtime paths except runner/journal: storage, CLI, adapters, Gateway startup, tier probes, fleet backlog, and direct-DDL test closure. |
|
||||
| `KBN-101-03` runner/manifest/schema foundation | 00,01 | Exclusively owns `packages/db/package.json`, `packages/db/src/cli.ts`, `packages/db/src/migrator/runner.ts`, `packages/db/src/migrator/config.dto.ts`, `packages/db/src/migrator/manifest.ts`, `packages/db/src/migrator/identity.ts`, `packages/db/src/migrator/tls.ts`, `docker/db-migrator.Dockerfile`, their tests, `packages/db/src/schema.ts`, generated snapshots/journal/migration, and exact DB/command tests. It owns the published `mosaic-db-migrator` bin, canonical Git-blob/LF manifest v1, fixed lock, journal logical-order repair, runner-only legacy-public bootstrap, relocation/approved-owner/shadow-path tests, ledger/manifest grants, and no renderer/bootstrap artifact. Shipped `0009` bytes remain unchanged. |
|
||||
| `KBN-101-04` installer/wizard/config | 01 | Production-like config persists no DSN and has no fallback; only non-secret references/injected variables. |
|
||||
| `KBN-101-05` compose/Portainer deployment | 00,01,03 | Exclusively owns `tools/db/render-postgres-secrets.ts` and tests; current local/federated Compose, Portainer test stack, two-gateway harness, and `apps/gateway/Dockerfile`; gateway `10001:10001`, verified PostgreSQL image UID/GID, secret references/targets, TLS server/client mounts, server configuration, one immutable-image `mosaic-db-migrator --run` Job per database before readiness, and standalone/federated/Swarm/two-gateway TLS/order tests. It consumes—not edits—the versioned `infra/pg-bootstrap` interface. |
|
||||
| `KBN-101-06` CI/test topology | 02,03,05 | Disposable identities and pre-migrated/runner test topology; owns the finite scanner classifier/allowlist review, every-path `DATABASE_URL`-only-before-connect denial matrix (including public-import and live-doc routes), runner-prepared persistent pgvector fixture, and repository checks rejecting DDL bypasses/insecure URLs. |
|
||||
| `KBN-101-07` Vault/runbook/observability | 00,04,05,06 | Named control-plane/Vault-bootstrap handoff, activation/rotation/incident/backup/rollback runbook, sanitized observability evidence, and replacement of direct-DDL operator documentation (`README.md`, `CLAUDE.md`, `docs/guides/dev-guide.md`, `docs/guides/deployment.md`, `docs/federation/SETUP.md`, `packages/storage/src/{cli,migrate-tier}.ts`). |
|
||||
| `KBN-101-08` foundation certification and **atomic activation release** | 00…07 | Independent review verifies all prepared cards and terminal-green CI; Mosaic control plane/Jason authorizes one ordered activation: backup → drain/scale zero all N-1 runtime clients → TLS server start → terminate non-TLS sessions and enforce `hostssl`/no `host` rule → verify no plaintext session → roles → runner → verified readiness → rolling non-owner runtime. Any red test/readiness/CI result aborts; no force-on-red or bypass. Remove/disable all temporary compatibility support before this gate. |
|
||||
| `KBN-101-09` post-KBN-100 certification | KBN-100,08 | Real deployed-role immutable INSERT/SELECT and UPDATE/DELETE-denial evidence plus independent security/Ultron approval. |
|
||||
|
||||
**Authority:** Mosaic control plane/Jason is the sole activation and rollback authority. CI, Gateway, migrator, Coordinator, and Certifier cannot activate, waive a red result, or force release. Before an incompatible KBN-100 switch, the authority stops/scales runtime, uses the approved backup/restore or separately reviewed runner artifact, restores only a known TLS-compatible runtime with its runtime secret after `hostssl` enforcement, and verifies no plaintext sessions plus TLS/readiness. Migration URL is never injected into Gateway to enable rollback. KBN-100 starts only after KBN-101-08; KBN-105 starts only after KBN-101-09.
|
||||
|
||||
@@ -190,16 +210,16 @@ Every KBN-101 card remains one PR with exclusive ownership. Cards `00`–`07` ma
|
||||
|
||||
## 8. Acceptance traceability
|
||||
|
||||
| Requirement / acceptance criterion | Required implementation evidence |
|
||||
| ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| K101-REQ-01 / AC-K101-01 | DTO and command matrix covers local/PGlite, standalone, federated, and both harness pairs; every §2 inventory entry rejects `DATABASE_URL`-only before connection/DDL; no runtime fallback/default. |
|
||||
| K101-REQ-02 / AC-K101-02 | KBN-101-03 one-session fixed two-int lock, contention/crash/readiness/unrelated-key tests; manifest-v1 canonical bytes/digest and all reconciliation states; Gateway/replica DDL impossibility. |
|
||||
| K101-REQ-03 / AC-K101-03 | KBN-101-03 catalog relocation and future-Drizzle-only-`mosaic` proof; KBN-101-01/03 role, `pg_catalog,mosaic`, `mosaic_extensions` qualification, identifier, ownership, membership, TEMP, ledger/default-grant, and pool-reset tests. |
|
||||
| K101-REQ-04 / AC-K101-04 | KBN-101-00/05 fresh/existing verified-TLS bootstrap and Compose/Swarm/two-gateway positives; both-pair CA/SAN/downgrade/key-permission negatives; exact UID/GID/mode and runtime/migrator secret-consumer rendering/CI negatives. |
|
||||
| K101-REQ-05 / AC-K101-05 | KBN-101-09 after KBN-100: real deployed runtime INSERT/SELECT success and UPDATE/DELETE denial for each frozen relation, with RESTRICT retention evidence. |
|
||||
| K101-REQ-06 / AC-K101-06 | KBN-101-00…08 prepared-card/no-intermediate-deploy evidence; one final activation authority record; N-1 drain/zero-plaintext-session/`hostssl`, backup/restore, CA overlap rotation, TLS-only rollback, Vault/redaction, and no-force-on-red evidence. |
|
||||
| K101-REQ-07 / KBN sequence | KBN-101-08 foundation certificate before KBN-100, KBN-101-09 real immutable-role certificate plus Ultron approval before KBN-105; KBN-100 rebases/restores Drizzle consistency and never bypasses the serial gates. |
|
||||
| Delivery integrity | One-card/one-PR DAG, exact file ownership, docs/link/contract checks, independent author≠reviewer re-review on the pushed exact head, and terminal-green CI for implementation cards. |
|
||||
| Requirement / acceptance criterion | Required implementation evidence |
|
||||
| ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| K101-REQ-01 / AC-K101-01 | DTO/command matrix covers local/PGlite, standalone, federated, and both harness pairs; every finite classified §2 path rejects `DATABASE_URL`-only before connection/DDL; no runtime fallback/default; `--help`, argv, import-compile, and live-operator-route negatives pass. |
|
||||
| K101-REQ-02 / AC-K101-02 | KBN-101-03 one-session fixed two-int lock, `--run`/`--verify` exit-code, contention/crash/readiness/unrelated-key tests; manifest-v1 canonical bytes/digest and all reconciliation states; Gateway/replica DDL impossibility. |
|
||||
| K101-REQ-03 / AC-K101-03 | KBN-101-03 catalog relocation and future-Drizzle-only-`mosaic` proof; fresh/approved-owner/shadow extension paths; `pg_extension.extowner`, member/schema/version, and runtime/migrator/schema-owner ALTER/DROP/member-update denials; KBN-101-01/03 role, path, TEMP, ledger/default-grant, and pool-reset tests. |
|
||||
| K101-REQ-04 / AC-K101-04 | KBN-101-00 bootstrap-interface and KBN-101-05 renderer/deployment tests; immutable-image exact Job commands and runner-before-readiness order; fresh/existing verified-TLS Compose/Swarm/two-gateway positives; both-pair CA/SAN/downgrade/key-permission negatives; exact UID/GID/mode and runtime/migrator secret-consumer rendering/CI negatives. |
|
||||
| K101-REQ-05 / AC-K101-05 | KBN-101-09 after KBN-100: real deployed runtime INSERT/SELECT success and UPDATE/DELETE denial for each frozen relation, with RESTRICT retention evidence. |
|
||||
| K101-REQ-06 / AC-K101-06 | KBN-101-00…08 prepared-card/no-intermediate-deploy evidence; one final activation authority record; N-1 drain/zero-plaintext-session/`hostssl`, backup/restore, CA overlap rotation, TLS-only rollback, Vault/redaction, and no-force-on-red evidence. |
|
||||
| K101-REQ-07 / KBN sequence | KBN-101-08 foundation certificate before KBN-100, KBN-101-09 real immutable-role certificate plus Ultron approval before KBN-105; KBN-100 rebases/restores Drizzle consistency and never bypasses the serial gates. |
|
||||
| Delivery integrity | One-card/one-PR DAG, exact file ownership, docs/link/contract checks, independent author≠reviewer re-review on the pushed exact head, and terminal-green CI for implementation cards. |
|
||||
|
||||
## 9. Non-goals and residual authority
|
||||
|
||||
|
||||
Reference in New Issue
Block a user