All checks were successful
Build and Publish Container Image / build-and-push (push) Successful in 38s
Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a02b22-9638-76d2-bbff-b7ea1770b118
140 lines
4.6 KiB
Markdown
140 lines
4.6 KiB
Markdown
# Operator guide
|
|
|
|
## Run locally
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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
|
|
|
|
```bash
|
|
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`, `ingest-error`, `source-unavailable`, or
|
|
`source-rejected`. A skip advances queue fairness but does not advance
|
|
`last_success_at`.
|
|
|
|
## Controlled Forgejo source
|
|
|
|
Production automation uses the contract selected by `CUST-WP-0064`: Repo
|
|
Manager projects a canonical Coulomb repository and full commit SHA, Activity
|
|
Core freezes that `source_ref` with its bounded target set, and Nexus alone
|
|
fetches and scans the source. Example projection:
|
|
|
|
```json
|
|
{
|
|
"source_ref": {
|
|
"kind": "forgejo-archive-v1",
|
|
"repository": "coulomb/example",
|
|
"revision": "0123456789abcdef0123456789abcdef01234567",
|
|
"observed_ref": "refs/heads/main",
|
|
"observed_at": "2026-08-22T20:00:00Z"
|
|
}
|
|
}
|
|
```
|
|
|
|
Enable only after the package has migration `0002`, bounded ephemeral storage,
|
|
and Forgejo/DNS egress:
|
|
|
|
```sh
|
|
export SBOM_NEXUS_CONTROLLED_SOURCE_ENABLED=true
|
|
export SBOM_NEXUS_FORGEJO_BASE_URL=http://forgejo-gitea-http.forgejo.svc.cluster.local:3000
|
|
export SBOM_NEXUS_SOURCE_TMP=/var/run/sbom-sources
|
|
```
|
|
|
|
The runtime defaults are one concurrent scan, 120 seconds each for fetch and
|
|
scan, 100 MiB compressed, 512 MiB expanded, and 100,000 archive members. The
|
|
source endpoint accepts no arbitrary URL and requires no credential for public
|
|
Coulomb repositories. Never substitute a Forgejo administrator token.
|
|
|
|
Automated ingest and skip requests send the same value in `Idempotency-Key`
|
|
and `X-Activity-Core-Operation-ID`. Nexus persists that operation identity in
|
|
the snapshot transaction. A retry with the same request replays the original
|
|
terminal outcome; reuse against a different route, repository, reason, or
|
|
source reference returns HTTP 409.
|
|
|
|
## Inspect catch-up
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
uv run python scripts/import_state_hub.py --dry-run
|
|
```
|
|
|
|
Then run against a backed-up Nexus database:
|
|
|
|
```bash
|
|
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.
|