feat(excititor): OCI OpenVEX artifact-backed configuration (SPRINT_20260423_001)

Closes SPRINT_20260423_001 — all 3 tasks DONE, sprint archived. Extends
the scalar persisted-settings model (7efa424fe) with an artifact-
reference storage layer for the complex OCI OpenVEX connector, which
carries image subscription lists + cosign keys + TUF roots that don't
fit the flat values/clearKeys shape.

OCI-CFG-001 — Storage model:
- New vex.provider_artifact_refs table via embedded migration
  009_vex_provider_artifact_refs.sql (auto-applied per §2.7). Columns:
  artifact_id (UUID PK), provider_id, tenant_id, mime_type, size_bytes,
  sha256, payload bytea, staged_at, staged_by. RLS via
  current_setting('app.tenant_id', TRUE); size_bytes CHECK >= 0.
- Upload / list / meta / delete API under /excititor/providers/{id}/
  artifacts. Multipart upload returns { artifactId, sha256, ... };
  meta never returns payload. Scopes: read=vex.read, write=vex.admin.
- VexProviderArtifactMaterializer: scoped session writes artifacts to
  %TMP%/stella-excititor-artifacts/<guid>/<artifactId>.bin with
  chmod 0700/0600 on POSIX; Dispose cleans up the scratch root.
  IDisposable + IAsyncDisposable, idempotent cleanup.
- PostgresVexProviderArtifactStore uses raw Npgsql for bytea I/O
  (bypasses EF compiled-model cache + change tracking); EF entity
  ProviderArtifactRefRow added for future EF-consumer completeness.

OCI-CFG-002 — Config wiring:
- VexProviderConfigurationFieldDefinition.FieldShape extended:
  scalar | list<string> | artifactRef | list<artifactRef>.
- list<string> encoded as newline-or-JSON text; artifactRef stored as
  the artifact UUID. Snapshot exposes FieldShape for clients.
- OCI-specific blocked sub-codes (all inside PROVIDER_CONFIG_INVALID /
  _REQUIRED envelope): PROVIDER_CONFIG_MISSING_IMAGE_SUBSCRIPTIONS,
  _INVALID_IMAGE_REFERENCE, _MISSING_COSIGN_KEY, _MISSING_COSIGN_ISSUER,
  _MISSING_COSIGN_SUBJECT, _MISSING_TUF_ROOT, _INVALID_COSIGN_MODE,
  _HTTP_REGISTRY_BLOCKED.
- Readiness reuses OciOpenVexAttestationConnectorOptions validator.

OCI-CFG-003 — CLI + Web:
- CLI `stella vex providers configure` flags: --image (repeatable),
  --upload-artifact <key>=@<path> (repeatable), --clear-artifact,
  --list-artifacts, --host-path-compat (CLI-only compatibility mode).
- New subcommand `stella vex providers artifacts <provider>` lists
  staged artifacts.
- OciOpenVexConfigurationComponent (Angular standalone): image-
  subscription list editor, artifact slots with file upload + staged
  meta, scalar fields for cosign mode/issuer/subject/registry auth/
  offline bundle, staged-artifact table with delete. Conditionally
  rendered in vex-provider-catalog when provider.id == 'excititor:
  oci-openvex'.
- docs/modules/excititor/operations/provider-credentials.md §5 OCI
  OpenVEX rewritten with 4 canonical setup flows: keyless, keypair,
  TUF+offline, private-registry.

Tests: targeted xUnit via scripts/test-targeted-xunit.ps1:
- VexProviderConfigurationServiceTests — 8/8 (regression)
- VexProviderOciOpenVexTests — 13/13 (OCI schema + sub-codes + artifact
  service size/quota/tenant-isolation + materializer tempfile lifecycle)

Worker-side wiring of VexProviderArtifactMaterializationSession into
DefaultVexProviderRunner (before cosign/TUF validation) is a natural
follow-up — the resolver + API + store exist; a future sprint ties
the runner into the session lifecycle when the first end-to-end OCI
provider scan lands.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
master
2026-04-23 08:10:14 +03:00
parent 5650ca6834
commit df66ce10c3
22 changed files with 2783 additions and 40 deletions

