Update module architecture docs and workflow tutorials

- Module dossiers: attestor, authority, cli, graph, scanner
- Policy assistant parameters guide
- UI v2-rewire navigation rendering policy
- Test suite overview update
- Workflow engine requirements and tutorial series (01-08)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
master
2026-03-30 17:25:37 +03:00
parent 5722d36c0e
commit a6ffb38ecf
17 changed files with 4442 additions and 4380 deletions

View File

@@ -1,20 +1,20 @@
# component_architecture_attestor.md — **Stella Ops Attestor** (2025Q4) # component_architecture_attestor.md **StellaOps Attestor** (2025Q4)
> Derived from Epic 19 – Attestor Console with provenance hooks aligned to the Export Center bundle workflows scoped in Epic 10. > Derived from Epic19 Attestor Console with provenance hooks aligned to the Export Center bundle workflows scoped in Epic10.
> **Scope.** Implementation‑ready architecture for the **Attestor**: the service that **submits** DSSE envelopes to **Rekor v2**, retrieves/validates inclusion proofs, caches results, and exposes verification APIs. It accepts DSSE **only** from the **Signer** over mTLS, enforces chainâ€ofâ€trust to Stella Ops roots, and returns `{uuid, index, proof, logURL}` to calling services (Scanner.WebService for SBOMs; backend for final reports; Excititor exports when configured). > **Scope.** Implementationready architecture for the **Attestor**: the service that **submits** DSSE envelopes to **Rekor v2**, retrieves/validates inclusion proofs, caches results, and exposes verification APIs. It accepts DSSE **only** from the **Signer** over mTLS, enforces chainoftrust to StellaOps roots, and returns `{uuid, index, proof, logURL}` to calling services (Scanner.WebService for SBOMs; backend for final reports; Excititor exports when configured).
--- ---
## 0) Mission & boundaries ## 0) Mission & boundaries
**Mission.** Turn a signed DSSE envelope from the Signer into a **transparency‑logged, verifiable fact** with a durable, replayable proof (Merkle inclusion + (optional) checkpoint anchoring). Provide **fast verification** for downstream consumers and a stable retrieval interface for UI/CLI. **Mission.** Turn a signed DSSE envelope from the Signer into a **transparencylogged, verifiable fact** with a durable, replayable proof (Merkle inclusion + (optional) checkpoint anchoring). Provide **fast verification** for downstream consumers and a stable retrieval interface for UI/CLI.
**Boundaries.** **Boundaries.**
* Attestor **does not sign**; it **must not** accept unsigned or third‑party‑signed bundles. * Attestor **does not sign**; it **must not** accept unsigned or thirdpartysigned bundles.
* Attestor **does not decide PASS/FAIL**; it logs attestations for SBOMs, reports, and export artifacts. * Attestor **does not decide PASS/FAIL**; it logs attestations for SBOMs, reports, and export artifacts.
* Rekor v2 backends may be **local** (self‑hosted) or **remote**; Attestor handles both with retries, backoff, and idempotency. * Rekor v2 backends may be **local** (selfhosted) or **remote**; Attestor handles both with retries, backoff, and idempotency.
--- ---
@@ -24,22 +24,22 @@
**Dependencies:** **Dependencies:**
* **Signer** (caller) — authenticated via **mTLS** and **Authority** OpToks. * **Signer** (caller) authenticated via **mTLS** and **Authority** OpToks.
* **Rekor v2** — tile‑backed transparency log endpoint(s). * **Rekor v2** tilebacked transparency log endpoint(s).
* **RustFS (S3-compatible)** — optional archive store for DSSE envelopes & verification bundles. * **RustFS (S3-compatible)** optional archive store for DSSE envelopes & verification bundles.
* **PostgreSQL** — local cache of `{uuid, index, proof, artifactSha256, bundleSha256}`; job state; audit. * **PostgreSQL** local cache of `{uuid, index, proof, artifactSha256, bundleSha256}`; job state; audit.
* **Valkey** — dedupe/idempotency keys and short‑lived rate‑limit buckets. * **Valkey** dedupe/idempotency keys and shortlived ratelimit buckets.
* **Licensing Service (optional)** — “endorse” call for cross‑log publishing when customer opts‑in. * **Licensing Service (optional)** — “endorse call for crosslog publishing when customer optsin.
Trust boundary: **Only the Signer** is allowed to call submission endpoints; enforced by **mTLS peer cert allowlist** + `aud=attestor` OpTok. Trust boundary: **Only the Signer** is allowed to call submission endpoints; enforced by **mTLS peer cert allowlist** + `aud=attestor` OpTok.
--- ---
### Roles, identities & scopes ### Roles, identities & scopes
- **Subjects** — immutable digests for artifacts (container images, SBOMs, reports) referenced in DSSE envelopes. - **Subjects** immutable digests for artifacts (container images, SBOMs, reports) referenced in DSSE envelopes.
- **Issuers** — authenticated builders/scanners/policy engines signing evidence; tracked with mode (`keyless`, `kms`, `hsm`, `fido2`) and tenant scope. - **Issuers** authenticated builders/scanners/policy engines signing evidence; tracked with mode (`keyless`, `kms`, `hsm`, `fido2`) and tenant scope.
- **Consumers** — Scanner, Export Center, CLI, Console, Policy Engine that verify proofs using Attestor APIs. - **Consumers** Scanner, Export Center, CLI, Console, Policy Engine that verify proofs using Attestor APIs.
- **Authority scopes** — `attestor.write`, `attestor.verify`, `attestor.read`, and administrative scopes for key management; all calls mTLS/DPoP-bound. - **Authority scopes** `attestor.write`, `attestor.verify`, `attestor.read`, and administrative scopes for key management; all calls mTLS/DPoP-bound.
### Supported predicate types ### Supported predicate types
- `StellaOps.BuildProvenance@1` - `StellaOps.BuildProvenance@1`
@@ -76,9 +76,9 @@ Each predicate embeds subject digests, issuer metadata, policy context, material
The Attestor implements RFC 6962-compliant Merkle inclusion proof verification for Rekor transparency log entries: The Attestor implements RFC 6962-compliant Merkle inclusion proof verification for Rekor transparency log entries:
**Components:** **Components:**
- `MerkleProofVerifier` — Verifies Merkle audit paths per RFC 6962 Section 2.1.1 - `MerkleProofVerifier` Verifies Merkle audit paths per RFC 6962 Section 2.1.1
- `CheckpointSignatureVerifier` — Parses and verifies Rekor checkpoint signatures (ECDSA/Ed25519) - `CheckpointSignatureVerifier` Parses and verifies Rekor checkpoint signatures (ECDSA/Ed25519)
- `RekorVerificationOptions` — Configuration for public keys, offline mode, and checkpoint caching - `RekorVerificationOptions` Configuration for public keys, offline mode, and checkpoint caching
**Verification Flow:** **Verification Flow:**
1. Parse checkpoint body (origin, tree size, root hash) 1. Parse checkpoint body (origin, tree size, root hash)
@@ -93,10 +93,10 @@ The Attestor implements RFC 6962-compliant Merkle inclusion proof verification f
- `AllowOfflineWithoutSignature` for fully disconnected scenarios (reduced security) - `AllowOfflineWithoutSignature` for fully disconnected scenarios (reduced security)
**Metrics:** **Metrics:**
- `attestor.rekor_inclusion_verify_total` — Verification attempts by result - `attestor.rekor_inclusion_verify_total` Verification attempts by result
- `attestor.rekor_checkpoint_verify_total` — Checkpoint signature verifications - `attestor.rekor_checkpoint_verify_total` Checkpoint signature verifications
- `attestor.rekor_offline_verify_total` — Offline mode verifications - `attestor.rekor_offline_verify_total` Offline mode verifications
- `attestor.rekor_checkpoint_cache_hits/misses` — Checkpoint cache performance - `attestor.rekor_checkpoint_cache_hits/misses` Checkpoint cache performance
### UI & CLI touchpoints ### UI & CLI touchpoints
- Console: Evidence browser, verification report, chain-of-custody graph, issuer/key management, attestation workbench, bulk verification views. - Console: Evidence browser, verification report, chain-of-custody graph, issuer/key management, attestation workbench, bulk verification views.
@@ -104,7 +104,7 @@ The Attestor implements RFC 6962-compliant Merkle inclusion proof verification f
- SDKs expose sign/verify primitives for build pipelines. - SDKs expose sign/verify primitives for build pipelines.
### Performance & observability targets ### Performance & observability targets
- Throughput goal: ≥1 000 envelopes/minute per worker with cached verification. - Throughput goal: ≥1000 envelopes/minute per worker with cached verification.
- Metrics: `attestor_submission_total`, `attestor_verify_seconds`, `attestor_rekor_latency_seconds`, `attestor_cache_hit_ratio`. - Metrics: `attestor_submission_total`, `attestor_verify_seconds`, `attestor_rekor_latency_seconds`, `attestor_cache_hit_ratio`.
- Logs include `tenant`, `issuer`, `subjectDigest`, `rekorUuid`, `proofStatus`; traces cover submission → Rekor → cache → response path. - Logs include `tenant`, `issuer`, `subjectDigest`, `rekorUuid`, `proofStatus`; traces cover submission → Rekor → cache → response path.
@@ -172,8 +172,8 @@ Database: `attestor`
Indexes: Indexes:
* `entries`: indexes on `artifact_sha256`, `bundle_sha256`, `created_at`, and composite `(status, created_at DESC)`. * `entries`: indexes on `artifact_sha256`, `bundle_sha256`, `created_at`, and composite `(status, created_at DESC)`.
* `dedupe`: unique index on `key`; scheduled job cleans rows where `ttl_at < NOW()` (24–48h retention). * `dedupe`: unique index on `key`; scheduled job cleans rows where `ttl_at < NOW()` (2448h retention).
* `audit`: index on `ts` for time‑range queries. * `audit`: index on `ts` for timerange queries.
--- ---
@@ -375,10 +375,10 @@ The exception signing service provides endpoints for signing, verifying, and ren
**Attestor accepts only** DSSE envelopes that satisfy all of: **Attestor accepts only** DSSE envelopes that satisfy all of:
1. **mTLS** peer certificate maps to `signer` service (CA‑pinned). 1. **mTLS** peer certificate maps to `signer` service (CApinned).
2. **Authority** OpTok with `aud=attestor`, `scope=attestor.write`, DPoP or mTLS bound. 2. **Authority** OpTok with `aud=attestor`, `scope=attestor.write`, DPoP or mTLS bound.
3. DSSE envelope is **signed by the Signer’s key** (or includes a **Fulcio‑issued** cert chain) and **chains to configured roots** (Fulcio/KMS). 3. DSSE envelope is **signed by the Signers key** (or includes a **Fulcioissued** cert chain) and **chains to configured roots** (Fulcio/KMS).
4. **Predicate type** is one of Stella Ops types (sbom/report/vex‑export) with valid schema. 4. **Predicate type** is one of StellaOps types (sbom/report/vexexport) with valid schema.
5. `subject[*].digest.sha256` is present and canonicalized. 5. `subject[*].digest.sha256` is present and canonicalized.
**Wire shape (JSON):** **Wire shape (JSON):**
@@ -405,7 +405,7 @@ The exception signing service provides endpoints for signing, verifying, and ren
`POST /api/v1/attestations:sign` *(mTLS + OpTok required)* `POST /api/v1/attestations:sign` *(mTLS + OpTok required)*
* **Purpose**: Deterministically wrap Stella Ops payloads in DSSE envelopes before Rekor submission. Reuses the submission rate limiter and honours caller tenancy/audience scopes. * **Purpose**: Deterministically wrap StellaOps payloads in DSSE envelopes before Rekor submission. Reuses the submission rate limiter and honours caller tenancy/audience scopes.
* **Body**: * **Body**:
```json ```json
@@ -428,7 +428,7 @@ The exception signing service provides endpoints for signing, verifying, and ren
* **Behaviour**: * **Behaviour**:
* Resolve the signing key from `attestor.signing.keys[]` (includes algorithm, provider, and optional KMS version). * Resolve the signing key from `attestor.signing.keys[]` (includes algorithm, provider, and optional KMS version).
* Compute DSSE pre‑authentication encoding, sign with the resolved provider (default EC, BouncyCastle Ed25519, or File‑KMS ES256), and add static + request certificate chains. * Compute DSSE preauthentication encoding, sign with the resolved provider (default EC, BouncyCastle Ed25519, or FileKMS ES256), and add static + request certificate chains.
* Canonicalise the resulting bundle, derive `bundleSha256`, and mirror the request meta shape used by `/api/v1/rekor/entries`. * Canonicalise the resulting bundle, derive `bundleSha256`, and mirror the request meta shape used by `/api/v1/rekor/entries`.
* Emit `attestor.sign_total{result,algorithm,provider}` and `attestor.sign_latency_seconds{algorithm,provider}` metrics and append an audit row (`action=sign`). * Emit `attestor.sign_total{result,algorithm,provider}` and `attestor.sign_latency_seconds{algorithm,provider}` metrics and append an audit row (`action=sign`).
* **Response 200**: * **Response 200**:
@@ -461,13 +461,13 @@ The exception signing service provides endpoints for signing, verifying, and ren
```json ```json
{ {
"uuid": "…", "uuid": "",
"index": 123456, "index": 123456,
"proof": { "proof": {
"checkpoint": { "origin": "rekor@site", "size": 987654, "rootHash": "…", "timestamp": "…" }, "checkpoint": { "origin": "rekor@site", "size": 987654, "rootHash": "", "timestamp": "" },
"inclusion": { "leafHash": "…", "path": ["…","…"] } "inclusion": { "leafHash": "", "path": ["…","…"] }
}, },
"logURL": "https://rekor…/api/v2/log/…/entries/…", "logURL": "https://rekor/api/v2/log//entries/",
"status": "included" "status": "included"
} }
``` ```
@@ -480,28 +480,28 @@ The exception signing service provides endpoints for signing, verifying, and ren
* Returns `entries` row (refreshes proof from Rekor if stale/missing). * Returns `entries` row (refreshes proof from Rekor if stale/missing).
* Accepts `?refresh=true` to force backend query. * Accepts `?refresh=true` to force backend query.
### 4.4 Verification (third‑party or internal) ### 4.4 Verification (thirdparty or internal)
`POST /api/v1/rekor/verify` `POST /api/v1/rekor/verify`
* **Body** (one of): * **Body** (one of):
* `{ "uuid": "…" }` * `{ "uuid": "" }`
* `{ "bundle": { …DSSE… } }` * `{ "bundle": { DSSE } }`
* `{ "artifactSha256": "…" }` *(looks up most recent entry)* * `{ "artifactSha256": "" }` *(looks up most recent entry)*
* **Checks**: * **Checks**:
1. **Bundle signature** → cert chain to Fulcio/KMS roots configured. 1. **Bundle signature** → cert chain to Fulcio/KMS roots configured.
2. **Inclusion proof** → recompute leaf hash; verify Merkle path against checkpoint root. 2. **Inclusion proof** → recompute leaf hash; verify Merkle path against checkpoint root.
3. Optionally verify **checkpoint** against local trust anchors (if Rekor signs checkpoints). 3. Optionally verify **checkpoint** against local trust anchors (if Rekor signs checkpoints).
4. Confirm **subject.digest** matches caller‑provided hash (when given). 4. Confirm **subject.digest** matches callerprovided hash (when given).
5. Fetch **transparency witness** statement when enabled; cache results and downgrade status to WARN when endorsements are missing or mismatched. 5. Fetch **transparency witness** statement when enabled; cache results and downgrade status to WARN when endorsements are missing or mismatched.
* **Response**: * **Response**:
```json ```json
{ "ok": true, "uuid": "…", "index": 123, "logURL": "…", "checkedAt": "…" } { "ok": true, "uuid": "", "index": 123, "logURL": "", "checkedAt": "" }
``` ```
### 4.5 Bulk verification ### 4.5 Bulk verification
@@ -512,9 +512,9 @@ The exception signing service provides endpoints for signing, verifying, and ren
**Worker path:** `BulkVerificationWorker` claims queued jobs (`status=queued → running`), executes items sequentially through the cached verification service, updates progress counters, and records metrics: **Worker path:** `BulkVerificationWorker` claims queued jobs (`status=queued → running`), executes items sequentially through the cached verification service, updates progress counters, and records metrics:
- `attestor.bulk_jobs_total{status}` – completed/failed jobs - `attestor.bulk_jobs_total{status}` completed/failed jobs
- `attestor.bulk_job_duration_seconds{status}` – job runtime - `attestor.bulk_job_duration_seconds{status}` job runtime
- `attestor.bulk_items_total{status}` – per-item outcomes (`succeeded`, `verification_failed`, `exception`) - `attestor.bulk_items_total{status}` per-item outcomes (`succeeded`, `verification_failed`, `exception`)
The worker honours `bulkVerification.itemDelayMilliseconds` for throttling and reschedules persistence conflicts with optimistic version checks. Results hydrate the verification cache; failed items record the error reason without aborting the overall job. The worker honours `bulkVerification.itemDelayMilliseconds` for throttling and reschedules persistence conflicts with optimistic version checks. Results hydrate the verification cache; failed items record the error reason without aborting the overall job.
@@ -524,7 +524,7 @@ The worker honours `bulkVerification.itemDelayMilliseconds` for throttling and r
* **Canonicalization**: DSSE envelopes are **normalized** (stable JSON ordering, no insignificant whitespace) before hashing and submission. * **Canonicalization**: DSSE envelopes are **normalized** (stable JSON ordering, no insignificant whitespace) before hashing and submission.
* **Transport**: HTTP/2 with retries (exponential backoff, jitter), budgeted timeouts. * **Transport**: HTTP/2 with retries (exponential backoff, jitter), budgeted timeouts.
* **Idempotency**: if backend returns “already exists,” map to existing `uuid`. * **Idempotency**: if backend returns already exists, map to existing `uuid`.
* **Proof acquisition**: * **Proof acquisition**:
* In synchronous mode, poll the log for inclusion up to `proofTimeoutMs`. * In synchronous mode, poll the log for inclusion up to `proofTimeoutMs`.
@@ -532,25 +532,25 @@ The worker honours `bulkVerification.itemDelayMilliseconds` for throttling and r
* **Mirrors/dual logs**: * **Mirrors/dual logs**:
* When `logPreference="both"`, submit to primary and mirror; store **both** UUIDs (primary canonical). * When `logPreference="both"`, submit to primary and mirror; store **both** UUIDs (primary canonical).
* Optional **cloud endorsement**: POST to the Stella Ops cloud `/attest/endorse` with `{uuid, artifactSha256}`; store returned endorsement id. * Optional **cloud endorsement**: POST to the StellaOps cloud `/attest/endorse` with `{uuid, artifactSha256}`; store returned endorsement id.
--- ---
## 6) Security model ## 6) Security model
* **mTLS required** for submission from **Signer** (CA‑pinned). * **mTLS required** for submission from **Signer** (CApinned).
* **Authority token** with `aud=attestor` and DPoP/mTLS binding must be presented; Attestor verifies both. * **Authority token** with `aud=attestor` and DPoP/mTLS binding must be presented; Attestor verifies both.
* **Bundle acceptance policy**: * **Bundle acceptance policy**:
* DSSE signature must chain to the configured **Fulcio** (keyless) or **KMS/HSM** roots. * DSSE signature must chain to the configured **Fulcio** (keyless) or **KMS/HSM** roots.
* SAN (Subject Alternative Name) must match **Signer identity** policy (e.g., `urn:stellaops:signer` or pinned OIDC issuer). * SAN (Subject Alternative Name) must match **Signer identity** policy (e.g., `urn:stellaops:signer` or pinned OIDC issuer).
* Predicate `predicateType` must be on allowlist (sbom/report/vex-export). * Predicate `predicateType` must be on allowlist (sbom/report/vex-export).
* `subject.digest.sha256` values must be present and well‑formed (hex). * `subject.digest.sha256` values must be present and wellformed (hex).
* **No public submission** path. **Never** accept bundles from untrusted clients. * **No public submission** path. **Never** accept bundles from untrusted clients.
* **Client certificate allowlists**: optional `security.mtls.allowedSubjects` / `allowedThumbprints` tighten peer identity checks beyond CA pinning. * **Client certificate allowlists**: optional `security.mtls.allowedSubjects` / `allowedThumbprints` tighten peer identity checks beyond CA pinning.
* **Rate limits**: token-bucket per caller derived from `quotas.perCaller` (QPS/burst) returns `429` + `Retry-After` when exceeded. * **Rate limits**: token-bucket per caller derived from `quotas.perCaller` (QPS/burst) returns `429` + `Retry-After` when exceeded.
* **Scope enforcement**: API separates `attestor.write`, `attestor.verify`, and `attestor.read` policies; verification/list endpoints accept read or verify scopes while submission endpoints remain write-only. * **Scope enforcement**: API separates `attestor.write`, `attestor.verify`, and `attestor.read` policies; verification/list endpoints accept read or verify scopes while submission endpoints remain write-only.
* **Request hygiene**: JSON content-type is mandatory (415 returned otherwise); DSSE payloads are capped (default 2 MiB), certificate chains limited to six entries, and signatures to six per envelope to mitigate parsing abuse. * **Request hygiene**: JSON content-type is mandatory (415 returned otherwise); DSSE payloads are capped (default 2MiB), certificate chains limited to six entries, and signatures to six per envelope to mitigate parsing abuse.
* **Redaction**: Attestor never logs secret material; DSSE payloads **should** be public by design (SBOMs/reports). If customers require redaction, enforce policy at Signer (predicate minimization) **before** Attestor. * **Redaction**: Attestor never logs secret material; DSSE payloads **should** be public by design (SBOMs/reports). If customers require redaction, enforce policy at Signer (predicate minimization) **before** Attestor.
--- ---
@@ -588,8 +588,8 @@ The worker honours `bulkVerification.itemDelayMilliseconds` for throttling and r
SLO guardrails: SLO guardrails:
* `attestor.verify_latency_seconds` P95 ≤ 2 s per policy. * `attestor.verify_latency_seconds` P95 ≤2s per policy.
* `attestor.verify_total{result="failed"}` ≤ 1 % of `attestor.verify_total` over 30 min rolling windows. * `attestor.verify_total{result="failed"}` ≤1% of `attestor.verify_total` over 30min rolling windows.
**Correlation**: **Correlation**:
@@ -682,7 +682,7 @@ attestor:
--- ---
## 10) Endâ€toâ€end sequences ## 10) Endtoend sequences
**A) Submit & include (happy path)** **A) Submit & include (happy path)**
@@ -741,8 +741,8 @@ sequenceDiagram
* Stateless; scale horizontally. * Stateless; scale horizontally.
* **Targets**: * **Targets**:
* Submit+proof P95 ≤ **300 ms** (warm log; local Rekor). * Submit+proof P95 ≤ **300ms** (warm log; local Rekor).
* Verify P95 ≤ **30 ms** from cache; ≤ **120 ms** with live proof fetch. * Verify P95 ≤ **30ms** from cache; ≤ **120ms** with live proof fetch.
* 1k submissions/minute per replica sustained. * 1k submissions/minute per replica sustained.
* **Hot caches**: `dedupe` (bundle hash → uuid), recent `entries` by artifact sha256. * **Hot caches**: `dedupe` (bundle hash → uuid), recent `entries` by artifact sha256.
@@ -752,8 +752,8 @@ sequenceDiagram
* **Happy path**: valid DSSE, inclusion within timeout. * **Happy path**: valid DSSE, inclusion within timeout.
* **Idempotency**: resubmit same `bundleSha256` → same `uuid`. * **Idempotency**: resubmit same `bundleSha256` → same `uuid`.
* **Security**: reject non‑Signer mTLS, wrong `aud`, DPoP replay, untrusted cert chain, forbidden predicateType. * **Security**: reject nonSigner mTLS, wrong `aud`, DPoP replay, untrusted cert chain, forbidden predicateType.
* **Rekor variants**: promise‑then‑proof, proof delayed, mirror dual‑submit, mirror failure. * **Rekor variants**: promisethenproof, proof delayed, mirror dualsubmit, mirror failure.
* **Verification**: corrupt leaf path, wrong root, tampered bundle. * **Verification**: corrupt leaf path, wrong root, tampered bundle.
* **Throughput**: soak test with 10k submissions; latency SLOs, zero drops. * **Throughput**: soak test with 10k submissions; latency SLOs, zero drops.
@@ -764,16 +764,16 @@ sequenceDiagram
* Language: **.NET 10** minimal API; `HttpClient` with **sockets handler** tuned for HTTP/2. * Language: **.NET 10** minimal API; `HttpClient` with **sockets handler** tuned for HTTP/2.
* JSON: **canonical writer** for DSSE payload hashing. * JSON: **canonical writer** for DSSE payload hashing.
* Crypto: use **BouncyCastle**/**System.Security.Cryptography**; PEM parsing for cert chains. * Crypto: use **BouncyCastle**/**System.Security.Cryptography**; PEM parsing for cert chains.
* Rekor client: pluggable driver; treat backend errors as retryable/non‑retryable with granular mapping. * Rekor client: pluggable driver; treat backend errors as retryable/nonretryable with granular mapping.
* Safety: size caps on bundles; decompress bombs guarded; strict UTF‑8. * Safety: size caps on bundles; decompress bombs guarded; strict UTF8.
* CLI integration: `stellaops verify attestation <uuid|bundle|artifact>` calls `/rekor/verify`. * CLI integration: `stellaops verify attestation <uuid|bundle|artifact>` calls `/rekor/verify`.
--- ---
## 15) Optional features ## 15) Optional features
* **Dual‑log** write (primary + mirror) and **cross‑log proof** packaging. * **Duallog** write (primary + mirror) and **crosslog proof** packaging.
* **Cloud endorsement**: send `{uuid, artifactSha256}` to Stella Ops cloud; store returned endorsement id for marketing/chainâ€ofâ€custody. * **Cloud endorsement**: send `{uuid, artifactSha256}` to StellaOps cloud; store returned endorsement id for marketing/chainofcustody.
* **Checkpoint pinning**: periodically pin latest Rekor checkpoints to an external audit store for independent monitoring. * **Checkpoint pinning**: periodically pin latest Rekor checkpoints to an external audit store for independent monitoring.
--- ---

