docs: select controlled SBOM source contract
Assistant: codex
This commit is contained in:
parent
6171607710
commit
cb62de1b7e
3 changed files with 202 additions and 6 deletions
|
|
@ -406,8 +406,8 @@
|
|||
| task | CUST-WP-0063-T03 | done | — | workplans/CUST-WP-0063-inbox-governance-packets.md |
|
||||
| task | CUST-WP-0063-T04 | done | — | workplans/CUST-WP-0063-inbox-governance-packets.md |
|
||||
| task | CUST-WP-0063-T05 | done | — | workplans/CUST-WP-0063-inbox-governance-packets.md |
|
||||
| task | CUST-WP-0064-T01 | progress | — | workplans/CUST-WP-0064-sbom-controlled-scan-inputs.md |
|
||||
| task | CUST-WP-0064-T02 | wait | — | workplans/CUST-WP-0064-sbom-controlled-scan-inputs.md |
|
||||
| task | CUST-WP-0064-T01 | done | — | workplans/CUST-WP-0064-sbom-controlled-scan-inputs.md |
|
||||
| task | CUST-WP-0064-T02 | progress | — | workplans/CUST-WP-0064-sbom-controlled-scan-inputs.md |
|
||||
| task | CUST-WP-0064-T03 | wait | — | workplans/CUST-WP-0064-sbom-controlled-scan-inputs.md |
|
||||
| task | CUST-WP-0064-T04 | wait | — | workplans/CUST-WP-0064-sbom-controlled-scan-inputs.md |
|
||||
| intake | CUST-IN-0001 | closed | green | intake-legacy-suggestions-migration.md |
|
||||
|
|
|
|||
186
docs/sbom-controlled-scan-input-contract-v1.md
Normal file
186
docs/sbom-controlled-scan-input-contract-v1.md
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
# Controlled SBOM scan input contract v1
|
||||
|
||||
**Status:** selected for implementation
|
||||
**Decision date:** 2026-08-22
|
||||
**Coordination:** `CUST-WP-0064`
|
||||
**Owners:** Repo Manager, SBOM Nexus, Activity Core, `rapp-sbom-nexus`
|
||||
|
||||
## Decision
|
||||
|
||||
Authoritative daily catch-up will scan a Forgejo source archive addressed by a
|
||||
full, immutable Git commit SHA. Repo Manager projects repository identity and
|
||||
an observed default-branch revision; SBOM Nexus validates, fetches, extracts,
|
||||
scans, persists provenance, and removes the source; Activity Core freezes the
|
||||
source reference with the already-bounded target set; `rapp-sbom-nexus`
|
||||
provides only the runtime filesystem and network boundary.
|
||||
|
||||
The initial provider is the public Coulomb Forgejo organization. The fetch
|
||||
path is equivalent to:
|
||||
|
||||
```text
|
||||
forgejo:coulomb/<repo-slug>@<40-lowercase-hex-commit>
|
||||
-> /coulomb/<repo-slug>/archive/<commit>.tar.gz
|
||||
```
|
||||
|
||||
The runtime constructs the fetch URL from configured Forgejo base and the
|
||||
validated identity. The ingest API does not accept an arbitrary URL. This
|
||||
prevents the scan path from becoming an SSRF surface or a general downloader.
|
||||
|
||||
An anonymous full-SHA archive fetch was proved on 2026-08-22 for
|
||||
`coulomb/sbom-nexus@b95fba9a9f489b70e2640b62a0b3317583be742d`. No source
|
||||
credential is required for v1. In particular, the Forgejo administrator PAT
|
||||
must not be reused. A private repository remains unsupported until a separate,
|
||||
least-privilege read identity and custody lane is approved.
|
||||
|
||||
## Wire contract
|
||||
|
||||
Repo Manager's projection supplies:
|
||||
|
||||
```json
|
||||
{
|
||||
"repo_slug": "sbom-nexus",
|
||||
"source_ref": {
|
||||
"kind": "forgejo-archive-v1",
|
||||
"repository": "coulomb/sbom-nexus",
|
||||
"revision": "b95fba9a9f489b70e2640b62a0b3317583be742d",
|
||||
"observed_ref": "refs/heads/main",
|
||||
"observed_at": "2026-08-22T21:38:58Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`revision` is always a full 40-character lowercase Git SHA. `repository` must
|
||||
match the canonical Forgejo remote owned by Repo Manager and, for v1, must be
|
||||
exactly `coulomb/<repo_slug>`. Repo Manager resolves the default-branch head
|
||||
from Forgejo and records when it was observed; workstation paths and local
|
||||
working-tree state are never used as production source.
|
||||
|
||||
SBOM Nexus stores the source reference in its repository projection and
|
||||
returns it with each selected entry from `GET /sbom/catch-up`. Activity Core
|
||||
records the exact returned reference in Temporal history together with the
|
||||
fixed, at-most-N target set. Its ingest call is:
|
||||
|
||||
```http
|
||||
POST /sbom/<repo_slug>/ingest
|
||||
Idempotency-Key: <stable per-workflow-run/per-repository key>
|
||||
X-Activity-Core-Operation-ID: <same value>
|
||||
Content-Type: application/json
|
||||
|
||||
{"source_ref": { ...the selected source reference... }}
|
||||
```
|
||||
|
||||
Retries reuse the same repository, source reference, and operation key even if
|
||||
the default branch advances. SBOM Nexus rejects a source identity/slug
|
||||
mismatch and a reused operation key with a different request fingerprint.
|
||||
Successful replay returns the original terminal response and never creates a
|
||||
second snapshot. This is the mutation-boundary requirement tracked by
|
||||
`CUST-IN-0013`.
|
||||
|
||||
## Fetch and extraction boundary
|
||||
|
||||
SBOM Nexus is the only component that fetches or scans source. For each request
|
||||
it creates a fresh directory on a dedicated ephemeral volume, streams the
|
||||
archive to disk, computes its SHA-256, safely extracts one top-level directory,
|
||||
scans it, commits the snapshot and operation receipt transactionally, and
|
||||
removes the request directory in a `finally` path.
|
||||
|
||||
The v1 limits are:
|
||||
|
||||
| Control | Limit |
|
||||
| --- | ---: |
|
||||
| Concurrent controlled-source scans per Nexus pod | 1 |
|
||||
| Fetch timeout | 120 seconds |
|
||||
| Scan timeout | 120 seconds |
|
||||
| Compressed archive | 100 MiB |
|
||||
| Expanded files | 512 MiB total |
|
||||
| Archive members | 100,000 |
|
||||
| Ephemeral volume | 768 MiB |
|
||||
| Redirects | same configured Forgejo host only |
|
||||
|
||||
Extraction rejects absolute paths, `..` traversal, links, devices, FIFOs, and
|
||||
members outside the single archive root. The implementation streams bytes and
|
||||
does not load the complete archive into memory. The application container
|
||||
keeps its read-only root filesystem; only the bounded source volume is
|
||||
writable. Logs may contain repository, revision, byte counts, durations,
|
||||
outcome, and operation-key hash, but never archive content or credential data.
|
||||
|
||||
The package permits egress only to the Forgejo HTTP workload on port 3000 and
|
||||
cluster DNS needed to resolve it, in addition to the existing PostgreSQL
|
||||
egress. The configured runtime base is the cluster-local Forgejo service; the
|
||||
canonical provenance identity remains `forgejo:coulomb/<slug>@<revision>`.
|
||||
|
||||
## Snapshot provenance and outcomes
|
||||
|
||||
An authoritative snapshot records at least:
|
||||
|
||||
- repository slug and canonical Forgejo repository identity;
|
||||
- immutable Git revision and observed branch metadata;
|
||||
- downloaded archive SHA-256 and byte count;
|
||||
- manifest-relative paths and their existing content SHA-256 values;
|
||||
- operation id, scanner version, start/end time, and terminal outcome.
|
||||
|
||||
The archive does not contain `.git`, so the scanner receives the validated
|
||||
revision explicitly instead of attempting to infer it from the extracted
|
||||
directory.
|
||||
|
||||
Terminal outcomes remain explicit:
|
||||
|
||||
| Condition | Outcome |
|
||||
| --- | --- |
|
||||
| Valid source, supported evidence, clean parse | `ingested` |
|
||||
| Valid source, no supported evidence | `no-manifest` |
|
||||
| Supported evidence with parser errors | `ingest-error` |
|
||||
| No eligible Forgejo source reference or revision cannot be fetched | `source-unavailable` |
|
||||
| Identity, redirect, archive, or extraction policy violation | `source-rejected` |
|
||||
|
||||
`source-unavailable` and `source-rejected` are additive skip reasons. Activity
|
||||
Core must accept and report them without selecting a replacement repository in
|
||||
the same fire. Only `ingested` advances `last_success_at`; every committed
|
||||
terminal outcome advances `last_attempt_at` and preserves queue fairness.
|
||||
|
||||
## Ownership
|
||||
|
||||
| Owner | Required change |
|
||||
| --- | --- |
|
||||
| Repo Manager | Normalize the canonical Forgejo repository, observe its default-branch full SHA, and project the structured source reference. Missing, private, non-Coulomb, or unresolvable repositories have no v1 source reference. |
|
||||
| SBOM Nexus | Add source-reference projection, controlled fetch/extraction, explicit revision override, snapshot provenance, new skip reasons, and durable operation receipts. It remains the only snapshot writer. |
|
||||
| Activity Core | Freeze `source_ref` with the original bounded target set, submit it on ingest, preserve the operation key across retries, and report the additive skip reasons. It must not fetch source itself. |
|
||||
| `rapp-sbom-nexus` | Add the size-limited ephemeral volume, Forgejo/DNS egress, configuration and resource controls, and live cleanup/rollback checks. It owns no scan semantics. |
|
||||
|
||||
## Unsupported and failure behavior
|
||||
|
||||
A missing source reference is visible in catch-up and becomes one terminal
|
||||
`source-unavailable` outcome when selected. HTTP not-found, an unavailable
|
||||
pinned commit, or a private repository without an approved v1 access path is
|
||||
also `source-unavailable`. Timeout, oversize content, unsafe archive members,
|
||||
an off-host redirect, identity mismatch, or malformed source reference is
|
||||
`source-rejected`. A transport ambiguity after a committed write is resolved
|
||||
only by replaying the same operation key; Activity Core must not synthesize a
|
||||
skip or move to another target.
|
||||
|
||||
## Rollout and rollback
|
||||
|
||||
Roll out dark behind `SBOM_NEXUS_CONTROLLED_SOURCE_ENABLED=false`, migrate the
|
||||
projection/provenance/idempotency schema, enable the source path for an
|
||||
attended one-repository canary, then enable it for the existing daily bounded
|
||||
definition. No increase to `catch_up_limit` is part of this change.
|
||||
|
||||
Rollback order is:
|
||||
|
||||
1. disable the Activity Core daily schedule and allow or cancel the one
|
||||
in-flight bounded run;
|
||||
2. set controlled source disabled and roll back the Nexus/package image digest;
|
||||
3. remove Forgejo/DNS egress and the ephemeral volume if reverting the package;
|
||||
4. retain all snapshots, operation receipts, and additive database columns.
|
||||
|
||||
Rollback never deletes SBOM history and never re-enables the retired weekly
|
||||
task-flood rule. Local Nexus CLI scans remain non-authoritative preview work;
|
||||
workstation checkouts are not a production fallback.
|
||||
|
||||
## Acceptance evidence
|
||||
|
||||
The implementation proof must show one attended and one normally scheduled
|
||||
fire with the original target bound, stable operation ids and revisions across
|
||||
retry, at least one real `ingested` snapshot, archive and manifest provenance,
|
||||
zero spawned tasks, empty transient-source storage after completion, and a
|
||||
rollback control that disables new source fetches without erasing history.
|
||||
|
|
@ -44,7 +44,7 @@ the deployment package owns the runtime/network boundary.
|
|||
|
||||
```task
|
||||
id: CUST-WP-0064-T01
|
||||
status: progress
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "02ac7278-8536-5ce8-9027-39345aab0539"
|
||||
```
|
||||
|
|
@ -58,20 +58,25 @@ behavior, cleanup, and the boundary between preview and authoritative ingest.
|
|||
Done when the four owning repos have one reviewable contract and rollback; do
|
||||
not enable source transfer from a prose-only assumption.
|
||||
|
||||
**Started (2026-08-22):** live verification confirmed the private Nexus pod
|
||||
**Done (2026-08-22):** live verification confirmed the private Nexus pod
|
||||
cannot reach projected workstation checkout paths and that queue fairness is
|
||||
therefore advancing through `no-checkout` outcomes. The Custodian fixed the
|
||||
non-negotiable boundary—revision-pinned input, no workstation mount, bounded
|
||||
credentials/egress/time/size, provenance, cleanup, and rollback—and routed
|
||||
owner participation to SBOM Nexus (`95c1b226`), Repo Manager (`075e21de`),
|
||||
Activity Core (`7233d2d1`), and the package owner (`e0af24b1`). Selection of
|
||||
the concrete transfer mechanism remains in progress.
|
||||
the concrete transfer mechanism is recorded in
|
||||
`docs/sbom-controlled-scan-input-contract-v1.md`: a public Coulomb Forgejo
|
||||
archive addressed by a full commit SHA, fetched and safely scanned by Nexus
|
||||
from a bounded ephemeral volume. A live full-SHA archive fetch succeeded; v1
|
||||
therefore needs no source credential and explicitly forbids reuse of the
|
||||
Forgejo administrator PAT.
|
||||
|
||||
## Implement the Nexus-owned authoritative scan path
|
||||
|
||||
```task
|
||||
id: CUST-WP-0064-T02
|
||||
status: wait
|
||||
status: progress
|
||||
priority: high
|
||||
state_hub_task_id: "2029e525-0573-5fea-881c-d3a418b91c9d"
|
||||
```
|
||||
|
|
@ -81,6 +86,11 @@ consume the selected input, scan at a pinned revision, persist provenance, and
|
|||
remove temporary source material. Preserve Nexus as the only snapshot writer
|
||||
and enforce `CUST-IN-0013` operation idempotency on the mutation boundary.
|
||||
|
||||
**Started (2026-08-22):** the selected v1 contract fixes the application and
|
||||
package changes, additive skip reasons, safety limits, durable replay rule,
|
||||
and rollback sequence. Implementation handoffs now carry one shared contract
|
||||
instead of separate mechanism proposals.
|
||||
|
||||
## Retarget bounded catch-up without widening it
|
||||
|
||||
```task
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue