feat: prepare postgres sbom cutover

Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a028f0-a42f-7582-89a8-ebaad7343834
This commit is contained in:
tegwick 2026-08-22 13:14:24 +02:00
parent cf7e3acb78
commit ba535e1f8f
26 changed files with 1573 additions and 411 deletions

View file

@ -0,0 +1,74 @@
# PostgreSQL and State Hub history rehearsal — 2026-08-22
## Scope
Evidence for `SBOM-WP-0001-T05`. No production SBOM authority or State Hub rows
were changed. PostgreSQL and Nexus history targets were disposable.
## PostgreSQL migration/runtime proof
A disposable `postgres:16-alpine` instance received:
1. `alembic upgrade head` from an empty database;
2. the PostgreSQL API contract test (health, repository upsert, manual ingest,
current view, and licence report); and
3. `alembic downgrade base`.
All three completed successfully. The container was stopped and removed after
the test.
The production `Containerfile` also built as `sbom-nexus:test`; a disposable
container started as uid 10001 and returned a successful `/state/health`
response using its writable `/data` SQLite development default.
## Live State Hub history baseline
Read-only source: workstation State Hub at `127.0.0.1:8000`.
| Measure | Value |
| --- | ---: |
| Repositories with history | 18 |
| Historical snapshots | 22 |
| Historical entries | 3,123 |
| Oldest snapshot | 2026-03-01T15:12:54.862697Z |
| Newest snapshot | 2026-07-08T20:37:48.255925Z |
## Disposable import result
The first import created all 22 snapshots in a temporary Nexus SQLite database.
Snapshot reconciliation succeeded immediately:
- expected/matched snapshots: 22/22;
- expected/target imported entries: 3,123/3,123;
- missing legacy ids: 0;
- snapshot field mismatches: 0.
The initial licence comparison reported a mismatch solely because the two APIs
returned equal groups in different orders. Group ordering is not contractual;
the importer was corrected to compare normalized groups keyed by licence.
The second run then proved both correctness and idempotence:
- `already_present`: 22;
- snapshot reconciliation: pass;
- licence groups: exact normalized match;
- source/target direct-production copyleft count: 4/4;
- overall result: pass.
## Production gate retained
This is migration-mechanism evidence, not authority cutover approval. Production
still requires a managed PostgreSQL service, backup/restore evidence, deployment
health, State Hub façade and projection changes, Repo Manager retargeting,
Activity Core bounded-ingest activation, and a stabilization window.
## Consumer handoffs
State Hub coordination messages were sent from `sbom-nexus` with the cutover
plan and explicit ownership boundaries:
| Consumer | Message id | Requested child slice |
| --- | --- | --- |
| State Hub | `5a28a4e8-476a-4766-8e11-9c8b4f4cf57a` | reversible compatibility façade and caller retargeting |
| Repo Manager | `62d3bd49-9050-458f-9aa9-b6af348f9eb0` | retarget scanner/report interface and remove competing authority |
| Activity Core | `3420be55-d0ad-4eb7-93b2-42ac4281c61d` | unblock bounded ingest while retaining deployment gates |

View file