View File

@@ -1,4 +1,4 @@
# component_architecture_authority.md — **Stella Ops Authority** (2025Q4) # component_architecture_authority.md **StellaOps Authority** (2025Q4)
> **Current tenant-selection ADR:** `docs/architecture/decisions/ADR-002-multi-tenant-same-api-key-selection.md` > **Current tenant-selection ADR:** `docs/architecture/decisions/ADR-002-multi-tenant-same-api-key-selection.md`
> **Service impact ledger:** `docs/technical/architecture/multi-tenant-service-impact-ledger.md` > **Service impact ledger:** `docs/technical/architecture/multi-tenant-service-impact-ledger.md`
@@ -8,18 +8,18 @@
> Consolidates identity and tenancy requirements documented across the AOC, Policy, and Platform guides, along with the dedicated Authority implementation plan. > Consolidates identity and tenancy requirements documented across the AOC, Policy, and Platform guides, along with the dedicated Authority implementation plan.
> **Scope.** Implementation‑ready architecture for **Stella Ops Authority**: the on‑prem **OIDC/OAuth2** service that issues **short‑lived, sender‑constrained operational tokens (OpToks)** to first‑party services and tools. Covers protocols (DPoP & mTLS binding), token shapes, endpoints, storage, rotation, HA, RBAC, audit, and testing. This component is the trust anchor for *who* is calling inside a Stella Ops installation. (Entitlement is proven separately by **PoE** from the cloud Licensing Service; Authority does not issue PoE.) > **Scope.** Implementationready architecture for **StellaOps Authority**: the onprem **OIDC/OAuth2** service that issues **shortlived, senderconstrained operational tokens (OpToks)** to firstparty services and tools. Covers protocols (DPoP & mTLS binding), token shapes, endpoints, storage, rotation, HA, RBAC, audit, and testing. This component is the trust anchor for *who* is calling inside a StellaOps installation. (Entitlement is proven separately by **PoE** from the cloud Licensing Service; Authority does not issue PoE.)
--- ---
## 0) Mission & boundaries ## 0) Mission & boundaries
**Mission.** Provide **fast, local, verifiable** authentication for Stella Ops microservices and tools by minting **very short‑lived** OAuth2/OIDC tokens that are **sender‑constrained** (DPoP or mTLS‑bound). Support RBAC scopes, multi‑tenant claims, and deterministic validation for APIs (Scanner, Signer, Attestor, Excititor, Concelier, UI, CLI, Zastava). **Mission.** Provide **fast, local, verifiable** authentication for StellaOps microservices and tools by minting **very shortlived** OAuth2/OIDC tokens that are **senderconstrained** (DPoP or mTLSbound). Support RBAC scopes, multitenant claims, and deterministic validation for APIs (Scanner, Signer, Attestor, Excititor, Concelier, UI, CLI, Zastava).
**Boundaries.** **Boundaries.**
* Authority **does not** validate entitlements/licensing. That’s enforced by **Signer** using **PoE** with the cloud Licensing Service. * Authority **does not** validate entitlements/licensing. Thats enforced by **Signer** using **PoE** with the cloud Licensing Service.
* Authority tokens are **operational only** (2–5 min TTL) and must not be embedded in long‑lived artifacts or stored in SBOMs. * Authority tokens are **operational only** (25min TTL) and must not be embedded in longlived artifacts or stored in SBOMs.
* Authority is **stateless for validation** (JWT) and **optional introspection** for services that prefer online checks. * Authority is **stateless for validation** (JWT) and **optional introspection** for services that prefer online checks.
--- ---
@@ -29,16 +29,16 @@
* **OIDC Discovery**: `/.well-known/openid-configuration` * **OIDC Discovery**: `/.well-known/openid-configuration`
* **OAuth2** grant types: * **OAuth2** grant types:
* **Client Credentials** (service↔service, with mTLS or private_key_jwt) * **Client Credentials** (serviceservice, with mTLS or private_key_jwt)
* **Device Code** (CLI login on headless agents; optional) * **Device Code** (CLI login on headless agents; optional)
* **Authorization Code + PKCE** (browser login for UI; optional) * **Authorization Code + PKCE** (browser login for UI; optional)
* **Sender constraint options** (choose per caller or per audience): * **Sender constraint options** (choose per caller or per audience):
* **DPoP** (Demonstration of Proofâ€ofâ€Possession): proof JWT on each HTTP request, bound to the access token via `cnf.jkt`. * **DPoP** (Demonstration of ProofofPossession): proof JWT on each HTTP request, bound to the access token via `cnf.jkt`.
* **OAuth 2.0 mTLS** (certificate‑bound tokens): token bound to client certificate thumbprint via `cnf.x5t#S256`. * **OAuth 2.0 mTLS** (certificatebound tokens): token bound to client certificate thumbprint via `cnf.x5t#S256`.
* **Signing algorithms**: **EdDSA (Ed25519)** preferred; fallback **ES256 (P‑256)**. Rotation is supported via **kid** in JWKS. * **Signing algorithms**: **EdDSA (Ed25519)** preferred; fallback **ES256 (P256)**. Rotation is supported via **kid** in JWKS.
* **Token format**: **JWT** access tokens (compact), optionally opaque reference tokens for services that insist on introspection. * **Token format**: **JWT** access tokens (compact), optionally opaque reference tokens for services that insist on introspection.
* **Clock skew tolerance**: ±60 s; issue `nbf`, `iat`, `exp` accordingly. * **Clock skew tolerance**: ±60s; issue `nbf`, `iat`, `exp` accordingly.
--- ---
@@ -47,7 +47,7 @@
* **Incident mode tokens** require the `obs:incident` scope, a human-supplied `incident_reason`, and remain valid only while `auth_time` stays within a five-minute freshness window. Resource servers enforce the same window and persist `incident.reason`, `incident.auth_time`, and the fresh-auth verdict in `authority.resource.authorize` events. Authority exposes `/authority/audit/incident` so auditors can review recent activations. * **Incident mode tokens** require the `obs:incident` scope, a human-supplied `incident_reason`, and remain valid only while `auth_time` stays within a five-minute freshness window. Resource servers enforce the same window and persist `incident.reason`, `incident.auth_time`, and the fresh-auth verdict in `authority.resource.authorize` events. Authority exposes `/authority/audit/incident` so auditors can review recent activations.
### 2.1 Access token (OpTok) — short‑lived (120–300 s) ### 2.1 Access token (OpTok) shortlived (120300s)
**Registered claims** **Registered claims**
@@ -62,7 +62,7 @@ jti = <uuid>
scope = "scanner.scan scanner.export signer.sign ..." scope = "scanner.scan scanner.export signer.sign ..."
``` ```
**Sender‑constraint (`cnf`)** **Senderconstraint (`cnf`)**
* **DPoP**: * **DPoP**:
@@ -84,11 +84,11 @@ roles = [ "svc.scanner", "svc.signer", "ui.admin", ... ]
plan? = <plan name> // optional hint for UIs; not used for enforcement plan? = <plan name> // optional hint for UIs; not used for enforcement
``` ```
> **Note**: Do **not** copy PoE claims into OpTok; OpTok ≠ entitlement. Only **Signer** checks PoE. > **Note**: Do **not** copy PoE claims into OpTok; OpTok entitlement. Only **Signer** checks PoE.
### 2.2 Refresh tokens (optional) ### 2.2 Refresh tokens (optional)
* Default **disabled**. If enabled (for UI interactive logins), pair with **DPoP‑bound** refresh tokens or **mTLS** client sessions; short TTL (≤ 8 h), rotating on use (replay‑safe). * Default **disabled**. If enabled (for UI interactive logins), pair with **DPoPbound** refresh tokens or **mTLS** client sessions; short TTL (≤ 8h), rotating on use (replaysafe).
### 2.3 ID tokens (optional) ### 2.3 ID tokens (optional)
@@ -100,8 +100,8 @@ plan? = <plan name> // optional hint for UIs; not used for e
### 3.1 OIDC discovery & keys ### 3.1 OIDC discovery & keys
* `GET /.well-known/openid-configuration` → endpoints, algs, jwks_uri * `GET /.well-known/openid-configuration` endpoints, algs, jwks_uri
* `GET /jwks` → JSON Web Key Set (rotating, at least 2 active keys during transition) * `GET /jwks` JSON Web Key Set (rotating, at least 2 active keys during transition)
> **KMS-backed keys.** When the signing provider is `kms`, Authority fetches only the public coordinates (`Qx`, `Qy`) and version identifiers from the backing KMS. Private scalars never leave the provider; JWKS entries are produced by re-exporting the public material via the `kms.version` metadata attached to each key. Retired keys keep the same `kms.version` metadata so audits can trace which cloud KMS version produced a token. > **KMS-backed keys.** When the signing provider is `kms`, Authority fetches only the public coordinates (`Qx`, `Qy`) and version identifiers from the backing KMS. Private scalars never leave the provider; JWKS entries are produced by re-exporting the public material via the `kms.version` metadata attached to each key. Retired keys keep the same `kms.version` metadata so audits can trace which cloud KMS version produced a token.
@@ -111,12 +111,12 @@ plan? = <plan name> // optional hint for UIs; not used for e
> Legacy aliases under `/oauth/token` are deprecated as of 1 November 2025 and now emit `Deprecation/Sunset/Warning` headers. See [`docs/api/authority-legacy-auth-endpoints.md`](../../api/authority-legacy-auth-endpoints.md) for timelines and migration guidance. > Legacy aliases under `/oauth/token` are deprecated as of 1 November 2025 and now emit `Deprecation/Sunset/Warning` headers. See [`docs/api/authority-legacy-auth-endpoints.md`](../../api/authority-legacy-auth-endpoints.md) for timelines and migration guidance.
* **Client Credentials** (service→service): * **Client Credentials** (serviceservice):
* **mTLS**: mutual TLS + `client_id` → bound token (`cnf.x5t#S256`) * **mTLS**: mutual TLS + `client_id` bound token (`cnf.x5t#S256`)
* `security.senderConstraints.mtls.enforceForAudiences` forces the mTLS path when requested `aud`/`resource` values intersect high-value audiences (defaults include `signer`). Authority rejects clients attempting to use DPoP/basic secrets for these audiences. * `security.senderConstraints.mtls.enforceForAudiences` forces the mTLS path when requested `aud`/`resource` values intersect high-value audiences (defaults include `signer`). Authority rejects clients attempting to use DPoP/basic secrets for these audiences.
* Stored `certificateBindings` are authoritative: thumbprint, subject, issuer, serial number, and SAN values are matched against the presented certificate, with rotation grace applied to activation windows. Failures surface deterministic error codes (e.g. `certificate_binding_subject_mismatch`). * Stored `certificateBindings` are authoritative: thumbprint, subject, issuer, serial number, and SAN values are matched against the presented certificate, with rotation grace applied to activation windows. Failures surface deterministic error codes (e.g. `certificate_binding_subject_mismatch`).
* **private_key_jwt**: JWT‑based client auth + **DPoP** header (preferred for tools and CLI) * **private_key_jwt**: JWTbased client auth + **DPoP** header (preferred for tools and CLI)
* **Device Code** (CLI): `POST /oauth/device/code` + `POST /oauth/token` poll * **Device Code** (CLI): `POST /oauth/device/code` + `POST /oauth/token` poll
* **Authorization Code + PKCE** (UI): standard * **Authorization Code + PKCE** (UI): standard
@@ -134,7 +134,7 @@ plan? = <plan name> // optional hint for UIs; not used for e
signed with the DPoP private key; header carries JWK. signed with the DPoP private key; header carries JWK.
3. Authority validates proof; issues access token with `cnf.jkt=<thumbprint(JWK)>`. 3. Authority validates proof; issues access token with `cnf.jkt=<thumbprint(JWK)>`.
4. Client uses the same DPoP key to sign **every subsequent API request** to services (Signer, Scanner, …). 4. Client uses the same DPoP key to sign **every subsequent API request** to services (Signer, Scanner, ).
**mTLS flow** **mTLS flow**
@@ -142,11 +142,11 @@ plan? = <plan name> // optional hint for UIs; not used for e
### 3.3 Introspection & revocation (optional) ### 3.3 Introspection & revocation (optional)
* `POST /introspect` → `{ active, sub, scope, aud, exp, cnf, ... }` * `POST /introspect` `{ active, sub, scope, aud, exp, cnf, ... }`
* `POST /revoke` → revokes refresh tokens or opaque access tokens. * `POST /revoke` revokes refresh tokens or opaque access tokens.
> Requests targeting the legacy `/oauth/{introspect|revoke}` paths receive deprecation headers and are scheduled for removal after 1 May 2026. > Requests targeting the legacy `/oauth/{introspect|revoke}` paths receive deprecation headers and are scheduled for removal after 1 May 2026.
* **Replay prevention**: maintain **DPoP `jti` cache** (TTL ≤ 10 min) to reject duplicate proofs when services supply DPoP nonces (Signer requires nonce for high‑value operations). * **Replay prevention**: maintain **DPoP `jti` cache** (TTL 10 min) to reject duplicate proofs when services supply DPoP nonces (Signer requires nonce for highvalue operations).
### 3.4 UserInfo (optional for UI) ### 3.4 UserInfo (optional for UI)
@@ -156,19 +156,19 @@ plan? = <plan name> // optional hint for UIs; not used for e
### 3.5 Vuln Explorer workflow safeguards ### 3.5 Vuln Explorer workflow safeguards
* **Anti-forgery flow** — Vuln Explorer’s mutation verbs call * **Anti-forgery flow** Vuln Explorers mutation verbs call
* `POST /vuln/workflow/anti-forgery/issue` * `POST /vuln/workflow/anti-forgery/issue`
* `POST /vuln/workflow/anti-forgery/verify` * `POST /vuln/workflow/anti-forgery/verify`
Callers must hold `vuln:operate` scopes. Issued tokens embed the actor, tenant, whitelisted actions, ABAC selectors (environment/owner/business tier), and optional context key/value pairs. Tokens are EdDSA/ES256 signed via the primary Authority signing key and default to a 10‑minute TTL (cap: 30 minutes). Verification enforces nonce reuse prevention, tenant match, and action membership before forwarding the request to Vuln Explorer. Callers must hold `vuln:operate` scopes. Issued tokens embed the actor, tenant, whitelisted actions, ABAC selectors (environment/owner/business tier), and optional context key/value pairs. Tokens are EdDSA/ES256 signed via the primary Authority signing key and default to a 10minute TTL (cap: 30minutes). Verification enforces nonce reuse prevention, tenant match, and action membership before forwarding the request to Vuln Explorer.
* **Attachment access** — Evidence bundles and attachments reference a ledger hash. Vuln Explorer obtains a scoped download token through: * **Attachment access** Evidence bundles and attachments reference a ledger hash. Vuln Explorer obtains a scoped download token through:
* `POST /vuln/attachments/tokens/issue` * `POST /vuln/attachments/tokens/issue`
* `POST /vuln/attachments/tokens/verify` * `POST /vuln/attachments/tokens/verify`
These tokens bind the ledger event hash, attachment identifier, optional finding/content metadata, and the actor. They default to a 30‑minute TTL (cap: 4 hours) and require `vuln:investigate`. These tokens bind the ledger event hash, attachment identifier, optional finding/content metadata, and the actor. They default to a 30minute TTL (cap: 4hours) and require `vuln:investigate`.
* **Audit trail** — Both flows emit `vuln.workflow.csrf.*` and `vuln.attachment.token.*` audit records with tenant, actor, ledger hash, nonce, and filtered context metadata so Offline Kit operators can reconcile actions against ledger entries. * **Audit trail** Both flows emit `vuln.workflow.csrf.*` and `vuln.attachment.token.*` audit records with tenant, actor, ledger hash, nonce, and filtered context metadata so Offline Kit operators can reconcile actions against ledger entries.
* **Configuration** * **Configuration**
@@ -200,7 +200,7 @@ plan? = <plan name> // optional hint for UIs; not used for e
### 4.1 Audiences ### 4.1 Audiences
* `signer` — only the **Signer** service should accept tokens with `aud=signer`. * `signer` only the **Signer** service should accept tokens with `aud=signer`.
* `attestor`, `scanner`, `concelier`, `excititor`, `ui`, `zastava` similarly. * `attestor`, `scanner`, `concelier`, `excititor`, `ui`, `zastava` similarly.
Services **must** verify `aud` and **sender constraint** (DPoP/mTLS) per their policy. Services **must** verify `aud` and **sender constraint** (DPoP/mTLS) per their policy.
@@ -227,13 +227,13 @@ Services **must** verify `aud` and **sender constraint** (DPoP/mTLS) per their p
| `authority:branding.read` / `authority:branding.write` | Authority | Branding admin | | `authority:branding.read` / `authority:branding.write` | Authority | Branding admin |
| `zastava.emit` / `zastava.enforce` | Scanner/Zastava | Runtime events / admission | | `zastava.emit` / `zastava.enforce` | Scanner/Zastava | Runtime events / admission |
**Roles → scopes mapping** is configured centrally (Authority policy) and pushed during token issuance. **Roles scopes mapping** is configured centrally (Authority policy) and pushed during token issuance.
--- ---
## 5) Storage & state ## 5) Storage & state
* **Configuration DB** (PostgreSQL/MySQL): clients, audiences, role→scope maps, tenant/installation registry, device code grants, persistent consents (if any). * **Configuration DB** (PostgreSQL/MySQL): clients, audiences, rolescope maps, tenant/installation registry, device code grants, persistent consents (if any).
* **Cache** (Valkey): * **Cache** (Valkey):
* DPoP **jti** replay cache (short TTL) * DPoP **jti** replay cache (short TTL)
@@ -247,20 +247,20 @@ Services **must** verify `aud` and **sender constraint** (DPoP/mTLS) per their p
* Maintain **at least 2 signing keys** active during rotation; tokens carry `kid`. * Maintain **at least 2 signing keys** active during rotation; tokens carry `kid`.
* Prefer **Ed25519** for compact tokens; maintain **ES256** fallback for FIPS contexts. * Prefer **Ed25519** for compact tokens; maintain **ES256** fallback for FIPS contexts.
* Rotation cadence: 30–90 days; emergency rotation supported. * Rotation cadence: 3090 days; emergency rotation supported.
* Publish new JWKS **before** issuing tokens with the new `kid` to avoid cold‑start validation misses. * Publish new JWKS **before** issuing tokens with the new `kid` to avoid coldstart validation misses.
* Keep **old keys** available **at least** for max token TTL + 5 minutes. * Keep **old keys** available **at least** for max token TTL + 5 minutes.
--- ---
## 7) HA & performance ## 7) HA & performance
* **Stateless issuance** (except device codes/refresh) → scale horizontally behind a load‑balancer. * **Stateless issuance** (except device codes/refresh) scale horizontally behind a loadbalancer.
* **DB** only for client metadata and optional flows; token checks are JWT‑local; introspection endpoints hit cache/DB minimally. * **DB** only for client metadata and optional flows; token checks are JWTlocal; introspection endpoints hit cache/DB minimally.
* **Targets**: * **Targets**:
* Token issuance P95 ≤ **20 ms** under warm cache. * Token issuance P95 **20ms** under warm cache.
* DPoP proof validation ≤ **1 ms** extra per request at resource servers (Signer/Scanner). * DPoP proof validation **1ms** extra per request at resource servers (Signer/Scanner).
* 99.9% uptime; HPA on CPU/latency. * 99.9% uptime; HPA on CPU/latency.
--- ---
@@ -269,7 +269,7 @@ Services **must** verify `aud` and **sender constraint** (DPoP/mTLS) per their p
* **Strict TLS** (1.3 preferred); HSTS; modern cipher suites. * **Strict TLS** (1.3 preferred); HSTS; modern cipher suites.
* **mTLS** enabled where required (Signer/Attestor paths). * **mTLS** enabled where required (Signer/Attestor paths).
* **Replay protection**: DPoP `jti` cache, nonce support for **Signer** (add `DPoP-Nonce` header on 401; clients re‑sign). * **Replay protection**: DPoP `jti` cache, nonce support for **Signer** (add `DPoP-Nonce` header on 401; clients resign).
* **Rate limits** per client & per IP; exponential backoff on failures. * **Rate limits** per client & per IP; exponential backoff on failures.
* **Secrets**: clients use **private_key_jwt** or **mTLS**; never basic secrets over the wire. * **Secrets**: clients use **private_key_jwt** or **mTLS**; never basic secrets over the wire.
* **CSP/CSRF** hardening on UI flows; `SameSite=Lax` cookies; PKCE enforced. * **CSP/CSRF** hardening on UI flows; `SameSite=Lax` cookies; PKCE enforced.
@@ -277,10 +277,10 @@ Services **must** verify `aud` and **sender constraint** (DPoP/mTLS) per their p
--- ---
## 9) Multi‑tenancy & installations ## 9) Multitenancy & installations
* **Tenant (`tid`)** and **Installation (`inst`)** registries define which audiences/scopes a client can request. * **Tenant (`tid`)** and **Installation (`inst`)** registries define which audiences/scopes a client can request.
* Cross‑tenant isolation enforced at issuance (disallow rogue `aud`), and resource servers **must** check that `tid` matches their configured tenant. * Crosstenant isolation enforced at issuance (disallow rogue `aud`), and resource servers **must** check that `tid` matches their configured tenant.
--- ---
@@ -293,7 +293,7 @@ Authority exposes two admin tiers:
``` ```
POST /admin/clients # create/update client (confidential/public) POST /admin/clients # create/update client (confidential/public)
POST /admin/audiences # register audience resource URIs POST /admin/audiences # register audience resource URIs
POST /admin/roles # define role→scope mappings POST /admin/roles # define rolescope mappings
POST /admin/tenants # create tenant/install entries POST /admin/tenants # create tenant/install entries
POST /admin/keys/rotate # rotate signing key (zero-downtime) POST /admin/keys/rotate # rotate signing key (zero-downtime)
GET /admin/metrics # Prometheus exposition (token issue rates, errors) GET /admin/metrics # Prometheus exposition (token issue rates, errors)
@@ -306,10 +306,10 @@ Declared client `audiences` flow through to the issued JWT `aud` claim and the t
## 11) Integration hard lines (what resource servers must enforce) ## 11) Integration hard lines (what resource servers must enforce)
Every Stella Ops service that consumes Authority tokens **must**: Every StellaOps service that consumes Authority tokens **must**:
1. Verify JWT signature (`kid` in JWKS), `iss`, `aud`, `exp`, `nbf`. 1. Verify JWT signature (`kid` in JWKS), `iss`, `aud`, `exp`, `nbf`.
2. Enforce **sender‑constraint**: 2. Enforce **senderconstraint**:
* **DPoP**: validate DPoP proof (`htu`, `htm`, `iat`, `jti`) and match `cnf.jkt`; cache `jti` for replay defense; honor nonce challenges. * **DPoP**: validate DPoP proof (`htu`, `htm`, `iat`, `jti`) and match `cnf.jkt`; cache `jti` for replay defense; honor nonce challenges.
* **mTLS**: match presented client cert thumbprint to token `cnf.x5t#S256`. * **mTLS**: match presented client cert thumbprint to token `cnf.x5t#S256`.
@@ -322,8 +322,8 @@ Every Stella Ops service that consumes Authority tokens **must**:
## 12) Error surfaces & UX ## 12) Error surfaces & UX
* Token endpoint errors follow OAuth2 (`invalid_client`, `invalid_grant`, `invalid_scope`, `unauthorized_client`). * Token endpoint errors follow OAuth2 (`invalid_client`, `invalid_grant`, `invalid_scope`, `unauthorized_client`).
* Resource servers use RFC 6750 style (`WWW-Authenticate: DPoP error="invalid_token", error_description="…", dpop_nonce="…" `). * Resource servers use RFC6750 style (`WWW-Authenticate: DPoP error="invalid_token", error_description="", dpop_nonce="" `).
* For DPoP nonce challenges, clients retry with the server‑supplied nonce once. * For DPoP nonce challenges, clients retry with the serversupplied nonce once.
--- ---
@@ -431,8 +431,8 @@ authority:
* **JWT validation**: wrong `aud`, expired `exp`, skewed `nbf`, stale `kid`. * **JWT validation**: wrong `aud`, expired `exp`, skewed `nbf`, stale `kid`.
* **DPoP**: invalid `htu`/`htm`, replayed `jti`, stale `iat`, wrong `jkt`, nonce dance. * **DPoP**: invalid `htu`/`htm`, replayed `jti`, stale `iat`, wrong `jkt`, nonce dance.
* **mTLS**: wrong client cert, wrong CA, thumbprint mismatch. * **mTLS**: wrong client cert, wrong CA, thumbprint mismatch.
* **RBAC**: scope enforcement per audience; over‑privileged client denied. * **RBAC**: scope enforcement per audience; overprivileged client denied.
* **Rotation**: JWKS rotation while load‑testing; zero‑downtime verification. * **Rotation**: JWKS rotation while loadtesting; zerodowntime verification.
* **HA**: kill one Authority instance; verify issuance continues; JWKS served by peers. * **HA**: kill one Authority instance; verify issuance continues; JWKS served by peers.
* **Performance**: 1k token issuance/sec on 2 cores with Valkey enabled for jti caching. * **Performance**: 1k token issuance/sec on 2 cores with Valkey enabled for jti caching.
@@ -442,18 +442,18 @@ authority:
| Threat | Vector | Mitigation | | Threat | Vector | Mitigation |
| ------------------- | ---------------- | ------------------------------------------------------------------------------------------ | | ------------------- | ---------------- | ------------------------------------------------------------------------------------------ |
| Token theft | Copy of JWT | **Short TTL**, **sender‑constraint** (DPoP/mTLS); replay blocked by `jti` cache and nonces | | Token theft | Copy of JWT | **Short TTL**, **senderconstraint** (DPoP/mTLS); replay blocked by `jti` cache and nonces |
| Replay across hosts | Reuse DPoP proof | Enforce `htu`/`htm`, `iat` freshness, `jti` uniqueness; services may require **nonce** | | Replay across hosts | Reuse DPoP proof | Enforce `htu`/`htm`, `iat` freshness, `jti` uniqueness; services may require **nonce** |
| Impersonation | Fake client | mTLS or `private_key_jwt` with pinned JWK; client registration & rotation | | Impersonation | Fake client | mTLS or `private_key_jwt` with pinned JWK; client registration & rotation |
| Key compromise | Signing key leak | HSM/KMS storage, key rotation, audit; emergency key revoke path; narrow token TTL | | Key compromise | Signing key leak | HSM/KMS storage, key rotation, audit; emergency key revoke path; narrow token TTL |
| Cross‑tenant abuse | Scope elevation | Enforce `aud`, `tid`, `inst` at issuance and resource servers | | Crosstenant abuse | Scope elevation | Enforce `aud`, `tid`, `inst` at issuance and resource servers |
| Downgrade to bearer | Strip DPoP | Resource servers require DPoP/mTLS based on `aud`; reject bearer without `cnf` | | Downgrade to bearer | Strip DPoP | Resource servers require DPoP/mTLS based on `aud`; reject bearer without `cnf` |
--- ---
## 17) Deployment & HA ## 17) Deployment & HA
* **Stateless** microservice, containerized; run ≥ 2 replicas behind LB. * **Stateless** microservice, containerized; run 2 replicas behind LB.
* **DB**: HA Postgres (or MySQL) for clients/roles; **Valkey** for device codes, DPoP nonces/jtis. * **DB**: HA Postgres (or MySQL) for clients/roles; **Valkey** for device codes, DPoP nonces/jtis.
* **Secrets**: mount client JWKs via K8s Secrets/HashiCorp Vault; signing keys via KMS. * **Secrets**: mount client JWKs via K8s Secrets/HashiCorp Vault; signing keys via KMS.
* **Backups**: DB daily; Valkey not critical (ephemeral). * **Backups**: DB daily; Valkey not critical (ephemeral).
@@ -470,7 +470,7 @@ authority:
--- ---
## 19) Quick reference — wire examples ## 19) Quick reference wire examples
**Access token (payload excerpt)** **Access token (payload excerpt)**
@@ -507,10 +507,10 @@ Signer validates that `hash(JWK)` in the proof matches `cnf.jkt` in the token.
## 20) Rollout plan ## 20) Rollout plan
1. **MVP**: Client Credentials (private_key_jwt + DPoP), JWKS, short OpToks, per‑audience scopes. 1. **MVP**: Client Credentials (private_key_jwt + DPoP), JWKS, short OpToks, peraudience scopes.
2. **Add**: mTLS‑bound tokens for Signer/Attestor; device code for CLI; optional introspection. 2. **Add**: mTLSbound tokens for Signer/Attestor; device code for CLI; optional introspection.
3. **Hardening**: DPoP nonce support; full audit pipeline; HA tuning. 3. **Hardening**: DPoP nonce support; full audit pipeline; HA tuning.
4. **UX**: Tenant/installation admin UI; role→scope editors; client bootstrap wizards. 4. **UX**: Tenant/installation admin UI; rolescope editors; client bootstrap wizards.
--- ---

