sbom-nexus/docs/operator-guide.md
tegwick 4143f7c004
All checks were successful
Build and Publish Container Image / build-and-push (push) Successful in 37s
fix: migrate as durable database owner
Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a028f0-a42f-7582-89a8-ebaad7343834
2026-08-22 18:15:23 +02:00

3.1 KiB

Operator guide

Run locally

make install
make run

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:

export SBOM_NEXUS_DATABASE_URL='postgresql+psycopg://user:password@host/sbom_nexus'
make migrate
make run

When migrations use short-lived login roles, set SBOM_NEXUS_MIGRATION_ROLE to the reviewed durable owner role. Alembic issues SET ROLE before creating or changing any object, so ownership does not become tied to an expiring lease. The production package uses sbom_nexus_owner.

Production deployments should mount a Secret and set SBOM_NEXUS_DATABASE_URL_FILE to its url file rather than exposing the DSN in a manifest or command argument. The same file setting is consumed by both Alembic and the API process.

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

curl -X PUT http://127.0.0.1:8010/repositories/example \
  -H 'Content-Type: application/json' \
  -d '{"checkout_path":"/srv/repos/example","active":true}'

curl -X POST http://127.0.0.1:8010/sbom/example/ingest

The ingest response is terminal: ingested, or skipped with one of no-checkout, no-manifest, or ingest-error. A skip advances queue fairness but does not advance last_success_at.

Inspect catch-up

curl -s 'http://127.0.0.1:8010/sbom/catch-up?limit=3' | python3 -m json.tool

The default stale threshold is 30 days. Override it globally with SBOM_NEXUS_STALE_DAYS or per query with stale_days during controlled operation.

Import State Hub history

Synchronize the minimum repository identity/path projection first. This command does not call an ingest endpoint or create an SBOM snapshot:

uv run python scripts/sync_repository_projections.py \
  --source-url http://127.0.0.1:8000 \
  --target-url http://127.0.0.1:8010 \
  --host-id bnt-lap001 \
  --dry-run

Remove --dry-run only after the target is deployed. The apply path upserts all active and inactive records, then reads them back and fails on a missing or mismatched active/path projection. Extra target rows are reported but retained.

Run a read-only preview first:

uv run python scripts/import_state_hub.py --dry-run

Then run against a backed-up Nexus database:

uv run python scripts/import_state_hub.py \
  --source-url http://127.0.0.1:8000 \
  --target-url http://127.0.0.1:8010

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 command fails unless every legacy snapshot id, repository, timestamp, entry count, licence group, and direct-production copyleft count reconciles.

Current production limitations

  • Authentication and authorization are not yet integrated.
  • 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.