@ -7,9 +7,19 @@ make install
make run
```
The API defaults to `127.0.0.1:8010` and `./sbom-nexus.db`. Set
`SBOM_NEXUS_DATABASE_PATH` to an explicit durable location for non-development
use.
The API defaults to `127.0.0.1:8010` and `./sbom-nexus.db`. SQLite schema is
created automatically for local development.
For PostgreSQL, migrate before starting the API:
```bash
export SBOM_NEXUS_DATABASE_URL='postgresql+psycopg://user:password@host/sbom_nexus'
make migrate
make run
```
PostgreSQL never auto-creates tables unless `SBOM_NEXUS_AUTO_CREATE=1` is set
explicitly. Normal production operation must use Alembic.
## Register and ingest a repository
@ -53,13 +63,13 @@ uv run python scripts/import_state_hub.py \
Imports are idempotent on the State Hub snapshot UUID. Before cutover, compare
the source/target repository, snapshot, and entry counts described in the
extraction review. The current script reports counts but is not yet the complete
production reconciliation gate.
extraction review. The command fails unless every legacy snapshot id, repository,
timestamp, entry count, licence group, and direct-production copyleft count
reconciles.
## Current production limitations
- The extraction store is SQLite and intended for local/single-node operation.
- Authentication and authorization are not yet integrated.
- Structured operational metrics, PostgreSQL migrations, backup/restore proof,
and retention policy are required before authority cutover.
- Structured operational metrics, backup/restore proof, and retention policy are
required before authority cutover.
- State Hub and Repo Manager callers have not yet been retargeted.

View file

@ -0,0 +1,83 @@
# SBOM Nexus production cutover plan
**Owner workplan:** `SBOM-WP-0002`
**Parent coordination:** `CUST-WP-0062`
**Consumers:** State Hub, Repo Manager, Activity Core
## Safety model
Cutover separates durable data movement from caller movement. State Hub remains
the rollback read/write path until Nexus history reconciles, the compatibility
façade passes, and the bounded Activity Core flow is proven. No step deletes
State Hub rows.
## Sequence
| Stage | Authority/write path | Exit evidence | Rollback |
| --- | --- | --- | --- |
| 0. Deploy dark | State Hub | Nexus health, migrated PostgreSQL, backup/restore drill | remove dark deployment |
| 1. Import history | State Hub | exact legacy-id, timestamp, entry, and licence reconciliation | discard Nexus database and restore backup |
| 2. Projection sync | State Hub | active repo/path projection in Nexus; catch-up counts sampled | stop projection sync |
| 3. Read façade | State Hub write; Nexus read behind flag | route compatibility suite and dashboard/MCP samples | flag reads back to State Hub |
| 4. Write façade | Nexus write; State Hub projection update | manual and repository ingest parity; truthful attempt/success mapping | flag writes back to State Hub |
| 5. Bounded activity | Nexus | at most N terminal outcomes, zero spawned tasks, deterministic progress | disable daily definition |
| 6. Stabilize | Nexus | two successful daily fires and one Monday with weekly flood at zero | return façade flags to State Hub |
| 7. Retire | Nexus | retention decision and final backup | restore retained State Hub snapshot store during window |
## Contract ownership
### SBOM Nexus
- `/sbom/*`, snapshots, entries, licence report, ingest outcomes, catch-up;
- both `last_attempt_at` and `last_success_at`;
- imported legacy UUID provenance;
- PostgreSQL schema and migration history.
### State Hub child change
- introduce a configurable Nexus client and `/sbom/*` façade;
- preserve legacy response shapes and `ManagedRepo.last_sbom_at` during the
transition;
- retarget dashboard, MCP, summary cache, DoI C8, onboarding, and CLI callers;
- meter façade reads/writes and retain a reversible flag;
- do not add new SBOM product behavior locally.
### Repo Manager child change
- retain repository identity, active status, host/checkout paths, and source
authority;
- change `rmgr sbom scan|licence-report` from independent product behavior to a
Nexus client/local compatibility adapter;
- pin `sbom-nexus.snapshot.v1` and remove competing historical ownership;
- preserve repository-source scanning usability when Nexus is unavailable only
as an explicitly non-authoritative local preview.
### Activity Core child change
- replace the stale test-double blocker note with the live Nexus contract;
- implement at-most-N `POST /sbom/{slug}/ingest` calls for selected targets;
- record `ingested` and skip reasons without task creation;
- keep the definition disabled until dark deployment and import pass;
- enable and capture two-fire plus Monday-window evidence.
## Timestamp compatibility decision needed
State Hub has one `last_sbom_at`; Nexus distinguishes attempt from success. The
recommended transitional mapping is `last_attempt_at`, because it preserves
catch-up fairness and the historical behavior that an ingest call advances the
field. New consumers must use `last_success_at` when they mean inventory
freshness. The State Hub child workplan must record this explicitly before write
cutover.
## Production acceptance
- PostgreSQL upgrade and restore are rehearsed against the deployed topology.
- Historical import report is `ok: true` with zero missing/mismatched snapshots.
- State Hub compatibility routes pass against Nexus.
- Repo Manager has no competing durable SBOM store.
- Activity Core updates or terminally skips at most N repositories per fire.
- The weekly flood remains disabled and creates zero tasks.
- After stabilization, State Hub SBOM rows are retained or removed only through
an explicit retention decision.