View File

@@ -1,26 +1,26 @@
# component_architecture_cli.md — **Stella Ops CLI** (2025Q4) # component_architecture_cli.md **StellaOps CLI** (2025Q4)
> Consolidates requirements captured in the Policy Engine, Policy Studio, Vulnerability Explorer, Export Center, and Notifications implementation plans and module guides. > Consolidates requirements captured in the Policy Engine, Policy Studio, Vulnerability Explorer, Export Center, and Notifications implementation plans and module guides.
> **Scope.** Implementation‑ready architecture for **Stella Ops CLI**: command surface, process model, auth (Authority/DPoP), integration with Scanner/Excititor/Concelier/Signer/Attestor, Buildx plug‑in management, offline kit behavior, packaging, observability, security posture, and CI ergonomics. > **Scope.** Implementationready architecture for **StellaOps CLI**: command surface, process model, auth (Authority/DPoP), integration with Scanner/Excititor/Concelier/Signer/Attestor, Buildx plugin management, offline kit behavior, packaging, observability, security posture, and CI ergonomics.
--- ---
## 0) Mission & boundaries ## 0) Mission & boundaries
**Mission.** Provide a **fast, deterministic, CI‑friendly** command‑line interface to drive Stella Ops workflows: **Mission.** Provide a **fast, deterministic, CIfriendly** commandline interface to drive StellaOps workflows:
* Build‑time SBOM generation via **Buildx generator** orchestration. * Buildtime SBOM generation via **Buildx generator** orchestration.
* Post‑build **scan/compose/diff/export** against **Scanner.WebService**. * Postbuild **scan/compose/diff/export** against **Scanner.WebService**.
* **Policy** operations and **VEX/Vuln** data pulls (operator tasks). * **Policy** operations and **VEX/Vuln** data pulls (operator tasks).
* **Verification** (attestation, referrers, signatures) for audits. * **Verification** (attestation, referrers, signatures) for audits.
* Air‑gapped/offline **kit** administration. * Airgapped/offline **kit** administration.
**Boundaries.** **Boundaries.**
* CLI **never** signs; it only calls **Signer**/**Attestor** via backend APIs when needed (e.g., `report --attest`). * CLI **never** signs; it only calls **Signer**/**Attestor** via backend APIs when needed (e.g., `report --attest`).
* CLI **does not** store long‑lived credentials beyond OS keychain; tokens are **short** (Authority OpToks). * CLI **does not** store longlived credentials beyond OS keychain; tokens are **short** (Authority OpToks).
* Heavy work (scanning, merging, policy) is executed **server‑side** (Scanner/Excititor/Concelier). * Heavy work (scanning, merging, policy) is executed **serverside** (Scanner/Excititor/Concelier).
--- ---
@@ -41,7 +41,7 @@ src/
**Plug-in verbs.** Non-core verbs (Excititor, runtime helpers, future integrations) ship as restart-time plug-ins under `plugins/cli/**` with manifest descriptors. The launcher loads plug-ins on startup; hot reloading is intentionally unsupported. The inaugural bundle, `StellaOps.Cli.Plugins.NonCore`, packages the Excititor, runtime, and offline-kit command groups and publishes its manifest at `plugins/cli/StellaOps.Cli.Plugins.NonCore/`. **Plug-in verbs.** Non-core verbs (Excititor, runtime helpers, future integrations) ship as restart-time plug-ins under `plugins/cli/**` with manifest descriptors. The launcher loads plug-ins on startup; hot reloading is intentionally unsupported. The inaugural bundle, `StellaOps.Cli.Plugins.NonCore`, packages the Excititor, runtime, and offline-kit command groups and publishes its manifest at `plugins/cli/StellaOps.Cli.Plugins.NonCore/`.
**OS targets**: linuxâ€'x64/arm64, windowsâ€'x64/arm64, macOSâ€'x64/arm64. **OS targets**: linuxx64/arm64, windowsx64/arm64, macOSx64/arm64.
--- ---
@@ -118,19 +118,19 @@ See [migration-v3.md](./guides/migration-v3.md) for user-facing migration instru
* `auth login` * `auth login`
* Modes: **device‑code** (default), **client‑credentials** (service principal). * Modes: **devicecode** (default), **clientcredentials** (service principal).
* Produces **Authority** access token (OpTok) + stores **DPoP** keypair in OS keychain. * Produces **Authority** access token (OpTok) + stores **DPoP** keypair in OS keychain.
* `auth status` — show current issuer, subject, audiences, expiry. * `auth status` show current issuer, subject, audiences, expiry.
* `auth logout` — wipe cached tokens/keys. * `auth logout` wipe cached tokens/keys.
### 2.2 Build‑time SBOM (Buildx) ### 2.2 Buildtime SBOM (Buildx)
* `buildx install` — install/update the **StellaOps.Scanner.Sbomer.BuildXPlugin** on the host. * `buildx install` install/update the **StellaOps.Scanner.Sbomer.BuildXPlugin** on the host.
* `buildx verify` — ensure generator is usable. * `buildx verify` ensure generator is usable.
* `buildx build` — thin wrapper around `docker buildx build --attest=type=sbom,generator=stellaops/sbom-indexer` with convenience flags: * `buildx build` thin wrapper around `docker buildx build --attest=type=sbom,generator=stellaops/sbom-indexer` with convenience flags:
* `--attest` (request Signer/Attestor via backend post‑push) * `--attest` (request Signer/Attestor via backend postpush)
* `--provenance` pass‑through (optional) * `--provenance` passthrough (optional)
### 2.3 Scanning & artifacts ### 2.3 Scanning & artifacts
@@ -138,20 +138,20 @@ See [migration-v3.md](./guides/migration-v3.md) for user-facing migration instru
* Options: `--force`, `--wait`, `--view=inventory|usage|both`, `--format=cdx-json|cdx-pb|spdx-json`, `--attest` (ask backend to sign/log). * Options: `--force`, `--wait`, `--view=inventory|usage|both`, `--format=cdx-json|cdx-pb|spdx-json`, `--attest` (ask backend to sign/log).
* Streams progress; exits early unless `--wait`. * Streams progress; exits early unless `--wait`.
* `diff image --old <digest> --new <digest> [--view ...]` — show layer‑attributed changes. * `diff image --old <digest> --new <digest> [--view ...]` show layerattributed changes.
* `export sbom <digest> [--view ... --format ... --out file]` — download artifact. * `export sbom <digest> [--view ... --format ... --out file]` download artifact.
* `sbom upload --file <path> --artifact <ref> [--format cyclonedx|spdx]` - BYOS upload into the scanner analysis pipeline (ledger join uses the SBOM digest). * `sbom upload --file <path> --artifact <ref> [--format cyclonedx|spdx]` - BYOS upload into the scanner analysis pipeline (ledger join uses the SBOM digest).
* `report final <digest> [--policy-revision ... --attest]` — request PASS/FAIL report from backend (policy+vex) and optional attestation. * `report final <digest> [--policy-revision ... --attest]` request PASS/FAIL report from backend (policy+vex) and optional attestation.
### 2.3.1 Compare Commands & Baseline Selection (SPRINT_20260208_029) ### 2.3.1 Compare Commands & Baseline Selection (SPRINT_20260208_029)
The `compare` command group supports diffing scan snapshots with automatic baseline resolution. The `compare` command group supports diffing scan snapshots with automatic baseline resolution.
#### Commands #### Commands
* `compare diff --base <digest> --target <digest>` â€" Full comparison showing detailed diff. * `compare diff --base <digest> --target <digest>` Full comparison showing detailed diff.
* `compare summary --base <digest> --target <digest>` â€" Quick summary of changes. * `compare summary --base <digest> --target <digest>` Quick summary of changes.
* `compare can-ship --base <digest> --target <digest>` â€" Check if target passes policy (exit code: 0=pass, 1=fail). * `compare can-ship --base <digest> --target <digest>` Check if target passes policy (exit code: 0=pass, 1=fail).
* `compare vulns --base <digest> --target <digest>` â€" List vulnerability changes only. * `compare vulns --base <digest> --target <digest>` List vulnerability changes only.
#### Baseline Selection Strategies #### Baseline Selection Strategies
@@ -165,9 +165,9 @@ All compare commands support `--baseline-strategy` for automatic baseline resolu
**Options:** **Options:**
* `--baseline-strategy <explicit|last-green|previous-release>` â€" Strategy for baseline selection * `--baseline-strategy <explicit|last-green|previous-release>` Strategy for baseline selection
* `--artifact <purl|oci-ref>` â€" Artifact identifier for auto-resolution strategies * `--artifact <purl|oci-ref>` Artifact identifier for auto-resolution strategies
* `--current-version <tag>` â€" Current version (helps `previous-release` find older releases) * `--current-version <tag>` Current version (helps `previous-release` find older releases)
* `--verification-report <path>` - Attach `bundle verify --output json` checks to compare output (hash/signature overlay) * `--verification-report <path>` - Attach `bundle verify --output json` checks to compare output (hash/signature overlay)
* `--reverify-bundle <directory>` - Recompute artifact hash and DSSE-sidecar status from local evidence bundle for live re-verification * `--reverify-bundle <directory>` - Recompute artifact hash and DSSE-sidecar status from local evidence bundle for live re-verification
* `--determinism-manifest <path>` - Attach determinism manifest score/threshold summary to compare output * `--determinism-manifest <path>` - Attach determinism manifest score/threshold summary to compare output
@@ -214,32 +214,32 @@ public interface IBaselineResolver
``` ```
### 2.4 Policy & data ### 2.4 Policy & data
* `policy get/set/apply` — fetch active policy, apply staged policy, compute digest. * `policy get/set/apply` fetch active policy, apply staged policy, compute digest.
* `concelier export` — trigger/export canonical JSON or Trivy DB (admin). * `concelier export` trigger/export canonical JSON or Trivy DB (admin).
* `excititor export` — trigger/export consensus/raw claims (admin). * `excititor export` trigger/export consensus/raw claims (admin).
### 2.5 Verification ### 2.5 Verification
* `verify attestation --uuid <rekor-uuid> | --artifact <sha256> | --bundle <path>` — call **Attestor /verify** and print proof summary. * `verify attestation --uuid <rekor-uuid> | --artifact <sha256> | --bundle <path>` call **Attestor /verify** and print proof summary.
* `verify referrers <digest>` — ask **Signer /verify/referrers** (is image Stella‑signed?). * `verify referrers <digest>` ask **Signer /verify/referrers** (is image Stellasigned?).
* `verify image-signature <ref|digest>` — standalone cosign verification (optional, local). * `verify image-signature <ref|digest>` standalone cosign verification (optional, local).
* `verify release <bundle> [--sbom <path>] [--vex <path>] [--trust-root <path>] [--checkpoint <path>]` — run promotion bundle verification and fail if source/build/rekor/signature links cannot be validated. * `verify release <bundle> [--sbom <path>] [--vex <path>] [--trust-root <path>] [--checkpoint <path>]` run promotion bundle verification and fail if source/build/rekor/signature links cannot be validated.
### 2.6 Runtime (Zastava helper) ### 2.6 Runtime (Zastava helper)
* `runtime policy test --image/-i <digest> [--file <path> --ns <name> --label key=value --json]` — ask backend `/policy/runtime` like the webhook would (accepts multiple `--image`, comma/space lists, or stdin pipelines). * `runtime policy test --image/-i <digest> [--file <path> --ns <name> --label key=value --json]` ask backend `/policy/runtime` like the webhook would (accepts multiple `--image`, comma/space lists, or stdin pipelines).
### 2.7 Offline kit ### 2.7 Offline kit
* `offline kit pull` — fetch latest **Concelier JSON + Trivy DB + Excititor exports** as a tarball from a mirror. * `offline kit pull` fetch latest **Concelier JSON + Trivy DB + Excititor exports** as a tarball from a mirror.
* `offline kit import <tar>` — upload the kit to on‑prem services (Concelier/Excititor). * `offline kit import <tar>` upload the kit to onprem services (Concelier/Excititor).
* `offline kit status` — list current seed versions. * `offline kit status` list current seed versions.
### 2.8 Utilities ### 2.8 Utilities
* `config set/get` — endpoint & defaults. * `config set/get` endpoint & defaults.
* `whoami` — short auth display. * `whoami` short auth display.
* `version` — CLI + protocol versions; release channel. * `version` CLI + protocol versions; release channel.
* `tools policy-dsl-validate <paths...> [--strict] [--json]` * `tools policy-dsl-validate <paths...> [--strict] [--json]`
* `tools policy-schema-export [--output <dir>] [--repo-root <path>]` * `tools policy-schema-export [--output <dir>] [--repo-root <path>]`
* `tools policy-simulation-smoke [--scenario-root <path>] [--output <dir>] [--repo-root <path>] [--fixed-time <ISO-8601>]` * `tools policy-simulation-smoke [--scenario-root <path>] [--output <dir>] [--repo-root <path>] [--fixed-time <ISO-8601>]`
@@ -356,7 +356,7 @@ Both subcommands honour offline-first expectations (no network access) and norma
- `decision compare` - `decision compare`
* Executes the benchmark harness against baseline scanners (Trivy/Syft/Grype/Snyk/Xray), capturing false-positive reduction, mean-time-to-decision, and reproducibility metrics into `results/summary.csv`. * Executes the benchmark harness against baseline scanners (Trivy/Syft/Grype/Snyk/Xray), capturing false-positive reduction, mean-time-to-decision, and reproducibility metrics into `results/summary.csv`.
* Flags regressions when Stella Ops produces more false positives or slower MTTD than the configured target. * Flags regressions when StellaOps produces more false positives or slower MTTD than the configured target.
All verbs require scopes `policy.findings:read`, `signer.verify`, and (for Rekor lookups) `attestor.read`. They honour sealed-mode rules by falling back to offline verification only when Rekor/Signer endpoints are unreachable. All verbs require scopes `policy.findings:read`, `signer.verify`, and (for Rekor lookups) `attestor.read`. They honour sealed-mode rules by falling back to offline verification only when Rekor/Signer endpoints are unreachable.
@@ -379,15 +379,15 @@ All verbs require scopes `policy.findings:read`, `signer.verify`, and (for Rekor
### 3.1 Token acquisition ### 3.1 Token acquisition
* **Device‑code**: the CLI opens an OIDC device code flow against **Authority**; the browser login is optional for service principals. * **Devicecode**: the CLI opens an OIDC device code flow against **Authority**; the browser login is optional for service principals.
* **Client‑credentials**: service principals use **private_key_jwt** or **mTLS** to get tokens. * **Clientcredentials**: service principals use **private_key_jwt** or **mTLS** to get tokens.
### 3.2 DPoP key management ### 3.2 DPoP key management
* On first login, the CLI generates an **ephemeral JWK** (Ed25519) and stores it in the **OS keychain** (Keychain/DPAPI/KWallet/Gnome Keyring). * On first login, the CLI generates an **ephemeral JWK** (Ed25519) and stores it in the **OS keychain** (Keychain/DPAPI/KWallet/Gnome Keyring).
* Every request to backend services includes a **DPoP proof**; CLI refreshes tokens as needed. * Every request to backend services includes a **DPoP proof**; CLI refreshes tokens as needed.
### 3.3 Multi‑audience & scopes ### 3.3 Multiaudience & scopes
* CLI requests **audiences** as needed per verb: * CLI requests **audiences** as needed per verb:
@@ -409,10 +409,10 @@ CLI rejects verbs if required scopes are missing.
### 4.2 Streaming ### 4.2 Streaming
* `scan` and `report` support **server‑sent JSON lines** (progress events). * `scan` and `report` support **serversent JSON lines** (progress events).
* `--json` prints machine events; human mode shows compact spinners and crucial updates only. * `--json` prints machine events; human mode shows compact spinners and crucial updates only.
### 4.3 Exit codes (CI‑safe) ### 4.3 Exit codes (CIsafe)
| Code | Meaning | | Code | Meaning |
| ---- | ------------------------------------------- | | ---- | ------------------------------------------- |
@@ -424,7 +424,7 @@ CLI rejects verbs if required scopes are missing.
| 6 | Rate limited / quota exceeded | | 6 | Rate limited / quota exceeded |
| 7 | Backend unavailable (retryable) | | 7 | Backend unavailable (retryable) |
| 9 | Invalid arguments | | 9 | Invalid arguments |
| 11–17 | Aggregation-only guard violation (`ERR_AOC_00x`) | | 1117 | Aggregation-only guard violation (`ERR_AOC_00x`) |
| 18 | Verification truncated (increase `--limit`) | | 18 | Verification truncated (increase `--limit`) |
| 70 | Transport/authentication failure | | 70 | Transport/authentication failure |
| 71 | CLI usage error (missing tenant, invalid cursor) | | 71 | CLI usage error (missing tenant, invalid cursor) |
@@ -468,9 +468,9 @@ Environment variables: `STELLAOPS_AUTHORITY`, `STELLAOPS_SCANNER_URL`, etc.
* `--attest=type=sbom,generator=stellaops/sbom-indexer` * `--attest=type=sbom,generator=stellaops/sbom-indexer`
* `--label org.stellaops.request=sbom` * `--label org.stellaops.request=sbom`
* Post‑build: CLI optionally calls **Scanner.WebService** to **verify referrers**, **compose** image SBOMs, and **attest** via Signer/Attestor. * Postbuild: CLI optionally calls **Scanner.WebService** to **verify referrers**, **compose** image SBOMs, and **attest** via Signer/Attestor.
**Detection**: If Buildx or generator unavailable, CLI falls back to **post‑build scan** with a warning. **Detection**: If Buildx or generator unavailable, CLI falls back to **postbuild scan** with a warning.
--- ---
@@ -484,27 +484,27 @@ Environment variables: `STELLAOPS_AUTHORITY`, `STELLAOPS_SCANNER_URL`, etc.
## 8) Security posture ## 8) Security posture
* **DPoP private keys** stored in **OS keychain**; metadata cached in config. * **DPoP private keys** stored in **OS keychain**; metadata cached in config.
* **No plaintext tokens** on disk; short‑lived **OpToks** held in memory. * **No plaintext tokens** on disk; shortlived **OpToks** held in memory.
* **TLS**: verify backend certificates; allow custom CA bundle for on‑prem. * **TLS**: verify backend certificates; allow custom CA bundle for onprem.
* **Redaction**: CLI logs remove `Authorization`, DPoP headers, PoE tokens. * **Redaction**: CLI logs remove `Authorization`, DPoP headers, PoE tokens.
* **Supply chain**: CLI distribution binaries are **cosign‑signed**; `stellaops version --verify` checks its own signature. * **Supply chain**: CLI distribution binaries are **cosignsigned**; `stellaops version --verify` checks its own signature.
--- ---
## 9) Observability ## 9) Observability
* `--verbose` adds request IDs, timings, and retry traces. * `--verbose` adds request IDs, timings, and retry traces.
* **Metrics** (optional, disabled by default): Prometheus text file exporter for local monitoring in long‑running agents. * **Metrics** (optional, disabled by default): Prometheus text file exporter for local monitoring in longrunning agents.
* **Structured logs** (`--json`): per‑event JSON lines with `ts`, `verb`, `status`, `latencyMs`. * **Structured logs** (`--json`): perevent JSON lines with `ts`, `verb`, `status`, `latencyMs`.
--- ---
## 10) Performance targets ## 10) Performance targets
* Startup ≤ **20 ms** (AOT). * Startup ≤ **20ms** (AOT).
* `scan image` request/response overhead ≤ **5 ms** (excluding server work). * `scan image` request/response overhead ≤ **5ms** (excluding server work).
* Buildx wrapper overhead negligible (<1 ms). * Buildx wrapper overhead negligible (<1ms).
* Large artifact download (100 MB) sustained ≥ **80 MB/s** on local networks. * Large artifact download (100MB) sustained ≥ **80MB/s** on local networks.
--- ---
@@ -512,7 +512,7 @@ Environment variables: `STELLAOPS_AUTHORITY`, `STELLAOPS_SCANNER_URL`, etc.
* **Unit tests**: argument parsing, config precedence, URL resolution, DPoP proof creation. * **Unit tests**: argument parsing, config precedence, URL resolution, DPoP proof creation.
* **Integration tests** (Testcontainers): mock Authority/Scanner/Attestor; CI pipeline with fake registry. * **Integration tests** (Testcontainers): mock Authority/Scanner/Attestor; CI pipeline with fake registry.
* **Golden outputs**: verb snapshots for `--json` across OSes; kept in `tests/golden/…`. * **Golden outputs**: verb snapshots for `--json` across OSes; kept in `tests/golden/`.
* **Contract tests**: ensure API shapes match service OpenAPI; fail build if incompatible. * **Contract tests**: ensure API shapes match service OpenAPI; fail build if incompatible.
--- ---
@@ -523,8 +523,8 @@ Environment variables: `STELLAOPS_AUTHORITY`, `STELLAOPS_SCANNER_URL`, etc.
``` ```
✖ Policy FAIL: 3 high, 1 critical (VEX suppressed 12) ✖ Policy FAIL: 3 high, 1 critical (VEX suppressed 12)
- pkg:rpm/openssl (CVE-2025-12345) — affected (vendor) — fixed in 3.0.14 - pkg:rpm/openssl (CVE-2025-12345) affected (vendor) fixed in 3.0.14
- pkg:npm/lodash (GHSA-xxxx) — affected — no fix - pkg:npm/lodash (GHSA-xxxx) affected no fix
See: https://ui.internal/scans/sha256:... See: https://ui.internal/scans/sha256:...
Exit code: 2 Exit code: 2
``` ```
@@ -551,7 +551,7 @@ Exit code: 2
* Emits **CycloneDX Protobuf** directly to stdout when `export sbom --format cdx-pb --out -`. * Emits **CycloneDX Protobuf** directly to stdout when `export sbom --format cdx-pb --out -`.
* Pipes to `jq`/`yq` cleanly in JSON mode. * Pipes to `jq`/`yq` cleanly in JSON mode.
* Can act as a **credential helper** for scripts: `stellaops auth token --aud scanner` prints a one‑shot token for curl. * Can act as a **credential helper** for scripts: `stellaops auth token --aud scanner` prints a oneshot token for curl.
--- ---
@@ -559,16 +559,16 @@ Exit code: 2
* **Installers**: deb/rpm (postinst registers completions), Homebrew, Scoop, Winget, MSI/MSIX. * **Installers**: deb/rpm (postinst registers completions), Homebrew, Scoop, Winget, MSI/MSIX.
* **Shell completions**: bash/zsh/fish/pwsh. * **Shell completions**: bash/zsh/fish/pwsh.
* **Update channel**: `stellaops self-update` (optional) fetches cosign‑signed release manifest; corporate environments can disable. * **Update channel**: `stellaops self-update` (optional) fetches cosignsigned release manifest; corporate environments can disable.
--- ---
## 16) Security hard lines ## 16) Security hard lines
* Refuse to print token values; redact Authorization headers in verbose output. * Refuse to print token values; redact Authorization headers in verbose output.
* Disallow `--insecure` unless `STELLAOPS_CLI_ALLOW_INSECURE=1` set (double opt‑in). * Disallow `--insecure` unless `STELLAOPS_CLI_ALLOW_INSECURE=1` set (double optin).
* Enforce **short token TTL**; refresh proactively when <30 s left. * Enforce **short token TTL**; refresh proactively when <30s left.
* Device‑code cache binding to **machine** and **user** (protect against copy to other machines). * Devicecode cache binding to **machine** and **user** (protect against copy to other machines).
--- ---
@@ -622,14 +622,14 @@ sequenceDiagram
* `scan fs <path>` (local filesystem tree) → upload to backend for analysis. * `scan fs <path>` (local filesystem tree) → upload to backend for analysis.
* `policy test --sbom <file>` (simulate policy results offline using local policy bundle). * `policy test --sbom <file>` (simulate policy results offline using local policy bundle).
* `runtime capture` (developer mode) — capture small `/proc/<pid>/maps` for troubleshooting. * `runtime capture` (developer mode) capture small `/proc/<pid>/maps` for troubleshooting.
* Pluggable output renderers for SARIF/HTML (admin‑controlled). * Pluggable output renderers for SARIF/HTML (admincontrolled).
--- ---
## 19) Example CI snippets ## 19) Example CI snippets
**GitHub Actions (post‑build)** **GitHub Actions (postbuild)**
```yaml ```yaml
- name: Login (device code w/ OIDC broker) - name: Login (device code w/ OIDC broker)

View File

@@ -1,6 +1,6 @@
# Graph architecture # Graph architecture
> Derived from Epic 5 – SBOM Graph Explorer; this section captures the core model, pipeline, and API expectations. Extend with diagrams as implementation matures. > Derived from Epic5 SBOM Graph Explorer; this section captures the core model, pipeline, and API expectations. Extend with diagrams as implementation matures.
## 1) Core model ## 1) Core model
@@ -25,11 +25,11 @@
## 3) APIs ## 3) APIs
- `POST /graph/search` — NDJSON node tiles with cursor paging, tenant + scope guards. - `POST /graph/search` NDJSON node tiles with cursor paging, tenant + scope guards.
- `POST /graph/query` — NDJSON nodes/edges/stats/cursor with budgets (tiles/nodes/edges) and optional inline overlays (`includeOverlays=true`) emitting `policy.overlay.v1` and `openvex.v1` payloads; overlay IDs are `sha256(tenant|nodeId|overlayKind)`; policy overlay may include a sampled `explainTrace`. - `POST /graph/query` NDJSON nodes/edges/stats/cursor with budgets (tiles/nodes/edges) and optional inline overlays (`includeOverlays=true`) emitting `policy.overlay.v1` and `openvex.v1` payloads; overlay IDs are `sha256(tenant|nodeId|overlayKind)`; policy overlay may include a sampled `explainTrace`.
- `POST /graph/paths` — bounded BFS (depth ≤6) returning path nodes/edges/stats; honours budgets and overlays. - `POST /graph/paths` bounded BFS (depth ≤6) returning path nodes/edges/stats; honours budgets and overlays.
- `POST /graph/diff` — compares `snapshotA` vs `snapshotB`, streaming node/edge added/removed/changed tiles plus stats; budget enforcement mirrors `/graph/query`. - `POST /graph/diff` compares `snapshotA` vs `snapshotB`, streaming node/edge added/removed/changed tiles plus stats; budget enforcement mirrors `/graph/query`.
- `POST /graph/export` — async job producing deterministic manifests (`sha256`, size, format) for `ndjson/csv/graphml/png/svg`; download via `/graph/export/{jobId}`. - `POST /graph/export` async job producing deterministic manifests (`sha256`, size, format) for `ndjson/csv/graphml/png/svg`; download via `/graph/export/{jobId}`.
- `POST /graph/lineage` - returns SBOM lineage nodes/edges anchored by `artifactDigest` or `sbomDigest`, with optional relationship filters and depth limits. - `POST /graph/lineage` - returns SBOM lineage nodes/edges anchored by `artifactDigest` or `sbomDigest`, with optional relationship filters and depth limits.
- **Edge Metadata API** (added 2025-01): - **Edge Metadata API** (added 2025-01):
- `POST /graph/edges/metadata` — batch query for edge explanations; request contains `EdgeIds[]`, response includes `EdgeTileWithMetadata[]` with full provenance. - `POST /graph/edges/metadata` — batch query for edge explanations; request contains `EdgeIds[]`, response includes `EdgeTileWithMetadata[]` with full provenance.

View File

@@ -1,16 +1,16 @@
# Advisory AI Assistant Parameters # Advisory AI Assistant Parameters
_Primary audience: platform operators & policy authors • Updated: 2026-01-13_ _Primary audience: platform operators & policy authors Updated: 2026-01-13_
This note centralises the tunable knobs that control Advisory AI’s planner, retrieval stack, inference clients, and guardrails. All options live under the `AdvisoryAI` configuration section and can be set via `appsettings.*` files or environment variables using ASP.NET Core’s double-underscore convention (`ADVISORYAI__Inference__Mode`, etc.). Chat quotas and tool allowlists can also be overridden per tenant/user via the chat settings endpoints; appsettings/env values are defaults. This note centralises the tunable knobs that control Advisory AIs planner, retrieval stack, inference clients, and guardrails. All options live under the `AdvisoryAI` configuration section and can be set via `appsettings.*` files or environment variables using ASP.NET Cores double-underscore convention (`ADVISORYAI__Inference__Mode`, etc.). Chat quotas and tool allowlists can also be overridden per tenant/user via the chat settings endpoints; appsettings/env values are defaults.
**Policy/version pin** — For Sprint 0111, use the policy bundle hash shipped on 2025-11-19 (same drop as `CLI-VULN-29-001` / `CLI-VEX-30-001`). Set `AdvisoryAI:PolicyVersion` or `ADVISORYAI__POLICYVERSION=2025.11.19` in deployments; include the hash in DSSE metadata for Offline Kits. **Policy/version pin** For Sprint 0111, use the policy bundle hash shipped on 2025-11-19 (same drop as `CLI-VULN-29-001` / `CLI-VEX-30-001`). Set `AdvisoryAI:PolicyVersion` or `ADVISORYAI__POLICYVERSION=2025.11.19` in deployments; include the hash in DSSE metadata for Offline Kits.
| Area | Key(s) | Environment variable | Default | Notes | | Area | Key(s) | Environment variable | Default | Notes |
| --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- |
| Inference mode | `AdvisoryAI:Inference:Mode` | `ADVISORYAI__INFERENCE__MODE` | `Local` | `Local` runs the deterministic pipeline only; `Remote` posts sanitized prompts to `Remote.BaseAddress`. | | Inference mode | `AdvisoryAI:Inference:Mode` | `ADVISORYAI__INFERENCE__MODE` | `Local` | `Local` runs the deterministic pipeline only; `Remote` posts sanitized prompts to `Remote.BaseAddress`. |
| Remote base URI | `AdvisoryAI:Inference:Remote:BaseAddress` | `ADVISORYAI__INFERENCE__REMOTE__BASEADDRESS` | — | Required when `Mode=Remote`. HTTPS strongly recommended. | | Remote base URI | `AdvisoryAI:Inference:Remote:BaseAddress` | `ADVISORYAI__INFERENCE__REMOTE__BASEADDRESS` | | Required when `Mode=Remote`. HTTPS strongly recommended. |
| Remote API key | `AdvisoryAI:Inference:Remote:ApiKey` | `ADVISORYAI__INFERENCE__REMOTE__APIKEY` | — | Injected as `Authorization: Bearer <key>` when present. | | Remote API key | `AdvisoryAI:Inference:Remote:ApiKey` | `ADVISORYAI__INFERENCE__REMOTE__APIKEY` | | Injected as `Authorization: Bearer <key>` when present. |
| Remote timeout | `AdvisoryAI:Inference:Remote:TimeoutSeconds` | `ADVISORYAI__INFERENCE__REMOTE__TIMEOUTSECONDS` | `30` | Failing requests fall back to the sanitized prompt with `inference.fallback_reason=remote_timeout`. | | Remote timeout | `AdvisoryAI:Inference:Remote:TimeoutSeconds` | `ADVISORYAI__INFERENCE__REMOTE__TIMEOUTSECONDS` | `30` | Failing requests fall back to the sanitized prompt with `inference.fallback_reason=remote_timeout`. |
| Guardrail prompt cap | `AdvisoryAI:Guardrails:MaxPromptLength` | `ADVISORYAI__GUARDRAILS__MAXPROMPTLENGTH` | `16000` | Prompts longer than the cap are blocked with `prompt_too_long`. | | Guardrail prompt cap | `AdvisoryAI:Guardrails:MaxPromptLength` | `ADVISORYAI__GUARDRAILS__MAXPROMPTLENGTH` | `16000` | Prompts longer than the cap are blocked with `prompt_too_long`. |
| Guardrail citations | `AdvisoryAI:Guardrails:RequireCitations` | `ADVISORYAI__GUARDRAILS__REQUIRECITATIONS` | `true` | When `true`, at least one citation must accompany every prompt. | | Guardrail citations | `AdvisoryAI:Guardrails:RequireCitations` | `ADVISORYAI__GUARDRAILS__REQUIRECITATIONS` | `true` | When `true`, at least one citation must accompany every prompt. |
@@ -39,12 +39,12 @@ This note centralises the tunable knobs that control Advisory AI’s planne
--- ---
## 1. Inference knobs & “temperature” ## 1. Inference knobs & temperature
Advisory AI supports two inference modes: Advisory AI supports two inference modes:
- **Local (default)** – The orchestrator emits deterministic prompts and the worker returns the sanitized prompt verbatim. This mode is offline-friendly and does **not** call any external LLMs. There is no stochastic “temperature” here—the pipeline is purely rule-based. - **Local (default)** The orchestrator emits deterministic prompts and the worker returns the sanitized prompt verbatim. This mode is offline-friendly and does **not** call any external LLMs. There is no stochastic temperature herethe pipeline is purely rule-based.
- **Remote** – Sanitized prompts, citations, and metadata are POSTed to `Remote.BaseAddress + Remote.Endpoint` (default `/v1/inference`). Remote providers control sampling temperature on their side. StellaOps treats remote responses deterministically: we record the provider’s `modelId`, token usage, and any metadata they return. If your remote tier exposes a temperature knob, set it there; Advisory AI simply forwards the prompt. - **Remote** Sanitized prompts, citations, and metadata are POSTed to `Remote.BaseAddress + Remote.Endpoint` (default `/v1/inference`). Remote providers control sampling temperature on their side. StellaOps treats remote responses deterministically: we record the providers `modelId`, token usage, and any metadata they return. If your remote tier exposes a temperature knob, set it there; Advisory AI simply forwards the prompt.
### Remote inference quick sample ### Remote inference quick sample
@@ -68,14 +68,14 @@ Advisory AI supports two inference modes:
| Setting | Default | Explanation | | Setting | Default | Explanation |
| --- | --- | --- | | --- | --- | --- |
| `MaxPromptLength` | 16000 chars | Upper bound enforced after redaction. Increase cautiously—remote providers typically cap prompts at 32k tokens. | | `MaxPromptLength` | 16000 chars | Upper bound enforced after redaction. Increase cautiouslyremote providers typically cap prompts at 32k tokens. |
| `RequireCitations` | `true` | Forces each prompt to include at least one citation. Disable only when testing synthetic prompts. | | `RequireCitations` | `true` | Forces each prompt to include at least one citation. Disable only when testing synthetic prompts. |
| `BlockedPhrases[]` | `ignore previous instructions`, `disregard earlier instructions`, `you are now the system`, `override the system prompt`, `please jailbreak` | Inline list merged with the optional file. Comparisons are case-insensitive. | | `BlockedPhrases[]` | `ignore previous instructions`, `disregard earlier instructions`, `you are now the system`, `override the system prompt`, `please jailbreak` | Inline list merged with the optional file. Comparisons are case-insensitive. |
| `BlockedPhraseFile` | — | Points to a newline-delimited list. Relative paths resolve against the content root (`AdvisoryAI.Hosting` sticks to AppContext base). | | `BlockedPhraseFile` | | Points to a newline-delimited list. Relative paths resolve against the content root (`AdvisoryAI.Hosting` sticks to AppContext base). |
| `EntropyThreshold` | `3.5` | Shannon entropy threshold for high-risk token redaction. Set to `0` to disable entropy checks. | | `EntropyThreshold` | `3.5` | Shannon entropy threshold for high-risk token redaction. Set to `0` to disable entropy checks. |
| `EntropyMinLength` | `20` | Minimum token length evaluated by the entropy scrubber. | | `EntropyMinLength` | `20` | Minimum token length evaluated by the entropy scrubber. |
| `AllowlistPatterns` | Defaults (sha256/sha1/sha384/sha512) | Regex patterns that bypass entropy redaction for known-safe identifiers. | | `AllowlistPatterns` | Defaults (sha256/sha1/sha384/sha512) | Regex patterns that bypass entropy redaction for known-safe identifiers. |
| `AllowlistFile` | — | Optional allowlist file (JSON array or newline-delimited). Paths resolve against the content root. | | `AllowlistFile` | | Optional allowlist file (JSON array or newline-delimited). Paths resolve against the content root. |
Violations surface in the response metadata (`guardrail.violations[*]`) and increment `advisory_ai_guardrail_blocks_total`. Console consumes the same payload for its ribbon state. Violations surface in the response metadata (`guardrail.violations[*]`) and increment `advisory_ai_guardrail_blocks_total`. Console consumes the same payload for its ribbon state.
@@ -95,9 +95,9 @@ Each task type (Summary, Conflict, Remediation) inherits the defaults below. Ove
| Task | `StructuredMaxChunks` | `VectorTopK` | `VectorQueries` (default) | `SbomMaxTimelineEntries` | `SbomMaxDependencyPaths` | `IncludeBlastRadius` | | Task | `StructuredMaxChunks` | `VectorTopK` | `VectorQueries` (default) | `SbomMaxTimelineEntries` | `SbomMaxDependencyPaths` | `IncludeBlastRadius` |
| --- | --- | --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- | --- | --- |
| Summary | 25 | 5 | `Summarize key facts`, `What is impacted?` | 10 | 20 | ✔ | | Summary | 25 | 5 | `Summarize key facts`, `What is impacted?` | 10 | 20 | |
| Conflict | 30 | 6 | `Highlight conflicting statements`, `Where do sources disagree?` | 8 | 15 | ✖ | | Conflict | 30 | 6 | `Highlight conflicting statements`, `Where do sources disagree?` | 8 | 15 | |
| Remediation | 35 | 6 | `Provide remediation steps`, `Outline mitigations and fixes` | 12 | 25 | ✔ | | Remediation | 35 | 6 | `Provide remediation steps`, `Outline mitigations and fixes` | 12 | 25 | |
These knobs act as weighting levers: lower `VectorTopK` emphasises deterministic evidence; higher values favor breadth. `StructuredMaxChunks` bounds how many CSAF/OSV/VEX chunks reach the prompt, keeping token budgets predictable. These knobs act as weighting levers: lower `VectorTopK` emphasises deterministic evidence; higher values favor breadth. `StructuredMaxChunks` bounds how many CSAF/OSV/VEX chunks reach the prompt, keeping token budgets predictable.
@@ -107,17 +107,17 @@ These knobs act as weighting levers: lower `VectorTopK` emphasises deterministic
| Task | Prompt tokens | Completion tokens | | Task | Prompt tokens | Completion tokens |
| --- | --- | --- | | --- | --- | --- |
| Summary | 2 048 | 512 | | Summary | 2048 | 512 |
| Conflict | 2 048 | 512 | | Conflict | 2048 | 512 |
| Remediation | 2 048 | 640 | | Remediation | 2048 | 640 |
Overwrite via `AdvisoryAI:Tasks:Summary:Budget:PromptTokens`, etc. The worker records actual consumption in the response metadata (`inference.prompt_tokens`, `inference.completion_tokens`). Overwrite via `AdvisoryAI:Tasks:Summary:Budget:PromptTokens`, etc. The worker records actual consumption in the response metadata (`inference.prompt_tokens`, `inference.completion_tokens`).
## 5. Cache TTLs & queue directories ## 5. Cache TTLs & queue directories
- **Plan cache TTLs** – In-memory and file-system caches honour `AdvisoryAI:PlanCache:DefaultTimeToLive` (default 10 minutes) and `CleanupInterval` (default 5 minutes). Shorten the TTL to reduce stale plans or increase it to favour offline reuse. Both values accept ISO 8601 or `hh:mm:ss` time spans. - **Plan cache TTLs** In-memory and file-system caches honour `AdvisoryAI:PlanCache:DefaultTimeToLive` (default 10 minutes) and `CleanupInterval` (default 5 minutes). Shorten the TTL to reduce stale plans or increase it to favour offline reuse. Both values accept ISO 8601 or `hh:mm:ss` time spans.
- **Queue & storage paths** – `AdvisoryAI:Queue:DirectoryPath`, `AdvisoryAI:Storage:PlanCacheDirectory`, and `AdvisoryAI:Storage:OutputDirectory` default to `data/advisory-ai/{queue,plans,outputs}` under the content root; override these when mounting RWX volumes in sovereign clusters. - **Queue & storage paths** `AdvisoryAI:Queue:DirectoryPath`, `AdvisoryAI:Storage:PlanCacheDirectory`, and `AdvisoryAI:Storage:OutputDirectory` default to `data/advisory-ai/{queue,plans,outputs}` under the content root; override these when mounting RWX volumes in sovereign clusters.
- **Output TTLs** – Output artefacts inherit the host file-system retention policies. Combine `DefaultTimeToLive` with a cron or systemd timer to prune `outputs/` periodically when operating in remote-inference-heavy environments. - **Output TTLs** Output artefacts inherit the host file-system retention policies. Combine `DefaultTimeToLive` with a cron or systemd timer to prune `outputs/` periodically when operating in remote-inference-heavy environments.
### Example: raised TTL & custom queue path ### Example: raised TTL & custom queue path
@@ -138,6 +138,6 @@ Overwrite via `AdvisoryAI:Tasks:Summary:Budget:PromptTokens`, etc. The worker re
## 6. Operational notes ## 6. Operational notes
- Updating **guardrail phrases** triggers only on host reload. When distributing blocked-phrase files via Offline Kits, keep filenames stable and version them through Git so QA can diff changes. - Updating **guardrail phrases** triggers only on host reload. When distributing blocked-phrase files via Offline Kits, keep filenames stable and version them through Git so QA can diff changes.
- **Temperature / sampling** remains a remote-provider concern. StellaOps records the provider’s `modelId` and exposes fallback metadata so policy authors can audit when sanitized prompts were returned instead of model output. - **Temperature / sampling** remains a remote-provider concern. StellaOps records the providers `modelId` and exposes fallback metadata so policy authors can audit when sanitized prompts were returned instead of model output.
- Always track changes in `docs/implplan/SPRINT_0111_0001_0001_advisoryai.md` (task `DOCS-AIAI-31-006`) when promoting this document so the guild can trace which parameters were added per sprint. - Always track changes in `docs/implplan/SPRINT_0111_0001_0001_advisoryai.md` (task `DOCS-AIAI-31-006`) when promoting this document so the guild can trace which parameters were added per sprint.

View File

@@ -1,8 +1,8 @@
# component_architecture_scanner.md — **Stella Ops Scanner** (2025Q4) # component_architecture_scanner.md **StellaOps Scanner** (2025Q4)
> Aligned with Epic 6 – Vulnerability Explorer and Epic 10 – Export Center. > Aligned with Epic6 Vulnerability Explorer and Epic10 Export Center.
> **Scope.** Implementation‑ready architecture for the **Scanner** subsystem: WebService, Workers, analyzers, SBOM assembly (inventory & usage), per‑layer caching, three‑way diffs, artifact catalog (RustFS default + PostgreSQL, S3-compatible fallback), attestation hand‑off, and scale/security posture. This document is the contract between the scanning plane and everything else (Policy, Excititor, Concelier, UI, CLI). > **Scope.** Implementationready architecture for the **Scanner** subsystem: WebService, Workers, analyzers, SBOM assembly (inventory & usage), perlayer caching, threeway diffs, artifact catalog (RustFS default + PostgreSQL, S3-compatible fallback), attestation handoff, and scale/security posture. This document is the contract between the scanning plane and everything else (Policy, Excititor, Concelier, UI, CLI).
> **Related:** `docs/modules/scanner/operations/ai-code-guard.md` > **Related:** `docs/modules/scanner/operations/ai-code-guard.md`
> **Storage profile:** `docs/modules/scanner/sbom-attestation-hot-lookup-profile.md` > **Storage profile:** `docs/modules/scanner/sbom-attestation-hot-lookup-profile.md`
@@ -10,13 +10,13 @@
## 0) Mission & boundaries ## 0) Mission & boundaries
**Mission.** Produce **deterministic**, **explainable** SBOMs and diffs for container images and filesystems, quickly and repeatedly, without guessing. Emit two views: **Inventory** (everything present) and **Usage** (entrypoint closure + actually linked libs). Attach attestations through **Signer→Attestor→Rekor v2**. **Mission.** Produce **deterministic**, **explainable** SBOMs and diffs for container images and filesystems, quickly and repeatedly, without guessing. Emit two views: **Inventory** (everything present) and **Usage** (entrypoint closure + actually linked libs). Attach attestations through **SignerAttestorRekor v2**.
**Boundaries.** **Boundaries.**
* Scanner **does not** produce PASS/FAIL. The backend (Policy + Excititor + Concelier) decides presentation and verdicts. * Scanner **does not** produce PASS/FAIL. The backend (Policy + Excititor + Concelier) decides presentation and verdicts.
* Scanner **does not** keep third‑party SBOM warehouses. It may **bind** to existing attestations for exact hashes. * Scanner **does not** keep thirdparty SBOM warehouses. It may **bind** to existing attestations for exact hashes.
* Core analyzers are **deterministic** (no fuzzy identity). Optional heuristic plug‑ins (e.g., patch‑presence) run under explicit flags and never contaminate the core SBOM. * Core analyzers are **deterministic** (no fuzzy identity). Optional heuristic plugins (e.g., patchpresence) run under explicit flags and never contaminate the core SBOM.
SBOM dependency reachability inference uses dependency graphs to reduce false positives and SBOM dependency reachability inference uses dependency graphs to reduce false positives and
apply reachability-aware severity adjustments. See `src/Scanner/docs/sbom-reachability-filtering.md` apply reachability-aware severity adjustments. See `src/Scanner/docs/sbom-reachability-filtering.md`
@@ -28,27 +28,27 @@ for policy configuration and reporting expectations.
``` ```
src/ src/
├─ StellaOps.Scanner.WebService/ # REST control plane, catalog, diff, exports ├─ StellaOps.Scanner.WebService/ # REST control plane, catalog, diff, exports
├─ StellaOps.Scanner.Worker/ # queue consumer; executes analyzers ├─ StellaOps.Scanner.Worker/ # queue consumer; executes analyzers
├─ StellaOps.Scanner.Models/ # DTOs, evidence, graph nodes, CDX/SPDX adapters ├─ StellaOps.Scanner.Models/ # DTOs, evidence, graph nodes, CDX/SPDX adapters
├─ StellaOps.Scanner.Storage/ # PostgreSQL repositories; RustFS object client (default) + S3 fallback; ILM/GC ├─ StellaOps.Scanner.Storage/ # PostgreSQL repositories; RustFS object client (default) + S3 fallback; ILM/GC
├─ StellaOps.Scanner.Queue/ # queue abstraction (Valkey/NATS/RabbitMQ) ├─ StellaOps.Scanner.Queue/ # queue abstraction (Valkey/NATS/RabbitMQ)
├─ StellaOps.Scanner.Cache/ # layer cache; file CAS; bloom/bitmap indexes ├─ StellaOps.Scanner.Cache/ # layer cache; file CAS; bloom/bitmap indexes
├─ StellaOps.Scanner.EntryTrace/ # ENTRYPOINT/CMD → terminal program resolver (shell AST) ├─ StellaOps.Scanner.EntryTrace/ # ENTRYPOINT/CMD terminal program resolver (shell AST)
├─ StellaOps.Scanner.Analyzers.OS.[Apk|Dpkg|Rpm]/ ├─ StellaOps.Scanner.Analyzers.OS.[Apk|Dpkg|Rpm]/
├─ StellaOps.Scanner.Analyzers.Lang.[Java|Node|Bun|Python|Go|DotNet|Rust|Ruby|Php]/ ├─ StellaOps.Scanner.Analyzers.Lang.[Java|Node|Bun|Python|Go|DotNet|Rust|Ruby|Php]/
├─ StellaOps.Scanner.Analyzers.Native.[ELF|PE|MachO]/ # PE/Mach-O planned (M2) ├─ StellaOps.Scanner.Analyzers.Native.[ELF|PE|MachO]/ # PE/Mach-O planned (M2)
├─ StellaOps.Scanner.Analyzers.Secrets/ # Secret leak detection (2026.01) ├─ StellaOps.Scanner.Analyzers.Secrets/ # Secret leak detection (2026.01)
├─ StellaOps.Scanner.Symbols.Native/ # NEW – native symbol reader/demangler (Sprint 401) ├─ StellaOps.Scanner.Symbols.Native/ # NEW native symbol reader/demangler (Sprint 401)
├─ StellaOps.Scanner.CallGraph.Native/ # NEW – function/call-edge builder + CAS emitter ├─ StellaOps.Scanner.CallGraph.Native/ # NEW function/call-edge builder + CAS emitter
├─ StellaOps.Scanner.Emit.CDX/ # CycloneDX (JSON + Protobuf) ├─ StellaOps.Scanner.Emit.CDX/ # CycloneDX (JSON + Protobuf)
├─ StellaOps.Scanner.Emit.SPDX/ # SPDX 3.0.1 JSON ├─ StellaOps.Scanner.Emit.SPDX/ # SPDX 3.0.1 JSON
├─ StellaOps.Scanner.Diff/ # imageâ†layerâ†component three‑way diff ├─ StellaOps.Scanner.Diff/ # imageâ†layerâ†component threeway diff
├─ StellaOps.Scanner.Index/ # BOM‑Index sidecar (purls + roaring bitmaps) ├─ StellaOps.Scanner.Index/ # BOMIndex sidecar (purls + roaring bitmaps)
├─ StellaOps.Scanner.Tests.* # unit/integration/e2e fixtures ├─ StellaOps.Scanner.Tests.* # unit/integration/e2e fixtures
└─ Tools/ └─ Tools/
├─ StellaOps.Scanner.Sbomer.BuildXPlugin/ # BuildKit generator (image referrer SBOMs) ├─ StellaOps.Scanner.Sbomer.BuildXPlugin/ # BuildKit generator (image referrer SBOMs)
└─ StellaOps.Scanner.Sbomer.DockerImage/ # CLI‑driven scanner container └─ StellaOps.Scanner.Sbomer.DockerImage/ # CLIdriven scanner container
``` ```
### 1.0 Cartographer Ownership (Sprint 201) ### 1.0 Cartographer Ownership (Sprint 201)
@@ -58,18 +58,18 @@ src/
- Legacy `src/Cartographer/` paths are retired; operational and build references now resolve through Scanner-owned solution/project paths. - Legacy `src/Cartographer/` paths are retired; operational and build references now resolve through Scanner-owned solution/project paths.
Per-analyzer notes (language analyzers): Per-analyzer notes (language analyzers):
- `docs/modules/scanner/analyzers-java.md` — Java/Kotlin (Maven, Gradle, fat archives) - `docs/modules/scanner/analyzers-java.md` Java/Kotlin (Maven, Gradle, fat archives)
- `docs/modules/scanner/dotnet-analyzer.md` — .NET (deps.json, NuGet, packages.lock.json, declared-only) - `docs/modules/scanner/dotnet-analyzer.md` .NET (deps.json, NuGet, packages.lock.json, declared-only)
- `docs/modules/scanner/analyzers-python.md` — Python (pip, Poetry, pipenv, conda, editables, vendored) - `docs/modules/scanner/analyzers-python.md` Python (pip, Poetry, pipenv, conda, editables, vendored)
- `docs/modules/scanner/analyzers-node.md` — Node.js (npm, Yarn, pnpm, multi-version locks) - `docs/modules/scanner/analyzers-node.md` Node.js (npm, Yarn, pnpm, multi-version locks)
- `docs/modules/scanner/analyzers-bun.md` — Bun (bun.lock v1, dev classification, patches) - `docs/modules/scanner/analyzers-bun.md` Bun (bun.lock v1, dev classification, patches)
- `docs/modules/scanner/analyzers-go.md` — Go (build info, modules) - `docs/modules/scanner/analyzers-go.md` Go (build info, modules)
Cross-analyzer contract (identity safety, evidence locators, container layout): Cross-analyzer contract (identity safety, evidence locators, container layout):
- `docs/modules/scanner/language-analyzers-contract.md` — PURL vs explicit-key rules, evidence formats, bounded scanning - `docs/modules/scanner/language-analyzers-contract.md` PURL vs explicit-key rules, evidence formats, bounded scanning
Semantic entrypoint analysis (Sprint 0411): Semantic entrypoint analysis (Sprint 0411):
- `docs/modules/scanner/semantic-entrypoint-schema.md` — Schema for intent, capabilities, threat vectors, and data boundaries - `docs/modules/scanner/semantic-entrypoint-schema.md` Schema for intent, capabilities, threat vectors, and data boundaries
Analyzer assemblies and buildx generators are packaged as **restart-time plug-ins** under `plugins/scanner/**` with manifests; services must restart to activate new plug-ins. Analyzer assemblies and buildx generators are packaged as **restart-time plug-ins** under `plugins/scanner/**` with manifests; services must restart to activate new plug-ins.
@@ -77,15 +77,15 @@ Analyzer assemblies and buildx generators are packaged as **restart-time plug-in
The **Semantic Entrypoint Engine** enriches scan results with application-level understanding: The **Semantic Entrypoint Engine** enriches scan results with application-level understanding:
- **Intent Classification** — Infers application type (WebServer, Worker, CliTool, Serverless, etc.) from framework detection and entrypoint analysis - **Intent Classification** Infers application type (WebServer, Worker, CliTool, Serverless, etc.) from framework detection and entrypoint analysis
- **Capability Detection** — Identifies system resource access patterns (network, filesystem, database, crypto) - **Capability Detection** Identifies system resource access patterns (network, filesystem, database, crypto)
- **Threat Vector Inference** — Maps capabilities to potential attack vectors with CWE/OWASP references - **Threat Vector Inference** Maps capabilities to potential attack vectors with CWE/OWASP references
- **Data Boundary Mapping** — Tracks data flow boundaries with sensitivity classification - **Data Boundary Mapping** Tracks data flow boundaries with sensitivity classification
Components: Components:
- `StellaOps.Scanner.EntryTrace/Semantic/` — Core semantic types and orchestrator - `StellaOps.Scanner.EntryTrace/Semantic/` Core semantic types and orchestrator
- `StellaOps.Scanner.EntryTrace/Semantic/Adapters/` — Language-specific adapters (Python, Java, Node, .NET, Go) - `StellaOps.Scanner.EntryTrace/Semantic/Adapters/` Language-specific adapters (Python, Java, Node, .NET, Go)
- `StellaOps.Scanner.EntryTrace/Semantic/Analysis/` — Capability detection, threat inference, boundary mapping - `StellaOps.Scanner.EntryTrace/Semantic/Analysis/` Capability detection, threat inference, boundary mapping
Integration points: Integration points:
- `LanguageComponentRecord` includes semantic fields (`intent`, `capabilities[]`, `threatVectors[]`) - `LanguageComponentRecord` includes semantic fields (`intent`, `capabilities[]`, `threatVectors[]`)
@@ -127,10 +127,10 @@ scanner:
The DI extension (`AddScannerQueue`) wires the transport. The DI extension (`AddScannerQueue`) wires the transport.
**Runtime form‑factor:** two deployables **Runtime formfactor:** two deployables
* **Scanner.WebService** (stateless REST) * **Scanner.WebService** (stateless REST)
* **Scanner.Worker** (N replicas; queue‑driven) * **Scanner.Worker** (N replicas; queuedriven)
--- ---
@@ -140,26 +140,26 @@ The DI extension (`AddScannerQueue`) wires the transport.
* **RustFS** (default, offline-first) for SBOM artifacts; S3-compatible interface with **Object Lock** semantics emulated via retention headers; **ILM** for TTL. * **RustFS** (default, offline-first) for SBOM artifacts; S3-compatible interface with **Object Lock** semantics emulated via retention headers; **ILM** for TTL.
* **PostgreSQL** for catalog, job state, diffs, ILM rules. * **PostgreSQL** for catalog, job state, diffs, ILM rules.
* **Queue** (Valkey Streams/NATS/RabbitMQ). * **Queue** (Valkey Streams/NATS/RabbitMQ).
* **Authority** (on‑prem OIDC) for **OpToks** (DPoP/mTLS). * **Authority** (onprem OIDC) for **OpToks** (DPoP/mTLS).
* **Signer** + **Attestor** (+ **Fulcio/KMS** + **Rekor v2**) for DSSE + transparency. * **Signer** + **Attestor** (+ **Fulcio/KMS** + **Rekor v2**) for DSSE + transparency.
--- ---
## 3) Contracts & data model ## 3) Contracts & data model
### 3.1 Evidence‑first component model ### 3.1 Evidencefirst component model
**Nodes** **Nodes**
* `Image`, `Layer`, `File` * `Image`, `Layer`, `File`
* `Component` (`purl?`, `name`, `version?`, `type`, `id` — may be `bin:{sha256}`) * `Component` (`purl?`, `name`, `version?`, `type`, `id` may be `bin:{sha256}`)
* `Executable` (ELF/PE/Mach‑O), `Library` (native or managed), `EntryScript` (shell/launcher) * `Executable` (ELF/PE/MachO), `Library` (native or managed), `EntryScript` (shell/launcher)
**Edges** (all carry **Evidence**) **Edges** (all carry **Evidence**)
* `contains(Image|Layer → File)` * `contains(Image|Layer → File)`
* `installs(PackageDB → Component)` (OS database row) * `installs(PackageDB → Component)` (OS database row)
* `declares(InstalledMetadata → Component)` (dist‑info, pom.properties, deps.json…) * `declares(InstalledMetadata → Component)` (distinfo, pom.properties, deps.json)
* `links_to(Executable → Library)` (ELF `DT_NEEDED`, PE imports) * `links_to(Executable → Library)` (ELF `DT_NEEDED`, PE imports)
* `calls(EntryScript → Program)` (file:line from shell AST) * `calls(EntryScript → Program)` (file:line from shell AST)
* `attests(Rekor → Component|Image)` (SBOM/predicate binding) * `attests(Rekor → Component|Image)` (SBOM/predicate binding)
@@ -247,7 +247,7 @@ When `scanner.events.enabled = true`, the WebService serialises the signed repor
### 5.1 Acquire & verify ### 5.1 Acquire & verify
1. **Resolve image** (prefer `repo@sha256:…`). 1. **Resolve image** (prefer `repo@sha256:`).
2. **(Optional) verify image signature** per policy (cosign). 2. **(Optional) verify image signature** per policy (cosign).
3. **Pull blobs**, compute layer digests; record metadata. 3. **Pull blobs**, compute layer digests; record metadata.
@@ -289,19 +289,19 @@ When `scanner.events.enabled = true`, the WebService serialises the signed repor
**C) Native link graph** **C) Native link graph**
* **ELF**: parse `PT_INTERP`, `DT_NEEDED`, RPATH/RUNPATH, **GNU symbol versions**; map **SONAMEs** to file paths; link executables → libs. * **ELF**: parse `PT_INTERP`, `DT_NEEDED`, RPATH/RUNPATH, **GNU symbol versions**; map **SONAMEs** to file paths; link executables → libs.
* **PE/Mach‑O** (planned M2): import table, delay‑imports; version resources; code signatures. * **PE/MachO** (planned M2): import table, delayimports; version resources; code signatures.
* Map libs back to **OS packages** if possible (via file lists); else emit `bin:{sha256}` components. * Map libs back to **OS packages** if possible (via file lists); else emit `bin:{sha256}` components.
* The exported metadata (`stellaops.os.*` properties, license list, source package) feeds policy scoring and export pipelines * The exported metadata (`stellaops.os.*` properties, license list, source package) feeds policy scoring and export pipelines
directly – Policy evaluates quiet rules against package provenance while Exporters forward the enriched fields into directly Policy evaluates quiet rules against package provenance while Exporters forward the enriched fields into
downstream JSON/Trivy payloads. downstream JSON/Trivy payloads.
* **Reachability lattice**: analyzers + runtime probes emit `Evidence`/`Mitigation` records (see `docs/modules/reach-graph/guides/lattice.md`). The lattice engine joins static path evidence, runtime hits (EventPipe/JFR), taint flows, environment gates, and mitigations into `ReachDecision` documents that feed VEX gating and event graph storage. * **Reachability lattice**: analyzers + runtime probes emit `Evidence`/`Mitigation` records (see `docs/modules/reach-graph/guides/lattice.md`). The lattice engine joins static path evidence, runtime hits (EventPipe/JFR), taint flows, environment gates, and mitigations into `ReachDecision` documents that feed VEX gating and event graph storage.
* Sprint 401 introduces `StellaOps.Scanner.Symbols.Native` (DWARF/PDB reader + demangler) and `StellaOps.Scanner.CallGraph.Native` * Sprint401 introduces `StellaOps.Scanner.Symbols.Native` (DWARF/PDB reader + demangler) and `StellaOps.Scanner.CallGraph.Native`
(function boundary detector + call-edge builder). These libraries feed `FuncNode`/`CallEdge` CAS bundles and enrich reachability (function boundary detector + call-edge builder). These libraries feed `FuncNode`/`CallEdge` CAS bundles and enrich reachability
graphs with `{code_id, confidence, evidence}` so Signals/Policy/UI can cite function-level justifications. graphs with `{code_id, confidence, evidence}` so Signals/Policy/UI can cite function-level justifications.
**D) EntryTrace (ENTRYPOINT/CMD → terminal program)** **D) EntryTrace (ENTRYPOINT/CMD → terminal program)**
* Read image config; parse shell (POSIX/Bash subset) with AST: `source`/`.` includes; `case/if`; `exec`/`command`; `run‑parts`. * Read image config; parse shell (POSIX/Bash subset) with AST: `source`/`.` includes; `case/if`; `exec`/`command`; `runparts`.
* Resolve commands via **PATH** within the **built rootfs**; follow language launchers (Java/Node/Python) to identify the terminal program (ELF/JAR/venv script). * Resolve commands via **PATH** within the **built rootfs**; follow language launchers (Java/Node/Python) to identify the terminal program (ELF/JAR/venv script).
* Record **file:line** and choices for each hop; output chain graph. * Record **file:line** and choices for each hop; output chain graph.
* Unresolvable dynamic constructs are recorded as **unknown** edges with reasons (e.g., `$FOO` unresolved). * Unresolvable dynamic constructs are recorded as **unknown** edges with reasons (e.g., `$FOO` unresolved).
@@ -310,11 +310,11 @@ When `scanner.events.enabled = true`, the WebService serialises the signed repor
Post-resolution, the `SemanticEntrypointOrchestrator` enriches entry trace results with semantic understanding: Post-resolution, the `SemanticEntrypointOrchestrator` enriches entry trace results with semantic understanding:
* **Application Intent** — Infers the purpose (WebServer, CliTool, Worker, Serverless, BatchJob, etc.) from framework detection and command patterns. * **Application Intent** Infers the purpose (WebServer, CliTool, Worker, Serverless, BatchJob, etc.) from framework detection and command patterns.
* **Capability Classes** — Detects capabilities (NetworkListen, DatabaseSql, ProcessSpawn, SecretAccess, etc.) via import/dependency analysis and framework signatures. * **Capability Classes** Detects capabilities (NetworkListen, DatabaseSql, ProcessSpawn, SecretAccess, etc.) via import/dependency analysis and framework signatures.
* **Attack Surface** — Maps capabilities to potential threat vectors (SqlInjection, Xss, Ssrf, Rce, PathTraversal) with CWE IDs and OWASP Top 10 categories. * **Attack Surface** Maps capabilities to potential threat vectors (SqlInjection, Xss, Ssrf, Rce, PathTraversal) with CWE IDs and OWASP Top 10 categories.
* **Data Boundaries** — Traces I/O edges (HttpRequest, DatabaseQuery, FileInput, EnvironmentVar) with direction and sensitivity classification. * **Data Boundaries** Traces I/O edges (HttpRequest, DatabaseQuery, FileInput, EnvironmentVar) with direction and sensitivity classification.
* **Confidence Scoring** — Each inference carries a score (0.0–1.0), tier (Definitive/High/Medium/Low/Unknown), and reasoning chain. * **Confidence Scoring** Each inference carries a score (0.01.0), tier (Definitive/High/Medium/Low/Unknown), and reasoning chain.
Language-specific adapters (`PythonSemanticAdapter`, `JavaSemanticAdapter`, `NodeSemanticAdapter`, `DotNetSemanticAdapter`, `GoSemanticAdapter`) recognize framework patterns: Language-specific adapters (`PythonSemanticAdapter`, `JavaSemanticAdapter`, `NodeSemanticAdapter`, `DotNetSemanticAdapter`, `GoSemanticAdapter`) recognize framework patterns:
* **Python**: Django, Flask, FastAPI, Celery, Click/Typer, Lambda handlers * **Python**: Django, Flask, FastAPI, Celery, Click/Typer, Lambda handlers
@@ -379,11 +379,11 @@ public sealed record BinaryFindingEvidence
### 5.5 SBOM assembly & emit ### 5.5 SBOM assembly & emit
* **Per-layer SBOM fragments**: components introduced by the layer (+ relationships). * **Per-layer SBOM fragments**: components introduced by the layer (+ relationships).
* **Image SBOMs**: merge fragments; refer back to them via **CycloneDX BOM‑Link** (or SPDX ExternalRef). * **Image SBOMs**: merge fragments; refer back to them via **CycloneDX BOMLink** (or SPDX ExternalRef).
* Emit both **Inventory** & **Usage** views. * Emit both **Inventory** & **Usage** views.
* When the native analyzer reports an ELF `buildId`, attach it to component metadata and surface it as `stellaops:buildId` in CycloneDX properties (and diff metadata). This keeps SBOM/diff output in lockstep with runtime events and the debug-store manifest. * When the native analyzer reports an ELF `buildId`, attach it to component metadata and surface it as `stellaops:buildId` in CycloneDX properties (and diff metadata). This keeps SBOM/diff output in lockstep with runtime events and the debug-store manifest.
* Serialize **CycloneDX 1.7 JSON** and **CycloneDX 1.7 Protobuf**; optionally **SPDX 3.0.1 JSON-LD** (`application/spdx+json; version=3.0.1`) with legacy tag-value output (`text/spdx`) when enabled (1.6 accepted for ingest compatibility). * Serialize **CycloneDX 1.7 JSON** and **CycloneDX 1.7 Protobuf**; optionally **SPDX 3.0.1 JSON-LD** (`application/spdx+json; version=3.0.1`) with legacy tag-value output (`text/spdx`) when enabled (1.6 accepted for ingest compatibility).
* Build **BOM‑Index** sidecar: purl table + roaring bitmap; flag `usedByEntrypoint` components for fast backend joins. * Build **BOMIndex** sidecar: purl table + roaring bitmap; flag `usedByEntrypoint` components for fast backend joins.
The emitted `buildId` metadata is preserved in component hashes, diff payloads, and `/policy/runtime` responses so operators can pivot from SBOM entries → runtime events → `debug/.build-id/<aa>/<rest>.debug` within the Offline Kit or release bundle. The emitted `buildId` metadata is preserved in component hashes, diff payloads, and `/policy/runtime` responses so operators can pivot from SBOM entries → runtime events → `debug/.build-id/<aa>/<rest>.debug` within the Offline Kit or release bundle.
@@ -475,7 +475,7 @@ Scanner API flows that operate on tenant-partitioned tables now require tenant a
--- ---
## 6) Three‑way diff (image → layer → component) ## 6) Threeway diff (image → layer → component)
### 6.1 Keys & classification ### 6.1 Keys & classification
@@ -503,13 +503,13 @@ Diffs are stored as artifacts and feed **UI** and **CLI**.
--- ---
## 7) Build‑time SBOMs (fast CI path) ## 7) Buildtime SBOMs (fast CI path)
**Scanner.Sbomer.BuildXPlugin** can act as a BuildKit **generator**: **Scanner.Sbomer.BuildXPlugin** can act as a BuildKit **generator**:
* During `docker buildx build --attest=type=sbom,generator=stellaops/sbom-indexer`, run analyzers on the build context/output; attach SBOMs as OCI **referrers** to the built image. * During `docker buildx build --attest=type=sbom,generator=stellaops/sbom-indexer`, run analyzers on the build context/output; attach SBOMs as OCI **referrers** to the built image.
* Optionally request **Signer/Attestor** to produce **Stella Opsâ€verified** attestation immediately; else, Scanner.WebService can verify and re‑attest post‑push. * Optionally request **Signer/Attestor** to produce **StellaOpsverified** attestation immediately; else, Scanner.WebService can verify and reattest postpush.
* Scanner.WebService trusts build‑time SBOMs per policy, enabling **no‑rescan** for unchanged bases. * Scanner.WebService trusts buildtime SBOMs per policy, enabling **norescan** for unchanged bases.
--- ---
@@ -551,26 +551,26 @@ scanner:
## 9) Scale & performance ## 9) Scale & performance
* **Parallelism**: per‑analyzer concurrency; bounded directory walkers; file CAS dedupe by sha256. * **Parallelism**: peranalyzer concurrency; bounded directory walkers; file CAS dedupe by sha256.
* **Distributed locks** per **layer digest** to prevent duplicate work across Workers. * **Distributed locks** per **layer digest** to prevent duplicate work across Workers.
* **Registry throttles**: per‑host concurrency budgets; exponential backoff on 429/5xx. * **Registry throttles**: perhost concurrency budgets; exponential backoff on 429/5xx.
* **Targets**: * **Targets**:
* **Build‑time**: P95 ≤ 3–5 s on warmed bases (CI generator). * **Buildtime**: P95 ≤35s on warmed bases (CI generator).
* **Post‑build delta**: P95 ≤ 10 s for 200 MB images with cache hit. * **Postbuild delta**: P95 ≤10s for 200MB images with cache hit.
* **Emit**: CycloneDX Protobuf ≤ 150 ms for 5k components; JSON ≤ 500 ms. * **Emit**: CycloneDX Protobuf ≤150ms for 5k components; JSON ≤500ms.
* **Diff**: ≤ 200 ms for 5k vs 5k components. * **Diff**: ≤200ms for 5k vs 5k components.
--- ---
## 10) Security posture ## 10) Security posture
* **AuthN**: Authority‑issued short OpToks (DPoP/mTLS). * **AuthN**: Authorityissued short OpToks (DPoP/mTLS).
* **AuthZ**: scopes (`scanner.scan`, `scanner.export`, `scanner.catalog.read`). * **AuthZ**: scopes (`scanner.scan`, `scanner.export`, `scanner.catalog.read`).
* **mTLS** to **Signer**/**Attestor**; only **Signer** can sign. * **mTLS** to **Signer**/**Attestor**; only **Signer** can sign.
* **No network fetches** during analysis (except registry pulls and optional Rekor index reads). * **No network fetches** during analysis (except registry pulls and optional Rekor index reads).
* **Sandboxing**: non‑root containers; read‑only FS; seccomp profiles; disable execution of scanned content. * **Sandboxing**: nonroot containers; readonly FS; seccomp profiles; disable execution of scanned content.
* **Release integrity**: all first‑party images are **cosign‑signed**; Workers/WebService self‑verify at startup. * **Release integrity**: all firstparty images are **cosignsigned**; Workers/WebService selfverify at startup.
--- ---
@@ -582,7 +582,7 @@ scanner:
* `scanner.layer_cache_hits_total`, `scanner.file_cas_hits_total` * `scanner.layer_cache_hits_total`, `scanner.file_cas_hits_total`
* `scanner.artifact_bytes_total{format}` * `scanner.artifact_bytes_total{format}`
* `scanner.attestation_latency_seconds`, `scanner.rekor_failures_total` * `scanner.attestation_latency_seconds`, `scanner.rekor_failures_total`
* `scanner_analyzer_golang_heuristic_total{indicator,version_hint}` — increments whenever the Go analyzer falls back to heuristics (build-id or runtime markers). Grafana panel: `sum by (indicator) (rate(scanner_analyzer_golang_heuristic_total[5m]))`; alert when the rate is ≥ 1 for 15 minutes to highlight unexpected stripped binaries. * `scanner_analyzer_golang_heuristic_total{indicator,version_hint}` increments whenever the Go analyzer falls back to heuristics (build-id or runtime markers). Grafana panel: `sum by (indicator) (rate(scanner_analyzer_golang_heuristic_total[5m]))`; alert when the rate is ≥1 for 15minutes to highlight unexpected stripped binaries.
* **Tracing**: spans for acquireâ†unionâ†analyzersâ†composeâ†emitâ†signâ†log. * **Tracing**: spans for acquireâ†unionâ†analyzersâ†composeâ†emitâ†signâ†log.
* **Audit logs**: DSSE requests log `license_id`, `image_digest`, `artifactSha256`, `policy_digest?`, Rekor UUID on success. * **Audit logs**: DSSE requests log `license_id`, `image_digest`, `artifactSha256`, `policy_digest?`, Rekor UUID on success.
@@ -592,12 +592,12 @@ scanner:
* **Analyzer contracts:** see `language-analyzers-contract.md` for cross-analyzer identity safety, evidence locators, and container layout rules. Per-analyzer docs: `analyzers-java.md`, `dotnet-analyzer.md`, `analyzers-python.md`, `analyzers-node.md`, `analyzers-bun.md`, `analyzers-go.md`. Implementation: `docs/implplan/SPRINT_0408_0001_0001_scanner_language_detection_gaps_program.md`. * **Analyzer contracts:** see `language-analyzers-contract.md` for cross-analyzer identity safety, evidence locators, and container layout rules. Per-analyzer docs: `analyzers-java.md`, `dotnet-analyzer.md`, `analyzers-python.md`, `analyzers-node.md`, `analyzers-bun.md`, `analyzers-go.md`. Implementation: `docs/implplan/SPRINT_0408_0001_0001_scanner_language_detection_gaps_program.md`.
* **Determinism:** given same image + analyzers → byte‑identical **CDX Protobuf**; JSON normalized. * **Determinism:** given same image + analyzers → byteidentical **CDX Protobuf**; JSON normalized.
* **OS packages:** ground‑truth images per distro; compare to package DB. * **OS packages:** groundtruth images per distro; compare to package DB.
* **Lang ecosystems:** sample images per ecosystem (Java/Node/Python/Go/.NET/Rust) with installed metadata; negative tests w/ lockfile‑only. * **Lang ecosystems:** sample images per ecosystem (Java/Node/Python/Go/.NET/Rust) with installed metadata; negative tests w/ lockfileonly.
* **Native & EntryTrace:** ELF graph correctness; shell AST cases (includes, run‑parts, exec, case/if). * **Native & EntryTrace:** ELF graph correctness; shell AST cases (includes, runparts, exec, case/if).
* **Diff:** layer attribution against synthetic two‑image sequences. * **Diff:** layer attribution against synthetic twoimage sequences.
* **Performance:** cold vs warm cache; large `node_modules` and `site‑packages`. * **Performance:** cold vs warm cache; large `node_modules` and `sitepackages`.
* **Security:** ensure no code execution from image; fuzz parser inputs; path traversal resistance on layer extract. * **Security:** ensure no code execution from image; fuzz parser inputs; path traversal resistance on layer extract.
--- ---
@@ -605,16 +605,16 @@ scanner:
## 13) Failure modes & degradations ## 13) Failure modes & degradations
* **Missing OS DB** (files exist, DB removed): record **files**; do **not** fabricate package components; emit `bin:{sha256}` where unavoidable; flag in evidence. * **Missing OS DB** (files exist, DB removed): record **files**; do **not** fabricate package components; emit `bin:{sha256}` where unavoidable; flag in evidence.
* **Unreadable metadata** (corrupt dist‑info): record file evidence; skip component creation; annotate. * **Unreadable metadata** (corrupt distinfo): record file evidence; skip component creation; annotate.
* **Dynamic shell constructs**: mark unresolved edges with reasons (env var unknown) and continue; **Usage** view may be partial. * **Dynamic shell constructs**: mark unresolved edges with reasons (env var unknown) and continue; **Usage** view may be partial.
* **Registry rate limits**: honor backoff; queue job retries with jitter. * **Registry rate limits**: honor backoff; queue job retries with jitter.
* **Signer refusal** (license/plan/version): scan completes; artifact produced; **no attestation**; WebService marks result as **unverified**. * **Signer refusal** (license/plan/version): scan completes; artifact produced; **no attestation**; WebService marks result as **unverified**.
--- ---
## 14) Optional plug‑ins (off by default) ## 14) Optional plugins (off by default)
* **Patch‑presence detector** (signature‑based backport checks). Reads curated function‑level signatures from advisories; inspects binaries for patched code snippets to lower false‑positives for backported fixes. Runs as a sidecar analyzer that **annotates** components; never overrides core identities. * **Patchpresence detector** (signaturebased backport checks). Reads curated functionlevel signatures from advisories; inspects binaries for patched code snippets to lower falsepositives for backported fixes. Runs as a sidecar analyzer that **annotates** components; never overrides core identities.
* **Runtime probes** (with Zastava): when allowed, compare **/proc/<pid>/maps** (DSOs actually loaded) with static **Usage** view for precision. * **Runtime probes** (with Zastava): when allowed, compare **/proc/<pid>/maps** (DSOs actually loaded) with static **Usage** view for precision.
--- ---
@@ -637,14 +637,14 @@ scanner:
## 17) Roadmap (Scanner) ## 17) Roadmap (Scanner)
* **M2**: Windows containers (MSI/SxS/GAC analyzers), PE/Mach‑O native analyzer, deeper Rust metadata. * **M2**: Windows containers (MSI/SxS/GAC analyzers), PE/MachO native analyzer, deeper Rust metadata.
* **M2**: Buildx generator GA (certified external registries), cross‑registry trust policies. * **M2**: Buildx generator GA (certified external registries), crossregistry trust policies.
* **M3**: Patch‑presence plug‑in GA (opt‑in), cross‑image corpus clustering (evidence‑only; not identity). * **M3**: Patchpresence plugin GA (optin), crossimage corpus clustering (evidenceonly; not identity).
* **M3**: Advanced EntryTrace (POSIX shell features breadth, busybox detection). * **M3**: Advanced EntryTrace (POSIX shell features breadth, busybox detection).
--- ---
### Appendix A — EntryTrace resolution (pseudo) ### Appendix A EntryTrace resolution (pseudo)
```csharp ```csharp
ResolveEntrypoint(ImageConfig cfg, RootFs fs): ResolveEntrypoint(ImageConfig cfg, RootFs fs):
@@ -675,9 +675,9 @@ ResolveEntrypoint(ImageConfig cfg, RootFs fs):
return Unknown(reason) return Unknown(reason)
``` ```
### Appendix A.1 — EntryTrace Explainability ### Appendix A.1 EntryTrace Explainability
### Appendix A.0 — Replay / Record mode ### Appendix A.0 Replay / Record mode
- WebService ships a **RecordModeService** that assembles replay manifests (schema v1) with policy/feed/tool pins and reachability references, then writes deterministic input/output bundles to the configured object store (RustFS default, S3/Minio fallback) under `replay/<head>/<digest>.tar.zst`. - WebService ships a **RecordModeService** that assembles replay manifests (schema v1) with policy/feed/tool pins and reachability references, then writes deterministic input/output bundles to the configured object store (RustFS default, S3/Minio fallback) under `replay/<head>/<digest>.tar.zst`.
- Bundles contain canonical manifest JSON plus inputs (policy/feed/tool/analyzer digests) and outputs (SBOM, findings, optional VEX/logs); CAS URIs follow `cas://replay/...` and are attached to scan snapshots as `ReplayArtifacts`. - Bundles contain canonical manifest JSON plus inputs (policy/feed/tool/analyzer digests) and outputs (SBOM, findings, optional VEX/logs); CAS URIs follow `cas://replay/...` and are attached to scan snapshots as `ReplayArtifacts`.
@@ -698,8 +698,8 @@ EntryTrace emits structured diagnostics and metrics so operators can quickly und
Diagnostics drive two metrics published by `EntryTraceMetrics`: Diagnostics drive two metrics published by `EntryTraceMetrics`:
- `entrytrace_resolutions_total{outcome}` — resolution attempts segmented by outcome (`resolved`, `partiallyresolved`, `unresolved`). - `entrytrace_resolutions_total{outcome}` resolution attempts segmented by outcome (`resolved`, `partiallyresolved`, `unresolved`).
- `entrytrace_unresolved_total{reason}` — diagnostic counts keyed by reason. - `entrytrace_unresolved_total{reason}` diagnostic counts keyed by reason.
Structured logs include `entrytrace.path`, `entrytrace.command`, `entrytrace.reason`, and `entrytrace.depth`, all correlated with scan/job IDs. Timestamps are normalized to UTC (microsecond precision) to keep DSSE attestations and UI traces explainable. Structured logs include `entrytrace.path`, `entrytrace.command`, `entrytrace.reason`, and `entrytrace.depth`, all correlated with scan/job IDs. Timestamps are normalized to UTC (microsecond precision) to keep DSSE attestations and UI traces explainable.

