docs consolidation
This commit is contained in:
@@ -4,5 +4,5 @@ af3459e8cf7179c264d1ac1f82a968e26e273e7e45cd103c8966d0dd261c3029 docs/api/conso
|
||||
336c55d72abea77bf4557f1e3dcaa4ab8366d79008670d87020f900dcfc833c0 docs/assets/advisory-ai/console/20251203-0000-list-view-build-r2-payload.json
|
||||
c55217e8526700c2d303677a66351a706007381219adab99773d4728cc61f293 docs/assets/advisory-ai/console/20251203-0000-list-view-build-r2.svg
|
||||
9bc89861ba873c7f470c5a30c97fb2cd089d6af23b085fba2095e88f8d1f8ede docs/assets/advisory-ai/console/evidence-drawer-b1820ad.svg
|
||||
f6093257134f38033abb88c940d36f7985b48f4f79870d5b6310d70de5a586f9 docs/samples/console/console-vex-30-001.json
|
||||
921bcb360454e801bb006a3df17f62e1fcfecaaccda471ae66f167147539ad1e docs/samples/console/console-vuln-29-001.json
|
||||
f6093257134f38033abb88c940d36f7985b48f4f79870d5b6310d70de5a586f9 docs/modules/platform/samples/console-vex-30-001.json
|
||||
921bcb360454e801bb006a3df17f62e1fcfecaaccda471ae66f167147539ad1e docs/modules/platform/samples/console-vuln-29-001.json
|
||||
|
||||
@@ -116,8 +116,8 @@ PY
|
||||
| `docs/assets/advisory-ai/console/20251203-0000-list-view-build-r2-payload.json` | `336c55d72abea77bf4557f1e3dcaa4ab8366d79008670d87020f900dcfc833c0` | List-view sealed payload. |
|
||||
| `docs/assets/advisory-ai/console/20251203-0000-list-view-build-r2.svg` | `c55217e8526700c2d303677a66351a706007381219adab99773d4728cc61f293` | Deterministic list-view capture. |
|
||||
| `docs/assets/advisory-ai/console/evidence-drawer-b1820ad.svg` | `9bc89861ba873c7f470c5a30c97fb2cd089d6af23b085fba2095e88f8d1f8ede` | Evidence drawer mock (keep until live capture). |
|
||||
| `docs/samples/console/console-vex-30-001.json` | `f6093257134f38033abb88c940d36f7985b48f4f79870d5b6310d70de5a586f9` | Console VEX search fixture. |
|
||||
| `docs/samples/console/console-vuln-29-001.json` | `921bcb360454e801bb006a3df17f62e1fcfecaaccda471ae66f167147539ad1e` | Console vuln search fixture. |
|
||||
| `docs/modules/platform/samples/console-vex-30-001.json` | `f6093257134f38033abb88c940d36f7985b48f4f79870d5b6310d70de5a586f9` | Console VEX search fixture. |
|
||||
| `docs/modules/platform/samples/console-vuln-29-001.json` | `921bcb360454e801bb006a3df17f62e1fcfecaaccda471ae66f167147539ad1e` | Console vuln search fixture. |
|
||||
|
||||
## 3. Accessibility & offline requirements
|
||||
- Console screens must pass WCAG 2.2 AA contrast and provide focus order that matches the keyboard shortcuts planned for Advisory AI (see `docs/modules/advisory-ai/overview.md`).
|
||||
@@ -176,7 +176,7 @@ Violations:
|
||||
1. **Volume readiness** – confirm the RWX volume (`/var/lib/advisory-ai/{queue,plans,outputs}`) is mounted; the console should poll `/api/v1/advisory-ai/health` and surface “Queue not available” if the worker is offline.
|
||||
2. **Cached responses** – when running air-gapped, highlight that only cached plans/responses are available by showing the `planFromCache` badge plus the `generatedAtUtc` timestamp.
|
||||
3. **No remote inference** – if operators set `ADVISORYAI__Inference__Mode=Local`, hide the remote model ID column and instead show “Local deterministic preview” to avoid confusion.
|
||||
4. **Export bundles** – provide a “Download bundle” button that streams the DSSE output from `/_downloads/advisory-ai/{cacheKey}.json` so operators can carry it into Offline Kit workflows documented in `docs/OFFLINE_KIT.md`. While staging endpoints are pending, reuse the Evidence Bundle v1 sample at `docs/samples/evidence-bundle/evidence-bundle-v1.tar.gz` (hash in `evidence-bundle-v1.tar.gz.sha256`) to validate wiring and any optional visual captures.
|
||||
4. **Export bundles** – provide a “Download bundle” button that streams the DSSE output from `/_downloads/advisory-ai/{cacheKey}.json` so operators can carry it into Offline Kit workflows documented in `docs/OFFLINE_KIT.md`. While staging endpoints are pending, reuse the Evidence Bundle v1 sample at `docs/modules/evidence-locker/samples/evidence-bundle-v1.tar.gz` (hash in `evidence-bundle-v1.tar.gz.sha256`) to validate wiring and any optional visual captures.
|
||||
|
||||
## 6. Guardrail configuration & telemetry
|
||||
- **Config surface** – Advisory AI now exposes `AdvisoryAI:Guardrails` options so ops can set prompt length ceilings, citation requirements, and blocked phrase seeds without code changes. Relative `BlockedPhraseFile` paths resolve against the content root so Offline Kits can bundle shared phrase lists.
|
||||
@@ -207,7 +207,7 @@ Violations:
|
||||
- [x] Refresh: deterministic list-view payload and guardrail banner remain sealed (2025-12-03); keep payload + hash alongside any optional captures generated later.
|
||||
|
||||
### Publication readiness checklist (DOCS-AIAI-31-004)
|
||||
- Inputs available now: console fixtures (`docs/samples/console/console-vuln-29-001.json`, `console-vex-30-001.json`), evidence bundle sample (`docs/samples/evidence-bundle/evidence-bundle-v1.tar.gz`), guardrail ribbon contract.
|
||||
- Inputs available now: console fixtures (`docs/modules/platform/samples/console-vuln-29-001.json`, `console-vex-30-001.json`), evidence bundle sample (`docs/modules/evidence-locker/samples/evidence-bundle-v1.tar.gz`), guardrail ribbon contract.
|
||||
- Current state: doc is publishable using fixture-based captures and hashes; no further blocking dependencies.
|
||||
- Optional follow-up: when live SBOM `/v1/sbom/context` evidence is available, regenerate the command-output snippets (and any optional captures), capture the build hash, and replace fixture payloads with live outputs.
|
||||
|
||||
@@ -215,8 +215,8 @@ Violations:
|
||||
|
||||
### Guardrail console fixtures (unchecked-integration)
|
||||
|
||||
- Vulnerability search sample: `docs/samples/console/console-vuln-29-001.json` (maps to CONSOLE-VULN-29-001).
|
||||
- VEX search sample: `docs/samples/console/console-vex-30-001.json` (maps to CONSOLE-VEX-30-001).
|
||||
- Vulnerability search sample: `docs/modules/platform/samples/console-vuln-29-001.json` (maps to CONSOLE-VULN-29-001).
|
||||
- VEX search sample: `docs/modules/platform/samples/console-vex-30-001.json` (maps to CONSOLE-VEX-30-001).
|
||||
- Use these until live endpoints are exposed; replace with real captures when staging is available.
|
||||
|
||||
### Fixture bundle regeneration (deterministic)
|
||||
|
||||
@@ -26,4 +26,4 @@ Schemas/examples for attestations handled by Attestor.
|
||||
- Use SHA-256 digests; include in envelope metadata.
|
||||
|
||||
## Examples
|
||||
- Place sample payloads in `docs/samples/attestor/payloads/` (add when available).
|
||||
- Place sample payloads in `docs/modules/attestor/samples/payloads/` (add when available).
|
||||
|
||||
@@ -25,7 +25,7 @@ Baseline directory layout is defined in `docs/product-advisories/14-Dec-2025 - O
|
||||
|
||||
The offline kit (or any offline DSSE evidence pack) may include a Rekor receipt alongside a DSSE statement.
|
||||
|
||||
- **Schema:** `docs/schemas/rekor-receipt.schema.json`
|
||||
- **Schema:** `docs/modules/attestor/schemas/rekor-receipt.schema.json`
|
||||
- **Source:** `docs/product-advisories/14-Dec-2025 - Rekor Integration Technical Reference.md` (Section 13.1) and `docs/product-advisories/14-Dec-2025 - Offline and Air-Gap Technical Reference.md` (Section 1.4)
|
||||
|
||||
Fields:
|
||||
|
||||
599
docs/modules/authority/AUTHORITY.md
Normal file
599
docs/modules/authority/AUTHORITY.md
Normal file
@@ -0,0 +1,599 @@
|
||||
# StellaOps Authority Service
|
||||
|
||||
> **Status:** Drafted 2025-10-12 (CORE5B.DOC / DOC1.AUTH) – aligns with Authority revocation store, JWKS rotation, and bootstrap endpoints delivered in Sprint 1.
|
||||
|
||||
## 1. Purpose
|
||||
The **StellaOps Authority** service issues OAuth2/OIDC tokens for every StellaOps module (Concelier, Backend, Agent, Zastava) and exposes the policy controls required in sovereign/offline environments. Authority is built as a minimal ASP.NET host that:
|
||||
|
||||
- brokers password, client-credentials, and device-code flows through pluggable identity providers;
|
||||
- persists access/refresh/device tokens in PostgreSQL with deterministic schemas for replay analysis and air-gapped audit copies;
|
||||
- distributes revocation bundles and JWKS material so downstream services can enforce lockouts without direct database access;
|
||||
- offers bootstrap APIs for first-run provisioning and key rotation without redeploying binaries.
|
||||
|
||||
Authority is deployed alongside Concelier in air-gapped environments and never requires outbound internet access. All trusted metadata (OpenIddict discovery, JWKS, revocation bundles) is cacheable, signed, and reproducible.
|
||||
|
||||
## 2. Component Architecture
|
||||
Authority is composed of five cooperating subsystems:
|
||||
|
||||
1. **Minimal API host** – configures OpenIddict endpoints (`/token`, `/authorize`, `/revoke`, `/jwks`), publishes the OpenAPI contract at `/.well-known/openapi`, and enables structured logging/telemetry. Rate limiting hooks (`AuthorityRateLimiter`) wrap every request.
|
||||
2. **Plugin host** – loads `StellaOps.Authority.Plugin.*.dll` assemblies, applies capability metadata, and exposes password/client provisioning surfaces through dependency injection.
|
||||
3. **PostgreSQL storage** – persists tokens, revocations, bootstrap invites, and plugin state in deterministic tables indexed for offline sync (`authority_tokens`, `authority_revocations`, etc.).
|
||||
4. **Cryptography layer** – `StellaOps.Cryptography` abstractions manage password hashing, signing keys, JWKS export, and detached JWS generation.
|
||||
5. **Offline ops APIs** – internal endpoints under `/internal/*` provide administrative flows (bootstrap users/clients, revocation export) guarded by API keys and deterministic audit events.
|
||||
|
||||
A high-level sequence for password logins:
|
||||
|
||||
```
|
||||
Client -> /token (password grant)
|
||||
-> Rate limiter & audit hooks
|
||||
-> Plugin credential store (Argon2id verification)
|
||||
-> Token persistence (PostgreSQL authority_tokens)
|
||||
-> Response (access/refresh tokens + deterministic claims)
|
||||
```
|
||||
|
||||
## 3. Token Lifecycle & Persistence
|
||||
Authority persists every issued token in PostgreSQL so operators can audit or revoke without scanning distributed caches.
|
||||
|
||||
- **Table:** `authority_tokens`
|
||||
- **Key fields:**
|
||||
- `tokenId`, `type` (`access_token`, `refresh_token`, `device_code`, `authorization_code`)
|
||||
- `subjectId`, `clientId`, ordered `scope` array
|
||||
- `tenant` (lower-cased tenant hint from the issuing client, omitted for global clients)
|
||||
|
||||
### Console OIDC client
|
||||
|
||||
- **Client ID**: `console-web`
|
||||
- **Grants**: `authorization_code` (PKCE required), `refresh_token`
|
||||
- **Audience**: `console`
|
||||
- **Scopes**: `openid`, `profile`, `email`, `advisory:read`, `advisory-ai:view`, `vex:read`, `aoc:verify`, `findings:read`, `scanner:read`, `scanner:scan`, `scanner:export`, `orch:read`, `vuln:view`, `vuln:investigate`, `vuln:operate`, `vuln:audit`, `ui.read`, `ui.admin`, `authority:*`
|
||||
- **Redirect URIs** (defaults): `https://console.stella-ops.local/oidc/callback`
|
||||
- **Post-logout redirect**: `https://console.stella-ops.local/`
|
||||
- **Tokens**: Access tokens inherit the global 2 minute lifetime; refresh tokens remain short-lived (30 days) and can be exchanged silently via `/token`.
|
||||
- **Roles**: Assign Authority role `Orch.Viewer` (exposed to tenants as `role/orch-viewer`) when operators need read-only access to Orchestrator telemetry via Console dashboards. Policy Studio ships dedicated roles (`role/policy-author`, `role/policy-reviewer`, `role/policy-approver`, `role/policy-operator`, `role/policy-auditor`) plus the new attestation verbs (`policy:publish`, `policy:promote`) that align with the `policy:*` scope family; issue them per tenant so audit trails remain scoped and interactive attestations stay attributable.
|
||||
- **Role bundles**: Module role bundles (Console, Scanner, Scheduler, Policy, Graph, Observability, etc.) are cataloged in `docs/technical/architecture/console-admin-rbac.md` and should be seeded into Authority to keep UI and CLI defaults consistent.
|
||||
|
||||
Configuration sample (`etc/authority.yaml.sample`) seeds the client with a confidential secret so Console can negotiate the code exchange on the backend while browsers execute the PKCE dance.
|
||||
|
||||
### Policy Studio scopes & signing workflow
|
||||
|
||||
- **Role bundles:** Issue the dedicated Policy Studio roles per tenant (`role/policy-author`, `role/policy-reviewer`, `role/policy-approver`, `role/policy-operator`, `role/policy-auditor`). Each maps to the `policy:*` scopes described in [Policy Lifecycle & Approvals](policy/lifecycle.md#2-roles--authority-scopes).
|
||||
- **Publish/promote scopes:** `policy:publish` and `policy:promote` are interactive-only. Authority rejects client-credential tokens; operators must log in via `stella auth login` (DPoP) and stay within the five-minute fresh-auth window.
|
||||
- **Required metadata:** Publishing attaches `policy_reason`, `policy_ticket`, and `policy_digest` headers. The CLI surface (`stella policy publish --reason --ticket --sign`) maps flags to these fields automatically. Missing metadata returns `422 policy_attestation_metadata_missing`.
|
||||
- **Attestations:** `stella policy publish --sign` produces a DSSE envelope stored in Policy Engine (`policy_attestations`) and on disk for Offline Kit evidence. Promotions (`stella policy promote --environment prod`) emit `policy.promoted` audit events referencing the attestation digest.
|
||||
- **Compliance checklist:** Before activation, verify each item in [§10 Compliance Checklist](policy/lifecycle.md#10--compliance-checklist) — role mapping, simulation evidence, approval note, attestation signature, promotion note, activation health, offline parity.
|
||||
|
||||
### Advisory AI scopes & remote inference
|
||||
|
||||
- `advisory-ai:view` — read Advisory AI artefacts (summaries, remediation packs, cached outputs).
|
||||
- `advisory-ai:operate` — submit Advisory AI inference jobs and remediation requests.
|
||||
- `advisory-ai:admin` — administer Advisory AI configuration, profile selection, and remote execution controls.
|
||||
|
||||
Authority publishes the trio in OpenID discovery (`stellaops_advisory_ai_scopes_supported`) so clients can self-discover capability. Remote/cloud inference is disabled by default; set `advisoryAi.remoteInference.enabled: true` and provide an explicit `allowedProfiles` whitelist (for example `cloud-openai`) when an installation opts in. The `requireTenantConsent` toggle (default `true`) enforces per-tenant opt-in before remote profiles are invoked, mirroring regulatory expectations for sovereign or air-gapped deployments.
|
||||
|
||||
### Console Authority endpoints
|
||||
|
||||
- `/console/tenants` - Requires `authority:tenants.read`; returns the tenant catalogue for the authenticated principal. Requests lacking the `X-Stella-Tenant` header are rejected (`tenant_header_missing`) and logged.
|
||||
- `/console/profile` - Requires `ui.read`; exposes subject metadata (roles, scopes, audiences) and indicates whether the session is within the five-minute fresh-auth window.
|
||||
- `/console/token/introspect` - Requires `ui.read`; introspects the active access token so the SPA can prompt for re-authentication before privileged actions.
|
||||
- `/console/admin/*` - Requires `ui.admin` plus the relevant `authority:*` scope. Used by Console Admin for tenant, user, role, client, token, audit, and branding workflows.
|
||||
|
||||
All endpoints demand DPoP-bound tokens and propagate structured audit events (`authority.console.*`). Gateways must forward the `X-Stella-Tenant` header derived from the access token; downstream services rely on the same value for isolation. Keep Console access tokens short-lived (default 15 minutes) and enforce the fresh-auth window for admin actions (`ui.admin`, `authority:*`, `policy:activate`, `exceptions:approve`).
|
||||
- `status` (`valid`, `revoked`, `expired`), `createdAt`, optional `expiresAt`
|
||||
- `revokedAt`, machine-readable `revokedReason`, optional `revokedReasonDescription`
|
||||
- `revokedMetadata` (string dictionary for plugin-specific context)
|
||||
- **Persistence flow:** `PersistTokensHandler` stamps missing JWT IDs, normalises scopes, and stores every principal emitted by OpenIddict.
|
||||
- **Revocation flow:** `AuthorityTokenStore.UpdateStatusAsync` flips status, records the reason metadata, and is invoked by token revocation handlers and plugin provisioning events (e.g., disabling a user).
|
||||
- **Expiry maintenance:** `AuthorityTokenStore.DeleteExpiredAsync` prunes non-revoked tokens past their `expiresAt` timestamp. Operators should schedule this in maintenance windows if large volumes of tokens are issued.
|
||||
|
||||
### Expectations for resource servers
|
||||
Resource servers (Concelier WebService, Backend, Agent) **must not** assume in-memory caches are authoritative. They should:
|
||||
|
||||
- cache `/jwks` and `/revocations/export` responses within configured lifetimes;
|
||||
- honour `revokedReason` metadata when shaping audit trails;
|
||||
- treat `status != "valid"` or missing tokens as immediate denial conditions.
|
||||
- propagate the `tenant` claim (`X-Stella-Tenant` header in REST calls) and reject requests when the tenant supplied by Authority does not match the resource server's scope; Concelier and Excititor guard endpoints refuse cross-tenant tokens.
|
||||
|
||||
### Tenant propagation
|
||||
|
||||
- Client provisioning (bootstrap or plug-in) accepts a `tenant` hint. Authority normalises the value (`trim().ToLowerInvariant()`) and persists it alongside the registration. Clients without an explicit tenant remain global.
|
||||
- Issued principals include the `stellaops:tenant` claim. `PersistTokensHandler` mirrors this claim into `authority_tokens.tenant`, enabling per-tenant revocation and reporting.
|
||||
- Rate limiter metadata now tags requests with `authority.tenant`, unlocking per-tenant throughput metrics and diagnostic filters. Audit events (`authority.client_credentials.grant`, `authority.password.grant`, bootstrap flows) surface the tenant and login attempt documents index on `{tenant, occurredAt}` for quick queries.
|
||||
- Client credentials that request `advisory:ingest`, `advisory:read`, `advisory-ai:view`, `advisory-ai:operate`, `advisory-ai:admin`, `vex:ingest`, `vex:read`, `signals:read`, `signals:write`, `signals:admin`, or `aoc:verify` now fail fast when the client registration lacks a tenant hint. Issued tokens are re-validated against persisted tenant metadata, and Authority rejects any cross-tenant replay (`invalid_client`/`invalid_token`), ensuring aggregation-only workloads remain tenant-scoped.
|
||||
- Client credentials that request `export.viewer`, `export.operator`, or `export.admin` must provide a tenant hint. Requests for `export.admin` also need accompanying `export_reason` and `export_ticket` parameters; Authority returns `invalid_request` when either value is missing and records the denial in token audit events.
|
||||
- Client credentials that request `notify.viewer`, `notify.operator`, or `notify.admin` must provide a tenant hint. Authority records scope violations when tenancy is missing and emits `authority.notify.scope_violation` audit metadata so operators can trace denied requests.
|
||||
- Policy Studio scopes (`policy:author`, `policy:review`, `policy:approve`, `policy:operate`, `policy:publish`, `policy:promote`, `policy:audit`, `policy:simulate`, `policy:run`, `policy:activate`) require a tenant assignment; Authority rejects tokens missing the hint with `invalid_client` and records `scope.invalid` metadata for auditing. The `policy:publish`/`policy:promote` scopes are interactive-only and demand additional metadata (see “Policy attestation metadata” below).
|
||||
- Policy attestation tokens must include three parameters: `policy_reason` (≤512 chars describing why the attestation is being produced), `policy_ticket` (≤128 chars change/request reference), and `policy_digest` (32–128 char hex digest of the policy package). Authority rejects requests missing any value, over the limits, or providing a non-hex digest. Password-grant issuance stamps these values into the resulting token/audit trail and enforces a five-minute fresh-auth window via the `auth_time` claim.
|
||||
- Task Pack scopes (`packs.read`, `packs.write`, `packs.run`, `packs.approve`) require a tenant assignment; Authority rejects tokens missing the hint with `invalid_client` and logs `authority.pack_scope_violation` metadata for audit correlation.
|
||||
- `packs.approve` tokens must include `pack_run_id`, `pack_gate_id`, `pack_plan_hash`, and an `auth_time` within five minutes. `/token` enforces the metadata, and the resource-server scope handler double-checks freshness before allowing approvals (see `docs/modules/packs-registry/guides/runbook.md#4-approvals-workflow`). Missing metadata or stale authentication produces deterministic audit telemetry tagged with `pack.*` properties.
|
||||
- **AOC pairing guardrails** – Tokens that request `advisory:read`, `advisory-ai:view`, `advisory-ai:operate`, `advisory-ai:admin`, `vex:read`, or any `signals:*` scope must also request `aoc:verify`. Authority rejects mismatches with `invalid_scope` (e.g., `Scope 'aoc:verify' is required when requesting advisory/advisory-ai/vex read scopes.` or `Scope 'aoc:verify' is required when requesting signals scopes.`) so automation surfaces deterministic errors.
|
||||
- **Signals ingestion guardrails** – Sensors and services requesting `signals:write`/`signals:admin` must also request `aoc:verify`; Authority records the `authority.aoc_scope_violation` tag when the pairing is missing so operators can trace failing sensors immediately.
|
||||
- Password grant flows reuse the client registration's tenant and enforce the configured scope allow-list. Requested scopes outside that list (or mismatched tenants) trigger `invalid_scope`/`invalid_client` failures, ensuring cross-tenant access is denied before token issuance.
|
||||
|
||||
### Default service scopes
|
||||
|
||||
| Client ID | Purpose | Scopes granted | Sender constraint | Tenant |
|
||||
|----------------------|---------------------------------------|--------------------------------------|-------------------|-----------------|
|
||||
| `concelier-ingest` | Concelier raw advisory ingestion | `advisory:ingest`, `advisory:read` | `dpop` | `tenant-default` |
|
||||
| `excitor-ingest` | Excititor raw VEX ingestion | `vex:ingest`, `vex:read` | `dpop` | `tenant-default` |
|
||||
| `aoc-verifier` | Aggregation-only contract verification | `aoc:verify`, `advisory:read`, `vex:read` | `dpop` | `tenant-default` |
|
||||
| `cartographer-service` | Graph snapshot construction | `graph:write`, `graph:read` | `dpop` | `tenant-default` |
|
||||
| `graph-api` | Graph Explorer gateway/API | `graph:read`, `graph:export`, `graph:simulate` | `dpop` | `tenant-default` |
|
||||
| `export-center-operator` | Export Center operator automation | `export.viewer`, `export.operator` | `dpop` | `tenant-default` |
|
||||
| `export-center-admin` | Export Center administrative automation | `export.viewer`, `export.operator`, `export.admin` | `dpop` | `tenant-default` |
|
||||
| `notify-service` | Notify WebService API | `notify.viewer`, `notify.operator` | `dpop` | `tenant-default` |
|
||||
| `notify-admin` | Notify administrative automation | `notify.viewer`, `notify.operator`, `notify.admin` | `dpop` | `tenant-default` |
|
||||
| `vuln-explorer-ui` | Vuln Explorer UI/API | `vuln:view`, `vuln:investigate`, `vuln:operate`, `vuln:audit` | `dpop` | `tenant-default` |
|
||||
| `signals-uploader` | Reachability sensor ingestion | `signals:write`, `signals:read`, `aoc:verify` | `dpop` | `tenant-default` |
|
||||
|
||||
> **Secret hygiene (2025‑10‑27):** The repository includes a convenience `etc/authority.yaml` for compose/helm smoke tests. Every entry’s `secretFile` points to `etc/secrets/*.secret`, which ship with `*-change-me` placeholders—replace them with strong values (and wire them through your vault/secret manager) before issuing tokens in CI, staging, or production.
|
||||
|
||||
For factory provisioning, issue sensors the **SignalsUploader** role template (`signals:write`, `signals:read`, `aoc:verify`). Authority rejects ingestion tokens that omit `aoc:verify`, preserving aggregation-only contract guarantees for reachability signals.
|
||||
|
||||
These registrations are provided as examples in `etc/authority.yaml.sample`. Clone them per tenant (for example `concelier-tenant-a`, `concelier-tenant-b`) so tokens remain tenant-scoped by construction.
|
||||
|
||||
### Policy attestation metadata
|
||||
|
||||
- **Interactive only.** `policy:publish` and `policy:promote` are restricted to password/device-code flows (Console, CLI) and are rejected when requested via client credentials or app secrets. Tokens inherit the 5-minute fresh-auth window; resource servers reject stale tokens and emit `authority.policy_attestation_validated=false`.
|
||||
- **Mandatory parameters.** Requests must include:
|
||||
- Authority enforces mTLS bindings on /token, /fresh-auth, and /introspect by comparing the presented TLS client certificate thumbprint against the stored claim. Requests missing a certificate or presenting a different certificate are rejected with , and the counter is incremented for operational alerts.
|
||||
- `policy_ticket` (≤128 chars) — change request / CAB identifier (e.g., `CR-2025-1102`).
|
||||
- `policy_digest` — lowercase hex digest (32–128 characters) of the policy bundle being published/promoted.
|
||||
- **Audit surfaces.** On success, the metadata is copied into the access token (`stellaops:policy_reason`, `stellaops:policy_ticket`, `stellaops:policy_digest`, `stellaops:policy_operation`) and recorded in [`authority.password.grant`] audit events as `policy.*` properties.
|
||||
- **Failure modes.** Missing/blank parameters, over-length values, or non-hex digests trigger `invalid_request` responses and `authority.policy_attestation_denied` audit tags. CLI/Console must bubble these errors to operators and provide retry UX.
|
||||
- **CLI / Console UX.** The CLI stores attestation metadata in `stella.toml` (`authority.policy.publishReason`, `authority.policy.publishTicket`) or accepts `STELLA_POLICY_REASON` / `STELLA_POLICY_TICKET` / `STELLA_POLICY_DIGEST` environment variables. Console prompts operators for the same trio before issuing attestation tokens and refuses to cache values longer than the session.
|
||||
- **Automation guidance.** CI workflows should compute the policy digest ahead of time (for example `sha256sum policy-package.tgz | cut -d' ' -f1`) and inject the reason/ticket/digest into CLI environment variables immediately before invoking `stella auth login` (using a profile configured to request `policy:publish`).
|
||||
|
||||
Graph Explorer introduces dedicated scopes: `graph:write` for Cartographer build jobs, `graph:read` for query/read operations, `graph:export` for long-running export downloads, and `graph:simulate` for what-if overlays. Assign only the scopes a client actually needs to preserve least privilege—UI-facing clients should typically request read/export access, while background services (Cartographer, Scheduler) require write privileges.
|
||||
|
||||
### Policy activation dual-control
|
||||
|
||||
- **Config knobs.** `PolicyEngine.activation.forceTwoPersonApproval` forces every activation to collect two distinct `policy:activate` approvals (first response = `202 pending_second_approval`). `PolicyEngine.activation.defaultRequiresTwoPersonApproval` sets the default when callers omit the flag.
|
||||
- **Operator choice.** When force is disabled, Console/CLI can opt any revision into dual-control by setting `requiresTwoPersonApproval: true`; the service persists the requirement alongside the revision metadata.
|
||||
- **Audit coverage.** With `PolicyEngine.activation.emitAuditLogs` (default `true`), every activation emits structured `policy.activation.*` logger scopes (pack id, revision, actor(s), tenant, approval count, comment) so SOC pipelines can diff the two-person trail.
|
||||
- **Status codes.** First approval on a dual-control revision returns `202 pending_second_approval`; duplicates produce `400 duplicate_approval`; the second distinct actor returns the usual `200 activated`.
|
||||
|
||||
|
||||
#### Least-privilege guidance for graph clients
|
||||
|
||||
- **Service identities** – The Cartographer worker should request `graph:write` and `graph:read` only; grant `graph:simulate` exclusively to pipeline automation that invokes Policy Engine overlays on demand. Keep `graph:export` scoped to API gateway components responsible for streaming GraphML/JSONL artifacts. Authority enforces this by rejecting `graph:write` tokens that lack `properties.serviceIdentity: cartographer`.
|
||||
- **Tenant propagation** – Every client registration must pin a `tenant` hint. Authority normalises the value and stamps it into issued tokens (`stellaops:tenant`) so downstream services (Scheduler, Graph API, Console) can enforce tenant isolation without custom headers. Graph scopes (`graph:read`, `graph:write`, `graph:export`, `graph:simulate`) are denied if the tenant hint is missing.
|
||||
- **SDK alignment** – Use the generated `StellaOpsScopes` constants in service code to request graph scopes. Hard-coded strings risk falling out of sync as additional graph capabilities are added.
|
||||
- **DPOP for automation** – Maintain sender-constrained (`dpop`) flows for Cartographer and Scheduler to limit reuse of access tokens if a build host is compromised. For UI-facing tokens, pair `graph:read`/`graph:export` with short lifetimes and enforce refresh-token rotation at the gateway.
|
||||
|
||||
#### Export Center scope guardrails
|
||||
|
||||
- **Viewer vs operator** – `export.viewer` grants read-only access to export profiles, manifests, and bundles. Automation that schedules or reruns exports should request `export.operator` (and typically `export.viewer`). Tenant hints remain mandatory; Authority refuses tokens without them.
|
||||
- **Administrative mutations** – Changes to retention policies, encryption key references, or schedule defaults require `export.admin`. When requesting tokens with this scope, clients must supply `export_reason` and `export_ticket` parameters; Authority persists the values for audit records and rejects missing metadata with `invalid_request`.
|
||||
- **Operational hygiene** – Rotate `export.admin` credentials infrequently and run them through fresh-auth workflows where possible. Prefer distributing verification tooling with `export.viewer` tokens for day-to-day bundle validation.
|
||||
|
||||
#### Notify scope guardrails
|
||||
|
||||
- **Viewer vs operator** – `notify.viewer` grants read-only access to rules, channels, and delivery history. Automation that edits rules or triggers test notifications must request `notify.operator` (and usually `notify.viewer`). Tenant hints remain mandatory.
|
||||
- **Administrative controls** – Changes to channel secrets, quiet hours, or escalation policies require `notify.admin`. Authority logs these operations and surfaces `authority.notify.scope_violation` when tokens omit the scope or tenant.
|
||||
- **Least privilege** – Assign `notify.admin` sparingly (platform operations, DR automation). Day-to-day rule editing should rely on `notify.operator` scoped per tenant.
|
||||
|
||||
#### Vuln Explorer scopes, ABAC, and permalinks
|
||||
|
||||
- **Scopes** – `vuln:view` unlocks read-only access and permalink issuance, `vuln:investigate` allows triage actions (assignment, comments, remediation notes), `vuln:operate` unlocks state transitions and workflow execution, and `vuln:audit` exposes immutable ledgers/exports. The legacy `vuln:read` scope is still emitted for backward compatibility but new clients should request the granular scopes.
|
||||
- **ABAC attributes** – Tenant roles can project attribute filters (`env`, `owner`, `business_tier`) via the `attributes` block in `authority.yaml` (see the sample `role/vuln-*` definitions). Authority now enforces the same filters on token issuance: client-credential requests must supply `vuln_env`, `vuln_owner`, and `vuln_business_tier` parameters when multiple values are configured, and the values must match the configured allow-list (or `*`). The accepted value pattern is `[a-z0-9:_-]{1,128}`. Issued tokens embed the resolved filters as `stellaops:vuln_env`, `stellaops:vuln_owner`, and `stellaops:vuln_business_tier` claims, and Authority persists the resulting actor chain plus service-account metadata in PostgreSQL for auditability.
|
||||
- **Service accounts** – Delegated Vuln Explorer identities (`svc-vuln-*`) should include the attribute filters in their seed definition. Authority enforces the supplied `attributes` during issuance and stores the selected values on the delegation token, making downstream revocation/audit exports aware of the effective ABAC envelope.
|
||||
- **Attachment tokens** – Evidence downloads require scoped tokens issued by Authority. `POST /vuln/attachments/tokens/issue` accepts ledger hashes plus optional metadata, signs the response with the primary Authority key, and records audit trails (`vuln.attachment.token.*`). `POST /vuln/attachments/tokens/verify` validates incoming tokens server-side. See “Attachment signing tokens” below.
|
||||
- **Token request parameters** – Minimum metadata for Vuln Explorer service accounts:
|
||||
- `service_account`: requested service-account id (still required).
|
||||
- `vuln_env`: single value or `*` (required when multiple environments are configured).
|
||||
- `vuln_owner`: owning team/code; optional only when the service account allows a single value.
|
||||
- `vuln_business_tier`: business criticality tier (`tier-1`, `tier-2`, etc.).
|
||||
Authority rejects missing parameters with `invalid_request` and records the violation via `authority.vuln_attr_*` audit properties.
|
||||
- **Signed links** – `POST /permalinks/vuln` requires `vuln:view`. The resulting JWT carries `vuln:view` plus the transitional `vuln:read` scope to preserve older consumers. Validation remains unchanged: verify the signature against `/jwks`, confirm tenant alignment, honour expiry, and enforce the scopes before honouring the permalink payload.
|
||||
|
||||
##### Attachment signing tokens
|
||||
|
||||
- **Issuance.** `POST /vuln/attachments/tokens/issue` (scope `vuln:investigate`) accepts an attachment identifier, the authoritative ledger hash, and optional metadata map (`metadata[]`). Authority returns a DSSE-style payload signed with the primary EdDSA key, capped by the configured TTL (`authority.vulnerabilityExplorer.attachments.defaultLifetime`, default 30 minutes).
|
||||
- **Verification.** Downstream services call `POST /vuln/attachments/tokens/verify` before honouring downloads. The endpoint enforces tenant, scope, ABAC attributes, TTL, and ledger hash matching, and emits `vuln.attachment.token.verify` audit events with the resolved metadata.
|
||||
- **Audit trail.** Every issuance logs `vuln.attachment.token.issue` with `delegation.service_account`, `ledger.hash`, and `attachment.id` properties so Offline Kit operators can reconcile evidence access. Tokens also embed the actor chain (`act`) so consuming services can trace automation pipelines.
|
||||
- **Example.**
|
||||
|
||||
```bash
|
||||
curl -u vuln-explorer-worker:s3cr3t \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"attachmentId": "finding-7d9d/evidence-2",
|
||||
"ledgerHash": "sha256:4a5160...",
|
||||
"metadata": { "download": "supporting-log.zip" }
|
||||
}' \
|
||||
https://authority.example.com/vuln/attachments/tokens/issue
|
||||
```
|
||||
|
||||
##### Ledger verification workflow
|
||||
|
||||
1. Resolve the attachment’s ledger entry (`finding_history`, `triage_actions`) and note the recorded hash/signature.
|
||||
2. Verify the issued attachment token via `/vuln/attachments/tokens/verify`; the response echoes the canonical hash and expiry.
|
||||
3. When downloading artefacts from Vuln Explorer, recompute the hash locally and compare it to both the ledger entry and the verified token payload.
|
||||
4. Cross-check Authority audit events (`vuln.attachment.token.*`) to confirm who issued and consumed the token; Offline Kit mirrors include the same audit feed.
|
||||
|
||||
##### Vuln Explorer security checklist
|
||||
|
||||
- [ ] Map tenant roles to the granular `vuln:*` scopes and ABAC filters in `etc/authority.yaml.sample`.
|
||||
- [ ] Require `vuln_env`, `vuln_owner`, and `vuln_business_tier` parameters for every delegated service-account request.
|
||||
- [ ] Exercise `/vuln/attachments/tokens/issue` and `/vuln/attachments/tokens/verify` in CI to confirm attachment signing is enforced.
|
||||
- [ ] Mirror Authority audit events (`vuln.attachment.token.*`, `authority.vuln_attr.*`) into your SOC pipeline.
|
||||
- [ ] Update Offline Kit runbooks so operators verify attachment hashes against both ledger entries and Authority-issued tokens before distribution.
|
||||
|
||||
|
||||
## 4. Revocation Pipeline
|
||||
Authority centralises revocation in `authority_revocations` with deterministic categories:
|
||||
|
||||
| Category | Meaning | Required fields |
|
||||
| --- | --- | --- |
|
||||
| `token` | Specific OAuth token revoked early. | `revocationId` (token id), `tokenType`, optional `clientId`, `subjectId` |
|
||||
| `subject` | All tokens for a subject disabled. | `revocationId` (= subject id) |
|
||||
| `client` | OAuth client registration revoked. | `revocationId` (= client id) |
|
||||
| `key` | Signing/JWE key withdrawn. | `revocationId` (= key id) |
|
||||
|
||||
`RevocationBundleBuilder` flattens PostgreSQL records into canonical JSON, sorts entries by (`category`, `revocationId`, `revokedAt`), and signs exports using detached JWS (RFC 7797) with cosign-compatible headers.
|
||||
|
||||
**Export surfaces** (deterministic output, suitable for Offline Kit):
|
||||
|
||||
- CLI: `stella auth revoke export --output ./out` writes `revocation-bundle.json`, `.jws`, `.sha256`.
|
||||
- Verification: `stella auth revoke verify --bundle <path> --signature <path> --key <path>` validates detached JWS signatures before distribution, selecting the crypto provider advertised in the detached header (see `docs/security/revocation-bundle.md`).
|
||||
- API: `GET /internal/revocations/export` (requires bootstrap API key) returns the same payload.
|
||||
- Verification: `stella auth revoke verify` validates schema, digest, and detached JWS using cached JWKS or offline keys, automatically preferring the hinted provider (libsodium builds honour `provider=libsodium`; other builds fall back to the managed provider).
|
||||
|
||||
**Consumer guidance:**
|
||||
|
||||
1. Mirror `revocation-bundle.json*` alongside Concelier exports. Offline agents fetch both over the existing update channel.
|
||||
2. Use bundle `sequence` and `bundleId` to detect replay or monotonicity regressions. Ignore bundles with older sequence numbers unless `bundleId` changes and `issuedAt` advances.
|
||||
3. Treat `revokedReason` taxonomy as machine-friendly codes (`compromised`, `rotation`, `policy`, `lifecycle`). Translating to human-readable logs is the consumer’s responsibility.
|
||||
|
||||
## 5. Signing Keys & JWKS Rotation
|
||||
Authority signs revocation bundles and publishes JWKS entries via the new signing manager:
|
||||
|
||||
- **Configuration (`authority.yaml`):**
|
||||
```yaml
|
||||
signing:
|
||||
enabled: true
|
||||
algorithm: ES256 # Defaults to ES256
|
||||
keySource: file # Loader identifier (file, vault, etc.)
|
||||
provider: default # Optional preferred crypto provider
|
||||
activeKeyId: authority-signing-dev
|
||||
keyPath: "../certificates/authority-signing-dev.pem"
|
||||
additionalKeys:
|
||||
- keyId: authority-signing-dev-2024
|
||||
path: "../certificates/authority-signing-dev-2024.pem"
|
||||
source: "file"
|
||||
```
|
||||
- **Sources:** The default loader supports PEM files relative to the content root; additional loaders can be registered via `IAuthoritySigningKeySource`.
|
||||
- **Providers:** Keys are registered against the `ICryptoProviderRegistry`, so alternative implementations (HSM, libsodium) can be plugged in without changing host code.
|
||||
- **OpenAPI discovery:** `GET /.well-known/openapi` returns the published authentication contract (JSON by default, YAML when requested). Responses include `X-StellaOps-Service`, `X-StellaOps-Api-Version`, `X-StellaOps-Build-Version`, plus grant and scope headers, and honour conditional requests via `ETag`/`If-None-Match`.
|
||||
- **JWKS output:** `GET /jwks` lists every signing key with `status` metadata (`active`, `retired`). Old keys remain until operators remove them from configuration, allowing verification of historical bundles/tokens.
|
||||
|
||||
### Rotation SOP (no downtime)
|
||||
1. Generate a new P-256 private key (PEM) on an offline workstation and place it where the Authority host can read it (e.g., `../certificates/authority-signing-2025.pem`).
|
||||
2. Call the authenticated admin API:
|
||||
```bash
|
||||
curl -sS -X POST https://authority.example.com/internal/signing/rotate \
|
||||
-H "x-stellaops-bootstrap-key: ${BOOTSTRAP_KEY}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"keyId": "authority-signing-2025",
|
||||
"location": "../certificates/authority-signing-2025.pem",
|
||||
"source": "file"
|
||||
}'
|
||||
```
|
||||
3. Verify the response reports the previous key as retired and fetch `/jwks` to confirm the new `kid` appears with `status: "active"`.
|
||||
4. Persist the old key path in `signing.additionalKeys` (the rotation API updates in-memory options; rewrite the YAML to match so restarts remain consistent).
|
||||
5. If you prefer automation, trigger the `.gitea/workflows/authority-key-rotation.yml` workflow with the new `keyId`/`keyPath`; it wraps `ops/authority/key-rotation.sh` and reads environment-specific secrets. The older key will be marked `retired` and appended to `signing.additionalKeys`.
|
||||
6. Re-run `stella auth revoke export` so revocation bundles are signed with the new key. Downstream caches should refresh JWKS within their configured lifetime (`StellaOpsAuthorityOptions.Signing` + client cache tolerance).
|
||||
|
||||
The rotation API leverages the same cryptography abstractions as revocation signing; no restart is required and the previous key is marked `retired` but kept available for verification.
|
||||
|
||||
## 6. Bootstrap & Administrative Endpoints
|
||||
Administrative APIs live under `/internal/*` and require the bootstrap API key plus rate-limiter compliance.
|
||||
|
||||
| Endpoint | Method | Description |
|
||||
| --- | --- | --- |
|
||||
| `/internal/users` | `POST` | Provision initial administrative accounts through the registered password-capable plug-in. Emits structured audit events. |
|
||||
| `/internal/clients` | `POST` | Provision OAuth clients (client credentials / device code). |
|
||||
| `/internal/revocations/export` | `GET` | Export revocation bundle + detached JWS + digest. |
|
||||
| `/internal/signing/rotate` | `POST` | Promote a new signing key (see SOP above). Request body accepts `keyId`, `location`, optional `source`, `algorithm`, `provider`, and metadata. |
|
||||
|
||||
All administrative calls emit `AuthEventRecord` entries enriched with correlation IDs, PII tags, and network metadata for offline SOC ingestion.
|
||||
|
||||
> **Tenant hint:** include a `tenant` entry inside `properties` when bootstrapping clients. Authority normalises the value, stores it on the registration, and stamps future tokens/audit events with the tenant.
|
||||
|
||||
### Bootstrap client example
|
||||
|
||||
```jsonc
|
||||
POST /internal/clients
|
||||
{
|
||||
"clientId": "concelier",
|
||||
"confidential": true,
|
||||
"displayName": "Concelier Backend",
|
||||
"allowedGrantTypes": ["client_credentials"],
|
||||
"allowedScopes": ["concelier.jobs.trigger", "advisory:ingest", "advisory:read"],
|
||||
"properties": {
|
||||
"tenant": "tenant-default"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
For environments with multiple tenants, repeat the call per tenant-specific client (e.g. `concelier-tenant-a`, `concelier-tenant-b`) or append suffixes to the client identifier.
|
||||
|
||||
### Aggregation-only verification tokens
|
||||
|
||||
- Issue a dedicated client (e.g. `aoc-verifier`) with the scopes `aoc:verify`, `advisory:read`, and `vex:read` for each tenant that runs guard checks. Authority refuses to mint tokens for these scopes unless the client registration provides a tenant hint.
|
||||
- The CLI (`stella aoc verify --tenant <tenant>`) and Console verification panel both call `/aoc/verify` on Concelier and Excititor. Tokens that omit the tenant claim or present a tenant that does not match the stored registration are rejected with `invalid_client`/`invalid_token`.
|
||||
- Audit: `authority.client_credentials.grant` entries record `scope.invalid="aoc:verify"` when requests are rejected because the tenant hint is missing or mismatched.
|
||||
|
||||
### Exception approvals & routing
|
||||
|
||||
- New scopes `exceptions:read`, `exceptions:write`, and `exceptions:approve` govern access to the exception lifecycle. Map these via tenant roles (`exceptions-service`, `exceptions-approver`) as described in `/docs/security/authority-scopes.md`.
|
||||
- Configure approval routing in `authority.yaml` with declarative templates. Each template exposes an `authorityRouteId` for downstream services (Policy Engine, Console) and an optional `requireMfa` flag:
|
||||
|
||||
```yaml
|
||||
exceptions:
|
||||
routingTemplates:
|
||||
- id: "secops"
|
||||
authorityRouteId: "approvals/secops"
|
||||
requireMfa: true
|
||||
description: "Security Operations approval chain"
|
||||
- id: "governance"
|
||||
authorityRouteId: "approvals/governance"
|
||||
requireMfa: false
|
||||
description: "Non-production waiver review"
|
||||
```
|
||||
|
||||
- Clients requesting exception scopes must include a tenant assignment. Authority rejects client-credential flows that request `exceptions:*` with `invalid_client` and logs `scope.invalid="exceptions:write"` (or the requested scope) in `authority.client_credentials.grant` audit events when the tenant hint is missing.
|
||||
- When any configured routing template sets `requireMfa: true`, user-facing tokens that contain `exceptions:approve` must be acquired through an MFA-capable identity provider. Password/OIDC flows that lack MFA support are rejected with `authority.password.grant` audit events where `reason="Exception approval scope requires an MFA-capable identity provider."`
|
||||
- Update interactive clients (Console) to request `exceptions:read` by default and elevate to `exceptions:approve` only inside fresh-auth workflows for approvers. Documented examples live in `etc/authority.yaml.sample`.
|
||||
- Verification responses map guard failures to `ERR_AOC_00x` codes and Authority emits `authority.client_credentials.grant` + `authority.token.validate_access` audit records containing the tenant and scopes so operators can trace who executed a run.
|
||||
- For air-gapped or offline replicas, pre-issue verification tokens per tenant and rotate them alongside ingest credentials; the guard endpoints never mutate data and remain safe to expose through the offline kit schedule.
|
||||
|
||||
### Sealed-mode CI confirmation
|
||||
|
||||
Set `airGap.sealedMode.enforcementEnabled: true` to require sealed-mode evidence before issuing tokens to sensitive clients. The guard expects the DevOps harness (`ops/devops/sealed-mode-ci/run-sealed-ci.sh`) to upload `authority-sealed-ci.json` under `artifacts/sealed-mode-ci/<timestamp>/`. Configure Authority with the absolute or relative path to that file plus freshness/health requirements:
|
||||
|
||||
```
|
||||
airGap:
|
||||
sealedMode:
|
||||
enforcementEnabled: true
|
||||
evidencePath: "artifacts/sealed-mode-ci/latest/authority-sealed-ci.json"
|
||||
maxEvidenceAge: "06:00:00"
|
||||
cacheLifetime: "00:01:00"
|
||||
requireAuthorityHealthPass: true
|
||||
requireSignerHealthPass: true
|
||||
requireAttestorHealthPass: true
|
||||
requireEgressProbePass: true
|
||||
```
|
||||
|
||||
Only clients that set `properties.requiresAirgapSealConfirmation: true` (new `AuthorityClientMetadataKeys.RequiresAirGapSealConfirmation`) are gated. When enabled, `/token` rejects those requests with `invalid_client` until:
|
||||
|
||||
1. `timestamp` inside the evidence file is newer than `maxEvidenceAge`.
|
||||
2. `health.authority/signer/attestor.status` are all `pass` (each requirement can be toggled off via the options above).
|
||||
3. `egressProbe.status` equals `pass`, confirming outbound traffic was blocked during the harness run.
|
||||
|
||||
Audit events now include `airgap.sealed=<state>` where `<state>` is `failure:<code>` (for example `failure:evidence_missing`) or `confirmed:<rfc3339 timestamp>`. Token validation spans also emit the `authority.sealed_mode` activity tag with the same value, so dashboards can alarm when evidence goes stale.
|
||||
|
||||
## 7. Configuration Reference
|
||||
|
||||
| Section | Key | Description | Notes |
|
||||
| --- | --- | --- | --- |
|
||||
| Root | `issuer` | Absolute HTTPS issuer advertised to clients. | Required. Loopback HTTP allowed only for development. |
|
||||
| Tokens | `accessTokenLifetime`, `refreshTokenLifetime`, etc. | Lifetimes for each grant (access, refresh, device, authorization code, identity). | Enforced during issuance; persisted on each token document. |
|
||||
| Storage | `storage.connectionString` | PostgreSQL connection string. | Required even for tests; offline kits ship snapshots for seeding. |
|
||||
| Signing | `signing.enabled` | Enable JWKS/revocation signing. | Disable only for development. |
|
||||
| Signing | `signing.algorithm` | Signing algorithm identifier. | Currently ES256; additional curves can be wired through crypto providers. |
|
||||
| Signing | `signing.keySource` | Loader identifier (`file`, `vault`, custom). | Determines which `IAuthoritySigningKeySource` resolves keys. |
|
||||
| Signing | `signing.keyPath` | Relative/absolute path understood by the loader. | Stored as-is; rotation request should keep it in sync with filesystem layout. |
|
||||
| Signing | `signing.activeKeyId` | Active JWKS / revocation signing key id. | Exposed as `kid` in JWKS and bundles. |
|
||||
| Signing | `signing.additionalKeys[].keyId` | Retired key identifier retained for verification. | Manager updates this automatically after rotation; keep YAML aligned. |
|
||||
| Signing | `signing.additionalKeys[].source` | Loader identifier per retired key. | Defaults to `signing.keySource` if omitted. |
|
||||
| Security | `security.rateLimiting` | Fixed-window limits for `/token`, `/authorize`, `/internal/*`. | See `docs/security/rate-limits.md` for tuning. |
|
||||
| Bootstrap | `bootstrap.apiKey` | Shared secret required for `/internal/*`. | Only required when `bootstrap.enabled` is true. |
|
||||
|
||||
### 7.1 Sender-constrained clients (DPoP & mTLS)
|
||||
> Rollout tracker: see [`docs/security/dpop-mtls-rollout.md`](security/dpop-mtls-rollout.md) for phase gates tied to `AUTH-DPOP-11-001` and `AUTH-MTLS-11-002`.
|
||||
|
||||
Authority now understands two flavours of sender-constrained OAuth clients:
|
||||
|
||||
- **DPoP proof-of-possession** – clients sign a `DPoP` header for `/token` requests. Authority validates the JWK thumbprint, HTTP method/URI, and replay window, then stamps the resulting access token with `cnf.jkt` so downstream services can verify the same key is reused.
|
||||
- Validation now covers **every** `/token` grant flow (client credentials, password, device code, refresh). If a client (or the audiences it targets) demands DPoP, missing proofs are rejected and the issued tokens — including interactive ones — carry both the `cnf.jkt` confirmation and the `authority_sender_constraint` claim so downstream services can trust the binding.
|
||||
- Configure under `security.senderConstraints.dpop`. `allowedAlgorithms`, `proofLifetime`, and `replayWindow` are enforced at validation time.
|
||||
- `security.senderConstraints.dpop.allowTemporaryBypass` toggles an emergency-only bypass for sealed drills. When set to `true`, Authority logs `authority.dpop.proof.bypass`, tags `authority.dpop_result=bypass`, and issues tokens without a DPoP `cnf` claim so downstream servers know sender constraints are disabled. **Reset to `false` immediately after the exercise.**
|
||||
- `security.senderConstraints.dpop.nonce.enabled` enables nonce challenges for high-value audiences (`requiredAudiences`, normalised to case-insensitive strings). When a nonce is required but missing or expired, `/token` replies with `WWW-Authenticate: DPoP error="use_dpop_nonce"` (and, when available, a fresh `DPoP-Nonce` header). Clients must retry with the issued nonce embedded in the proof.
|
||||
- Refresh-token requests honour the original sender constraint (DPoP or mTLS). `/token` revalidates the proof/certificate, enforces the recorded thumbprint/JKT, and reuses that metadata so the new access/refresh tokens remain bound to the same key.
|
||||
- `security.senderConstraints.dpop.nonce.store` selects `memory` (default) or `redis`. When `redis` is configured, set `security.senderConstraints.dpop.nonce.redisConnectionString` so replicas share nonce issuance and high-value clients avoid replay gaps during failover.
|
||||
- Telemetry: every nonce challenge increments `authority_dpop_nonce_miss_total{reason=...}` while mTLS mismatches increment `authority_mtls_mismatch_total{reason=...}`.
|
||||
- Example (enabling Redis-backed nonces; adjust audiences per deployment):
|
||||
```yaml
|
||||
security:
|
||||
senderConstraints:
|
||||
dpop:
|
||||
enabled: true
|
||||
proofLifetime: "00:02:00"
|
||||
replayWindow: "00:05:00"
|
||||
allowedAlgorithms: [ "ES256", "ES384" ]
|
||||
nonce:
|
||||
enabled: true
|
||||
ttl: "00:10:00"
|
||||
maxIssuancePerMinute: 120
|
||||
store: "redis" # Uses Valkey (Redis-compatible)
|
||||
redisConnectionString: "valkey:6379"
|
||||
requiredAudiences:
|
||||
- "signer"
|
||||
- "attestor"
|
||||
```
|
||||
Operators can override any field via environment variables (e.g. `STELLAOPS_AUTHORITY__SECURITY__SENDERCONSTRAINTS__DPOP__NONCE__STORE=redis`).
|
||||
- Declare client `audiences` in bootstrap manifests or plug-in provisioning metadata; Authority now defaults the token `aud` claim and `resource` indicator from this list, which is also used to trigger nonce enforcement for audiences such as `signer` and `attestor`.
|
||||
- **Mutual TLS clients** – client registrations may declare an mTLS binding (`senderConstraint: mtls`). When enabled via `security.senderConstraints.mtls`, Authority validates the presented client certificate against stored bindings (`certificateBindings[]`), optional chain verification, and timing windows. Successful requests embed `cnf.x5t#S256` into the access token (and introspection output) so resource servers can enforce the certificate thumbprint.
|
||||
- `security.senderConstraints.mtls.enforceForAudiences` forces mTLS whenever the requested `aud`/`resource` (or the client's configured audiences) intersect the configured allow-list (default includes `signer`). Clients configured for different sender constraints are rejected early so operator policy remains consistent.
|
||||
- Authority enforces mTLS bindings on `/token`, `/fresh-auth`, and `/introspect` by comparing the presented TLS client certificate thumbprint against the stored `authority_sender_certificate_hex` claim. Requests missing a certificate or presenting a different certificate are rejected with `invalid_token`, and the `authority_mtls_mismatch_total{reason=...}` counter is incremented for visibility.
|
||||
- Certificate bindings now act as an allow-list: Authority verifies thumbprint, subject, issuer, serial number, and any declared SAN values against the presented certificate, with rotation grace windows applied to `notBefore/notAfter`. Operators can enforce subject regexes, SAN type allow-lists (`dns`, `uri`, `ip`), trusted certificate authorities, and rotation grace via `security.senderConstraints.mtls.*`.
|
||||
|
||||
Both modes persist additional metadata in `authority_tokens`: `senderConstraint` records the enforced policy, while `senderKeyThumbprint` stores the DPoP JWK thumbprint or mTLS certificate hash captured at issuance. Downstream services can rely on these fields (and the corresponding `cnf` claim) when auditing offline copies of the token store.
|
||||
|
||||
### 7.2 Policy Engine clients & scopes
|
||||
|
||||
Policy Engine v2 introduces dedicated scopes and a service identity that materialises effective findings. Configure Authority as follows when provisioning policy clients:
|
||||
|
||||
| Client | Scopes | Notes |
|
||||
| --- | --- | --- |
|
||||
| `policy-engine` (service) | `policy:run`, `findings:read`, `effective:write` | Must include `properties.serviceIdentity: policy-engine` and a tenant. Authority rejects `effective:write` tokens without the marker or tenant. |
|
||||
| `policy-cli` / automation | `policy:read`, `policy:author`, `policy:review`, `policy:simulate`, `findings:read` *(optionally add `policy:approve` / `policy:operate` / `policy:activate` for promotion pipelines)* | Keep scopes minimal; reroll CLI/CI tokens issued before 2025‑10‑27 so they drop legacy scope names and adopt the new set. |
|
||||
| UI/editor sessions | `policy:read`, `policy:author`, `policy:simulate` (+ reviewer/approver/operator scopes as appropriate) | Issue tenant-specific clients so audit and rate limits remain scoped. |
|
||||
|
||||
Sample YAML entry:
|
||||
|
||||
```yaml
|
||||
- clientId: "policy-engine"
|
||||
displayName: "Policy Engine Service"
|
||||
grantTypes: [ "client_credentials" ]
|
||||
audiences: [ "api://policy-engine" ]
|
||||
scopes: [ "policy:run", "findings:read", "effective:write" ]
|
||||
tenant: "tenant-default"
|
||||
properties:
|
||||
serviceIdentity: "policy-engine"
|
||||
senderConstraint: "dpop"
|
||||
auth:
|
||||
type: "client_secret"
|
||||
secretFile: "../secrets/policy-engine.secret"
|
||||
```
|
||||
|
||||
Compliance checklist:
|
||||
|
||||
- [ ] `policy-engine` client includes `properties.serviceIdentity: policy-engine` and a tenant hint; logins missing either are rejected.
|
||||
- [ ] Non-service clients omit `effective:write` and receive only the scopes required for their role (`policy:read`, `policy:author`, `policy:review`, `policy:approve`, `policy:operate`, `policy:simulate`, etc.).
|
||||
- [ ] Legacy tokens using `policy:write`/`policy:submit`/`policy:edit` are rotated to the new scope set before Production change freeze (see release migration note below).
|
||||
- [ ] Approval/activation workflows use identities distinct from authoring identities; tenants are provisioned per client to keep telemetry segregated.
|
||||
- [ ] Operators document reviewer assignments and incident procedures alongside `/docs/security/policy-governance.md` and archive policy evidence bundles (`stella policy bundle export`) with each release.
|
||||
|
||||
### 7.3 Orchestrator roles & scopes
|
||||
|
||||
| Role / Client | Scopes | Notes |
|
||||
| --- | --- | --- |
|
||||
| `Orch.Viewer` role | `orch:read` | Read-only access to Orchestrator dashboards, queues, and telemetry. |
|
||||
| `Orch.Operator` role | `orch:read`, `orch:operate` | Issue short-lived tokens for control actions (pause/resume, retry, sync). Token requests **must** include `operator_reason` (≤256 chars) and `operator_ticket` (≤128 chars); Authority rejects requests missing either value and records both in audit events. |
|
||||
| `Orch.Admin` role | `orch:read`, `orch:operate`, `orch:quota`, `orch:backfill` | Manage tenant quotas, burst ceilings, and historical backfill allowances. Quota tokens **must** include `quota_reason` (≤256 chars) and may include `quota_ticket` (≤128 chars); backfill tokens **must** include both `backfill_reason` (≤256 chars) and `backfill_ticket` (≤128 chars). Authority records all values in audit trails. |
|
||||
|
||||
Token request example via client credentials:
|
||||
|
||||
```bash
|
||||
curl -u orch-operator:s3cr3t! \
|
||||
-d 'grant_type=client_credentials' \
|
||||
-d 'scope=orch:operate' \
|
||||
-d 'operator_reason=resume source after maintenance' \
|
||||
-d 'operator_ticket=INC-2045' \
|
||||
https://authority.example.com/token
|
||||
```
|
||||
|
||||
Tokens lacking `operator_reason` or `operator_ticket` receive `invalid_request`; audit events (`authority.client_credentials.grant`) surface the supplied values under `request.reason` and `request.ticket` for downstream review.
|
||||
CLI clients set these parameters via `Authority.OperatorReason` / `Authority.OperatorTicket` (environment variables `STELLAOPS_ORCH_REASON` and `STELLAOPS_ORCH_TICKET`).
|
||||
|
||||
Quota administration tokens follow the same pattern:
|
||||
|
||||
```bash
|
||||
curl -u orch-admin:s3cr3t! \
|
||||
-d 'grant_type=client_credentials' \
|
||||
-d 'scope=orch:quota' \
|
||||
-d 'quota_reason=temporary burst for release catch-up' \
|
||||
-d 'quota_ticket=CHG-8821' \
|
||||
https://authority.example.com/token
|
||||
```
|
||||
|
||||
CLI automation should supply these values via `Authority.QuotaReason` / `Authority.QuotaTicket` (environment variables `STELLAOPS_ORCH_QUOTA_REASON` and `STELLAOPS_ORCH_QUOTA_TICKET`). Missing `quota_reason` yields `invalid_request`; when provided, both reason and ticket are captured in audit properties (`quota.reason`, `quota.ticket`).
|
||||
|
||||
Backfill run tokens extend the same pattern:
|
||||
|
||||
```bash
|
||||
curl -u orch-admin:s3cr3t! \
|
||||
-d 'grant_type=client_credentials' \
|
||||
-d 'scope=orch:backfill' \
|
||||
-d 'backfill_reason=rebuild historical findings for tenant-default' \
|
||||
-d 'backfill_ticket=INC-9905' \
|
||||
https://authority.example.com/token
|
||||
```
|
||||
|
||||
CLI clients configure these values via `Authority.BackfillReason` / `Authority.BackfillTicket` (environment variables `STELLAOPS_ORCH_BACKFILL_REASON` and `STELLAOPS_ORCH_BACKFILL_TICKET`). Tokens missing either field are rejected with `invalid_request`; audit events store the supplied values as `backfill.reason` and `backfill.ticket`.
|
||||
|
||||
### 7.4 Delegated service accounts
|
||||
|
||||
StellaOps Authority issues short-lived delegated tokens for service accounts so automation can operate on behalf of a tenant without sharing the underlying client identity.
|
||||
|
||||
**Configuration summary**
|
||||
|
||||
```yaml
|
||||
delegation:
|
||||
quotas:
|
||||
maxActiveTokens: 50
|
||||
serviceAccounts:
|
||||
- accountId: "svc-observer"
|
||||
tenant: "tenant-default"
|
||||
displayName: "Observability Exporter"
|
||||
description: "Delegated identity used by Export Center to read findings."
|
||||
enabled: true
|
||||
allowedScopes: [ "jobs:read", "findings:read" ]
|
||||
authorizedClients: [ "export-center-worker" ]
|
||||
|
||||
tenants:
|
||||
- name: "tenant-default"
|
||||
delegation:
|
||||
maxActiveTokens: 25
|
||||
```
|
||||
|
||||
* `delegation.quotas.maxActiveTokens` caps concurrent delegated tokens per tenant. Authority enforces both a tenant-wide ceiling and a per-account ceiling (the same value by default).
|
||||
* `serviceAccounts[].allowedScopes` lists scopes that the delegate may request. Requests for scopes outside this set return `invalid_scope`.
|
||||
* `serviceAccounts[].authorizedClients` restricts which OAuth clients may assume the delegate. Leave empty to allow any tenant client.
|
||||
* `tenants[].delegation.maxActiveTokens` optionally overrides the quota for a specific tenant.
|
||||
|
||||
**Bootstrap administration**
|
||||
|
||||
Bootstrap operators can inspect and rotate delegated identities via the internal API group:
|
||||
|
||||
```text
|
||||
GET /internal/service-accounts?tenant={tenantId}
|
||||
GET /internal/service-accounts/{accountId}/tokens
|
||||
POST /internal/service-accounts/{accountId}/revocations
|
||||
```
|
||||
|
||||
Requests must include the bootstrap API key header (`X-StellaOps-Bootstrap-Key`). Listing returns the seeded accounts with their configuration; the token listing call shows currently active delegation tokens (status, client, scopes, actor chain) and the revocation endpoint supports bulk or targeted token revocation with audit logging.
|
||||
|
||||
Bootstrap seeding reuses the existing PostgreSQL `id`/`created_at` values. When Authority restarts with updated configuration it upserts rows without mutating immutable fields, avoiding duplicate or conflicting service-account records.
|
||||
|
||||
**Requesting a delegated token**
|
||||
|
||||
```bash
|
||||
curl -u export-center-worker:s3cr3t \
|
||||
-d 'grant_type=client_credentials' \
|
||||
-d 'scope=jobs:read findings:read' \
|
||||
-d 'service_account=svc-observer' \
|
||||
https://authority.example.com/token
|
||||
```
|
||||
|
||||
Optional `delegation_actor` metadata appends an identity to the actor chain:
|
||||
|
||||
```bash
|
||||
-d 'delegation_actor=pipeline://exporter/step/42'
|
||||
```
|
||||
|
||||
**Token shape & observability**
|
||||
|
||||
* Access tokens include `stellaops:service_account` and an `act` claim describing the caller hierarchy (`client_id` ⇒ optional `delegation_actor`).
|
||||
* `authority_tokens` records `tokenKind = "service_account"`, the `serviceAccountId`, and the normalized `actorChain[]`.
|
||||
* Audit events (`authority.client_credentials.grant`) emit `delegation.service_account`, `delegation.actor`, and quota outcomes (`delegation.quota.exceeded = true/false`).
|
||||
* When quota limits are exceeded Authority returns `invalid_request` (`Delegation token quota exceeded for tenant/service account`) and annotates the audit log.
|
||||
|
||||
Delegated tokens still honour scope validation, tenant enforcement, sender constraints (DPoP/mTLS), and fresh-auth checks.
|
||||
|
||||
## 8. Offline & Sovereign Operation
|
||||
- **No outbound dependencies:** Authority only contacts PostgreSQL and local plugins. Discovery and JWKS are cached by clients with offline tolerances (`AllowOfflineCacheFallback`, `OfflineCacheTolerance`). Operators should mirror these responses for air-gapped use.
|
||||
- **Structured logging:** Every revocation export, signing rotation, bootstrap action, and token issuance emits structured logs with `traceId`, `client_id`, `subjectId`, and `network.remoteIp` where applicable. Mirror logs to your SIEM to retain audit trails without central connectivity.
|
||||
- **Determinism:** Sorting rules in token and revocation exports guarantee byte-for-byte identical artefacts given the same datastore state. Hashes and signatures remain stable across machines.
|
||||
|
||||
## 9. Operational Checklist
|
||||
- [ ] Protect the bootstrap API key and disable bootstrap endpoints (`bootstrap.enabled: false`) once initial setup is complete.
|
||||
- [ ] Schedule `stella auth revoke export` (or `/internal/revocations/export`) at the same cadence as Concelier exports so bundles remain in lockstep.
|
||||
- [ ] Rotate signing keys before expiration; keep at least one retired key until all cached bundles/tokens signed with it have expired.
|
||||
- [ ] Monitor `/health` and `/ready` plus rate-limiter metrics to detect plugin outages early.
|
||||
- [ ] Ensure downstream services cache JWKS and revocation bundles within tolerances; stale caches risk accepting revoked tokens.
|
||||
|
||||
For plug-in specific requirements, refer to **[Authority Plug-in Developer Guide](dev/AUTHORITY_PLUGIN_DEVELOPER_GUIDE.md)**. For revocation bundle validation workflow, see **[Authority Revocation Bundle](security/revocation-bundle.md)**.
|
||||
@@ -21,9 +21,9 @@
|
||||
- Recipes must remain compatible with CLI/SDK surface referenced in `docs/modules/cli/guides/` and devportal snippets.
|
||||
|
||||
## Testing lanes and catalog
|
||||
- CI lane filters are defined by `docs/testing/TEST_CATALOG.yml` and aligned with `docs/testing/testing-strategy-models.md`.
|
||||
- CI lane filters are defined by `docs/technical/testing/TEST_CATALOG.yml` and aligned with `docs/technical/testing/testing-strategy-models.md`.
|
||||
- Standard categories: Unit, Contract, Integration, Security, Performance, Live (opt-in only).
|
||||
- Any new test gate or lane must update `docs/19_TEST_SUITE_OVERVIEW.md` and `docs/testing/ci-quality-gates.md`.
|
||||
- Any new test gate or lane must update `docs/technical/testing/TEST_SUITE_OVERVIEW.md` and `docs/technical/testing/ci-quality-gates.md`.
|
||||
|
||||
## Change process
|
||||
- Track active work in `docs/implplan/SPRINT_0315_0001_0001_docs_modules_ci.md` and mirror statuses in `./TASKS.md`.
|
||||
|
||||
@@ -277,7 +277,7 @@ python -m pip install markdown pygments
|
||||
Ajv compiles every event schema to guard against syntax or format regressions. The workflow uses `ajv-formats` for UUID/date-time support.
|
||||
|
||||
```bash
|
||||
for schema in docs/events/*.json; do
|
||||
for schema in docs/modules/signals/events/*.json; do
|
||||
npx ajv compile -c ajv-formats -s "$schema"
|
||||
done
|
||||
```
|
||||
@@ -307,7 +307,7 @@ Policy Engine v2 pipelines now fail fast if policy documents are malformed. Afte
|
||||
dotnet run \
|
||||
--project src/Tools/PolicyDslValidator/PolicyDslValidator.csproj \
|
||||
-- \
|
||||
--strict docs/samples/policy/*.yaml
|
||||
--strict docs/modules/policy/samples/*.yaml
|
||||
```
|
||||
|
||||
- `--strict` treats warnings as errors so missing metadata doesn’t slip through.
|
||||
|
||||
155
docs/modules/cli/cli-vs-ui-parity.md
Normal file
155
docs/modules/cli/cli-vs-ui-parity.md
Normal file
@@ -0,0 +1,155 @@
|
||||
# Console CLI ↔ UI Parity Matrix
|
||||
|
||||
> **Audience:** Docs Guild, Console Guild, CLI Guild, DevOps automation.
|
||||
> **Scope:** Track feature-level parity between the StellaOps Console and the `stella` CLI, surface pending work, and describe the parity CI check owned by CONSOLE-DOC-23-502.
|
||||
|
||||
Status key:
|
||||
|
||||
- **✅ Available** – command exists in `StellaOps.Cli` and is documented.
|
||||
- **🟡 In progress** – command implemented but still under active delivery (task status `DOING`).
|
||||
- **🟩 Planned** – command spec’d but not yet implemented (task `TODO`).
|
||||
- **⚪ UI-only** – no CLI equivalent required.
|
||||
- **🔴 Gap** – CLI feature missing with no active task; file a task before sprint exit.
|
||||
|
||||
---
|
||||
|
||||
## 1 · Navigation & Tenancy
|
||||
|
||||
| UI capability | CLI command(s) | Status | Notes / Tasks |
|
||||
|---------------|----------------|--------|---------------|
|
||||
| Login / token cache status (`/console/profile`) | `stella auth login`, `stella auth status`, `stella auth whoami` | ✅ Available | Command definitions in `CommandFactory.BuildAuthCommand`. |
|
||||
| Fresh-auth challenge for sensitive actions | `stella auth fresh-auth` | ✅ Available | Referenced in `docs/UI_GUIDE.md` (Admin). |
|
||||
| Tenant switcher (UI shell) | `--tenant` flag across CLI commands | ✅ Available | All multi-tenant commands require explicit `--tenant`. |
|
||||
| Tenant creation / suspension | *(pending CLI)* | 🟩 Planned | No `stella auth tenant *` commands yet – track via `CLI-TEN-47-001` (scopes & tenancy). |
|
||||
|
||||
---
|
||||
|
||||
## 2 · Policies & Findings
|
||||
|
||||
| UI capability | CLI command(s) | Status | Notes / Tasks |
|
||||
|---------------|----------------|--------|---------------|
|
||||
| Policy simulation diff, explain | `stella policy simulate` | 🟡 In progress | Implementation present; task `CLI-POLICY-20-002` marked DOING. |
|
||||
| Promote / activate policy | `stella policy promote`, `stella policy activate` | 🟩 Planned | Spec tracked under `CLI-POLICY-23-005`. |
|
||||
| History & explain trees | `stella policy history`, `stella policy explain` | 🟩 Planned | `CLI-POLICY-23-006`. |
|
||||
| Findings explorer export | `stella findings get`, `stella findings export` | 🟩 Planned | Part of `CLI-POLICY-20-003`. |
|
||||
| Explain drawer JSON | `stella policy simulate --format json` | 🟡 In progress | Same command; JSON output flagged for CLI tests. |
|
||||
|
||||
---
|
||||
|
||||
## 3 · Runs & Evidence
|
||||
|
||||
| UI capability | CLI command(s) | Status | Notes / Tasks |
|
||||
|---------------|----------------|--------|---------------|
|
||||
| Run retry / cancel | `stella runs retry`, `stella runs cancel` | 🟩 Planned | Included in export suite task `CLI-EXPORT-35-001`. |
|
||||
| Manual run submit / preview | `stella runs submit`, `stella runs preview` | 🟩 Planned | `CLI-EXPORT-35-001`. |
|
||||
| Evidence bundle export | `stella runs export --run <id> --bundle` | 🟩 Planned | `CLI-EXPORT-35-001`. |
|
||||
| Run status polling | `stella runs status` | 🟩 Planned | Same task. |
|
||||
|
||||
---
|
||||
|
||||
## 4 · Advisories, VEX, SBOM
|
||||
|
||||
| UI capability | CLI command(s) | Status | Notes / Tasks |
|
||||
|---------------|----------------|--------|---------------|
|
||||
| Advisory observations search | `stella vuln observations` | ✅ Available | Implemented via `BuildVulnCommand`. |
|
||||
| Advisory linkset export | `stella advisory linkset show/export` | 🟩 Planned | `CLI-LNM-22-001`. |
|
||||
| VEX observations / linksets | `stella vex obs get/linkset show` | 🟩 Planned | `CLI-LNM-22-002`. |
|
||||
| SBOM overlay export | `stella sbom overlay apply/export` | 🟩 Planned | Scoped to upcoming SBOM CLI sprint (`SBOM-CONSOLE-23-001/002` + CLI backlog). |
|
||||
|
||||
---
|
||||
|
||||
## 5 · Downloads & Offline Kit
|
||||
|
||||
| UI capability | CLI command(s) | Status | Notes / Tasks |
|
||||
|---------------|----------------|--------|---------------|
|
||||
| Manifest lookup (Console Downloads) | `stella downloads manifest show --artifact <id>` | 🟩 Planned | Delivered with `CONSOLE-DOC-23-502` + CLI parity commands. |
|
||||
| Mirror digest to OCI archive | `stella downloads mirror --artifact <id> --to <target>` | 🟩 Planned | Same task bundle (`CONSOLE-DOC-23-502`). |
|
||||
| Console health check | `stella console status --endpoint <url>` | 🟩 Planned | Tracked in `CONSOLE-DOC-23-502`; interim use `curl` as documented. |
|
||||
| Offline kit import/export | `stella offline kit import`, `stella offline kit export` | ✅ Available | Implemented (see `CommandHandlers.HandleOfflineKitImportAsync/HandleOfflineKitPullAsync`). |
|
||||
|
||||
---
|
||||
|
||||
## 6 · Admin & Security
|
||||
|
||||
| UI capability | CLI command(s) | Status | Notes / Tasks |
|
||||
|---------------|----------------|--------|---------------|
|
||||
| Client creation / rotation | `stella auth client create` *(planned)* | 🟩 Planned | Pending tenancy backlog `CLI-TEN-47-001`. |
|
||||
| Token revoke | `stella auth revoke export/verify` | ✅ Available | Already implemented. |
|
||||
| Audit export | `stella auth audit export` | 🟩 Planned | Needs CLI work item (Authority guild). |
|
||||
| Signing key rotation | `stella auth signing rotate` | 🟩 Planned | To be added with AUTH-CONSOLE-23-003 follow-up. |
|
||||
|
||||
---
|
||||
|
||||
## 7 · Telemetry & Observability
|
||||
|
||||
| UI capability | CLI command(s) | Status | Notes / Tasks |
|
||||
|---------------|----------------|--------|---------------|
|
||||
| Telemetry dashboard parity | `stella obs top`, `stella obs trace`, `stella obs logs` | 🟩 Planned | CLI observability epic (`CLI-OBS-51-001`, `CLI-OBS-52-001`). |
|
||||
| Incident mode toggle | `stella obs incident-mode enable|disable|status` | 🟩 Planned | CLI task `CLI-OBS-55-001`. |
|
||||
| Verify console telemetry health | `stella console status --telemetry` | 🟩 Planned | Part of `CONSOLE-DOC-23-502`. |
|
||||
|
||||
---
|
||||
|
||||
## 8 · Parity Gaps & Follow-up
|
||||
|
||||
- **Tenant and client lifecycle CLI**: create/suspend tenants, manage clients. Coordinate with Authority CLI epic (`CLI-TEN-47-001`, `CLI-TEN-49-001`).
|
||||
- **Downloads parity commands**: blocked on `CONSOLE-DOC-23-502` and DevOps pipeline `DOWNLOADS-CONSOLE-23-001`.
|
||||
- **Policy promotion/history**: requires completion of CLI policy epic (`CLI-POLICY-23-005`/`23-006`).
|
||||
- **Runs/evidence exports**: waiting on `CLI-EXPORT-35-001`.
|
||||
- **Observability tooling**: deliver `stella obs` commands before enabling parity CI checks for telemetry.
|
||||
|
||||
Document updates should occur whenever a row changes status. When promoting a command from Planned → Available, ensure:
|
||||
|
||||
1. CLI command merged with help text.
|
||||
2. Relevant UI doc references updated to remove “pending” callouts.
|
||||
3. This matrix row status updated to ✅ and task IDs moved to release notes.
|
||||
|
||||
---
|
||||
|
||||
## 9 · Parity CI Check (CONSOLE-DOC-23-502)
|
||||
|
||||
- **Owner:** Docs Guild + DevEx/CLI Guild.
|
||||
- **Artefact:** Planned `.gitea/workflows/cli-parity-console.yml`.
|
||||
- **What it does:** Runs `scripts/check-console-cli-parity.sh` (to be committed with the workflow) which:
|
||||
1. Parses this matrix (YAML view exported from Markdown) to identify rows marked ✅.
|
||||
2. Executes `stella --help` to confirm listed commands exist.
|
||||
3. Optionally triggers smoke commands in sandbox mode (e.g., `stella policy simulate --help`).
|
||||
- **Failure action:** Workflow fails when a listed command is missing or when a row marked ✅ still contains “pending” notes. Update the matrix or fix CLI implementation before merging.
|
||||
|
||||
Until the workflow lands, run the checker locally:
|
||||
|
||||
```bash
|
||||
# Pending CONSOLE-DOC-23-502 – placeholder command
|
||||
./scripts/check-console-cli-parity.sh
|
||||
```
|
||||
|
||||
The script should emit a parity report that feeds into the Downloads workspace (`kind = "parity.report"`).
|
||||
|
||||
---
|
||||
|
||||
## 10 · Compliance checklist
|
||||
|
||||
- [ ] Matrix reflects latest command availability (statuses accurate, task IDs linked).
|
||||
- [ ] Notes include owning backlog items for every 🟩 / 🟡 row.
|
||||
- [ ] CLI commands marked ✅ have corresponding entries in `/docs/modules/cli/guides/*.md` or module-specific docs.
|
||||
- [ ] CI parity workflow description kept in sync with CONSOLE-DOC-23-502 implementation.
|
||||
- [ ] Downloads workspace links to latest parity report.
|
||||
- [ ] Install / observability guides reference this matrix for pending CLI parity.
|
||||
- [ ] Offline workflows capture CLI fallbacks when commands are pending.
|
||||
- [ ] Docs Guild review recorded in sprint log once parity CI lands.
|
||||
|
||||
---
|
||||
|
||||
## 11 · References
|
||||
|
||||
- `docs/UI_GUIDE.md` – console workflow overview for parity context.
|
||||
- `/docs/operations/console-docker-install.md` – CLI parity section for deployments.
|
||||
- `/docs/observability/ui-telemetry.md` – telemetry metrics referencing CLI checks.
|
||||
- `/docs/security/console-security.md` – security metrics & CLI parity expectations.
|
||||
- `src/Cli/StellaOps.Cli/TASKS.md` – authoritative status for CLI backlog.
|
||||
- `/docs/implplan/archived/updates/2025-10-28-docs-guild.md` – coordination note for Authority/Security follow-up.
|
||||
|
||||
---
|
||||
|
||||
*Last updated: 2025-10-28 (Sprint 23).*
|
||||
|
||||
@@ -90,4 +90,4 @@ Options:
|
||||
## Related Docs
|
||||
|
||||
- Exceptions API entry point: `docs/api/exceptions.md`
|
||||
- Exception governance migration guide: `docs/migration/exception-governance.md`
|
||||
- Exception governance migration guide: `docs/technical/migration/exception-governance.md`
|
||||
|
||||
@@ -53,4 +53,4 @@ StellaOps:
|
||||
|
||||
The CLI reads the profile, applies the Authority configuration, and requests the listed scopes so the resulting tokens satisfy Task Runner and Packs Registry expectations.
|
||||
|
||||
> **Pack approval tip** – `stella pack approve` now relays `--pack-run-id`, `--pack-gate-id`, and `--pack-plan-hash` to Authority whenever it asks for `packs.approve`. Profiles don’t store these values (they change per run), but keeping the approver profile loaded ensures the CLI can prompt for the metadata, validate it against the plan hash, and satisfy the Authority procedure documented in `docs/task-packs/runbook.md#4-approvals-workflow`.
|
||||
> **Pack approval tip** – `stella pack approve` now relays `--pack-run-id`, `--pack-gate-id`, and `--pack-plan-hash` to Authority whenever it asks for `packs.approve`. Profiles don’t store these values (they change per run), but keeping the approver profile loaded ensures the CLI can prompt for the metadata, validate it against the plan hash, and satisfy the Authority procedure documented in `docs/modules/packs-registry/guides/runbook.md#4-approvals-workflow`.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Stella CLI — Policy Commands
|
||||
|
||||
> **Audience:** Policy authors, reviewers, operators, and CI engineers using the `stella` CLI to interact with Policy Engine.
|
||||
> **Imposed rule:** Submit/approve/publish flows must include lint, simulate, coverage, and shadow evidence; CLI blocks if required attachments are missing.
|
||||
# Stella CLI — Policy Commands
|
||||
|
||||
> **Audience:** Policy authors, reviewers, operators, and CI engineers using the `stella` CLI to interact with Policy Engine.
|
||||
> **Imposed rule:** Submit/approve/publish flows must include lint, simulate, coverage, and shadow evidence; CLI blocks if required attachments are missing.
|
||||
> **Supported from:** `stella` CLI ≥ 0.20.0 (Policy Engine v2 sprint line).
|
||||
> **Prerequisites:** Authority-issued bearer token with the scopes noted per command (export `STELLA_TOKEN` or pass `--token`).
|
||||
> **2025-10-27 scope update:** CLI/CI tokens issued prior to Sprint 23 (AUTH-POLICY-23-001) must drop `policy:write`/`policy:submit`/`policy:edit` and instead request `policy:read`, `policy:author`, `policy:review`, and `policy:simulate` (plus `policy:approve`/`policy:operate`/`policy:activate` for promotion pipelines).
|
||||
@@ -219,15 +219,15 @@ Options:
|
||||
`stella policy run status <runId>` retrieves run metadata.
|
||||
`stella policy run list --status failed --limit 20` returns recent runs.
|
||||
|
||||
### 4.3 History
|
||||
|
||||
```
|
||||
stella policy history P-7 --limit 20 --format table
|
||||
```
|
||||
|
||||
Shows version list with status, shadow flag, IR hash, attestation, submission/approval timestamps. Add `--runs` to include last run status per version. Exit code `0` success; `12` on RBAC error.
|
||||
|
||||
### 4.4 Replay & Cancel
|
||||
### 4.3 History
|
||||
|
||||
```
|
||||
stella policy history P-7 --limit 20 --format table
|
||||
```
|
||||
|
||||
Shows version list with status, shadow flag, IR hash, attestation, submission/approval timestamps. Add `--runs` to include last run status per version. Exit code `0` success; `12` on RBAC error.
|
||||
|
||||
### 4.4 Replay & Cancel
|
||||
|
||||
```
|
||||
stella policy run replay run:P-7:2025-10-26:auto --output bundles/replay.tgz
|
||||
@@ -239,7 +239,7 @@ Replay downloads sealed bundle for deterministic verification.
|
||||
### 4.4 Schema artefacts for CLI validation
|
||||
|
||||
- CI publishes canonical JSON Schema exports for `PolicyRunRequest`, `PolicyRunStatus`, `PolicyDiffSummary`, and `PolicyExplainTrace` as the `policy-schema-exports` artifact (see `.gitea/workflows/build-test-deploy.yml`).
|
||||
- Each run writes the files to `artifacts/policy-schemas/<commit>/` and stores a unified diff (`policy-schema-diff.patch`) comparing them with the tracked baseline in `docs/schemas/`.
|
||||
- Each run writes the files to `artifacts/policy-schemas/<commit>/` and stores a unified diff (`policy-schema-diff.patch`) comparing them with the tracked baseline in `docs/modules/policy/schemas/`.
|
||||
- Schema changes trigger an alert in Slack `#policy-engine` via the `POLICY_ENGINE_SCHEMA_WEBHOOK` secret so CLI maintainers know to refresh fixtures or validation rules.
|
||||
- Consume these artefacts in CLI tests to keep payload validation aligned without committing generated files into the repo.
|
||||
|
||||
@@ -324,4 +324,4 @@ All non-zero exits emit structured error envelope on stderr when `--format json`
|
||||
|
||||
---
|
||||
|
||||
*Last updated: 2025-11-26 (Sprint 307).*
|
||||
*Last updated: 2025-11-26 (Sprint 307).*
|
||||
|
||||
@@ -67,7 +67,7 @@ Notes:
|
||||
- For empty matches, the endpoint returns empty `observations` and `linksets` with `hasMore=false`.
|
||||
|
||||
Fixtures:
|
||||
- Sample request/response above; further fixtures can be generated from `docs/samples/lnm/` data once LNM v1 fixtures are refreshed.
|
||||
- Sample request/response above; further fixtures can be generated from `docs/modules/concelier/samples/` data once LNM v1 fixtures are refreshed.
|
||||
|
||||
Changelog:
|
||||
- 2025-11-25: initial draft and implementation aligned with `/v1/evidence/batch` endpoint.
|
||||
|
||||
@@ -69,7 +69,7 @@ Purpose: unblock PREP-CONCELIER-VULN-29-001 by defining the request/response con
|
||||
- Optional: `fix_version` when present; keep absent otherwise (no empty strings).
|
||||
|
||||
## Test fixtures
|
||||
- Location: `docs/samples/console/console-vex-30-001.json` already includes VEX sample keyed by advisory; add Concelier response sample to `docs/samples/console/concelier-vuln-29-001.json` (to be generated alongside implementation).
|
||||
- Location: `docs/modules/platform/samples/console-vex-30-001.json` already includes VEX sample keyed by advisory; add Concelier response sample to `docs/modules/platform/samples/concelier-vuln-29-001.json` (to be generated alongside implementation).
|
||||
|
||||
## Owners
|
||||
- Concelier WebService Guild (producer)
|
||||
|
||||
@@ -166,7 +166,7 @@ When an advisory source publishes a revised version of an advisory:
|
||||
- String normalization: lowercase `source`, trim/normalize PURLs, stable sort arrays.
|
||||
|
||||
## Sample documents
|
||||
See `docs/samples/lnm/observation-ghsa.json` and `docs/samples/lnm/linkset-ghsa.json` (added with this draft) for concrete payloads.
|
||||
See `docs/modules/concelier/samples/observation-ghsa.json` and `docs/modules/concelier/samples/linkset-ghsa.json` (added with this draft) for concrete payloads.
|
||||
|
||||
## Approval path
|
||||
1) Architecture + Concelier Core review this document.
|
||||
|
||||
@@ -42,8 +42,8 @@ Each conflict includes `field`, `reason`, and `values` (array of `source: value`
|
||||
- `provenance.hashes`: sorted list of `observationHash` values; used by replay bundles.
|
||||
|
||||
## Fixtures
|
||||
- `docs/samples/lnm/linkset-lnm-21-002-sample.json`: two-source agreement (high confidence, no conflicts).
|
||||
- `docs/samples/lnm/linkset-lnm-21-002-conflict.json`: three-source disagreement showing conflict records and confidence < 0.7.
|
||||
- `docs/modules/concelier/samples/linkset-lnm-21-002-sample.json`: two-source agreement (high confidence, no conflicts).
|
||||
- `docs/modules/concelier/samples/linkset-lnm-21-002-conflict.json`: three-source disagreement showing conflict records and confidence < 0.7.
|
||||
All fixtures use ASCII ordering and ISO-8601 UTC timestamps and may be used as golden outputs in tests.
|
||||
|
||||
## Implementation checklist
|
||||
|
||||
@@ -8,7 +8,7 @@ This utility generates JSON Schema documents for the Policy Engine run contracts
|
||||
scripts/export-policy-schemas.sh [output-directory]
|
||||
```
|
||||
|
||||
When no output directory is supplied, schemas are written to `docs/schemas/`.
|
||||
When no output directory is supplied, schemas are written to `docs/modules/policy/schemas/`.
|
||||
|
||||
The exporter builds against `StellaOps.Scheduler.Models` and emits:
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ _\* READY with caveat - remaining work noted in Section 3._
|
||||
| Item | Owner | Tracking Ref | Target / Next Step | Impact |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| Tenant scope propagation and audit coverage | Authority Core Guild | `AUTH-AOC-19-002` (DOING 2025-10-26) | Land enforcement + audit fixtures by Sprint 19 freeze | Medium - required for multi-tenant GA but does not block initial cutover if tenants scoped manually. |
|
||||
| Orchestrator event envelopes + Notifier handshake | Scanner WebService Guild | `SCANNER-EVENTS-16-301` (BLOCKED), `SCANNER-EVENTS-16-302` (DOING) | Coordinate with Gateway/Notifier owners on preview package replacement or binding redirects; rerun `dotnet test` once patch lands and refresh schema docs. Share envelope samples in `docs/events/` after tests pass. | High — gating Notifier migration; legacy notify path remains functional meanwhile. |
|
||||
| Orchestrator event envelopes + Notifier handshake | Scanner WebService Guild | `SCANNER-EVENTS-16-301` (BLOCKED), `SCANNER-EVENTS-16-302` (DOING) | Coordinate with Gateway/Notifier owners on preview package replacement or binding redirects; rerun `dotnet test` once patch lands and refresh schema docs. Share envelope samples in `docs/modules/signals/events/` after tests pass. | High — gating Notifier migration; legacy notify path remains functional meanwhile. |
|
||||
| Offline Kit Python analyzer bundle | Offline Kit Guild + Scanner Guild | `DEVOPS-OFFLINE-18-005` (DONE 2025-10-26) | Monitor for follow-up manifest updates and rerun smoke script when analyzers change. | Medium - ensures language analyzer coverage stays current for offline installs. |
|
||||
| Offline Kit debug store mirror | Offline Kit Guild + DevOps Guild | `DEVOPS-OFFLINE-17-004` (TODO 2025-11-23) | Release pipeline now publishes `out/release/debug`; run `mirror_debug_store.py`, verify hashes, and commit `metadata/debug-store.json`. | Low - symbol lookup remains accessible from staging assets but required before next Offline Kit tag. |
|
||||
| Mongo schema validators for advisory ingestion | Concelier Storage Guild | `CONCELIER-STORE-AOC-19-001` (TODO) | Finalize JSON schema + migration toggles; coordinate with Ops for rollout window | Low - current validation handled in app layer; schema guard adds defense-in-depth. |
|
||||
|
||||
@@ -36,8 +36,8 @@ Scope: Evidence Bundle v1 produced by Evidence Locker and consumed by Concelier,
|
||||
- Emit verification report JSON (deterministic key order) and store beside bundle as `verify.json`.
|
||||
|
||||
## Fixtures
|
||||
- Sample bundle + report: `docs/samples/evidence-locker/bundle-v1-sample.tar.gz` (sha256 TBD at publish time).
|
||||
- Sample attestation envelope: `docs/samples/evidence-locker/attestation-v1-sample.json`.
|
||||
- Sample bundle + report: `docs/modules/evidence-locker/samples/bundle-v1-sample.tar.gz` (sha256 TBD at publish time).
|
||||
- Sample attestation envelope: `docs/modules/evidence-locker/samples/attestation-v1-sample.json`.
|
||||
|
||||
## Ownership
|
||||
- Primary: Evidence Locker Guild.
|
||||
|
||||
@@ -45,8 +45,8 @@ Frozen contract for Evidence Bundle v1 covering AdvisoryAI/Concelier/Excititor e
|
||||
- Tenant must be lowercase; include in manifest and any attestation subject claims.
|
||||
|
||||
## Example bundle (sample)
|
||||
- Path: `docs/samples/evidence-bundle/evidence-bundle-m0.tar.gz`
|
||||
- SHA256: `$(cat docs/samples/evidence-bundle/evidence-bundle-m0.tar.gz.sha256 | awk '{print $1}')`
|
||||
- Path: `docs/modules/evidence-locker/samples/evidence-bundle-m0.tar.gz`
|
||||
- SHA256: `$(cat docs/modules/evidence-locker/samples/evidence-bundle-m0.tar.gz.sha256 | awk '{print $1}')`
|
||||
- Contains sample manifest/observations/linksets/transparency per above.
|
||||
|
||||
## Attestation linkage
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
> **Status:** Implementation in Progress (SPRINT_3000_0100_0002)
|
||||
> **Type URI:** `https://stellaops.dev/evidence-pack@v1`
|
||||
> **Schema:** [`docs/schemas/stellaops-evidence-pack.v1.schema.json`](../schemas/stellaops-evidence-pack.v1.schema.json)
|
||||
> **Schema:** [`docs/modules/evidence-locker/schemas/stellaops-evidence-pack.v1.schema.json`](../schemas/stellaops-evidence-pack.v1.schema.json)
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -32,8 +32,8 @@
|
||||
- Attach verification report alongside attestation as `chunk-verify.json` (hashes + signature check results).
|
||||
|
||||
## Sample payloads
|
||||
- `docs/samples/excititor/chunk-sample.ndjson`
|
||||
- `docs/samples/excititor/chunk-attestation-sample.json`
|
||||
- `docs/modules/excititor/samples/chunk-sample.ndjson`
|
||||
- `docs/modules/excititor/samples/chunk-attestation-sample.json`
|
||||
|
||||
## Integration points
|
||||
- Evidence Locker contract v1 (see `docs/modules/evidence-locker/attestation-contract.md`).
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
**Location & format.**
|
||||
- Schema: `docs/modules/excititor/schemas/connector-signer-metadata.schema.json` (JSON Schema 2020‑12).
|
||||
- Sample: `docs/samples/excititor/connector-signer-metadata-sample.json` (aligns with schema).
|
||||
- Sample: `docs/modules/excititor/samples/connector-signer-metadata-sample.json` (aligns with schema).
|
||||
- Expected production artifact: NDJSON or JSON stamped per release; store in offline kits alongside connector bundles.
|
||||
|
||||
## Required fields (summary)
|
||||
@@ -28,7 +28,7 @@
|
||||
6) **Record decisions** in sprint Decisions & Risks when changing trust tiers or fingerpints; update this doc if formats change.
|
||||
|
||||
## Sample entries (non-production)
|
||||
See `docs/samples/excititor/connector-signer-metadata-sample.json` for MSRC, Oracle, Ubuntu, and StellaOps example entries. These fingerprints are illustrative only; replace with real values before shipping.
|
||||
See `docs/modules/excititor/samples/connector-signer-metadata-sample.json` for MSRC, Oracle, Ubuntu, and StellaOps example entries. These fingerprints are illustrative only; replace with real values before shipping.
|
||||
|
||||
## Consumer expectations
|
||||
- Deterministic: sort connectors alphabetically before persistence; avoid clock-based defaults.
|
||||
|
||||
@@ -106,7 +106,7 @@ This note defines the deterministic, aggregation-only contract that Excititor ex
|
||||
- Emitted for every import attempt; stored on the import record and logged for audit.
|
||||
|
||||
## Samples
|
||||
- NDJSON sample: `docs/samples/excititor/chunks-sample.ndjson` (hashes in `.sha256`) aligned to the schema above.
|
||||
- NDJSON sample: `docs/modules/excititor/samples/chunks-sample.ndjson` (hashes in `.sha256`) aligned to the schema above.
|
||||
|
||||
## Versioning
|
||||
- Contract version: `v1` (this document). Changes must be additive; breaking changes require `v2` path and updated doc.
|
||||
|
||||
@@ -82,6 +82,6 @@ Defines the graph-ready overlay built from Link-Not-Merge observations/linksets
|
||||
|
||||
## Handoff
|
||||
- Consumers (Console, Vuln Explorer, Policy Engine, Risk) should treat `vex_overlay.schema.json` as the authoritative contract.
|
||||
- Offline kits must bundle the schema file and sample payloads under `docs/samples/excititor/` with SHA256 manifests.
|
||||
- Offline kits must bundle the schema file and sample payloads under `docs/modules/excititor/samples/` with SHA256 manifests.
|
||||
- Future schema versions must bump `schemaVersion` and add migration notes to this document and `docs/modules/excititor/architecture.md`.
|
||||
- Policy and Risk surfaces in WebService now read overlays directly (with claim-store fallback for policy tests) to produce lookup and risk feeds; overlay cache/store are selected per tenant (in-memory by default, Postgres `vex.graph_overlays` when configured).
|
||||
|
||||
@@ -20,5 +20,5 @@ Example curl
|
||||
curl -X POST https://excitor.local/vex/evidence/chunks \
|
||||
-H "Authorization: Bearer <token>" \
|
||||
-H "Content-Type: application/x-ndjson" \
|
||||
--data-binary @docs/samples/excititor/chunk-sample.ndjson
|
||||
--data-binary @docs/modules/excititor/samples/chunk-sample.ndjson
|
||||
```
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
- `vex_observations` indexes:
|
||||
- `{ tenant: 1, component.purl: 1, advisoryId: 1, source: 1, modifiedAt: -1 }`
|
||||
- Sparse `{ tenant: 1, component.purl: 1, status: 1 }`
|
||||
- Optional materialized `vex_overlays` cache: unique `{ tenant: 1, purl: 1 }`, TTL on `cachedAt` driven by `excititor:graph:overlayTtlSeconds` (default 300s); payload must validate against `docs/modules/excititor/schemas/vex_overlay.schema.json` (schemaVersion 1.0.0). Bundle sample payload `docs/samples/excititor/vex-overlay-sample.json` in Offline Kits.
|
||||
- Optional materialized `vex_overlays` cache: unique `{ tenant: 1, purl: 1 }`, TTL on `cachedAt` driven by `excititor:graph:overlayTtlSeconds` (default 300s); payload must validate against `docs/modules/excititor/schemas/vex_overlay.schema.json` (schemaVersion 1.0.0). Bundle sample payload `docs/modules/excititor/samples/vex-overlay-sample.json` in Offline Kits.
|
||||
|
||||
## Determinism
|
||||
- Ordering: input PURL order → `advisoryId` → `source` for linkouts; overlays follow input order.
|
||||
|
||||
@@ -1 +1 @@
|
||||
4d638b24d6f8f703bcbcac23a0185265f3db5defb9f3d7f33b7be7fccc0de738 docs/samples/excititor/chunks-sample.ndjson
|
||||
4d638b24d6f8f703bcbcac23a0185265f3db5defb9f3d7f33b7be7fccc0de738 docs/modules/excititor/samples/chunks-sample.ndjson
|
||||
|
||||
@@ -82,7 +82,7 @@ All endpoints require Authority-issued JWT + DPoP tokens with scopes `export:run
|
||||
|
||||
Audit bundles are a specialized Export Center output: a deterministic, immutable evidence pack for a single subject (and optional time window) suitable for audits and incident response.
|
||||
|
||||
- **Schema**: `docs/schemas/audit-bundle-index.schema.json` (bundle index/manifest with integrity hashes and referenced artefacts).
|
||||
- **Schema**: `docs/modules/evidence-locker/schemas/audit-bundle-index.schema.json` (bundle index/manifest with integrity hashes and referenced artefacts).
|
||||
- **Core APIs**:
|
||||
- `POST /v1/audit-bundles` - Create a new bundle (async generation).
|
||||
- `GET /v1/audit-bundles` - List previously created bundles.
|
||||
|
||||
@@ -16,9 +16,9 @@ This contract defines how air-gapped StellaOps installations maintain trusted ti
|
||||
|
||||
| Schema | Location |
|
||||
|--------|----------|
|
||||
| Time Anchor | `docs/schemas/time-anchor.schema.json` |
|
||||
| Ledger Staleness | `docs/schemas/ledger-airgap-staleness.schema.json` |
|
||||
| Sealed Mode | `docs/schemas/sealed-mode.schema.json` |
|
||||
| Time Anchor | `docs/modules/airgap/schemas/time-anchor.schema.json` |
|
||||
| Ledger Staleness | `docs/modules/airgap/schemas/ledger-airgap-staleness.schema.json` |
|
||||
| Sealed Mode | `docs/modules/airgap/schemas/sealed-mode.schema.json` |
|
||||
|
||||
## 3. Architecture
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ stella pack init --name sbom-remediation
|
||||
### 3.4 Configure approvals
|
||||
|
||||
- Add `spec.approvals` entries for each required review.
|
||||
- Capture the metadata Authority enforces: `runId`, `gateId`, and `planHash` should be documented so approvers can pass them through `stella pack approve --pack-run-id/--pack-gate-id/--pack-plan-hash` (see `docs/task-packs/runbook.md#4-approvals-workflow`).
|
||||
- Capture the metadata Authority enforces: `runId`, `gateId`, and `planHash` should be documented so approvers can pass them through `stella pack approve --pack-run-id/--pack-gate-id/--pack-plan-hash` (see `docs/modules/packs-registry/guides/runbook.md#4-approvals-workflow`).
|
||||
- Provide informative `reasonTemplate` with placeholders.
|
||||
- Set `expiresAfter` to match operational policy (e.g., 4 h for security reviews).
|
||||
- Document fallback contacts in `docs/runbook.md`.
|
||||
|
||||
@@ -171,12 +171,12 @@ Extensions must be deterministic and derived from signed bundle data.
|
||||
## 11 · TP Gap Remediation (2025-12)
|
||||
|
||||
- **Signed registry record (TP7):** Every pack version stores DSSE envelopes for bundle + attestation, SBOM path, and revocation list reference. Imports fail-closed when signatures or revocation proofs are missing.
|
||||
- **Offline bundle schema (TP8):** Registry exports offline artefacts that must satisfy `docs/task-packs/packs-offline-bundle.schema.json`; publish pipeline invokes `scripts/packs/verify_offline_bundle.py --require-dsse` before promotion.
|
||||
- **Offline bundle schema (TP8):** Registry exports offline artefacts that must satisfy `docs/modules/packs-registry/guides/packs-offline-bundle.schema.json`; publish pipeline invokes `scripts/packs/verify_offline_bundle.py --require-dsse` before promotion.
|
||||
- **Hash ledger (TP1/TP2):** Publish step writes `hashes[]` (sha256) for manifest, canonical plan, `inputs.lock`, approvals ledger, SBOM, and revocations; digests surface in audit events and `digestmap.json`.
|
||||
- **Sandbox + quotas (TP6):** Registry metadata carries `sandbox.mode`, explicit egress allowlists, CPU/memory limits, and quota seconds; Task Runner refuses packs missing these fields.
|
||||
- **SLO + alerting (TP9):** Pack metadata includes SLOs (`runP95Seconds`, `approvalP95Seconds`, `maxQueueDepth`); registry emits metrics/alerts when declared SLOs are exceeded during publish/import flows.
|
||||
- **Fail-closed imports (TP10):** Import/mirror paths abort when DSSE, hash entries, or revocation files are absent or stale, returning actionable error codes for CLI/Task Runner.
|
||||
- **Approval ledger schema:** Registry exposes `docs/task-packs/approvals-ledger.schema.json` for DSSE approval records (planHash must be `sha256:<64-hex>`); import validation rejects non-conforming ledgers.
|
||||
- **Approval ledger schema:** Registry exposes `docs/modules/packs-registry/guides/approvals-ledger.schema.json` for DSSE approval records (planHash must be `sha256:<64-hex>`); import validation rejects non-conforming ledgers.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -122,7 +122,7 @@ stella pack approve \
|
||||
|
||||
## 9 · Runbooks for Common Packs
|
||||
|
||||
Maintain per-pack playbooks in `docs/task-packs/runbook/<pack-name>.md`. Include:
|
||||
Maintain per-pack playbooks in `docs/modules/packs-registry/guides/runbook/<pack-name>.md`. Include:
|
||||
|
||||
- Purpose and scope.
|
||||
- Required inputs and secrets.
|
||||
|
||||
@@ -131,7 +131,7 @@ spec:
|
||||
| `metadata` | Human-facing metadata; used for registry listings and RBAC hints. | `name` (DNS-1123), `version` (SemVer), `description` ≤ 2048 chars. |
|
||||
| `spec.inputs` | Declarative inputs validated at plan time. | Must include type; custom schema optional but recommended. |
|
||||
| `spec.secrets` | Secrets requested at runtime; never stored in pack bundle. | Each secret references Authority scope; CLI prompts or injects from profiles. |
|
||||
| `spec.approvals` | Named approval gates with required grants and TTL. | ID unique per pack; `grants` map to Authority roles. Approval metadata (`runId`, `gateId`, `planHash`) feeds Authority’s `pack_run_id`/`pack_gate_id`/`pack_plan_hash` parameters (see `docs/task-packs/runbook.md#4-approvals-workflow`). |
|
||||
| `spec.approvals` | Named approval gates with required grants and TTL. | ID unique per pack; `grants` map to Authority roles. Approval metadata (`runId`, `gateId`, `planHash`) feeds Authority’s `pack_run_id`/`pack_gate_id`/`pack_plan_hash` parameters (see `docs/modules/packs-registry/guides/runbook.md#4-approvals-workflow`). |
|
||||
| `spec.steps` | Execution graph; each step is `run`, `gate`, `parallel`, or `map`. | Steps must declare deterministic `uses` module and `id`. |
|
||||
| `spec.outputs` | Declared artifacts for downstream automation. | `type` can be `file`, `object`, or `url`; path/expression required. |
|
||||
| `success` / `failure` | Messages + retry policy. | `failure.retries.maxAttempts` + `backoffSeconds` default to 0. |
|
||||
@@ -175,10 +175,10 @@ Packs must pass CLI validation before publishing.
|
||||
- **Deterministic RNG/time (TP5):** RNG seed is derived from `plan.hash`; timestamps use UTC ISO-8601; log ordering is monotonic.
|
||||
- **Sandbox + egress quotas (TP6):** Packs declare `sandbox.mode`, explicit `egressAllowlist`, CPU/memory limits, and optional `quotaSeconds`; missing fields cause fail-closed refusal.
|
||||
- **Registry signing + revocation (TP7):** Bundles carry SBOM + DSSE envelopes and reference a revocation list enforced during registry import.
|
||||
- **Offline bundle schema + verifier (TP8):** Offline exports must satisfy `docs/task-packs/packs-offline-bundle.schema.json` and pass `scripts/packs/verify_offline_bundle.py --require-dsse`.
|
||||
- **Offline bundle schema + verifier (TP8):** Offline exports must satisfy `docs/modules/packs-registry/guides/packs-offline-bundle.schema.json` and pass `scripts/packs/verify_offline_bundle.py --require-dsse`.
|
||||
- **SLO + alerting (TP9):** Manifests declare `slo.runP95Seconds`, `slo.approvalP95Seconds`, `slo.maxQueueDepth`, and optional `slo.alertRules`; telemetry enforces and alerts on breaches.
|
||||
- **Fail-closed gates (TP10):** Approval/policy/timeline gates fail closed when DSSE, hash entries, or quotas are missing/expired; CLI surfaces remediation hints.
|
||||
- **Approval ledger schema:** Approval decisions must conform to `docs/task-packs/approvals-ledger.schema.json`; planHash is `sha256:<64-hex>` and DSSE envelopes must reference ledger digest.
|
||||
- **Approval ledger schema:** Approval decisions must conform to `docs/modules/packs-registry/guides/approvals-ledger.schema.json`; planHash is `sha256:<64-hex>` and DSSE envelopes must reference ledger digest.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ This dossier summarises the end-to-end runtime topology after the Aggregation-On
|
||||
|
||||
> Need a quick orientation? The [Developer Quickstart](../onboarding/dev-quickstart.md) (29-Nov-2025 advisory) captures the core repositories, determinism checks, DSSE conventions, and starter tasks that explain how the platform pieces fit together.
|
||||
|
||||
> Testing strategy models and CI lanes live in `docs/testing/testing-strategy-models.md`, with the source catalog in `docs/testing/TEST_CATALOG.yml`.
|
||||
> Testing strategy models and CI lanes live in `docs/technical/testing/testing-strategy-models.md`, with the source catalog in `docs/technical/testing/TEST_CATALOG.yml`.
|
||||
|
||||
> Planner note: the [SBOM→VEX proof blueprint](../product-advisories/29-Nov-2025 - SBOM to VEX Proof Pipeline Blueprint.md) shows the DSSE → Rekor v2 tiles → VEX linkage, so threat-model and compliance teams can copy the capture/verification checkpoints.
|
||||
|
||||
|
||||
101
docs/modules/policy/POLICY_TEMPLATES.md
Executable file
101
docs/modules/policy/POLICY_TEMPLATES.md
Executable file
@@ -0,0 +1,101 @@
|
||||
# Policy Templates — YAML & Rego Examples
|
||||
|
||||
Stella Ops lets you enforce *pass / fail* rules in two ways:
|
||||
|
||||
1. **YAML “quick policies”** — simple equality / inequality checks.
|
||||
2. **OPA Rego modules** — full‑power logic for complex organisations.
|
||||
|
||||
> **Precedence:** If the same image is subject to both a YAML rule *and* a Rego
|
||||
> module, the **Rego result wins**. That is, `deny` in Rego overrides any
|
||||
> `allow` in YAML.
|
||||
|
||||
---
|
||||
|
||||
## 1 · YAML quick policy
|
||||
|
||||
```yaml
|
||||
# file: policies/root_user.yaml
|
||||
version: 1
|
||||
id: root-user
|
||||
description: Disallow images that run as root
|
||||
severity: high
|
||||
|
||||
rules:
|
||||
- field: ".config.user"
|
||||
operator: "equals"
|
||||
value: "root"
|
||||
deny_message: "Image runs as root — block."
|
||||
````
|
||||
|
||||
Place the file under `/opt/stella/plugins/policies/`.
|
||||
|
||||
---
|
||||
|
||||
## 2 · Rego example (deny on critical CVE)
|
||||
|
||||
```rego
|
||||
# file: policies/deny_critical.rego
|
||||
package stella.policy
|
||||
|
||||
default deny = []
|
||||
|
||||
deny[msg] {
|
||||
some f
|
||||
input.findings[f].severity == "critical"
|
||||
msg := sprintf("Critical CVE %s – build blocked", [input.findings[f].id])
|
||||
}
|
||||
```
|
||||
|
||||
*Input schema* — the Rego `input` document matches the public
|
||||
`ScanResult` POCO (see SDK). Use the bundled JSON schema in
|
||||
`share/schemas/scanresult.schema.json` for IDE autocompletion.
|
||||
|
||||
---
|
||||
|
||||
## 3 · Pass‑through warnings (Rego)
|
||||
|
||||
Return a `warn` array to surface non‑blocking messages in the UI:
|
||||
|
||||
```rego
|
||||
package stella.policy
|
||||
|
||||
warn[msg] {
|
||||
input.image.base == "ubuntu:16.04"
|
||||
msg := "Image uses EOL Ubuntu 16.04 — please upgrade."
|
||||
}
|
||||
```
|
||||
|
||||
Warnings decrement the **quality score** but do *not* affect the CLI exit
|
||||
code.
|
||||
|
||||
---
|
||||
|
||||
## 4 · Testing policies locally
|
||||
|
||||
```bash
|
||||
# run policy evaluation without pushing to DB
|
||||
stella scan alpine:3.20 --policy-only \
|
||||
--policies ./policies/
|
||||
```
|
||||
|
||||
The CLI prints `PASS`, `WARN` or `DENY` plus structured JSON.
|
||||
|
||||
Unit‑test your Rego modules with the OPA binary:
|
||||
|
||||
```bash
|
||||
opa test policies/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5 · Developer quick‑start (plug‑ins)
|
||||
|
||||
Need logic beyond Rego? Implement a plug‑in via **C#/.NET {{ dotnet }}** and
|
||||
the `StellaOps.SDK` NuGet:
|
||||
|
||||
* Tutorial: [`dev/PLUGIN_DEV_GUIDE.md`](dev/PLUGIN_DEV_GUIDE.md)
|
||||
* Quick reference: `/plugins/`
|
||||
|
||||
---
|
||||
|
||||
*Last updated {{ "now" | date: "%Y‑%m‑%d" }} — constants auto‑injected.*
|
||||
@@ -16,7 +16,7 @@ This contract defines the integration between the Signals service (reachability
|
||||
|
||||
The canonical JSON schema is at:
|
||||
```
|
||||
docs/schemas/reachability-input.schema.json
|
||||
docs/modules/policy/schemas/reachability-input.schema.json
|
||||
```
|
||||
|
||||
## 3. Data Flow
|
||||
|
||||
93
docs/modules/policy/guides/QUOTA_ENFORCEMENT_FLOW.md
Executable file
93
docs/modules/policy/guides/QUOTA_ENFORCEMENT_FLOW.md
Executable file
@@ -0,0 +1,93 @@
|
||||
# Quota Enforcement — Flow Diagram (rev 2.1)
|
||||
|
||||
> **Scope** – this document explains *how* the free‑tier limits are enforced
|
||||
> inside the scanner service. For policy rationale and legal aspects, see
|
||||
> [`29_LEGAL_FAQ_QUOTA.md`](29_LEGAL_FAQ_QUOTA.md).
|
||||
|
||||
---
|
||||
|
||||
## 0 · Key parameters (rev 2.1)
|
||||
|
||||
| Symbol | Value | Meaning |
|
||||
|--------|-------|---------|
|
||||
| `L_anon` | **{{ quota_anon }}** | Daily ceiling for anonymous users |
|
||||
| `L_jwt` | **{{ quota_token }}** | Daily ceiling for token holders |
|
||||
| `T_warn` | `200` | Soft reminder threshold |
|
||||
| `D_soft` | `5 000 ms` | Delay for *first 30* over‑quota scans |
|
||||
| `D_hard` | `60 000 ms` | Delay for all scans beyond the soft window |
|
||||
|
||||
`L_active` is `L_jwt` if a valid token is present; else `L_anon`.
|
||||
|
||||
---
|
||||
|
||||
## 1 · Sequence diagram
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant C as Client
|
||||
participant API as Scanner API
|
||||
participant VALKEY as Valkey (quota)
|
||||
C->>API: /scan
|
||||
API->>VALKEY: INCR quota:<key>
|
||||
VALKEY-->>API: new_count
|
||||
alt new_count ≤ L_active
|
||||
API-->>C: 202 Accepted (no delay)
|
||||
else new_count ≤ L_active + 30
|
||||
API->>C: wait D_soft
|
||||
API-->>C: 202 Accepted
|
||||
else
|
||||
API->>C: wait D_hard
|
||||
API-->>C: 202 Accepted
|
||||
end
|
||||
````
|
||||
|
||||
*Counters auto‑expire **24 h** after first increment (00:00 UTC reset).*
|
||||
|
||||
---
|
||||
|
||||
## 2 · Valkey key layout
|
||||
|
||||
| Key pattern | TTL | Description |
|
||||
| ---------------------- | ---- | --------------------------------- |
|
||||
| `quota:ip:<sha256>` | 24 h | Anonymous quota per *hashed* IP |
|
||||
| `quota:tid:<sha256>` | 24 h | Token quota per *hashed* token‑ID |
|
||||
| `quota:ip:<sha256>:ts` | 24 h | First‑seen timestamp (ISO 8601) |
|
||||
|
||||
Keys share a common TTL for efficient mass expiry via `valkey-cli --scan`.
|
||||
|
||||
---
|
||||
|
||||
## 3 · Pseudocode (Go‑style)
|
||||
|
||||
```go
|
||||
func gate(key string, limit int) (delay time.Duration) {
|
||||
cnt, _ := rdb.Incr(ctx, key).Result()
|
||||
|
||||
switch {
|
||||
case cnt <= limit:
|
||||
return 0 // under quota
|
||||
case cnt <= limit+30:
|
||||
return 5 * time.Second
|
||||
default:
|
||||
return 60 * time.Second
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
*The middleware applies `time.Sleep(delay)` **before** processing the scan
|
||||
request; it never returns `HTTP 429` under the free tier.*
|
||||
|
||||
---
|
||||
|
||||
## 4 · Metrics & monitoring
|
||||
|
||||
| Metric | PromQL sample | Alert |
|
||||
| ------------------------------ | ------------------------------------------ | --------------------- |
|
||||
| `stella_quota_soft_hits_total` | `increase(...[5m]) > 50` | Many users near limit |
|
||||
| `stella_quota_hard_hits_total` | `rate(...[1h]) > 0.1` | Potential abuse |
|
||||
| Average delay per request | `histogram_quantile(0.95, sum(rate(...)))` | P95 < 1 s expected |
|
||||
|
||||
---
|
||||
|
||||
|
||||
*Generated {{ "now" | date: "%Y‑%m‑%d" }} — values pulled from central constants.*
|
||||
120
docs/modules/policy/guides/QUOTA_OVERVIEW.md
Executable file
120
docs/modules/policy/guides/QUOTA_OVERVIEW.md
Executable file
@@ -0,0 +1,120 @@
|
||||
# Free‑Tier Quota — **{{ quota_anon }}/ {{ quota_token }} Scans per UTC Day**
|
||||
|
||||
Stella Ops is free for individual developers and small teams.
|
||||
To avoid registry abuse the scanner enforces a **two‑tier daily quota**
|
||||
— fully offline capable.
|
||||
|
||||
| Mode | Daily ceiling | How to obtain |
|
||||
|------|---------------|---------------|
|
||||
| **Anonymous** | **{{ quota_anon }} scans** | No registration. Works online or air‑gapped. |
|
||||
| **Free JWT token** | **{{ quota_token }} scans** | Email `token@stella-ops.org` (blank body). Bot replies with a signed JWT. |
|
||||
|
||||
*Soft reminder banner appears at 200 scans. Exceeding the limit never blocks –
|
||||
the CLI/UI introduce a delay, detailed below.*
|
||||
|
||||
---
|
||||
|
||||
## 1 · Token structure
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"iss": "stella-ops.org",
|
||||
"sub": "free-tier",
|
||||
"tid": "7d2285…", // 32‑byte random token‑ID
|
||||
"tier": {{ quota_token }}, // daily scans allowed
|
||||
"exp": 1767139199 // POSIX seconds (mandatory) – token expiry
|
||||
}
|
||||
````
|
||||
|
||||
* The **token‑ID (`tid`)** – not the e‑mail – is hashed *(SHA‑256 + salt)*
|
||||
and stored for counter lookup.
|
||||
* Verification uses the bundled public key (`keys/cosign.pub`) so **offline
|
||||
hosts validate tokens locally**. An optional `exp` claim may be present;
|
||||
if absent, the default is a far‑future timestamp used solely for schema
|
||||
compatibility.
|
||||
|
||||
---
|
||||
|
||||
## 2 · Enforcement algorithm (rev 2.1)
|
||||
|
||||
| Step | Operation | Typical latency |
|
||||
| ---- | ------------------------------------------------------------------------------ | ------------------------------------ |
|
||||
| 1 | `key = sha256(ip)` *or* `sha256(tid)` | < 0.1 ms |
|
||||
| 2 | `count = INCR quota:<key>` in Valkey (24 h TTL) | 0.2 ms (Lua) |
|
||||
| 3 | If `count > limit` → `WAIT delay_ms` | first 30 × 5 000 ms → then 60 000 ms |
|
||||
| 4 | Return HTTP 429 **only if** `delay > 60 s` (should never fire under free tier) | — |
|
||||
|
||||
*Counters reset at **00:00 UTC**.*
|
||||
|
||||
---
|
||||
|
||||
## 3 · CLI / API integration
|
||||
|
||||
```bash
|
||||
# Example .env
|
||||
docker run --rm \
|
||||
-e DOCKER_HOST="$DOCKER_HOST" \ # remote‑daemon pointer
|
||||
-v "$WORKSPACE/${SBOM_FILE}:/${SBOM_FILE}:ro" \ # mount SBOM under same name at container root
|
||||
-e STELLA_OPS_URL="https://${STELLA_URL}" \ # where the CLI posts findings
|
||||
"$STELLA_URL/registry/stella-cli:latest" \
|
||||
scan --sbom "/${SBOM_FILE}" "$IMAGE"
|
||||
```
|
||||
|
||||
*No JWT? → scanner defaults to anonymous quota.*
|
||||
|
||||
---
|
||||
|
||||
## 4 · Data retention & privacy
|
||||
|
||||
| Data | Retention | Purpose |
|
||||
| ---------------------- | ------------------------------------ | ---------------- |
|
||||
| IP hash (`quota:ip:*`) | 7 days, then salted hash only | Abuse rate‑limit |
|
||||
| Token‑ID hash | Until revoked | Counter lookup |
|
||||
| E‑mail (token request) | ≤ 7 days unless newsletters opted‑in | Deliver the JWT |
|
||||
|
||||
*No personal data leaves your infrastructure when running offline.*
|
||||
|
||||
---
|
||||
|
||||
## 5 · Common questions
|
||||
|
||||
<details>
|
||||
<summary>What happens at exactly 200 scans?</summary>
|
||||
|
||||
> The UI/CLI shows a yellow “fair‑use reminder”.
|
||||
> No throttling is applied yet.
|
||||
> Once you cross the full limit, the **first 30** over‑quota scans incur a
|
||||
> 5‑second delay; further excess scans delay **60 s** each.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Does the quota differ offline?</summary>
|
||||
|
||||
> No. Counters are evaluated locally in Valkey; the same limits apply even
|
||||
> without Internet access.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Can I reset counters manually?</summary>
|
||||
|
||||
> Yes – delete the `quota:*` keys in Valkey, but we recommend letting them
|
||||
> expire at midnight to keep statistics meaningful.
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
## 6 · Revision history
|
||||
|
||||
| Version | Date | Notes |
|
||||
| ------- | ---------- | ------------------------------------------------------------------- |
|
||||
| **2.1** | 2025‑07‑16 | Consolidated into single source; delays re‑tuned (30 × 5 s → 60 s). |
|
||||
| 2.0 | 2025‑04‑07 | Switched from MongoDB (removed Sprint 4400) to Valkey (Redis-compatible) for quota counters. |
|
||||
| 1.0 | 2024‑12‑20 | Initial free‑tier design. |
|
||||
|
||||
---
|
||||
|
||||
**Authoritative source** — any doc or website section that references quotas
|
||||
*must* link to this file instead of duplicating text.
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
> **Status:** Implementation in Progress (SPRINT_3000_0100_0001)
|
||||
> **Predicate URI:** `https://stellaops.dev/predicates/policy-verdict@v1`
|
||||
> **Schema:** [`docs/schemas/stellaops-policy-verdict.v1.schema.json`](../schemas/stellaops-policy-verdict.v1.schema.json)
|
||||
> **Schema:** [`docs/modules/policy/schemas/stellaops-policy-verdict.v1.schema.json`](../schemas/stellaops-policy-verdict.v1.schema.json)
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
- Keep outputs deterministic and tenant-scoped while offline/air-gap friendly.
|
||||
|
||||
## Scope & Decisions
|
||||
- Schema path: `docs/schemas/replay-retention.schema.json`.
|
||||
- Schema path: `docs/modules/replay/schemas/replay-retention.schema.json`.
|
||||
- Fields:
|
||||
- `retention_policy_id` (string, stable ID for policy version).
|
||||
- `tenant_id` (string, required).
|
||||
|
||||
@@ -8,7 +8,7 @@ Scanner analyses container images layer-by-layer, producing deterministic SBOM f
|
||||
- Python analyzer picks up `requirements*.txt`, `Pipfile.lock`, and `poetry.lock`, tagging installed distributions with lock provenance and generating declared-only components for policy. Use `stella python lock-validate` to run the same checks locally before images are built.
|
||||
- Java analyzer now parses `gradle.lockfile`, `gradle/dependency-locks/**/*.lockfile`, and `pom.xml` dependencies via the new `JavaLockFileCollector`, merging lock metadata onto jar evidence and emitting declared-only components when jars are absent. The new CLI verb `stella java lock-validate` reuses that collector offline (table/JSON output) and records `stellaops.cli.java.lock_validate.count{outcome}` for observability.
|
||||
- Worker/WebService now resolve cache roots and feature flags via `StellaOps.Scanner.Surface.Env`; misconfiguration warnings are documented in `docs/modules/scanner/design/surface-env.md` and surfaced through startup validation.
|
||||
- Platform events rollout (2025-10-19) continues to publish scanner.report.ready@1 and scanner.scan.completed@1 envelopes with embedded DSSE payloads (see docs/updates/2025-10-19-scanner-policy.md and docs/updates/2025-10-19-platform-events.md). Service and consumer tests should round-trip the canonical samples under docs/events/samples/.
|
||||
- Platform events rollout (2025-10-19) continues to publish scanner.report.ready@1 and scanner.scan.completed@1 envelopes with embedded DSSE payloads (see docs/updates/2025-10-19-scanner-policy.md and docs/updates/2025-10-19-platform-events.md). Service and consumer tests should round-trip the canonical samples under docs/modules/signals/events/samples/.
|
||||
- OS/non-language analyzers: evidence is rootfs-relative, warnings are structured/capped, hashing is bounded, and Linux OS analyzers support surface-cache reuse. See `os-analyzers-evidence.md`.
|
||||
|
||||
## Responsibilities
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
Purpose: unblock PREP tasks by freezing analyzer inputs/outputs, resolver traces, and fixtures for Node bundle/source-map coverage, native/WASM detection, and AOC-compliant observation emission.
|
||||
|
||||
## Output artefacts
|
||||
- Sample NDJSON: `docs/samples/scanner/node-phase22/node-phase22-sample.ndjson` (covers 22-006/007/008 in one run).
|
||||
- Sample NDJSON: `docs/modules/scanner/samples/node-phase22/node-phase22-sample.ndjson` (covers 22-006/007/008 in one run).
|
||||
- Resolver trace spec and reason codes (below) are binding for workers and tests.
|
||||
|
||||
## 22-006 · Bundle + source-map reconstruction
|
||||
@@ -37,7 +37,7 @@ Purpose: unblock PREP tasks by freezing analyzer inputs/outputs, resolver traces
|
||||
- Large maps: emit `ERR_NODE_BUNDLE_MAP_TOO_LARGE` and skip map (still report bundle presence with `confidence:0.51`).
|
||||
|
||||
## Fixtures
|
||||
- `docs/samples/scanner/node-phase22/node-phase22-sample.ndjson` contains:
|
||||
- `docs/modules/scanner/samples/node-phase22/node-phase22-sample.ndjson` contains:
|
||||
1) webpack bundle w/ source map mapping to `/src/app.js` (22-006)
|
||||
2) native addon load via `process.dlopen('./native/addon.node')` (22-007)
|
||||
3) WASM module import via `WebAssembly.instantiateStreaming(fetch('./pkg.wasm'))` (22-007)
|
||||
|
||||
@@ -63,7 +63,7 @@ graph LR
|
||||
|
||||
| Artifact | Owner | Location |
|
||||
|----------|-------|----------|
|
||||
| RFC Document | Scanner TL | `docs/rfcs/scanner/` |
|
||||
| RFC Document | Scanner TL | `docs/adr/` |
|
||||
| Mapping CSV | Scanner TL | `docs/modules/scanner/fixtures/adapters/` |
|
||||
| Golden Fixtures | QA | `docs/modules/scanner/fixtures/cdx17-cbom/` |
|
||||
| Hash List | QA | `docs/modules/scanner/fixtures/*/hashes.txt` |
|
||||
@@ -138,7 +138,7 @@ Triggered by:
|
||||
"since": "v2.5.0",
|
||||
"removal": "v3.0.0",
|
||||
"replacement": "ratings[method=CVSSv31]",
|
||||
"migrationGuide": "docs/migrations/cvss-v30-removal.md"
|
||||
"migrationGuide": "docs/technical/migration/cvss-v30-removal.md"
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -167,7 +167,7 @@ To modify a locked adapter:
|
||||
|
||||
| Record | Location | Retention |
|
||||
|--------|----------|-----------|
|
||||
| RFC decisions | `docs/rfcs/scanner/` | Permanent |
|
||||
| RFC decisions | `docs/adr/` | Permanent |
|
||||
| Hash changes | Git history + `CHANGELOG.md` | Permanent |
|
||||
| Approval records | PR comments | Permanent |
|
||||
| DSSE envelopes | CAS + offline kit | Permanent |
|
||||
|
||||
@@ -9,7 +9,7 @@ This runbook confirms that Scanner.WebService now surfaces the metadata Runtime
|
||||
## 1. Prerequisites
|
||||
|
||||
- Scanner.WebService release includes **SCANNER-POLICY-09-107** (adds quieted provenance and score inputs to `/reports`).
|
||||
- Docs repository at commit containing `docs/events/scanner.report.ready@1.json` with `quietedFindingCount`.
|
||||
- Docs repository at commit containing `docs/modules/signals/events/scanner.report.ready@1.json` with `quietedFindingCount`.
|
||||
- Access to a Scanner environment (staging or sandbox) with an image capable of producing policy verdicts.
|
||||
|
||||
---
|
||||
@@ -26,15 +26,15 @@ This runbook confirms that Scanner.WebService now surfaces the metadata Runtime
|
||||
2. **Check emitted event** – pull the latest `scanner.report.ready` event (from the queue or sample capture). Confirm the payload includes:
|
||||
- `quietedFindingCount` equal to the `summary.quieted` value.
|
||||
- Updated `summary` block with the quieted counter.
|
||||
3. **Schema validation** – optionally validate the payload against `docs/events/scanner.report.ready@1.json` to guarantee downstream compatibility:
|
||||
3. **Schema validation** – optionally validate the payload against `docs/modules/signals/events/scanner.report.ready@1.json` to guarantee downstream compatibility:
|
||||
```bash
|
||||
npx ajv validate -c ajv-formats \
|
||||
-s docs/events/scanner.report.ready@1.json \
|
||||
-s docs/modules/signals/events/scanner.report.ready@1.json \
|
||||
-d <payload.json>
|
||||
```
|
||||
(Use `npm install --no-save ajv ajv-cli ajv-formats` once per clone.)
|
||||
|
||||
> Snapshot fixtures: see `docs/events/samples/scanner.event.report.ready@1.sample.json` for a canonical orchestrator event that already carries `quietedFindingCount`.
|
||||
> Snapshot fixtures: see `docs/modules/signals/events/samples/scanner.event.report.ready@1.sample.json` for a canonical orchestrator event that already carries `quietedFindingCount`.
|
||||
|
||||
---
|
||||
|
||||
|
||||
147
docs/modules/scanner/scanner-core-contracts.md
Normal file
147
docs/modules/scanner/scanner-core-contracts.md
Normal file
@@ -0,0 +1,147 @@
|
||||
# Scanner Core Contracts
|
||||
|
||||
The **Scanner Core** library provides shared contracts, observability helpers, and security utilities consumed by `Scanner.WebService`, `Scanner.Worker`, analyzers, and tooling. These primitives guarantee deterministic identifiers, timestamps, and log context for all scanning flows.
|
||||
|
||||
## Canonical DTOs
|
||||
|
||||
- `ScanJob` & `ScanJobStatus` – canonical job metadata (image reference/digest, tenant, correlation ID, timestamps, failure details). Constructors normalise timestamps to UTC microsecond precision and canonicalise image digests. Round-trips with `JsonSerializerDefaults.Web` using `ScannerJsonOptions`.
|
||||
- `ScanProgressEvent` & `ScanStage`/`ScanProgressEventKind` – stage-level progress surface for queue/stream consumers. Includes deterministic sequence numbers, optional progress percentage, attributes, and attached `ScannerError`.
|
||||
- `ScannerError` & `ScannerErrorCode` – shared error taxonomy spanning queue, analyzers, storage, exporters, and signing. Carries severity, retryability, structured details, and microsecond-precision timestamps.
|
||||
- `ScanJobId` – strongly-typed identifier rendered as `Guid` (lowercase `N` format) with deterministic parsing.
|
||||
|
||||
### Canonical JSON samples
|
||||
|
||||
The golden fixtures consumed by `ScannerCoreContractsTests` document the wire shape shared with downstream services. They live under `src/Scanner/__Tests/StellaOps.Scanner.Core.Tests/Fixtures/` and a representative extract is shown below.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "8f4cc9c582454b9d9b4f5ae049631b7d",
|
||||
"status": "running",
|
||||
"imageReference": "registry.example.com/stellaops/scanner:1.2.3",
|
||||
"imageDigest": "sha256:abcdef",
|
||||
"createdAt": "2025-10-18T14:30:15.123456+00:00",
|
||||
"updatedAt": "2025-10-18T14:30:20.123456+00:00",
|
||||
"correlationId": "scan-analyzeoperatingsystem-8f4cc9c582454b9d9b4f5ae049631b7d",
|
||||
"tenantId": "tenant-a",
|
||||
"metadata": {
|
||||
"requestId": "req-1234",
|
||||
"source": "ci"
|
||||
},
|
||||
"failure": {
|
||||
"code": "analyzerFailure",
|
||||
"severity": "error",
|
||||
"message": "Analyzer failed to parse layer",
|
||||
"timestamp": "2025-10-18T14:30:15.123456+00:00",
|
||||
"retryable": false,
|
||||
"stage": "AnalyzeOperatingSystem",
|
||||
"component": "os-analyzer",
|
||||
"details": {
|
||||
"layerDigest": "sha256:deadbeef",
|
||||
"attempt": "1"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Progress events follow the same conventions (`jobId`, `stage`, `kind`, `timestamp`, `attributes`, optional embedded `ScannerError`). The fixtures are verified via deterministic JSON comparison in every CI run.
|
||||
|
||||
## Deterministic helpers
|
||||
|
||||
- `ScannerIdentifiers` – derives `ScanJobId`, correlation IDs, and SHA-256 hashes from normalised inputs (image reference/digest, tenant, salt). Ensures case-insensitive stability and reproducible metric keys.
|
||||
- `ScannerTimestamps` – trims to microsecond precision, provides ISO-8601 (`yyyy-MM-ddTHH:mm:ss.ffffffZ`) rendering, and parsing helpers.
|
||||
- `ScannerJsonOptions` – standard JSON options (web defaults, camel-case enums) shared by services/tests.
|
||||
- `ScanAnalysisStore` & `ScanAnalysisKeys` – shared in-memory analysis cache flowing through Worker stages. OS analyzers populate
|
||||
`analysis.os.packages` (raw output), `analysis.os.fragments` (per-analyzer component fragments), and merge into
|
||||
`analysis.layers.fragments` so emit/diff stages can compose SBOMs and diffs without knowledge of individual analyzer
|
||||
implementations.
|
||||
|
||||
## Observability primitives
|
||||
|
||||
- `ScannerDiagnostics` – global `ActivitySource`/`Meter` for scanner components. `StartActivity` seeds deterministic tags (`job_id`, `stage`, `component`, `correlation_id`).
|
||||
- `ScannerMetricNames` – centralises metric prefixes (`stellaops.scanner.*`) and deterministic job/event tag builders.
|
||||
- `ScannerCorrelationContext` & `ScannerCorrelationContextAccessor` – ambient correlation propagation via `AsyncLocal` for log scopes, metrics, and diagnostics.
|
||||
- `ScannerLogExtensions` – `ILogger` scopes for jobs/progress events with automatic correlation context push, minimal allocations, and consistent structured fields.
|
||||
|
||||
### Observability overhead validation
|
||||
|
||||
A micro-benchmark executed on 2025-10-19 (4 vCPU runner, .NET 10.0.100-rc.1) measured the average scope cost across 1 000 000 iterations:
|
||||
|
||||
| Scope | Mean (µs/call) |
|
||||
|-------|----------------|
|
||||
| `BeginScanScope` (logger attached) | 0.80 |
|
||||
| `BeginScanScope` (noop logger) | 0.31 |
|
||||
| `BeginProgressScope` | 0.57 |
|
||||
|
||||
To reproduce, run `dotnet test src/Scanner/__Tests/StellaOps.Scanner.Core.Tests -c Release` (see `ScannerLogExtensionsPerformanceTests`) or copy the snippet below into a throwaway `dotnet run` console project and execute it with `dotnet run -c Release`:
|
||||
|
||||
```csharp
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using StellaOps.Scanner.Core.Contracts;
|
||||
using StellaOps.Scanner.Core.Observability;
|
||||
using StellaOps.Scanner.Core.Utility;
|
||||
|
||||
var factory = LoggerFactory.Create(builder => builder.AddFilter(static _ => true));
|
||||
var logger = factory.CreateLogger("bench");
|
||||
|
||||
var jobId = ScannerIdentifiers.CreateJobId("registry.example.com/stellaops/scanner:1.2.3", "sha256:abcdef", "tenant-a", "benchmark");
|
||||
var correlationId = ScannerIdentifiers.CreateCorrelationId(jobId, nameof(ScanStage.AnalyzeOperatingSystem));
|
||||
var now = ScannerTimestamps.Normalize(new DateTimeOffset(2025, 10, 19, 12, 0, 0, TimeSpan.Zero));
|
||||
|
||||
var job = new ScanJob(jobId, ScanJobStatus.Running, "registry.example.com/stellaops/scanner:1.2.3", "sha256:abcdef", now, now, correlationId, "tenant-a", new Dictionary<string, string>(StringComparer.Ordinal) { ["requestId"] = "req-bench" });
|
||||
var progress = new ScanProgressEvent(jobId, ScanStage.AnalyzeOperatingSystem, ScanProgressEventKind.Progress, 42, now, 10.5, "benchmark", new Dictionary<string, string>(StringComparer.Ordinal) { ["sample"] = "true" });
|
||||
|
||||
Console.WriteLine("Scanner Core Observability micro-bench (1,000,000 iterations)");
|
||||
Report("BeginScanScope (logger)", Measure(static ctx => ctx.Logger.BeginScanScope(ctx.Job, ctx.Stage, ctx.Component), new ScopeContext(logger, job, nameof(ScanStage.AnalyzeOperatingSystem), "os-analyzer")));
|
||||
Report("BeginScanScope (no logger)", Measure(static ctx => ScannerLogExtensions.BeginScanScope(null, ctx.Job, ctx.Stage, ctx.Component), new ScopeContext(logger, job, nameof(ScanStage.AnalyzeOperatingSystem), "os-analyzer")));
|
||||
Report("BeginProgressScope", Measure(static ctx => ctx.Logger.BeginProgressScope(ctx.Progress!, ctx.Component), new ScopeContext(logger, job, nameof(ScanStage.AnalyzeOperatingSystem), "os-analyzer", progress)));
|
||||
|
||||
static double Measure(Func<ScopeContext, IDisposable> factory, ScopeContext context)
|
||||
{
|
||||
const int iterations = 1_000_000;
|
||||
for (var i = 0; i < 10_000; i++)
|
||||
{
|
||||
using var scope = factory(context);
|
||||
}
|
||||
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
GC.Collect();
|
||||
|
||||
var sw = Stopwatch.StartNew();
|
||||
for (var i = 0; i < iterations; i++)
|
||||
{
|
||||
using var scope = factory(context);
|
||||
}
|
||||
|
||||
sw.Stop();
|
||||
return sw.Elapsed.TotalSeconds * 1_000_000 / iterations;
|
||||
}
|
||||
|
||||
static void Report(string label, double microseconds)
|
||||
=> Console.WriteLine($"{label,-28}: {microseconds:F3} µs");
|
||||
|
||||
readonly record struct ScopeContext(ILogger Logger, ScanJob Job, string? Stage, string? Component, ScanProgressEvent? Progress = null);
|
||||
```
|
||||
|
||||
Both guardrails enforce the ≤ 5 µs acceptance target for SP9-G1.
|
||||
|
||||
## Security utilities
|
||||
|
||||
- `AuthorityTokenSource` – caches short-lived OpToks per audience+scope using deterministic keys and refresh skew (default 30 s). Integrates with `StellaOps.Auth.Client`.
|
||||
- `DpopProofValidator` – validates DPoP proofs (alg allowlist, `htm`/`htu`, nonce, replay window, signature) backed by pluggable `IDpopReplayCache`. Ships with `InMemoryDpopReplayCache` for restart-only deployments.
|
||||
- `RestartOnlyPluginGuard` – enforces restart-time plug-in registration (deterministic path normalisation; throws if new plug-ins added post-seal).
|
||||
- `ServiceCollectionExtensions.AddScannerAuthorityCore` – DI helper wiring Authority client, OpTok source, DPoP validation, replay cache, and plug-in guard.
|
||||
|
||||
## Testing guarantees
|
||||
|
||||
Unit tests (`StellaOps.Scanner.Core.Tests`) assert:
|
||||
|
||||
- DTO JSON round-trips are stable and deterministic (`ScannerCoreContractsTests` + golden fixtures).
|
||||
- Identifier/hash helpers ignore case and emit lowercase hex.
|
||||
- Timestamp normalisation retains UTC semantics.
|
||||
- Log scopes push/pop correlation context predictably while staying under the 5 µs envelope.
|
||||
- Authority token caching honours refresh skew and invalidation.
|
||||
- DPoP validator accepts valid proofs, rejects nonce mismatch/replay, and enforces signature validation.
|
||||
- Restart-only plug-in guard blocks runtime additions post-seal.
|
||||
@@ -16,7 +16,7 @@ This contract defines the provenance tracking for runtime facts, callgraph stora
|
||||
|
||||
| Schema | Location |
|
||||
|--------|----------|
|
||||
| Provenance Feed | `docs/schemas/provenance-feed.schema.json` |
|
||||
| Provenance Feed | `docs/modules/signals/schemas/provenance-feed.schema.json` |
|
||||
| Runtime Facts | `docs/modules/signals/guides/runtime-facts.md` |
|
||||
| Reachability Input | `docs/modules/policy/contracts/reachability-input-contract.md` |
|
||||
|
||||
|
||||
@@ -51,19 +51,19 @@ For Scanner orchestrator events, `links` include console and API deep links (`re
|
||||
When adding new optional fields, document the behaviour in the schema’s `description` block and update the consumer checklist in the next sprint sync.
|
||||
|
||||
## Canonical samples & validation
|
||||
Reference payloads live under `docs/events/samples/`, mirroring the schema version (`<event-name>@<version>.sample.json`). They illustrate common field combinations, including the optional attributes that downstream teams rely on for UI affordances and audit trails. Scanner samples reuse the exact DSSE envelope checked into `samples/api/reports/report-sample.dsse.json`, and unit tests (`ReportSamplesTests`, `PlatformEventSchemaValidationTests`) guard that payloads stay canonical and continue to satisfy the published schemas.
|
||||
Reference payloads live under `docs/modules/signals/events/samples/`, mirroring the schema version (`<event-name>@<version>.sample.json`). They illustrate common field combinations, including the optional attributes that downstream teams rely on for UI affordances and audit trails. Scanner samples reuse the exact DSSE envelope checked into `samples/api/reports/report-sample.dsse.json`, and unit tests (`ReportSamplesTests`, `PlatformEventSchemaValidationTests`) guard that payloads stay canonical and continue to satisfy the published schemas.
|
||||
|
||||
Run the following loop offline to validate both schemas and samples:
|
||||
|
||||
```bash
|
||||
# Validate schemas (same check as CI)
|
||||
for schema in docs/events/*.json; do
|
||||
for schema in docs/modules/signals/events/*.json; do
|
||||
npx ajv compile -c ajv-formats -s "$schema"
|
||||
done
|
||||
|
||||
# Validate canonical samples against their schemas
|
||||
for sample in docs/events/samples/*.sample.json; do
|
||||
schema="docs/events/$(basename "${sample%.sample.json}").json"
|
||||
for sample in docs/modules/signals/events/samples/*.sample.json; do
|
||||
schema="docs/modules/signals/events/$(basename "${sample%.sample.json}").json"
|
||||
npx ajv validate -c ajv-formats -s "$schema" -d "$sample"
|
||||
done
|
||||
```
|
||||
@@ -74,7 +74,7 @@ Consumers can copy the samples into integration tests to guarantee backwards com
|
||||
The Docs CI workflow (`.gitea/workflows/docs.yml`) installs `ajv-cli` and compiles every schema on pull requests. Run the same check locally before opening a PR:
|
||||
|
||||
```bash
|
||||
for schema in docs/events/*.json; do
|
||||
for schema in docs/modules/signals/events/*.json; do
|
||||
npx ajv compile -c ajv-formats -s "$schema"
|
||||
done
|
||||
```
|
||||
@@ -86,6 +86,6 @@ If a schema references additional files, include `-r` flags so CI and local runs
|
||||
## Working with schemas
|
||||
- Producers should validate outbound payloads using the matching schema during unit tests.
|
||||
- Consumers should pin to a specific version and log when encountering unknown versions to catch missing migrations early.
|
||||
- Store real payload samples under `docs/events/samples/` (mirrors the schema version) and mirror them into `samples/events/` when you need fixtures in integration repositories.
|
||||
- Store real payload samples under `docs/modules/signals/events/samples/` (mirrors the schema version) and mirror them into `samples/events/` when you need fixtures in integration repositories.
|
||||
|
||||
Contact the Platform Events group in Docs Guild if you need help shaping a new event or version strategy.
|
||||
|
||||
@@ -24,7 +24,7 @@ Orchestrator events share a deterministic JSON envelope:
|
||||
| `attributes` | `object` | Flat string map for frequently queried metadata (e.g., policy revision). |
|
||||
| `payload` | `object` | Event-specific body (see §2). |
|
||||
|
||||
Canonical schemas live under `docs/events/scanner.event.*@1.json`. Samples that round-trip through `NotifyCanonicalJsonSerializer` are stored in `docs/events/samples/`.
|
||||
Canonical schemas live under `docs/modules/signals/events/scanner.event.*@1.json`. Samples that round-trip through `NotifyCanonicalJsonSerializer` are stored in `docs/modules/signals/events/samples/`.
|
||||
|
||||
## 2. Event kinds and payloads
|
||||
|
||||
@@ -53,8 +53,8 @@ Emitted once a signed report is persisted and attested. Payload highlights:
|
||||
- `dsse` — embedded DSSE envelope (payload, type, signature list).
|
||||
- `report` — canonical report document; identical to the DSSE payload.
|
||||
|
||||
Schema: `docs/events/scanner.event.report.ready@1.json`
|
||||
Sample: `docs/events/samples/scanner.event.report.ready@1.sample.json`
|
||||
Schema: `docs/modules/signals/events/scanner.event.report.ready@1.json`
|
||||
Sample: `docs/modules/signals/events/samples/scanner.event.report.ready@1.sample.json`
|
||||
|
||||
### 2.2 `scanner.event.scan.completed`
|
||||
|
||||
@@ -67,8 +67,8 @@ Emitted after scan execution finishes (success or policy failure). Payload highl
|
||||
- `findings` — array of surfaced findings with `id`, `severity`, optional `cve`, `purl`, and `reachability`.
|
||||
- `links`, `dsse`, `report` — same structure as §2.1 (allows Notifier to reuse signatures).
|
||||
|
||||
Schema: `docs/events/scanner.event.scan.completed@1.json`
|
||||
Sample: `docs/events/samples/scanner.event.scan.completed@1.sample.json`
|
||||
Schema: `docs/modules/signals/events/scanner.event.scan.completed@1.json`
|
||||
Sample: `docs/modules/signals/events/samples/scanner.event.scan.completed@1.sample.json`
|
||||
|
||||
### 2.3 Relationship to legacy events
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# TaskRunner Architecture (v1)
|
||||
|
||||
> Canonical contract for TaskRunner delivery scoped by SPRINT_0157_0001_0002 (TaskRunner Blockers) and SPRINT_0157_0001_0001 (TaskRunner I). Anchored in product advisory **"29-Nov-2025 - Task Pack Orchestration and Automation"** and the Task Pack runbook/spec (`docs/task-packs/*.md`).
|
||||
> Canonical contract for TaskRunner delivery scoped by SPRINT_0157_0001_0002 (TaskRunner Blockers) and SPRINT_0157_0001_0001 (TaskRunner I). Anchored in product advisory **"29-Nov-2025 - Task Pack Orchestration and Automation"** and the Task Pack runbook/spec (`docs/modules/packs-registry/guides/*.md`).
|
||||
|
||||
## 1. Purpose and Scope
|
||||
- Execute Task Packs deterministically with approvals, sealed-mode enforcement, and evidence capture.
|
||||
@@ -89,11 +89,11 @@
|
||||
- **Deterministic ordering/RNG/time (TP5):** Execution order derives from the canonical graph, RNG seed is derived from `planHash`, and all timestamps are UTC ISO-8601 with monotonic log sequences.
|
||||
- **Sandbox + egress quotas (TP6):** Runs declare `sandbox.mode` (`sealed`/`restricted`), explicit `egressAllowlist`, CPU/memory limits, and optional wall-clock quota. Missing entries cause fail-closed refusal during plan or execution.
|
||||
- **Registry signing + SBOM + revocation (TP7):** Packs accepted by Task Runner must include DSSE envelopes for bundle + attestation, a pack SBOM, and a revocation list path; imports fail when digests or revocation proofs are absent.
|
||||
- **Offline bundle schema + verifier (TP8):** Offline bundles must satisfy `docs/task-packs/packs-offline-bundle.schema.json` and pass `scripts/packs/verify_offline_bundle.py --require-dsse`. Evidence locker records the verifier version used.
|
||||
- **Offline bundle schema + verifier (TP8):** Offline bundles must satisfy `docs/modules/packs-registry/guides/packs-offline-bundle.schema.json` and pass `scripts/packs/verify_offline_bundle.py --require-dsse`. Evidence locker records the verifier version used.
|
||||
- **Run/approval SLOs (TP9):** Plan validation enforces declared SLOs (`runP95Seconds`, `approvalP95Seconds`, `maxQueueDepth`) and wires alert rules into telemetry (burn-rate alerts on approval latency + queue depth).
|
||||
- **Fail-closed gates (TP10):** Approval/policy/timeline gates default to fail-closed on missing evidence, expired DSSE, or absent quotas; remediation hints surface in `pack_run_logs` and API error payloads.
|
||||
|
||||
## 13. References
|
||||
- Product advisory: `docs/product-advisories/29-Nov-2025 - Task Pack Orchestration and Automation.md`.
|
||||
- Task Pack spec + authoring + runbook: `docs/task-packs/spec.md`, `docs/task-packs/authoring-guide.md`, `docs/task-packs/runbook.md`.
|
||||
- Task Pack spec + authoring + runbook: `docs/modules/packs-registry/guides/spec.md`, `docs/modules/packs-registry/guides/authoring-guide.md`, `docs/modules/packs-registry/guides/runbook.md`.
|
||||
- Migration detail: `docs/modules/taskrunner/migrations/pack-run-collections.md`.
|
||||
|
||||
@@ -423,7 +423,7 @@ Load tests validate TTFS performance under realistic conditions.
|
||||
- Sprint 3 (UI): `docs/implplan/SPRINT_0340_0001_0001_first_signal_card_ui.md`
|
||||
- Sprint 4 (Enhancements): `docs/implplan/SPRINT_0341_0001_0001_ttfs_enhancements.md`
|
||||
- TTE Architecture: `docs/modules/telemetry/architecture.md`
|
||||
- Telemetry Schema: `docs/schemas/ttfs-event.schema.json`
|
||||
- Telemetry Schema: `docs/modules/telemetry/schemas/ttfs-event.schema.json`
|
||||
- Database Schema: `docs/db/schemas/ttfs.sql`
|
||||
- Grafana Dashboard: `docs/modules/telemetry/operations/dashboards/ttfs-observability.json`
|
||||
- Alert Rules: `docs/modules/telemetry/operations/alerts/ttfs-alerts.yaml`
|
||||
|
||||
@@ -121,7 +121,7 @@ Each feature folder builds as a **standalone route** (lazy loaded). All HTTP sha
|
||||
* **Workspace**: artifact-first split layout (finding cards on the left; explainability tabs on the right: Overview, Reachability, Policy, Attestations).
|
||||
* **VEX decisions**: evidence-first VEX modal with scope + validity + evidence links; bulk apply supported; uses `/v1/vex-decisions`.
|
||||
* **Audit bundles**: "Create immutable audit bundle" UX to build and download an evidence pack; uses `/v1/audit-bundles`.
|
||||
* **Schemas**: `docs/schemas/vex-decision.schema.json`, `docs/schemas/attestation-vuln-scan.schema.json`, `docs/schemas/audit-bundle-index.schema.json`.
|
||||
* **Schemas**: `docs/modules/vuln-explorer/schemas/vex-decision.schema.json`, `docs/modules/attestor/schemas/attestation-vuln-scan.schema.json`, `docs/modules/evidence-locker/schemas/audit-bundle-index.schema.json`.
|
||||
* **Reference**: `docs/product-advisories/archived/27-Nov-2025-superseded/28-Nov-2025 - Vulnerability Triage UX & VEX-First Decisioning.md`.
|
||||
|
||||
### 3.10 Integration Hub (Sprint 011)
|
||||
@@ -212,13 +212,13 @@ Each feature folder builds as a **standalone route** (lazy loaded). All HTTP sha
|
||||
* **SSE** helper (EventSource) with auto‑reconnect & backpressure.
|
||||
* **DPoP** injector & nonce handling.
|
||||
|
||||
* Typed API clients (DTOs in `core/api/models.ts`):
|
||||
|
||||
* `ScannerApi`, `PolicyApi`, `ExcititorApi`, `ConcelierApi`, `AttestorApi`, `AuthorityApi`.
|
||||
|
||||
* **Offline-first UX**: Ops dashboards must display a "data as of" banner with staleness thresholds when serving cached snapshots.
|
||||
|
||||
**DTO examples (abbrev):**
|
||||
* Typed API clients (DTOs in `core/api/models.ts`):
|
||||
|
||||
* `ScannerApi`, `PolicyApi`, `ExcititorApi`, `ConcelierApi`, `AttestorApi`, `AuthorityApi`.
|
||||
|
||||
* **Offline-first UX**: Ops dashboards must display a "data as of" banner with staleness thresholds when serving cached snapshots.
|
||||
|
||||
**DTO examples (abbrev):**
|
||||
|
||||
```ts
|
||||
export type ImageDigest = `sha256:${string}`;
|
||||
|
||||
@@ -22,7 +22,7 @@ See:
|
||||
|
||||
- `docs/security/scopes-and-roles.md`
|
||||
- `docs/security/tenancy-overview.md`
|
||||
- `docs/architecture/console-admin-rbac.md`
|
||||
- `docs/technical/architecture/console-admin-rbac.md`
|
||||
|
||||
## Safety and Auditability
|
||||
|
||||
|
||||
96
docs/modules/vuln-explorer/VULNERABILITY_EXPLORER_GUIDE.md
Normal file
96
docs/modules/vuln-explorer/VULNERABILITY_EXPLORER_GUIDE.md
Normal file
@@ -0,0 +1,96 @@
|
||||
# Vulnerability Explorer and Findings Ledger (Guide)
|
||||
|
||||
The Vulnerability Explorer is the StellaOps interface for vulnerability triage and remediation planning. It brings together SBOM facts, advisory/VEX evidence, reachability signals, and policy explanations into a single, auditable workflow.
|
||||
|
||||
This guide is intentionally conceptual. Concrete schemas, identifiers, and endpoint shapes are defined in the module dossiers and schema files.
|
||||
|
||||
## Core Objects
|
||||
|
||||
- **Finding record:** the current, enriched view of a vulnerability for a specific artifact/context (tenant, image digest/artifact id, policy version).
|
||||
- **Finding history:** append-only state transitions (who/what changed status and why), suitable for audit replay.
|
||||
- **Triage actions:** discrete operator actions (assignment, comment, mitigation note, ticket link, exception request) with provenance.
|
||||
- **Evidence references:** stable pointers to SBOM slices, advisory observations, VEX observations/linksets, reachability proofs, and attestation bundles.
|
||||
|
||||
## Triage UX Contract (Console)
|
||||
|
||||
Every triage surface should answer, in order:
|
||||
|
||||
1. Can I ship this?
|
||||
2. If not, what exactly blocks me?
|
||||
3. What's the minimum safe change to unblock?
|
||||
|
||||
Key expectations:
|
||||
|
||||
- **Narrative-first:** the default view for a finding is a case-style summary ("why") plus a visible evidence rail.
|
||||
- **Proof-linking is mandatory:** every chip/badge/assertion links to the evidence objects that justify it.
|
||||
- **Quiet by default, never silent:** muted/non-actionable lanes are hidden by default but surfaced via counts and toggles; muting never deletes evidence.
|
||||
- **Replayable:** the UI should support exporting a deterministic evidence bundle for offline/audit verification.
|
||||
|
||||
## Workflow (Operator View)
|
||||
|
||||
1. Start from a finding list filtered to the relevant tenant and time window.
|
||||
2. Open a finding to review:
|
||||
- Policy outcome (block/ship/needs exception)
|
||||
- Effective VEX status (and the underlying issuer evidence)
|
||||
- Reachability/impact signals (where available)
|
||||
- Advisory provenance and conflicts
|
||||
3. Record a triage action (assign, comment, request exception) with justification.
|
||||
4. Export an evidence bundle when review, escalation, or offline verification is required.
|
||||
|
||||
The default posture is VEX-first: VEX evidence and issuer trust are treated as first-class inputs to decisioning and explainability.
|
||||
|
||||
## Lanes and Signed Decisions
|
||||
|
||||
Most UIs need "lanes" (visibility buckets) derived from deterministic risk and operator decisions. Common examples:
|
||||
|
||||
- `ACTIVE`
|
||||
- `BLOCKED`
|
||||
- `NEEDS_EXCEPTION`
|
||||
- `MUTED_REACH` (not reachable)
|
||||
- `MUTED_VEX` (effective VEX is not_affected)
|
||||
- `COMPENSATED` (controls satisfy policy)
|
||||
|
||||
Decisions that change visibility or gating should be:
|
||||
|
||||
- Signed and auditable (who did what, when, and why).
|
||||
- Append-only (revoke/expire instead of delete).
|
||||
- Linked to the policy and evidence that justified the change.
|
||||
|
||||
## Smart-Diff History
|
||||
|
||||
The Explorer should make meaningful changes obvious:
|
||||
|
||||
- Maintain immutable snapshots of inputs/outputs for each finding.
|
||||
- Highlight meaningful changes (verdict/lane changes, threshold crossings, reachability changes, effective VEX changes).
|
||||
- Keep "details" available without overwhelming the default view.
|
||||
|
||||
## Determinism, Integrity, and Replay
|
||||
|
||||
The Explorer is designed to be replayable and tamper-evident:
|
||||
|
||||
- History and actions are append-only.
|
||||
- Exports use deterministic ordering and UTC timestamps.
|
||||
- Evidence bundles carry hashes/manifests so a third party can verify integrity without trusting a live service.
|
||||
- When Merkle anchoring is enabled, exports can include roots and inclusion proofs for additional tamper evidence.
|
||||
|
||||
## Offline / Air-Gap Operation
|
||||
|
||||
- Explorer workflows must work against Offline Kit snapshots when running in sealed environments.
|
||||
- The Console should surface snapshot identity and staleness (feeds, VEX, policy versions) rather than hiding it.
|
||||
- Export bundles are the primary bridge between online and offline review.
|
||||
|
||||
## Integration Points
|
||||
|
||||
- **Console UI:** findings list + triage case view; evidence drawers; export/download flows.
|
||||
- **Policy engine:** produces explainability traces and gates actions (for example, exception workflows).
|
||||
- **Graph/Reachability:** overlays and evidence slices for reachable vs not reachable decisions where available.
|
||||
- **VEX Lens / Excititor:** issuer trust, provenance, linksets, and effective status (see `docs/VEX_CONSENSUS_GUIDE.md`).
|
||||
|
||||
## Related Docs
|
||||
|
||||
- `docs/UI_GUIDE.md`
|
||||
- `docs/VEX_CONSENSUS_GUIDE.md`
|
||||
- `docs/modules/vuln-explorer/architecture.md`
|
||||
- `docs/modules/findings-ledger/schema.md`
|
||||
- `docs/modules/findings-ledger/merkle-anchor-policy.md`
|
||||
- `docs/ARCHITECTURE_OVERVIEW.md`
|
||||
@@ -98,7 +98,7 @@ Primary actions per card:
|
||||
|
||||
### 8.2 VEX Decision Model
|
||||
|
||||
VEX decisions follow the `VexDecision` schema (`docs/schemas/vex-decision.schema.json`):
|
||||
VEX decisions follow the `VexDecision` schema (`docs/modules/vuln-explorer/schemas/vex-decision.schema.json`):
|
||||
|
||||
**Status values:**
|
||||
- `NOT_AFFECTED` - Vulnerability does not apply to this artifact
|
||||
@@ -160,7 +160,7 @@ Request/response follows `VexDecisionDto` per schema.
|
||||
|
||||
### 8.5 Audit Bundle Export
|
||||
|
||||
Immutable audit bundles follow the `AuditBundleIndex` schema (`docs/schemas/audit-bundle-index.schema.json`):
|
||||
Immutable audit bundles follow the `AuditBundleIndex` schema (`docs/modules/evidence-locker/schemas/audit-bundle-index.schema.json`):
|
||||
|
||||
**Bundle contents:**
|
||||
- Vulnerability reports (scanner outputs)
|
||||
@@ -192,8 +192,8 @@ The triage UX aligns with industry patterns from:
|
||||
|
||||
The following JSON schemas define the data contracts for VEX and audit functionality:
|
||||
|
||||
- `docs/schemas/vex-decision.schema.json` - VEX decision form and persistence
|
||||
- `docs/schemas/attestation-vuln-scan.schema.json` - Vulnerability scan attestation predicate
|
||||
- `docs/schemas/audit-bundle-index.schema.json` - Audit bundle manifest
|
||||
- `docs/modules/vuln-explorer/schemas/vex-decision.schema.json` - VEX decision form and persistence
|
||||
- `docs/modules/attestor/schemas/attestation-vuln-scan.schema.json` - Vulnerability scan attestation predicate
|
||||
- `docs/modules/evidence-locker/schemas/audit-bundle-index.schema.json` - Audit bundle manifest
|
||||
|
||||
These schemas are referenced by both backend DTOs and frontend TypeScript interfaces.
|
||||
|
||||
Reference in New Issue
Block a user