99 lines
8.4 KiB
Markdown
99 lines
8.4 KiB
Markdown
# Migrating to the Federated Tier
|
|
|
|
> **KBN-101-07 ownership:** This active documentation is a **non-operative KBN-101
|
|
> contract** with no current command authority until KBN-101-00, KBN-101-02, KBN-101-03, KBN-101-05, and KBN-101-06 land and
|
|
> KBN-101-08 activates an exact reviewed release. The commands below describe the produced interface only. Do not run them on the
|
|
> current branch or replace them with direct PostgreSQL, raw SQL, legacy storage migration, or
|
|
> credential-on-argv procedures.
|
|
|
|
## Held future procedure
|
|
|
|
This section is non-operative and grants no current command authority until KBN-101-00, KBN-101-03, and KBN-101-05 land.
|
|
|
|
The deployment control plane executes the complete held future procedure, in order: external bootstrap → TLS/roles → `mosaic-db-migrator --run` → `mosaic-db-migrator --verify` → Gateway/Compose readiness. The
|
|
runner is the only attestation producer after its verified TLS, identity, manifest, and schema
|
|
checks. A data importer is never a schema bootstrap, extension installer, repair command, or DDL
|
|
consumer.
|
|
|
|
## Target material contract
|
|
|
|
KBN-101-05 obtains the target URL from Vault KV-v2
|
|
`secret-{env}/mosaic-stack/database/importer`, key `url`, and reads its authenticated version from
|
|
the same successful response `data.metadata.version`. A hash or DSN byte sequence is not a
|
|
provider version. The renderer treats URL bytes and provider version as one generation, writes a
|
|
temporary generation directory with fsync plus atomic rename, and creates separate immutable
|
|
consumer mounts. Swarm uses distinct versioned secret/config references. A deployment cannot mix
|
|
generations.
|
|
|
|
| Consumer | Permitted material |
|
|
| --------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
| Migrator-attestation producer (`10003:10003`) | Its own migration URL/CA; read-only `/run/secrets/mosaic-migrate-target-url` and `/run/secrets/mosaic-migrate-target-version`, each `0400`, solely to bind; producer-only attestation output at `/run/mosaic-attestations-producer/migrate-target.v1.json`; root-wrapper-only signing key. It never connects with, uses, exports, logs, or forwards the importer URL/version. |
|
|
| Privileged deployment handoff controller | After runner success and before importer creation, it receives only root-owned non-secret expected provider-version/URL-SHA-256/generation descriptor and pinned public verifier key—not URL bytes or private key. It safe-opens/verifies descriptor and producer artifact, copies exact bytes to a new importer-only mount with fsync/atomic rename, sets `10002:10002` `0400`, seals it read-only, and refuses importer start on any partial/wrong-generation/wrong-owner/mode result. |
|
|
| Importer (`10002:10002`) | Its own immutable `0400` copies at the same URL/version paths; CA at exact `DATABASE_TLS_CA_CERT_PATH=/run/secrets/mosaic-db-ca.crt`; pinned Ed25519 public key; read-only `/run/mosaic-attestations/migrate-target.v1.json` supplied only by the sealed handoff. |
|
|
| Gateway/runtime/unrelated container | No importer URL/version, importer artifact, attestation private key, or unrelated CA mount. |
|
|
|
|
The migrator and importer safe-open URL, provider-version, attestation, and public-key files only
|
|
with `O_RDONLY|O_CLOEXEC|O_NOFOLLOW`; they validate from the opened fd that the file is regular,
|
|
has its expected owner/mode and link count one. The migrator digests only that URL fd for binding,
|
|
then zeroizes/closes it. The importer reads URL bytes once into protected memory, validates the
|
|
signed binding and exact CA before connecting from those same bytes, then zeroizes/closes every
|
|
fd. It neither logs nor exposes a URL/version/attestation/key oracle.
|
|
|
|
## Produced command interface
|
|
|
|
After activation and only after approved target preparation, the future interface is:
|
|
|
|
```bash
|
|
# Deployment control plane has already completed the held runner procedure above.
|
|
mosaic storage migrate-tier --to federated \
|
|
--target-url-file /run/secrets/mosaic-migrate-target-url \
|
|
--target-attestation-file /run/mosaic-attestations/migrate-target.v1.json \
|
|
--dry-run
|
|
```
|
|
|
|
The provider-version file is fixed deployment material, not argv. This connecting dry-run consumes its nonce; before an actual copy, the deployment control plane must provide fresh runner verification and a new sealed handoff. The runner uses its migration
|
|
identity; the importer connects only as non-DDL `mosaic_data_importer` and only after all
|
|
pre-connect validation. After verified TLS and before DML it compares PostgreSQL system ID,
|
|
database OID, `current_user`, CA/SPKI, and manifest/schema fingerprints to the artifact.
|
|
|
|
## Required refusals and evidence
|
|
|
|
KBN-101-02/-03/-05/-06 must prove, with stable sanitized errors, that no target connection occurs
|
|
for missing/unsafe URL/version/attestation/public-key files; symlink, hardlink, owner, mode, or
|
|
TOCTOU violations; mixed URL/version generations; missing/wrong CA mount; stale/replayed/tampered
|
|
or revoked-key artifacts; provider rotation/revocation; wrong TLS/server/database/role/manifest
|
|
binding; raw `--target-url`; `DATABASE_URL` fallback; runtime/owner identity; consumer leakage;
|
|
or any DDL attempt. Post-connect identity mismatch closes with zero DML/DDL. Tests also prove no
|
|
forwarding, child environment, logging, or error oracle leaks URL/version/key/artifact contents.
|
|
|
|
The attestation is credential-free JCS with detached Ed25519 signature and binds issued/expiry,
|
|
nonce, authenticated provider version, exact URL-fd SHA-256, TLS host/port/database, CA/SPKI,
|
|
PostgreSQL system ID/database OID, importer role, manifest/schema, and producer identity. Provider
|
|
version rotation invalidates an old artifact and requires a fresh rendered generation plus runner
|
|
verification.
|
|
|
|
## Actual copy after dry-run
|
|
|
|
After reviewed dry-run, obtain the required fresh verification/attestation generation, then use:
|
|
|
|
```bash
|
|
# Deployment control plane has supplied fresh runner verification and attestation.
|
|
mosaic storage migrate-tier --to federated \
|
|
--target-url-file /run/secrets/mosaic-migrate-target-url \
|
|
--target-attestation-file /run/mosaic-attestations/migrate-target.v1.json \
|
|
--yes
|
|
```
|
|
|
|
The dry-run artifact is terminally replayed and must be rejected; `--yes` bypasses no file,
|
|
generation, signature, TLS, identity, or DDL control.
|
|
|
|
## Data boundary and recovery
|
|
|
|
The importer has only an allowlisted mutable-table DML registry. It has no grant for immutable KBN
|
|
relations, schemas, roles, memberships, extensions, catalogs, or the Drizzle ledger. Source PGlite
|
|
uses its explicit local directory and does not make a PostgreSQL URL fallback valid.
|
|
|
|
A failed or ambiguous migration is a control-plane incident: preserve sanitized evidence, retain
|
|
the approved backup/rollback state, and retry only after independent review. Never inspect,
|
|
unlock, repair, or initialize the target with ad hoc SQL or copied credentials.
|