View File

@@ -1,81 +1,143 @@
# S00 Nav Rendering Policy # S00 Nav Rendering Policy
Status: Frozen Status: Frozen
Date: 2026-02-18 Date: 2026-03-29
Working directory: `docs/modules/ui/v2-rewire` Working directory: `docs/modules/ui/v2-rewire`
Sprint: `20260218_005`, task `R0-03` Sprint: `20260218_005`, task `R0-03` (updated 2026-03-29 for 6-group restructure)
## Policy statement ## Policy statement
Release Control-owned capabilities may be rendered as direct shortcuts in the sidebar if and only if: Capabilities may be rendered as direct shortcuts in the sidebar if and only if:
1. Ownership is labeled as **Release Control** in breadcrumbs and page headers. 1. Ownership is labeled with the correct **group name** in breadcrumbs and page headers.
2. The canonical routes for those capabilities live under `/release-control/*`. 2. The canonical routes for those capabilities live under the group's route prefix.
3. The sidebar shortcut links to the canonical route, not an alias. 3. The sidebar shortcut links to the canonical route, not an alias.
This policy prevents mixed rendering where the same screen appears to be owned by two domains. This policy prevents mixed rendering where the same screen appears to be owned by two domains.
## Canonical sidebar groups (6 groups)
The sidebar is organized into exactly 6 menu groups identified by `menuGroupId`:
| # | menuGroupId | Rendered label | Purpose |
|---|-------------|----------------|---------|
| 1 | `home` | *(none)* | Dashboard entry point |
| 2 | `release-control` | Release Control | Deployments, Releases, Environments, Readiness |
| 3 | `security` | Security | Vulnerabilities, Posture, Scan, VEX & Exceptions, Risk & Governance (incl. Simulation, Audit) |
| 4 | `evidence` | Evidence | Evidence Overview, Decision Capsules, Audit Log, Export Center |
| 5 | `operations` | Operations | Operations Hub, Policy Packs, Scheduled Jobs, Feeds & Airgap, Agent Fleet, Signals, Scripts, Diagnostics |
| 6 | `setup-admin` | Settings | Integrations, Identity & Access, Certificates & Trust, Theme & Branding, User Preferences |
### Policy dissolution
The former `policy` group was dissolved in the 7-to-6 restructure. Policy is a toolset, not a persona -- its items were distributed by audience:
- **Security** absorbed: VEX & Exceptions, Risk & Governance (including Simulation and Policy Audit). These are consumed by security practitioners during triage and risk assessment.
- **Operations** absorbed: Policy Packs. Pack authoring and management is an operational/admin workflow.
There is no standalone "Policy" group in the sidebar. The `/ops/policy/*` route prefix is retained for backward compatibility but items are surfaced under their new parent groups.
### New items surfaced (2026-03-29)
- **Readiness** (`/releases/readiness`) -- added to Release Control. Surfaces gate status and release readiness checks before promotion.
- **Findings Explorer** (`/security/findings`) -- added as a child of Security Posture. Provides a filterable view of all security findings across the fleet.
### Items removed from direct navigation
The following items are no longer direct sidebar entries but remain routable for deep links and contextual navigation:
- Runtime Drift, Notifications, Watchlist -- accessible from Operations Hub landing page
- Replay & Verify, Bundles, Trust -- accessible from Evidence Overview and Decision Capsules detail
## Allowed rendering model ## Allowed rendering model
### Desktop (expanded sidebar) ### Desktop (expanded sidebar)
``` ```
Dashboard [Home]
Dashboard
Release Control Release Control
├── Releases [shortcut direct nav allowed] Deployments [badge: failed runs + pending approvals]
├── Approvals [shortcut direct nav allowed] Releases [badge: blocked gates]
├── Bundles [nested only — no direct shortcut] Environments
├── Deployments [nested only — no direct shortcut] Readiness
└── Regions & Environments [nested only — no direct shortcut] Security
Security & Risk Vulnerabilities [badge: critical findings]
Evidence & Audit Security Posture [sparkline: security trend]
Integrations Supply-Chain Data
Platform Ops Findings Explorer
Administration Reachability
Unknowns
Scan Image
VEX & Exceptions
Risk & Governance
Simulation
Policy Audit
Evidence
Evidence Overview
Decision Capsules
Audit Log
Export Center
Operations
Operations Hub [sparkline: platform trend]
Policy Packs
Scheduled Jobs
Feeds & Airgap
Agent Fleet
Signals
Scripts
Diagnostics
Settings
Integrations
Identity & Access
Certificates & Trust
Theme & Branding
User Preferences
``` ```
`Releases` and `Approvals` may appear as direct children under `Release Control` in the sidebar ### Desktop (collapsed sidebar -- icons only)
(rather than requiring expand → click).
`Bundles`, `Deployments`, and `Regions & Environments` remain nested and require expand.
### Desktop (collapsed sidebar — icons only) - Show icon for each group root only.
- Tooltip on hover shows the group label.
- Show icon for Release Control root only. - Click navigates to the first item in the group or last active child.
- Tooltip on hover shows "Release Control". - No separate child icons in collapsed mode.
- Click navigates to Release Control overview or last active child.
- No separate Releases / Approvals icons in collapsed mode.
### Mobile (navigation drawer) ### Mobile (navigation drawer)
- All root domains appear as top-level items in the drawer. - All 6 groups appear as top-level items in the drawer.
- Release Control expands in-place to show child nav items. - Groups expand in-place to show child nav items.
- `Releases` and `Approvals` may appear as drawer children with Release Control as visible parent. - No capabilities may appear as top-level drawer items separate from their group.
- No Release Control capabilities may appear as top-level drawer items separate from the Release Control group.
## Breadcrumb rules ## Breadcrumb rules
Canonical format: `Root Domain > Capability > [Sub-page]` Canonical format: `Group > Capability > [Sub-page]`
| Scenario | Breadcrumb | Notes | | Scenario | Breadcrumb | Notes |
| --- | --- | --- | | --- | --- | --- |
| Releases list | `Release Control > Releases` | No shortcut bypasses ownership label | | Deployments list | `Release Control > Deployments` | |
| Release detail | `Release Control > Releases > RCB-1234` | ID or name appended | | Release detail | `Release Control > Releases > RCB-1234` | ID or name appended |
| Approvals queue | `Release Control > Approvals` | | | Readiness | `Release Control > Readiness` | New item |
| Approval detail | `Release Control > Approvals > APR-5678` | | | Vulnerabilities | `Security > Vulnerabilities` | |
| Bundle catalog | `Release Control > Bundles` | | | Findings Explorer | `Security > Security Posture > Findings Explorer` | Nested under posture |
| Bundle detail | `Release Control > Bundles > my-bundle` | | | VEX & Exceptions | `Security > VEX & Exceptions` | Absorbed from Policy |
| Bundle version detail | `Release Control > Bundles > my-bundle > v1.3.0` | | | Risk & Governance | `Security > Risk & Governance` | Absorbed from Policy |
| Deployments | `Release Control > Deployments` | | | Policy Simulation | `Security > Risk & Governance > Simulation` | Nested child |
| Environments list | `Release Control > Regions & Environments` | | | Evidence Overview | `Evidence > Evidence Overview` | |
| Environment detail | `Release Control > Regions & Environments > staging-eu` | | | Decision Capsules | `Evidence > Decision Capsules` | |
| Audit Log | `Evidence > Audit Log` | |
| Operations Hub | `Operations > Operations Hub` | |
| Policy Packs | `Operations > Policy Packs` | Absorbed from Policy |
| Integrations | `Settings > Integrations` | |
| Certificates & Trust | `Settings > Certificates & Trust` | Renamed from "Certificates" |
### Concrete counter-examples (forbidden) ### Concrete counter-examples (forbidden)
| Forbidden breadcrumb | Reason | | Forbidden breadcrumb | Reason |
| --- | --- | | --- | --- |
| `Approvals > APR-5678` | Missing Release Control ownership prefix | | `Policy > VEX & Exceptions` | Policy group no longer exists; VEX is under Security |
| `Releases` (no parent) | Same — no domain context | | `Policy > Packs` | Policy group dissolved; Packs is under Operations |
| `Settings > Policy Governance` | Policy Governance owner is Administration, not Settings | | `Approvals > APR-5678` | Missing group ownership prefix |
| `Evidence & Audit > Trust & Signing` | Trust & Signing owner is Administration; Evidence may only show a consumer link | | `Releases` (no parent) | No domain context |
| `Evidence > Trust` | Trust item removed from Evidence nav |
## Legacy label transition behavior ## Legacy label transition behavior
@@ -83,7 +145,7 @@ Where users know a surface by an old label, show a compact transition label duri
Rules: Rules:
- Transition labels appear only in page headers and sidebar items, not in breadcrumbs. - Transition labels appear only in page headers and sidebar items, not in breadcrumbs.
- Format: canonical label is primary; old label appears parenthetically e.g., `Policy Governance (formerly Policy Studio)`. - Format: canonical label is primary; old label appears parenthetically -- e.g., `Certificates & Trust (formerly Certificates)`.
- Transition labels are removed at sprint 016 cutover unless traffic evidence requires extension. - Transition labels are removed at sprint 016 cutover unless traffic evidence requires extension.
- Canonical labels are always primary; old labels never replace canonical ones. - Canonical labels are always primary; old labels never replace canonical ones.
@@ -91,26 +153,26 @@ Planned transition labels:
| Canonical label | Transition label (migration window only) | Remove at | | Canonical label | Transition label (migration window only) | Remove at |
| --- | --- | --- | | --- | --- | --- |
| `Security & Risk` | `Security & Risk (formerly Security)` | Sprint 016 | | `Security` | `Security (formerly Security & Risk)` | Sprint 016 |
| `Platform Ops` | `Platform Ops (formerly Operations)` | Sprint 016 | | `Operations` | `Operations (formerly Platform Ops)` | Sprint 016 |
| `Evidence & Audit` | `Evidence & Audit (formerly Evidence)` | Sprint 016 | | `Evidence` | `Evidence (formerly Evidence & Audit)` | Sprint 016 |
| `Policy Governance` | `Policy Governance (formerly Policy Studio / Policy)` | Sprint 016 | | `Certificates & Trust` | `Certificates & Trust (formerly Certificates)` | Sprint 016 |
## Explicit do-not list ## Explicit do-not list
The following rendering patterns are forbidden in any sprint implementation: The following rendering patterns are forbidden in any sprint implementation:
1. **Do not** place Release Control capability screens (`Releases`, `Approvals`, `Bundles`, `Deployments`, `Environments`) as root-level sidebar items independent from the `Release Control` group. 1. **Do not** place any capability as a root-level sidebar item independent from its canonical group.
2. **Do not** display a breadcrumb that omits the canonical root domain prefix. 2. **Do not** display a breadcrumb that omits the canonical group prefix.
3. **Do not** show different ownership labels on desktop vs. mobile for the same screen. 3. **Do not** show different ownership labels on desktop vs. mobile for the same screen.
4. **Do not** use legacy root-level nav paths (e.g., `/approvals`, `/releases`) as the canonical nav target they must redirect to `/release-control/*` canonical targets. 4. **Do not** use legacy root-level nav paths (e.g., `/approvals`, `/releases` at root) as the canonical nav target -- they must redirect to canonical targets.
5. **Do not** label `Trust & Signing` as owned by Evidence & Audit or Security in any nav or header. 5. **Do not** reintroduce a `policy` group -- Policy items live under Security and Operations.
6. **Do not** label `Policy Governance` as owned by Release Control in any nav or header. 6. **Do not** label Policy Packs as owned by Security -- Packs authoring is an Operations concern.
7. **Do not** introduce a new root domain that is not in the canonical 7: Dashboard, Release Control, Security & Risk, Evidence & Audit, Integrations, Platform Ops, Administration. 7. **Do not** introduce a new root domain that is not in the canonical 6: Home, Release Control, Security, Evidence, Operations, Settings.
## Route alias requirements for migration ## Route alias requirements for migration
During the alias window, current root-level paths (`/releases`, `/approvals`) must: During the alias window, legacy paths must:
- Resolve to the canonical `/release-control/releases` and `/release-control/approvals` routes. - Resolve to the canonical routes under the new group structure.
- Render the canonical breadcrumb (e.g., `Release Control > Releases`) not an alias-derived breadcrumb. - Render the canonical breadcrumb (e.g., `Security > VEX & Exceptions`) -- not an alias-derived breadcrumb.
- Not appear as primary nav items in the sidebar; the sidebar must link to canonical paths only. - Not appear as primary nav items in the sidebar; the sidebar must link to canonical paths only.

