docs(#771): remediate Ultron role and migration findings

This commit is contained in:
Hermes Agent
2026-07-15 03:20:12 -05:00
parent 11f09a15e4
commit f60144eb3e
8 changed files with 206 additions and 155 deletions

View File

@@ -1,39 +1,79 @@
# Migrating to the Federated Tier
Step-by-step guide to migrate from `local` (PGlite) or `standalone` (PostgreSQL without pgvector) to `federated` (PostgreSQL 17 + pgvector + Valkey).
> **KBN-101-07 ownership:** this is an **active** operator guide, exclusively owned by
> KBN-101-07. It is not a historical document. The KBN-101-06 finite operator-document
> inventory records this route and its secure-interface fields.
## When to migrate
This guide is active documentation but a **non-operative KBN-101 contract** until
KBN-101-02, -03, and -06 land and KBN-101-08 activates the reviewed artifacts. The command
blocks below specify the produced interface; they are not available on the current branch and
MUST NOT be attempted until the activation certificate names the exact release. Until then, do
not use a direct PostgreSQL target URL, raw SQL, or any legacy storage migration command as a
production procedure.
Migrate to federated tier when:
## Non-negotiable boundary
- Scaling from single-user to multi-user deployments
- Adding vector embeddings or RAG features
- Running Mosaic across multiple hosts
- Requires distributed task queueing and caching
- Moving to production with high availability
The PostgreSQL destination schema is prepared separately before data migration:
1. The dedicated DDL runner executes `mosaic-db-migrator --run` using its migration-only
identity.
2. The same runner succeeds at `mosaic-db-migrator --verify` against the exact destination.
3. Only then may `mosaic storage migrate-tier` connect as dedicated non-DDL
`mosaic_data_importer` to copy data.
The data migration never executes DDL or changes PostgreSQL roles, memberships, schemas,
extensions, extension members, system catalogs, or the Drizzle ledger. Its only target writes are
an allowlisted data-copy DML registry: inserts and idempotent conflict updates on declared mutable
application tables. It has no DML grant for immutable KBN relations, extension/catalog objects, or
any schema authority. It is not a schema bootstrap, extension installer, or repair procedure.
PostgreSQL is always pre-migrated and runner-verified, including when the source is PGlite.
## Prerequisites
- Federated stack running and healthy (see [Federated Tier Setup](../federation/SETUP.md))
- Source database accessible and empty target database at the federated URL
- Backup of source database (recommended before any migration)
- Federated stack is running and healthy (see [Federated Tier Setup](../federation/SETUP.md)).
- An approved source backup exists and the target is identified in the KBN-101 change record.
- The target passed the runner `--run` and `--verify` sequence above.
- The operator has an approved, dedicated importer credential file at
`/run/secrets/mosaic_migrate_target_url`; the **path reference** is non-secret, but its
contents are never printed, copied, committed, or placed on argv.
- KBN-101-02 validates that file before any target connection: it must be a regular,
non-symlink file, owned by the expected importer UID:GID, mode `0600`, readable only by
that importer process, and its value must be redacted from all output.
### Source modes
- **Local PGlite:** the source is the explicit `PGLITE_DATA_DIR`; it does not read or
interpret a PostgreSQL URL.
- **PostgreSQL source:** this active route does not accept `DATABASE_URL` as a source or
target fallback. A PostgreSQL-source procedure needs its own independently approved,
non-secret credential-reference contract before it is enabled.
## Dry-run first
Always run a dry-run to validate the migration:
Run the migration only after the target schema is prepared and verified:
```bash
mosaic-db-migrator --run
mosaic-db-migrator --verify
mosaic storage migrate-tier --to federated \
--target-url postgresql://mosaic:mosaic@localhost:5433/mosaic \
--target-url-file /run/secrets/mosaic_migrate_target_url \
--dry-run
```
Expected output (partial example):
The frozen KBN-101-02 option is exactly
`--target-url-file /run/secrets/mosaic_migrate_target_url`. It is a file reference, not a
URL option. The runner and importer use distinct connections: the runner has migration
DDL authority; the importer has only approved data-copy DML authority.
```
[migrate-tier] Analyzing source tier: pglite
[migrate-tier] Analyzing target tier: federated
[migrate-tier] Precondition: target is empty ✓
Expected dry-run output is sanitized and contains no connection string, username, host,
database name, SQL, or credential value:
```text
[migrate-tier] Source tier: pglite
[migrate-tier] Target tier: federated
[migrate-tier] Target schema: verified by mosaic-db-migrator
[migrate-tier] Target connection: dedicated importer
[migrate-tier] Precondition: target is ready
users: 5 rows
teams: 2 rows
conversations: 12 rows
@@ -43,26 +83,47 @@ Expected output (partial example):
[migrate-tier] DRY-RUN COMPLETE (no data written). 206 total rows would be migrated.
```
Review the output. If it shows an error (e.g., target not empty), address it before proceeding.
Review the output. If it reports a failed runner verification, unexpected role, unsafe
secret-file owner/mode, non-empty target, or any connection/DDL precondition error, stop
and remediate through the KBN-101 control plane.
## Run the migration
## Run the data migration
When ready, run without `--dry-run`:
After a reviewed dry-run, repeat the same verified route with `--yes`:
```bash
mosaic-db-migrator --verify
mosaic storage migrate-tier --to federated \
--target-url postgresql://mosaic:mosaic@localhost:5433/mosaic \
--target-url-file /run/secrets/mosaic_migrate_target_url \
--yes
```
The `--yes` flag skips the confirmation prompt (required in non-TTY environments like CI).
`--yes` only confirms data copy in non-TTY automation. It does not bypass schema
verification, target-file validation, role checks, redaction, or the no-DDL boundary.
The command will:
1. Acquire an advisory lock (blocks concurrent invocations)
2. Copy data from source to target in dependency order
3. Report rows migrated per table
4. Display any warnings (e.g., null vector embeddings)
1. Verify the runner-prepared target schema before opening the importer target connection.
2. Validate the credential-file reference and use the dedicated importer identity.
3. Copy data in dependency order without DDL.
4. Report sanitized row counts and warnings.
## Rejected interfaces and failure-before-connect rules
KBN-101-02 and KBN-101-06 must prove these failures occur before a target connection or
DDL attempt:
- Raw `--target-url` or any URL/credential value on argv.
- `DATABASE_URL` as a target or source fallback.
- A runtime owner, migrator, schema owner, or extension owner used as the data importer.
- Missing, non-regular, symlinked, wrong-owner, or wrong-mode target credential file.
- Any mode other than the runner-prepared/verified PostgreSQL destination route.
- Any direct DDL attempt by the migration command or its target connection.
KBN-101-02 SHALL add `packages/storage/src/migrate-tier.spec.ts::secureTargetRoute` and
its integration counterpart. They prove no target connection is opened on each interface
rejection. The finite operator-document scanner rejects an unowned route or a guide that
reintroduces a credential-bearing command.
## What gets migrated
@@ -88,60 +149,28 @@ Three tables are intentionally not migrated:
| **verifications** | One-time tokens (email verify, password reset) that have either expired or been consumed |
| **admin_tokens** | Hashed tokens bound to the old environment's secret keys; must be re-issued |
**Note on accounts and provider_credentials:** These durable credentials ARE migrated because they are user-bound and required for resuming agent work on the target environment. After migration to a multi-tenant federated deployment, operators may want to audit or wipe these if users are untrusted or credentials should not be shared.
**Note on accounts and provider_credentials:** These durable credentials are migrated because
they are user-bound and required for resuming agent work on the target. After migration to a
multi-tenant federated deployment, operators may want to audit or wipe these if users are
untrusted or credentials should not be shared.
## Idempotency and concurrency
## Idempotency, concurrency, and verification
The migration is **idempotent**:
The data copy is idempotent and uses an importer-scoped advisory lock. It may be retried only
through the same runner-verified, file-reference route. Do not inspect, unlock, or repair the
target with ad hoc SQL; a failed or ambiguous run is a control-plane incident with backup and
rollback evidence.
- Re-running is safe (uses `ON CONFLICT DO UPDATE` internally)
- Ideal for retries on transient failures
- Concurrent invocations are blocked by a Postgres advisory lock; the second caller will wait
If a previous run is stuck, check for advisory locks:
```sql
SELECT * FROM pg_locks WHERE locktype='advisory';
```
If you need to force-unlock (dangerous):
```sql
SELECT pg_advisory_unlock(<lock_id>);
```
## Verify the migration
After migration completes, spot-check the target:
```bash
# Count rows on a few critical tables
psql postgresql://mosaic:mosaic@localhost:5433/mosaic -c \
"SELECT 'users' as table, COUNT(*) FROM users UNION ALL
SELECT 'conversations' as table, COUNT(*) FROM conversations UNION ALL
SELECT 'messages' as table, COUNT(*) FROM messages;"
```
Verify a known user or project exists by ID:
```bash
psql postgresql://mosaic:mosaic@localhost:5433/mosaic -c \
"SELECT id, email FROM users WHERE email='<your-email>';"
```
Ensure vector embeddings are NULL (if source was PGlite) or populated (if source was postgres + pgvector):
```bash
psql postgresql://mosaic:mosaic@localhost:5433/mosaic -c \
"SELECT embedding IS NOT NULL as has_vector FROM insights LIMIT 5;"
```
After a successful migration, use the KBN-101-approved sanitized verification command or
read-only operator report. Do not paste a target connection string into a shell command,
terminal history, ticket, or documentation.
## Rollback
There is no in-place rollback. If the migration fails:
There is no in-place rollback. If data migration fails:
1. Restore the target database from a pre-migration backup
2. Investigate the failure logs
3. Rerun the migration
1. Keep the previous target state and restore the approved pre-migration backup where required.
2. Preserve sanitized failure and audit evidence; do not expose the credential file or contents.
3. Investigate through the KBN-101 control plane and rerun only after independent review.
Always test migrations in a staging environment first.
Always rehearse this route in a staging environment first.