View File

@@ -1,84 +0,0 @@
# Sprint 20260423-001 — Excititor OCI OpenVEX artifact-backed configuration
## Topic & Scope
- Implement the complex `excititor:oci-openvex` configuration path separated out of SPRINT_20260422_007 (EXCITITOR-CFG-04 was BLOCKED there because it needs a distinct secret-reference / artifact-reference storage model that the scalar-settings path can't cleanly absorb).
- OCI OpenVEX connector configuration carries nested data that does not fit the flat string-map pattern the persisted-settings contract was designed for: image subscription lists (often dozens per tenant), registry credentials, cosign signature verification material (public keys, TUF trust roots, Fulcio identity constraints), and optional offline bundle inputs.
- Working directory: `src/Concelier/__Libraries/StellaOps.Excititor.Core/`, `StellaOps.Excititor.Connectors.OCI.OpenVEX.Attest/`, `StellaOps.Excititor.Persistence/`, `StellaOps.Excititor.WebService/`, plus CLI + Web surfaces.
- Expected evidence: new artifact-reference storage model, `VexProviderConfigurationService` extended for non-scalar field types, CLI + Web entries for the nested shape, focused tests, operator docs.
## Dependencies & Concurrency
- Upstream: SPRINT_20260422_007 EXCITITOR-CFG-01/02/03 (archived via commit `7efa424fe`) shipped the scalar persisted-settings machinery. This sprint extends it for complex types.
- Adjacent: SPRINT_20260422_008 FE-STAB4 (open, FE suite work) — no overlap.
- Safe parallelism: stays within `src/Concelier/__Libraries/StellaOps.Excititor.*` + Excititor WebService + CLI + Web OCI configuration panel.
## Documentation Prerequisites
- `docs-archived/implplan/SPRINT_20260422_007_Concelier_excititor_persisted_provider_credentials.md` — baseline CFG-01/02/03 contract.
- `src/Concelier/__Libraries/StellaOps.Excititor.Connectors.OCI.OpenVEX.Attest/OciOpenVexAttestationConnectorOptions.cs` — the real options validator to reuse.
- `docs/modules/excititor/operations/provider-control-plane.md` — current credential-entry dossier to extend.
## Delivery Tracker
### OCI-CFG-001 — Design + implement a staged artifact-reference storage model
Status: TODO
Dependency: none
Owners: Developer / Implementer
Task description:
- Extend `vex.provider_settings` (or add a sibling table `vex.provider_artifact_refs`) so settings can carry references to server-side staged artifacts rather than inlined binary material. A reference is an opaque, operator-uploaded blob identified by an ID, mime type, and optional checksum / signature.
- Define a minimal artifact-staging API: `POST /excititor/providers/{id}/artifacts` (multipart upload or base64) returning `{ artifactId, sha256 }`; `GET /excititor/providers/{id}/artifacts/{artifactId}/meta` for inspection (never returns the binary itself after staging); `DELETE` for removal.
- Artifacts live server-side with tenant isolation (RLS) and a size cap (e.g. 10 MiB per artifact, 50 MiB per provider total).
- Settings JSONB references artifacts by ID; the effective-settings resolver swaps refs for file-system-scoped material at runtime (e.g. writes a temp file to pass to cosign's verification library, then cleans up).
Completion criteria:
- [ ] Artifact-reference storage schema + migration (embedded, auto-applied per §2.7).
- [ ] Upload / meta / delete API with size-cap enforcement + tenant RLS.
- [ ] Effective-settings resolver materializes artifact refs to disk for runtime consumption, cleaning up after.
- [ ] Secrets never echoed on read — meta endpoint returns only `{ artifactId, sha256, mime, sizeBytes, stagedAt }`.
### OCI-CFG-002 — Wire OCI OpenVEX provider configuration to the new model
Status: TODO
Dependency: OCI-CFG-001
Owners: Developer / Implementer
Task description:
- `VexProviderConfigurationService` must support nested field types: `artifactRef`, `list<string>` (image subscriptions), `list<artifactRef>` (multi-key / multi-TUF-root).
- Reuse `OciOpenVexAttestationConnectorOptions` validator to compute readiness. Blocked reasons must be operator-facing: `PROVIDER_CONFIG_MISSING_COSIGN_KEY`, `PROVIDER_CONFIG_INVALID_IMAGE_REFERENCE`, etc. — sub-codes within the existing `PROVIDER_CONFIG_INVALID` envelope.
- Worker + orchestrator paths: no change — they already read through the effective-settings resolver which now transparently materializes artifact refs.
Completion criteria:
- [ ] OCI provider field schema exposes all 3 complex field types.
- [ ] Validator-backed readiness surfaces OCI-specific missing-material reasons.
- [ ] Targeted tests cover ready vs blocked vs invalid cases.
### OCI-CFG-003 — CLI + Web surfaces for complex fields
Status: TODO
Dependency: OCI-CFG-002
Owners: Developer / Implementer, Documentation author
Task description:
- CLI: `stella vex providers configure excititor:oci-openvex --image ghcr.io/foo/bar:v1 --image ...` (repeatable), `--upload-artifact cosign-key=@/path/to/cosign.pub`, `--clear-artifact cosign-key`, `--list-artifacts`. Host-path inputs explicitly supported behind a `--host-path-compat` flag and marked in CLI help as compatibility mode (CLI-only, never surfaced in UI).
- Web: new `OciOpenVexConfigurationComponent` (Angular standalone) with:
- image subscription list editor (add/remove rows, chip view).
- artifact-reference slots with upload + staged-secret rendering (similar masked-secret pattern).
- readiness panel showing the OCI-specific blocked reasons inline with the offending field.
- Routing: attach to the existing advisory-vex-sources detail flow via a conditional render when `provider.kind == 'oci-openvex'`.
- Docs: extend `docs/modules/excititor/operations/provider-credentials.md` with an OCI OpenVEX operator dossier (image subscription list management, cosign material acquisition, TUF root setup if applicable, offline bundle flow if retained).
Completion criteria:
- [ ] CLI commands cover list + upload + clear per artifact + image subscription editing.
- [ ] Web OCI configuration component renders + submits correctly against the new API.
- [ ] Operator doc updated with walk-through for the 34 canonical OCI OpenVEX setup shapes.
## Execution Log
| Date (UTC) | Update | Owner |
| --- | --- | --- |
| 2026-04-23 | Sprint created by splitting EXCITITOR-CFG-04 out of SPRINT_20260422_007 (now archivable). OCI artifact-backed configuration needs its own storage-model design — scalar settings store can't absorb image subscription lists + binary credential material cleanly. | Claude |
## Decisions & Risks
- **Decision**: artifact-reference table is a sibling of `vex.provider_settings`, not an extension of the JSONB column. Keeps scalar-settings semantics simple and lets binary material use its own RLS-enforced storage with size caps.
- **Decision**: host-path compat stays in CLI only, flagged. The UI must not surface file paths on an operator's host because the server won't be able to read them in production deployments.
- **Risk**: cosign/TUF verification libraries generally want on-disk paths. Mitigation: effective-settings resolver writes tempfiles per-request, ensures cleanup, keeps the temp directory chmod'd appropriately.
- **Risk**: artifact size caps (10 MiB per artifact, 50 MiB per provider) may be too tight for some TUF-root scenarios. Mitigation: caps are soft + configurable via platform environment settings; document the tunable.
- **Risk**: nested field types (`list<artifactRef>`) change the `values/clearKeys` API shape. Mitigation: keep the scalar shape unchanged, introduce a separate `/artifacts` endpoint path so the existing client code on scalar providers doesn't regress.
## Next Checkpoints
- OCI-CFG-001 DONE: artifact-reference schema + upload/meta/delete API + effective-settings materializer.
- OCI-CFG-002 DONE: OCI provider wired to the new model; validator-backed OCI-specific blocked reasons.
- OCI-CFG-003 DONE: CLI + Web + docs; sprint archivable when worker can run a sealed OCI OpenVEX provider end-to-end.