View File

@@ -1,10 +1,10 @@
# Automated Test-Suite Overview # Automated Test-Suite Overview
This document enumerates **every automated check** executed by the Stella Ops This document enumerates **every automated check** executed by the Stella Ops
CI pipeline, from unit level to chaos experiments. It is intended for CI pipeline, from unit level to chaos experiments. It is intended for
contributors who need to extend coverage or diagnose failures. contributors who need to extend coverage or diagnose failures.
> **Build parameters** – values such as `{{ dotnet }}` (runtime) and > **Build parameters** values such as `{{ dotnet }}` (runtime) and
> `{{ angular }}` (UI framework) are injected at build time. > `{{ angular }}` (UI framework) are injected at build time.
--- ---
@@ -13,7 +13,7 @@ contributors who need to extend coverage or diagnose failures.
### Core Principles ### Core Principles
1. **Determinism as Contract**: Scan verdicts must be reproducible. Same inputs → byte-identical outputs. 1. **Determinism as Contract**: Scan verdicts must be reproducible. Same inputs byte-identical outputs.
2. **Offline by Default**: Every test (except explicitly tagged "online") runs without network access. 2. **Offline by Default**: Every test (except explicitly tagged "online") runs without network access.
3. **Evidence-First Validation**: Assertions verify the complete evidence chain, not just pass/fail. 3. **Evidence-First Validation**: Assertions verify the complete evidence chain, not just pass/fail.
4. **Interop is Required**: Compatibility with ecosystem tools (Syft, Grype, Trivy, cosign) blocks releases. 4. **Interop is Required**: Compatibility with ecosystem tools (Syft, Grype, Trivy, cosign) blocks releases.
@@ -78,16 +78,16 @@ the required test types per project model and the module-to-model mapping.
| Metric | Budget | Gate | | Metric | Budget | Gate |
|--------|--------|------| |--------|--------|------|
| API unit coverage | ≥ 85% lines | PR merge | | API unit coverage | 85% lines | PR merge |
| API response P95 | ≤ 120 ms | nightly alert | | API response P95 | 120 ms | nightly alert |
| Δ-SBOM warm scan P95 (4 vCPU) | ≤ 5 s | nightly alert | | Δ-SBOM warm scan P95 (4 vCPU) | 5 s | nightly alert |
| Lighthouse performance score | ≥ 90 | nightly alert | | Lighthouse performance score | 90 | nightly alert |
| Lighthouse accessibility score | ≥ 95 | nightly alert | | Lighthouse accessibility score | 95 | nightly alert |
| k6 sustained RPS drop | < 5% vs baseline | nightly alert | | k6 sustained RPS drop | < 5% vs baseline | nightly alert |
| **Replay determinism** | 0 byte diff | **Release** | | **Replay determinism** | 0 byte diff | **Release** |
| **Interop findings parity** | ≥ 95% | **Release** | | **Interop findings parity** | 95% | **Release** |
| **Offline E2E** | All pass with no network | **Release** | | **Offline E2E** | All pass with no network | **Release** |
| **Unknowns budget (prod)** | ≤ configured limit | **Release** | | **Unknowns budget (prod)** | configured limit | **Release** |
| **Router Retry-After compliance** | 100% | Nightly | | **Router Retry-After compliance** | 100% | Nightly |
--- ---
@@ -109,7 +109,7 @@ dotnet test --filter "Category=Interop"
The script spins up PostgreSQL/Valkey via Testcontainers and requires: The script spins up PostgreSQL/Valkey via Testcontainers and requires:
* Docker ≥ 25 * Docker 25
* Node 20 (for Jest/Playwright) * Node 20 (for Jest/Playwright)
### PostgreSQL Testcontainers ### PostgreSQL Testcontainers
@@ -158,7 +158,7 @@ stella replay verify --manifest run-manifest.json
### Evidence Index ### Evidence Index
The **Evidence Index** links verdicts to their supporting evidence chain: The **Evidence Index** links verdicts to their supporting evidence chain:
- Verdict → SBOM digests → Attestation IDs → Tool versions - Verdict SBOM digests Attestation IDs Tool versions
### Golden Corpus ### Golden Corpus
@@ -191,7 +191,7 @@ public class OfflineTests : NetworkIsolatedTestBase
--- ---
## Concelier OSV↔GHSA Parity Fixtures ## Concelier OSVGHSA Parity Fixtures
The Concelier connector suite includes a regression test (`OsvGhsaParityRegressionTests`) The Concelier connector suite includes a regression test (`OsvGhsaParityRegressionTests`)
that checks a curated set of GHSA identifiers against OSV responses. The fixture that checks a curated set of GHSA identifiers against OSV responses. The fixture

View File

@@ -1,4 +1,4 @@
# 01. Requirements And Principles # 01. Requirements And Principles
## 1. Product Goal ## 1. Product Goal
@@ -239,7 +239,7 @@ They are related, but they are not the same data structure.
### 5.3 Run To Wait ### 5.3 Run To Wait
The engine should never keep a workflow instance “hot” in memory for correctness. The engine should never keep a workflow instance “hot” in memory for correctness.
Execution should run until: Execution should run until:

View File

@@ -1,15 +1,15 @@
# Tutorial 1: Hello World # Tutorial 1: Hello World
The simplest possible workflow: initialize state from a start request, activate a single human task, and complete the workflow when the task is done. The simplest possible workflow: initialize state from a start request, activate a single human task, and complete the workflow when the task is done.
## Concepts Introduced ## Concepts Introduced
- `IDeclarativeWorkflow<T>` — the contract every workflow implements - `IDeclarativeWorkflow<T>` the contract every workflow implements
- `WorkflowSpec.For<T>()` — the builder entry point - `WorkflowSpec.For<T>()` the builder entry point
- `.InitializeState()` — transforms the start request into workflow state - `.InitializeState()` transforms the start request into workflow state
- `.StartWith(task)` — sets the first task to activate - `.StartWith(task)` sets the first task to activate
- `WorkflowHumanTask.For<T>()` — defines a human task - `WorkflowHumanTask.For<T>()` defines a human task
- `.OnComplete(flow => flow.Complete())` — terminal step - `.OnComplete(flow => flow.Complete())` terminal step
## What Happens at Runtime ## What Happens at Runtime
@@ -17,7 +17,7 @@ The simplest possible workflow: initialize state from a start request, activate
2. State initializes to `{ "customerName": "John" }` 2. State initializes to `{ "customerName": "John" }`
3. Task "Greet Customer" is created with status "Pending" 3. Task "Greet Customer" is created with status "Pending"
4. A user assigns the task to themselves, then completes it 4. A user assigns the task to themselves, then completes it
5. `OnComplete` executes `.Complete()` — the workflow finishes 5. `OnComplete` executes `.Complete()` the workflow finishes
## Variants ## Variants
@@ -26,5 +26,5 @@ The simplest possible workflow: initialize state from a start request, activate
## Next ## Next
[Tutorial 2: Service Tasks](../02-service-tasks/) — call external services before or after human tasks. [Tutorial 2: Service Tasks](../02-service-tasks/) call external services before or after human tasks.

View File

@@ -1,15 +1,15 @@
# Tutorial 2: Service Tasks # Tutorial 2: Service Tasks
Call external services (microservices, HTTP APIs, GraphQL, RabbitMQ) from within a workflow. Handle failures and timeouts gracefully. Call external services (microservices, HTTP APIs, GraphQL, RabbitMQ) from within a workflow. Handle failures and timeouts gracefully.
## Concepts Introduced ## Concepts Introduced
- `.Call()` — invoke a transport with payload and optional response capture - `.Call()` invoke a transport with payload and optional response capture
- Address types — `LegacyRabbit`, `Microservice`, `Http`, `Graphql`, `Rabbit` - Address types `LegacyRabbit`, `Microservice`, `Http`, `Graphql`, `Rabbit`
- `resultKey` — store the service response in workflow state - `resultKey` store the service response in workflow state
- `whenFailure` / `whenTimeout` — recovery branches - `whenFailure` / `whenTimeout` recovery branches
- `WorkflowHandledBranchAction.Complete` — shorthand for "complete on error" - `WorkflowHandledBranchAction.Complete` shorthand for "complete on error"
- `timeoutSeconds` — per-step timeout override (default: 1 hour) - `timeoutSeconds` per-step timeout override (default: 1 hour)
## Key Points ## Key Points
@@ -25,5 +25,5 @@ Call external services (microservices, HTTP APIs, GraphQL, RabbitMQ) from within
## Next ## Next
[Tutorial 3: Decisions](../03-decisions/) — branch workflow logic based on conditions. [Tutorial 3: Decisions](../03-decisions/) branch workflow logic based on conditions.

View File

@@ -1,13 +1,13 @@
# Tutorial 3: Decisions # Tutorial 3: Decisions
Branch workflow logic based on conditions — state values, payload answers, or complex expressions. Branch workflow logic based on conditions state values, payload answers, or complex expressions.
## Concepts Introduced ## Concepts Introduced
- `.WhenExpression()` — branch on any boolean expression - `.WhenExpression()` branch on any boolean expression
- `.WhenStateFlag()` — shorthand for checking a boolean state value - `.WhenStateFlag()` shorthand for checking a boolean state value
- `.WhenPayloadEquals()` — shorthand for checking a task completion payload value - `.WhenPayloadEquals()` shorthand for checking a task completion payload value
- Nested decisions — decisions inside decisions for complex routing - Nested decisions decisions inside decisions for complex routing
## Decision Types ## Decision Types
@@ -24,5 +24,5 @@ Branch workflow logic based on conditions — state values, payload answers
## Next ## Next
[Tutorial 4: Human Tasks](../04-human-tasks/) — approve/reject patterns with OnComplete flows. [Tutorial 4: Human Tasks](../04-human-tasks/) approve/reject patterns with OnComplete flows.

View File

@@ -1,26 +1,26 @@
# Tutorial 4: Human Tasks with OnComplete Flows # Tutorial 4: Human Tasks with OnComplete Flows
The approve/reject pattern — the most common human task flow in insurance workflows. The approve/reject pattern the most common human task flow in insurance workflows.
## Concepts Introduced ## Concepts Introduced
- `WorkflowHumanTask.For<T>()` — define a task with name, type, route, and roles - `WorkflowHumanTask.For<T>()` define a task with name, type, route, and roles
- `.WithPayload()` — data sent to the UI when the task is displayed - `.WithPayload()` data sent to the UI when the task is displayed
- `.WithTimeout(seconds)` — optional deadline for the task - `.WithTimeout(seconds)` optional deadline for the task
- `.WithRoles()` — restrict which roles can interact with this task - `.WithRoles()` restrict which roles can interact with this task
- `.OnComplete(flow => ...)` — sequence executed after user completes the task - `.OnComplete(flow => ...)` sequence executed after user completes the task
- `.ActivateTask()` — pause workflow and wait for user action - `.ActivateTask()` pause workflow and wait for user action
- `.AddTask()` — register a task in the workflow spec (separate from activation) - `.AddTask()` register a task in the workflow spec (separate from activation)
- Re-activation — send the user back to the same task on validation failure - Re-activation send the user back to the same task on validation failure
## Approve/Reject Pattern ## Approve/Reject Pattern
1. Workflow starts, runs some service tasks 1. Workflow starts, runs some service tasks
2. `.ActivateTask("Approve")` — workflow pauses 2. `.ActivateTask("Approve")` workflow pauses
3. User sees the task in their inbox, assigns it, submits an answer 3. User sees the task in their inbox, assigns it, submits an answer
4. `.OnComplete` checks `payload.answer`: 4. `.OnComplete` checks `payload.answer`:
- `"approve"` — run confirmation operations, convert to policy - `"approve"` run confirmation operations, convert to policy
- `"reject"` — cancel the application - `"reject"` cancel the application
5. If operations fail, re-activate the same task for correction 5. If operations fail, re-activate the same task for correction
## Variants ## Variants
@@ -30,5 +30,5 @@ The approve/reject pattern — the most common human task flow in insurance
## Next ## Next
[Tutorial 5: Sub-Workflows](../05-sub-workflows/) — inline vs fire-and-forget child workflows. [Tutorial 5: Sub-Workflows](../05-sub-workflows/) inline vs fire-and-forget child workflows.

View File

@@ -1,14 +1,14 @@
# Tutorial 5: Sub-Workflows & Continuations # Tutorial 5: Sub-Workflows & Continuations
Compose workflows by invoking child workflows — either inline (SubWorkflow) or fire-and-forget (ContinueWith). Compose workflows by invoking child workflows either inline (SubWorkflow) or fire-and-forget (ContinueWith).
## SubWorkflow vs ContinueWith ## SubWorkflow vs ContinueWith
| Feature | `.SubWorkflow()` | `.ContinueWith()` | | Feature | `.SubWorkflow()` | `.ContinueWith()` |
|---------|-----------------|-------------------| |---------|-----------------|-------------------|
| Parent waits | Yes — resumes after child completes | No — parent completes immediately | | Parent waits | Yes resumes after child completes | No parent completes immediately |
| State flows back | Yes — child state merges into parent | No — child is independent | | State flows back | Yes child state merges into parent | No child is independent |
| Same instance | Yes — tasks appear under parent instance | No — new workflow instance | | Same instance | Yes tasks appear under parent instance | No new workflow instance |
| Use when | Steps must complete before parent continues | Fire-and-forget, scheduled work | | Use when | Steps must complete before parent continues | Fire-and-forget, scheduled work |
## Variants ## Variants
@@ -18,5 +18,5 @@ Compose workflows by invoking child workflows — either inline (SubWorkflo
## Next ## Next
[Tutorial 6: Advanced Patterns](../06-advanced-patterns/) — Fork, Repeat, Timer, External Signal. [Tutorial 6: Advanced Patterns](../06-advanced-patterns/) Fork, Repeat, Timer, External Signal.

View File

@@ -1,4 +1,4 @@
# Tutorial 6: Advanced Patterns # Tutorial 6: Advanced Patterns
Fork (parallel branches), Repeat (retry loops), Timer (delays), and External Signal (wait for events). Fork (parallel branches), Repeat (retry loops), Timer (delays), and External Signal (wait for events).
@@ -18,5 +18,5 @@ Fork (parallel branches), Repeat (retry loops), Timer (delays), and External Sig
## Next ## Next
[Tutorial 7: Shared Helpers](../07-shared-helpers/) — organizing reusable workflow components. [Tutorial 7: Shared Helpers](../07-shared-helpers/) organizing reusable workflow components.

View File

@@ -1,4 +1,4 @@
# Tutorial 7: Shared Support Helpers # Tutorial 7: Shared Support Helpers
When building many workflows for the same domain (e.g., 50+ policy change workflows), extract reusable components into a support helper class. When building many workflows for the same domain (e.g., 50+ policy change workflows), extract reusable components into a support helper class.
@@ -6,19 +6,19 @@ When building many workflows for the same domain (e.g., 50+ policy change workfl
| Component | Example | | Component | Example |
|-----------|---------| |-----------|---------|
| **Address constants** | `LegacyRabbitAddress`, `HttpAddress` — centralized routing | | **Address constants** | `LegacyRabbitAddress`, `HttpAddress` centralized routing |
| **Workflow references** | `WorkflowReference` — for SubWorkflow/ContinueWith targets | | **Workflow references** | `WorkflowReference` for SubWorkflow/ContinueWith targets |
| **Payload builders** | Static methods returning `WorkflowExpressionDefinition` | | **Payload builders** | Static methods returning `WorkflowExpressionDefinition` |
| **State initializers** | Base state + override pattern | | **State initializers** | Base state + override pattern |
| **Flow extensions** | Extension methods on `WorkflowFlowBuilder<T>` for common sequences | | **Flow extensions** | Extension methods on `WorkflowFlowBuilder<T>` for common sequences |
## C#-Only Tutorial ## C#-Only Tutorial
This tutorial has no JSON equivalent — it covers C# code organization patterns. This tutorial has no JSON equivalent it covers C# code organization patterns.
- [C# Example](csharp/) - [C# Example](csharp/)
## Next ## Next
[Tutorial 8: Expressions](../08-expressions/) — path navigation, functions, and operators. [Tutorial 8: Expressions](../08-expressions/) path navigation, functions, and operators.

View File

@@ -1,4 +1,4 @@
# Tutorial 8: Expressions # Tutorial 8: Expressions
The expression system enables declarative logic that compiles to portable canonical JSON. All expressions are evaluable at runtime without recompilation. The expression system enables declarative logic that compiles to portable canonical JSON. All expressions are evaluable at runtime without recompilation.
@@ -32,5 +32,5 @@ The expression system enables declarative logic that compiles to portable canoni
## Next ## Next
[Tutorial 9: Testing](../09-testing/) — unit test setup with recording transports. [Tutorial 9: Testing](../09-testing/) unit test setup with recording transports.