diff --git a/.gitea/workflows/build-test-deploy.yml b/.gitea/workflows/build-test-deploy.yml index 291f84a7..61825e84 100644 --- a/.gitea/workflows/build-test-deploy.yml +++ b/.gitea/workflows/build-test-deploy.yml @@ -64,6 +64,9 @@ jobs: with: fetch-depth: 0 + - name: Verify policy scope configuration + run: python3 scripts/verify-policy-scopes.py + - name: Validate NuGet restore source ordering run: python3 ops/devops/validate_restore_sources.py diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index 63f091f6..0ef66cf7 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -139,6 +139,14 @@ jobs: - name: Install Cosign uses: sigstore/cosign-installer@v3.4.0 + - name: Install Syft + run: | + set -euo pipefail + SYFT_VERSION="v1.21.0" + curl -fsSL "https://github.com/anchore/syft/releases/download/${SYFT_VERSION}/syft_${SYFT_VERSION#v}_linux_amd64.tar.gz" -o /tmp/syft.tgz + tar -xzf /tmp/syft.tgz -C /tmp + sudo install -m 0755 /tmp/syft /usr/local/bin/syft + - name: Determine release metadata id: meta run: | @@ -182,6 +190,10 @@ jobs: echo "calendar=$CALENDAR_INPUT" >> "$GITHUB_OUTPUT" echo "push=$PUSH_FLAG" >> "$GITHUB_OUTPUT" + - name: Enforce CLI parity gate + run: | + python3 ops/devops/check_cli_parity.py + - name: Log in to registry if: steps.meta.outputs.push == 'true' uses: docker/login-action@v3 diff --git a/.gitignore b/.gitignore index 31af354d..e4166469 100644 --- a/.gitignore +++ b/.gitignore @@ -31,5 +31,4 @@ src/StellaOps.Web/node_modules/**/* src/StellaOps.Web/.angular/**/* **/node_modules/**/* node_modules -node_modules -node_modules +tmp/**/* diff --git a/.venv/pyvenv.cfg b/.venv/pyvenv.cfg new file mode 100644 index 00000000..ecf82ea0 --- /dev/null +++ b/.venv/pyvenv.cfg @@ -0,0 +1,5 @@ +home = /usr/bin +include-system-site-packages = false +version = 3.12.3 +executable = /usr/bin/python3.12 +command = /usr/bin/python3 -m venv /mnt/e/dev/git.stella-ops.org/.venv diff --git a/SPRINTS.md b/SPRINTS.md index a28244c2..dd09a0db 100644 --- a/SPRINTS.md +++ b/SPRINTS.md @@ -31,7 +31,7 @@ This file describe implementation of Stella Ops (docs/README.md). Implementation | Sprint 19 | Aggregation-Only Contract Enforcement | src/StellaOps.Concelier.Storage.Mongo/TASKS.md | TODO | Concelier Storage Guild | CONCELIER-STORE-AOC-19-002 | Create idempotency unique index backed by migration scripts. | | Sprint 19 | Aggregation-Only Contract Enforcement | src/StellaOps.Concelier.Storage.Mongo/TASKS.md | TODO | Concelier Storage Guild | CONCELIER-STORE-AOC-19-003 | Deliver append-only migration/backfill plan with supersedes chaining. | | Sprint 19 | Aggregation-Only Contract Enforcement | src/StellaOps.Concelier.Storage.Mongo/TASKS.md | TODO | Concelier Storage Guild, DevOps Guild | CONCELIER-STORE-AOC-19-004 | Document validator deployment steps for online/offline clusters. | -| Sprint 19 | Aggregation-Only Contract Enforcement | src/StellaOps.Concelier.WebService/TASKS.md | TODO | Concelier WebService Guild | CONCELIER-WEB-AOC-19-001 | Implement raw advisory ingestion endpoints with AOC guard and verifier. | +| Sprint 19 | Aggregation-Only Contract Enforcement | src/StellaOps.Concelier.WebService/TASKS.md | DONE (2025-10-28) | Concelier WebService Guild | CONCELIER-WEB-AOC-19-001 | Implement raw advisory ingestion endpoints with AOC guard and verifier. | | Sprint 19 | Aggregation-Only Contract Enforcement | src/StellaOps.Concelier.WebService/TASKS.md | TODO | Concelier WebService Guild, Observability Guild | CONCELIER-WEB-AOC-19-002 | Emit AOC observability metrics, traces, and structured logs. | | Sprint 19 | Aggregation-Only Contract Enforcement | src/StellaOps.Concelier.WebService/TASKS.md | TODO | QA Guild | CONCELIER-WEB-AOC-19-003 | Add schema/guard unit tests covering AOC error codes. | | Sprint 19 | Aggregation-Only Contract Enforcement | src/StellaOps.Concelier.WebService/TASKS.md | TODO | Concelier WebService Guild, QA Guild | CONCELIER-WEB-AOC-19-004 | Build integration suite validating deterministic ingest under load. | @@ -50,7 +50,7 @@ This file describe implementation of Stella Ops (docs/README.md). Implementation | Sprint 19 | Aggregation-Only Contract Enforcement | src/StellaOps.Excititor.WebService/TASKS.md | TODO | Excititor WebService Guild, QA Guild | EXCITITOR-WEB-AOC-19-004 | Validate large VEX ingest runs and CLI verification parity. | | Sprint 19 | Aggregation-Only Contract Enforcement | src/StellaOps.Excititor.Worker/TASKS.md | TODO | Excititor Worker Guild | EXCITITOR-WORKER-AOC-19-001 | Rewire worker to persist raw VEX docs with guard enforcement. | | Sprint 19 | Aggregation-Only Contract Enforcement | src/StellaOps.Excititor.Worker/TASKS.md | TODO | Excititor Worker Guild | EXCITITOR-WORKER-AOC-19-002 | Enforce signature/checksum verification prior to raw writes. | -| Sprint 19 | Aggregation-Only Contract Enforcement | src/StellaOps.Excititor.Worker/TASKS.md | TODO | QA Guild | EXCITITOR-WORKER-AOC-19-003 | Expand worker tests for deterministic batching and restart safety. | +| Sprint 19 | Aggregation-Only Contract Enforcement | src/StellaOps.Excititor.Worker/TASKS.md | DONE (2025-10-28) | QA Guild | EXCITITOR-WORKER-AOC-19-003 | Expand worker tests for deterministic batching and restart safety. | | Sprint 19 | Aggregation-Only Contract Enforcement | src/StellaOps.Policy/TASKS.md | TODO | Policy Guild | POLICY-AOC-19-001 | Add lint preventing ingestion modules from referencing Policy-only helpers. | | Sprint 19 | Aggregation-Only Contract Enforcement | src/StellaOps.Policy/TASKS.md | TODO | Policy Guild, Security Guild | POLICY-AOC-19-002 | Enforce Policy-only writes to `effective_finding_*` collections. | | Sprint 19 | Aggregation-Only Contract Enforcement | src/StellaOps.Policy/TASKS.md | TODO | Policy Guild | POLICY-AOC-19-003 | Update Policy readers to consume only raw document fields. | diff --git a/docs/09_API_CLI_REFERENCE.md b/docs/09_API_CLI_REFERENCE.md index df84da3d..c1d0822c 100755 --- a/docs/09_API_CLI_REFERENCE.md +++ b/docs/09_API_CLI_REFERENCE.md @@ -626,6 +626,8 @@ See `docs/dev/32_AUTH_CLIENT_GUIDE.md` for recommended profiles (online vs. air- | `stellaops-cli offline kit pull` | Download the latest offline kit bundle and manifest | `--bundle-id ` (optional)
`--destination `
`--overwrite`
`--no-resume` | Streams the bundle + manifest from the configured mirror/backend, resumes interrupted downloads, verifies SHA-256, and writes signatures plus a `.metadata.json` manifest alongside the artefacts. | | `stellaops-cli offline kit import` | Upload an offline kit bundle to the backend | `` (argument)
`--manifest `
`--bundle-signature `
`--manifest-signature ` | Validates digests when metadata is present, then posts multipart payloads to `POST /api/offline-kit/import`; logs the submitted import ID/status for air-gapped rollout tracking. | | `stellaops-cli offline kit status` | Display imported offline kit details | `--json` | Shows bundle id/kind, captured/imported timestamps, digests, and component versions; `--json` emits machine-readable output for scripting. | +| `stellaops-cli sources ingest --dry-run` | Dry-run guard validation for individual payloads | `--source `
`--input `
`--tenant `
`--format table\|json`
`--output ` | Normalises gzip/base64 payloads, invokes `api/aoc/ingest/dry-run`, and maps guard failures to deterministic `ERR_AOC_00x` exit codes. | +| `stellaops-cli aoc verify` | Replay AOC guardrails over stored documents | `--since `
`--limit `
`--sources `
`--codes `
`--format table\|json`
`--export ` | Summarises checked counts/violations, supports JSON evidence exports, and returns `0`, `11…17`, `18`, `70`, or `71` depending on guard outcomes. | | `stellaops-cli config show` | Display resolved configuration | — | Masks secret values; helpful for air‑gapped installs | | `stellaops-cli runtime policy test` | Ask Scanner.WebService for runtime verdicts (Webhook parity) | `--image/-i ` (repeatable, comma/space lists supported)
`--file/-f `
`--namespace/--ns `
`--label/-l key=value` (repeatable)
`--json` | Posts to `POST /api/v1/scanner/policy/runtime`, deduplicates image digests, and prints TTL/policy revision plus per-image columns for signed state, SBOM referrers, quieted-by metadata, confidence, Rekor attestation (uuid + verified flag), and recently observed build IDs (shortened for readability). Accepts newline/whitespace-delimited stdin when piped; `--json` emits the raw response without additional logging. | diff --git a/docs/10_CONCELIER_CLI_QUICKSTART.md b/docs/10_CONCELIER_CLI_QUICKSTART.md index 3f352fda..a159bb98 100644 --- a/docs/10_CONCELIER_CLI_QUICKSTART.md +++ b/docs/10_CONCELIER_CLI_QUICKSTART.md @@ -167,8 +167,8 @@ rely on environment variables for ephemeral runners. dotnet run --project src/StellaOps.Cli -- db export --format json # Trivy DB (delta example) - dotnet run --project src/StellaOps.Cli -- db export --format trivy-db --delta - ``` + dotnet run --project src/StellaOps.Cli -- db export --format trivy-db --delta + ``` Concelier always produces a deterministic OCI layout. The first run after a clean bootstrap emits a **full** baseline; subsequent `--delta` runs reuse the previous @@ -190,19 +190,58 @@ rely on environment variables for ephemeral runners. jq -r '.mode,.baseExportId' "$delta/metadata.json" - base_manifest=$(jq -r '.manifests[0].digest' "$base/index.json") - delta_manifest=$(jq -r '.manifests[0].digest' "$delta/index.json") - printf 'baseline manifest: %s\ndelta manifest: %s\n' "$base_manifest" "$delta_manifest" + base_manifest=$(jq -r '.manifests[0].digest' "$base/index.json") + delta_manifest=$(jq -r '.manifests[0].digest' "$delta/index.json") + printf 'baseline manifest: %s\ndelta manifest: %s\n' "$base_manifest" "$delta_manifest" - layer_digest=$(jq -r '.layers[0].digest' "$base/blobs/sha256/${base_manifest#sha256:}") - cmp "$base/blobs/sha256/${layer_digest#sha256:}" \ - "$delta/blobs/sha256/${layer_digest#sha256:}" - ``` + layer_digest=$(jq -r '.layers[0].digest' "$base/blobs/sha256/${base_manifest#sha256:}") + cmp "$base/blobs/sha256/${layer_digest#sha256:}" \ + "$delta/blobs/sha256/${layer_digest#sha256:}" + ``` `cmp` returning exit code `0` confirms the delta export reuses the baseline’s `db.tar.gz` layer instead of rebuilding it. -4. **Manage scanners (optional)** +4. **Verify guard compliance** + + ```bash + export STELLA_TENANT="${STELLA_TENANT:-tenant-a}" + + dotnet run --project src/StellaOps.Cli -- aoc verify \ + --since 24h \ + --format table \ + --tenant "$STELLA_TENANT" + + # Optional: capture JSON evidence for pipelines/audits + dotnet run --project src/StellaOps.Cli -- aoc verify \ + --since 7d \ + --limit 100 \ + --format json \ + --export artifacts/aoc-verify.json \ + --tenant "$STELLA_TENANT" + ``` + + The CLI exits with `0` when no violations are detected. Guard failures map + to `ERR_AOC_00x` codes (`11…17`), while truncated results return `18`. Use + `--sources`/`--codes` to focus on noisy connectors and feed the exported JSON + into dashboards or evidence lockers for compliance reviews. + +5. **Pre-flight individual payloads** + + ```bash + stella sources ingest --dry-run \ + --source redhat \ + --input ./fixtures/redhat/RHSA-2025-9999.json \ + --tenant "$STELLA_TENANT" \ + --format json \ + --output artifacts/redhat-dry-run.json + ``` + + Exit code `0` confirms the candidate document is AOC compliant. Any guard + violation is emitted as deterministic `ERR_AOC_00x` exit codes (`11…17`); + reuse the exported JSON in PRs or incident timelines to show offending paths. + +6. **Manage scanners (optional)** ```bash dotnet run --project src/StellaOps.Cli -- scanner download --channel stable diff --git a/docs/11_AUTHORITY.md b/docs/11_AUTHORITY.md index 2944eb61..aaf00014 100644 --- a/docs/11_AUTHORITY.md +++ b/docs/11_AUTHORITY.md @@ -1,54 +1,75 @@ -# 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 MongoDB 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`) and 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. **Mongo storage** – persists tokens, revocations, bootstrap invites, and plugin state in deterministic collections 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 (Mongo authority_tokens) - -> Response (access/refresh tokens + deterministic claims) -``` - -## 3. Token Lifecycle & Persistence -Authority persists every issued token in MongoDB so operators can audit or revoke without scanning distributed caches. - -- **Collection:** `authority_tokens` -- **Key fields:** +# 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 MongoDB 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. **Mongo storage** – persists tokens, revocations, bootstrap invites, and plugin state in deterministic collections 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 (Mongo authority_tokens) + -> Response (access/refresh tokens + deterministic claims) +``` + +## 3. Token Lifecycle & Persistence +Authority persists every issued token in MongoDB so operators can audit or revoke without scanning distributed caches. + +- **Collection:** `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`, `vex:read`, `aoc:verify`, `findings:read`, `orch:read`, `vuln:read` +- **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`) that align with the new `policy:*` scope family; issue them per tenant so audit trails remain scoped. + +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. + +### 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. + +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: - +- **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. @@ -59,7 +80,11 @@ Resource servers (Concelier WebService, Backend, Agent) **must not** assume in-m - 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`, `vex:ingest`, `vex:read`, 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 `advisory:ingest`, `advisory:read`, `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. +- Policy Studio scopes (`policy:author`, `policy:review`, `policy:approve`, `policy:operate`, `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. +- **AOC pairing guardrails** – Tokens that request `advisory:read`, `vex:read`, or any `signals:*` scope must also request `aoc:verify`. Authority rejects mismatches with `invalid_scope` (`Scope 'aoc:verify' is required when requesting advisory/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 @@ -71,10 +96,15 @@ Resource servers (Concelier WebService, Backend, Agent) **must not** assume in-m | `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` | | `vuln-explorer-ui` | Vuln Explorer UI/API | `vuln:read` | `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. 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. @@ -86,79 +116,86 @@ Graph Explorer introduces dedicated scopes: `graph:write` for Cartographer build - **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. + #### Vuln Explorer permalinks - **Scope** – `vuln:read` authorises Vuln Explorer to fetch advisory/linkset evidence and issue shareable links. Assign it only to front-end/API clients that must render vulnerability details. - **Signed links** – `POST /permalinks/vuln` (requires `vuln:read`) accepts `{ "tenant": "tenant-a", "resourceKind": "vulnerability", "state": { ... }, "expiresInSeconds": 86400 }` and returns a JWT (`token`) plus `issuedAt`/`expiresAt`. The token embeds the tenant, requested state, and `vuln:read` scope and is signed with the same Authority signing keys published via `/jwks`. - **Validation** – Resource servers verify the permalink using cached JWKS: check signature, ensure the tenant matches the current request context, honour the expiry, and enforce the contained `vuln:read` scope. The payload’s `resource.state` block is opaque JSON so UIs can round-trip filters/search terms without new schema changes. - -## 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 Mongo documents 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 --signature --key ` 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. -- **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 + +## 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 Mongo documents 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 --signature --key ` 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 | @@ -194,30 +231,53 @@ For environments with multiple tenants, repeat the call per tenant-specific clie - 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 `) 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. ## 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` | MongoDB 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) - -Authority now understands two flavours of sender-constrained OAuth clients: - + +| 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` | MongoDB 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) + +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. - Configure under `security.senderConstraints.dpop`. `allowedAlgorithms`, `proofLifetime`, and `replayWindow` are enforced at validation time. - `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. @@ -256,8 +316,8 @@ Policy Engine v2 introduces dedicated scopes and a service identity that materia | 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:write`, `policy:submit`, `policy:run`, `findings:read` | Keep scopes minimal; only trusted automation should add `policy:approve`/`policy:activate`. | -| UI/editor sessions | `policy:read`, `policy:write`, `policy:simulate` (+ reviewer/approver scopes as appropriate) | Issue tenant-specific clients so audit and rate limits remain scoped. | +| `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: @@ -279,20 +339,42 @@ Sample YAML entry: 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:write`, `policy:submit`, `policy:approve`, `policy:activate`, etc.). +- [ ] 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. | + +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`). + ## 8. Offline & Sovereign Operation - **No outbound dependencies:** Authority only contacts MongoDB 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/31_AUTHORITY_PLUGIN_DEVELOPER_GUIDE.md)**. For revocation bundle validation workflow, see **[Authority Revocation Bundle](security/revocation-bundle.md)**. + +## 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/31_AUTHORITY_PLUGIN_DEVELOPER_GUIDE.md)**. For revocation bundle validation workflow, see **[Authority Revocation Bundle](security/revocation-bundle.md)**. diff --git a/docs/24_OFFLINE_KIT.md b/docs/24_OFFLINE_KIT.md index 1c48a821..f5c358ad 100755 --- a/docs/24_OFFLINE_KIT.md +++ b/docs/24_OFFLINE_KIT.md @@ -68,6 +68,13 @@ Outputs: - `telemetry/telemetry-offline-bundle.tar.gz` + `.sha256` — packaged OTLP collector assets for environments without upstream access - `plugins/scanner/analyzers/lang/StellaOps.Scanner.Analyzers.Lang.Python/*.sig` (+ `.sha256`) — Cosign signatures for the Python analyzer DLL and manifest +### Policy Gateway configuration bundle + +- Copy `etc/policy-gateway.yaml` (or the `*.sample` template if you expect operators to override values) into `config/policy-gateway/policy-gateway.yaml` within the staging tree. +- Include the gateway DPoP private key under `secrets/policy-gateway/policy-gateway-dpop.pem` and reference the location inside the manifest notes. Set the permissions explicitly (`chmod 600 secrets/policy-gateway/policy-gateway-dpop.pem`) so only the kit importer can read it; the importer will refuse keys that are broader. +- Document the gateway base URL and activation verification steps in `docs/policy/gateway.md` (bundled alongside the kit). Operators can use those curl snippets to smoke-test pack CRUD once the Offline Kit is imported. +- Ensure the Prometheus snapshot captured during packaging contains `policy_gateway_activation_requests_total` so auditors can reconcile activation attempts performed via the gateway during the validation window. + Provide `--cosign-key` / `--cosign-identity-token` (and optional `--cosign-password`) to generate Cosign signatures for both the tarball and manifest. --- @@ -189,6 +196,30 @@ The CLI validates recorded digests (when `.metadata.json` is present) before str * Old feeds are kept until the new bundle is fully verified. * Import time on a SATA SSD: ≈ 25 s for a 300 MB kit. +### 2.1 Validator + idempotency enablement (air-gap) + +The Offline Kit carries the same helper scripts under `scripts/`: + +1. **Duplicate audit:** run + ```bash + mongo concelier ops/devops/scripts/check-advisory-raw-duplicates.js --eval 'var LIMIT=200;' + ``` + to verify no `(vendor, upstream_id, content_hash, tenant)` conflicts remain before enabling the idempotency index. +2. **Apply validators:** execute `mongo concelier ops/devops/scripts/apply-aoc-validators.js` (and the Excititor equivalent) with `validationLevel: "moderate"` in maintenance mode. +3. **Restart Concelier** so migrations `20251028_advisory_raw_idempotency_index` and `20251028_advisory_supersedes_backfill` run automatically. After the restart: + - Confirm `db.advisory` resolves to a view on `advisory_backup_20251028`. + - Spot-check a few `advisory_raw` entries to ensure `supersedes` chains are populated deterministically. +4. **Smoke test:** run `stella sources ingest --dry-run --fixture advisory` (bundled fixtures) to confirm ingestion succeeds post-guard and the CLI reports zero violations. + +### Authority scope sanity check + +Offline installs rely on the bundled `etc/authority.yaml.sample`. Before promoting the kit, confirm the sample clients keep the Aggregation-Only guardrails: + +- `aoc-verifier` requests `aoc:verify`, `advisory:read`, and `vex:read`. +- `signals-uploader` requests `signals:write`, `signals:read`, and `aoc:verify`. + +Authority now rejects tokens that request `advisory:read`, `vex:read`, or any `signals:*` scope without `aoc:verify`; the sample has been updated to match. If you maintain tenant-specific overlays, mirror the same pairing so air-gapped automation fails deterministically with `invalid_scope` when misconfigured. + **Quick smoke test:** before import, verify the tarball carries the Go analyzer plug-in: ```bash diff --git a/docs/ARCHITECTURE_CLI.md b/docs/ARCHITECTURE_CLI.md index e3cf9cde..ce2c541f 100644 --- a/docs/ARCHITECTURE_CLI.md +++ b/docs/ARCHITECTURE_CLI.md @@ -103,6 +103,18 @@ src/ * `whoami` — short auth display. * `version` — CLI + protocol versions; release channel. +### 2.9 Aggregation-only guard helpers + +* `sources ingest --dry-run --source --input [--tenant ... --format table|json --output file]` + + * Normalises documents (handles gzip/base64), posts them to the backend `aoc/ingest/dry-run` route, and exits non-zero when guard violations are detected. + * Defaults to table output with ANSI colour; `--json`/`--output` produce deterministic JSON for CI pipelines. + +* `aoc verify [--since ] [--limit ] [--sources list] [--codes list] [--format table|json] [--export file] [--tenant id] [--no-color]` + + * Replays guard checks against stored raw documents. Maps backend `ERR_AOC_00x` codes onto deterministic exit codes so CI can block regressions. + * Supports pagination hints (`--limit`, `--since`), tenant scoping via `--tenant` or `STELLA_TENANT`, and JSON exports for evidence lockers. + --- ## 3) AuthN: Authority + DPoP @@ -154,6 +166,10 @@ CLI rejects verbs if required scopes are missing. | 6 | Rate limited / quota exceeded | | 7 | Backend unavailable (retryable) | | 9 | Invalid arguments | +| 11–17 | Aggregation-only guard violation (`ERR_AOC_00x`) | +| 18 | Verification truncated (increase `--limit`) | +| 70 | Transport/authentication failure | +| 71 | CLI usage error (missing tenant, invalid cursor) | --- diff --git a/docs/README.md b/docs/README.md index e8729f9f..523d6ceb 100755 --- a/docs/README.md +++ b/docs/README.md @@ -79,10 +79,19 @@ Everything here is open‑source and versioned — when you check out a git ta - **68 – [Policy Observability](observability/policy.md)** - **69 – [Console Observability](observability/ui-telemetry.md)** - **70 – [Policy Governance & Least Privilege](security/policy-governance.md)** +- **70a – [Policy Gateway](policy/gateway.md)** - **71 – [Policy Examples](examples/policies/README.md)** - **72 – [Policy FAQ](faq/policy-faq.md)** - **73 – [Policy Run DTOs](../src/StellaOps.Scheduler.Models/docs/SCHED-MODELS-20-001-POLICY-RUNS.md)** - **30 – [Fixture Maintenance](dev/fixtures.md)** +- **74 – [Export Center Overview](export-center/overview.md)** +- **75 – [Export Center Architecture](export-center/architecture.md)** +- **76 – [Export Center Profiles](export-center/profiles.md)** +- **77 – [Export Center API Reference](export-center/api.md)** +- **78 – [Export Center CLI Guide](export-center/cli.md)** +- **79 – [Export Center Trivy Adapters](export-center/trivy-adapter.md)** +- **80 – [Export Center Mirror Bundles](export-center/mirror-bundles.md)** +- **81 – [Export Center Provenance & Signing](export-center/provenance-and-signing.md)** ### User & operator guides - **14 – [Glossary](14_GLOSSARY_OF_TERMS.md)** @@ -118,6 +127,14 @@ Everything here is open‑source and versioned — when you check out a git ta - **42 – [Telemetry Storage Deployment](ops/telemetry-storage.md)** - **43 – [Authority Scopes & Tenancy](security/authority-scopes.md)** - **44 – [Container Deployment (AOC)](deploy/containers.md)** +- **45 – [Export Center Operations Runbook](operations/export-runbook.md)** + +### Notifications Studio +- **81 – [Notifications Overview](notifications/overview.md)** +- **82 – [Notifications Architecture](notifications/architecture.md)** +- **83 – [Notifications Rules](notifications/rules.md)** +- **84 – [Notifications Templates](notifications/templates.md)** +- **85 – [Notifications Digests](notifications/digests.md)** ### Legal & licence - **32 – [Legal & Quota FAQ](29_LEGAL_FAQ_QUOTA.md)** diff --git a/docs/TASKS.md b/docs/TASKS.md index 65dfc384..98984bd4 100644 --- a/docs/TASKS.md +++ b/docs/TASKS.md @@ -157,11 +157,11 @@ | DOCS-CONSOLE-23-013 | DONE (2025-10-28) | Docs Guild, Observability Guild | TELEMETRY-CONSOLE-23-001, CONSOLE-QA-23-403 | Write `/docs/observability/ui-telemetry.md` cataloguing metrics/logs/traces, dashboards, alerts, and feature flags. | Doc merged with instrumentation tables, dashboard screenshots, checklist appended. | | DOCS-CONSOLE-23-014 | DONE (2025-10-28) | Docs Guild, Console Guild, CLI Guild | CONSOLE-DOC-23-502 | Maintain `/docs/cli-vs-ui-parity.md` matrix and integrate CI check guidance. | Matrix published with parity status, CI workflow documented, compliance checklist appended. | > 2025-10-28: Install Docker guide references pending CLI commands (`stella downloads manifest`, `stella downloads mirror`, `stella console status`). Update once CLI parity lands. -| DOCS-CONSOLE-23-015 | TODO | Docs Guild, Architecture Guild | CONSOLE-CORE-23-001, WEB-CONSOLE-23-001 | Produce `/docs/architecture/console.md` describing frontend packages, data flow diagrams, SSE design, performance budgets. | Architecture doc merged with diagrams + compliance checklist; reviewers approve. | +| DOCS-CONSOLE-23-015 | DONE (2025-10-27) | Docs Guild, Architecture Guild | CONSOLE-CORE-23-001, WEB-CONSOLE-23-001 | Produce `/docs/architecture/console.md` describing frontend packages, data flow diagrams, SSE design, performance budgets. | Architecture doc merged with diagrams + compliance checklist; reviewers approve. | | DOCS-CONSOLE-23-016 | DONE (2025-10-28) | Docs Guild, Accessibility Guild | CONSOLE-QA-23-402, CONSOLE-FEAT-23-102 | Refresh `/docs/accessibility.md` with Console-specific keyboard flows, color tokens, testing tools, and compliance checklist updates. | Accessibility doc updated; audits referenced; checklist appended. | > 2025-10-28: Added guide covering keyboard matrix, screen reader behaviour, colour/focus tokens, testing workflow, offline guidance, and compliance checklist. -| DOCS-CONSOLE-23-017 | TODO | Docs Guild, Console Guild | CONSOLE-FEAT-23-101..109 | Create `/docs/examples/ui-tours.md` providing triage, audit, policy rollout walkthroughs with annotated screenshots and GIFs. | UI tours doc merged; media assets stored; compliance checklist appended. | -| DOCS-CONSOLE-23-018 | TODO | Docs Guild, Security Guild | DOCS-CONSOLE-23-012 | Execute console security compliance checklist and capture Security Guild sign-off in Sprint 23 log. | Checklist completed; findings addressed or tickets filed; sign-off noted in updates file. | +| DOCS-CONSOLE-23-017 | DONE (2025-10-27) | Docs Guild, Console Guild | CONSOLE-FEAT-23-101..109 | Create `/docs/examples/ui-tours.md` providing triage, audit, policy rollout walkthroughs with annotated screenshots and GIFs. | UI tours doc merged; capture instructions + asset placeholders committed; compliance checklist appended. | +| DOCS-CONSOLE-23-018 | DONE (2025-10-27) | Docs Guild, Security Guild | DOCS-CONSOLE-23-012 | Execute console security compliance checklist and capture Security Guild sign-off in Sprint 23 log. | Checklist completed; findings addressed or tickets filed; sign-off noted in updates file. | | DOCS-LNM-22-006 | DONE (2025-10-27) | Docs Guild, Architecture Guild | CONCELIER-LNM-21-001..005, EXCITITOR-LNM-21-001..005 | Refresh `/docs/architecture/conseiller.md` and `/docs/architecture/excitator.md` describing observation/linkset pipelines and event contracts. | Architecture docs updated with observation/linkset flow + event tables; revisit once service implementations land. | > Follow-up: align diagrams/examples after `CONCELIER-LNM-21` & `EXCITITOR-LNM-21` work merges (currently TODO). | DOCS-LNM-22-007 | TODO | Docs Guild, Observability Guild | CONCELIER-LNM-21-005, EXCITITOR-LNM-21-005, DEVOPS-LNM-22-002 | Publish `/docs/observability/aggregation.md` with metrics/traces/logs/SLOs. | Observability doc merged; dashboards referenced; checklist appended. | @@ -229,16 +229,20 @@ | ID | Status | Owner(s) | Depends on | Description | Exit Criteria | |----|--------|----------|------------|-------------|---------------| -| DOCS-EXPORT-35-001 | TODO | Docs Guild | EXPORT-SVC-35-001..006 | Author `/docs/export-center/overview.md` covering purpose, profiles, security, AOC alignment, surfaces, ending with imposed rule statement. | Doc merged with diagrams/examples; imposed rule line present; index updated. | -| DOCS-EXPORT-35-002 | TODO | Docs Guild | EXPORT-SVC-35-002..005 | Publish `/docs/export-center/architecture.md` describing planner, adapters, manifests, signing, distribution flows, restating imposed rule. | Architecture doc merged; sequence diagrams included; rule statement appended. | -| DOCS-EXPORT-35-003 | TODO | Docs Guild | EXPORT-SVC-35-003..004 | Publish `/docs/export-center/profiles.md` detailing schema fields, examples, compatibility, and imposed rule reminder. | Profiles doc merged; JSON schemas linked; imposed rule noted. | -| DOCS-EXPORT-36-004 | TODO | Docs Guild | EXPORT-SVC-36-001..004, WEB-EXPORT-36-001 | Publish `/docs/export-center/api.md` covering endpoints, payloads, errors, and mention imposed rule. | API doc merged; examples validated; rule included. | -| DOCS-EXPORT-36-005 | TODO | Docs Guild | CLI-EXPORT-35-001, CLI-EXPORT-36-001 | Publish `/docs/export-center/cli.md` with command reference, CI scripts, verification steps, restating imposed rule. | CLI doc merged; script snippets tested; rule appended. | -| DOCS-EXPORT-36-006 | TODO | Docs Guild | EXPORT-SVC-36-001, DEVOPS-EXPORT-36-001 | Publish `/docs/export-center/trivy-adapter.md` covering field mappings, compatibility matrix, and imposed rule reminder. | Doc merged; mapping tables validated; rule included. | -| DOCS-EXPORT-37-001 | TODO | Docs Guild | EXPORT-SVC-37-001, DEVOPS-EXPORT-37-001 | Publish `/docs/export-center/mirror-bundles.md` describing filesystem/OCI layouts, delta/encryption, import guide, ending with imposed rule. | Doc merged; diagrams provided; verification steps tested; rule stated. | -| DOCS-EXPORT-37-002 | TODO | Docs Guild | EXPORT-SVC-35-005, EXPORT-SVC-37-002 | Publish `/docs/export-center/provenance-and-signing.md` detailing manifests, attestation flow, verification, reiterating imposed rule. | Doc merged; signature examples validated; rule appended. | -| DOCS-EXPORT-37-003 | TODO | Docs Guild | DEVOPS-EXPORT-37-001 | Publish `/docs/operations/export-runbook.md` covering failures, tuning, capacity planning, with imposed rule reminder. | Runbook merged; procedures validated; rule included. | +| DOCS-EXPORT-35-001 | DONE (2025-10-29) | Docs Guild | EXPORT-SVC-35-001..006 | Author `/docs/export-center/overview.md` covering purpose, profiles, security, AOC alignment, surfaces, ending with imposed rule statement. | Doc merged with diagrams/examples; imposed rule line present; index updated. | +| DOCS-EXPORT-35-002 | DONE (2025-10-29) | Docs Guild | EXPORT-SVC-35-002..005 | Publish `/docs/export-center/architecture.md` describing planner, adapters, manifests, signing, distribution flows, restating imposed rule. | Architecture doc merged; sequence diagrams included; rule statement appended. | +| DOCS-EXPORT-35-003 | DONE (2025-10-29) | Docs Guild | EXPORT-SVC-35-003..004 | Publish `/docs/export-center/profiles.md` detailing schema fields, examples, compatibility, and imposed rule reminder. | Profiles doc merged; JSON schemas linked; imposed rule noted. | +| DOCS-EXPORT-36-004 | DONE (2025-10-29) | Docs Guild | EXPORT-SVC-36-001..004, WEB-EXPORT-36-001 | Publish `/docs/export-center/api.md` covering endpoints, payloads, errors, and mention imposed rule. | API doc merged; examples validated; rule included. | +| DOCS-EXPORT-36-005 | DONE (2025-10-29) | Docs Guild | CLI-EXPORT-35-001, CLI-EXPORT-36-001 | Publish `/docs/export-center/cli.md` with command reference, CI scripts, verification steps, restating imposed rule. | CLI doc merged; script snippets tested; rule appended. | +| DOCS-EXPORT-36-006 | DONE (2025-10-29) | Docs Guild | EXPORT-SVC-36-001, DEVOPS-EXPORT-36-001 | Publish `/docs/export-center/trivy-adapter.md` covering field mappings, compatibility matrix, and imposed rule reminder. | Doc merged; mapping tables validated; rule included. | +| DOCS-EXPORT-37-001 | DONE (2025-10-29) | Docs Guild | EXPORT-SVC-37-001, DEVOPS-EXPORT-37-001 | Publish `/docs/export-center/mirror-bundles.md` describing filesystem/OCI layouts, delta/encryption, import guide, ending with imposed rule. | Doc merged; diagrams provided; verification steps tested; rule stated. | +| DOCS-EXPORT-37-002 | DONE (2025-10-29) | Docs Guild | EXPORT-SVC-35-005, EXPORT-SVC-37-002 | Publish `/docs/export-center/provenance-and-signing.md` detailing manifests, attestation flow, verification, reiterating imposed rule. | Doc merged; signature examples validated; rule appended. | +| DOCS-EXPORT-37-003 | DONE (2025-10-29) | Docs Guild | DEVOPS-EXPORT-37-001 | Publish `/docs/operations/export-runbook.md` covering failures, tuning, capacity planning, with imposed rule reminder. | Runbook merged; procedures validated; rule included. | | DOCS-EXPORT-37-004 | TODO | Docs Guild | AUTH-EXPORT-37-001, EXPORT-SVC-37-002 | Publish `/docs/security/export-hardening.md` outlining RBAC, tenancy, encryption, redaction, restating imposed rule. | Security doc merged; checklist updated; rule appended. | +| DOCS-EXPORT-37-101 | TODO | Docs Guild, DevEx/CLI Guild | CLI-EXPORT-37-001 | Refresh CLI verification sections once `stella export verify` lands (flags, exit codes, samples). | `docs/export-center/cli.md` & `docs/export-center/provenance-and-signing.md` updated with final command syntax; examples tested; rule reminder retained. | +| DOCS-EXPORT-37-102 | TODO | Docs Guild, DevOps Guild | DEVOPS-EXPORT-37-001 | Embed export dashboards/alerts references into provenance/runbook docs after Grafana work ships. | Docs updated with dashboard IDs/alert notes; update logged; rule reminder present. | +| DOCS-EXPORT-37-005 | TODO | Docs Guild, Exporter Service Guild | EXPORT-SVC-35-006, DEVOPS-EXPORT-36-001 | Validate Export Center docs against live Trivy/mirror bundles once implementation lands; refresh examples and CLI snippets accordingly. | Real bundle examples recorded; docs updated; verification steps confirmed with production artefacts. | +> Note (2025-10-29): Blocked until exporter API (`EXPORT-SVC-35-006`) and Trivy/mirror adapters (`EXPORT-SVC-36-001`, `EXPORT-SVC-37-001`) ship. Requires access to CI smoke outputs (`DEVOPS-EXPORT-36-001`) for verification artifacts. ## Reachability v1 @@ -349,8 +353,8 @@ | ID | Status | Owner(s) | Depends on | Description | Exit Criteria | |----|--------|----------|------------|-------------|---------------| -| DOCS-NOTIFY-38-001 | TODO | Docs Guild, Notifications Service Guild | NOTIFY-SVC-38-001..004 | Publish `/docs/notifications/overview.md` and `/docs/notifications/architecture.md`, each ending with imposed rule reminder. | Docs merged; diagrams verified; imposed rule appended. | -| DOCS-NOTIFY-39-002 | TODO | Docs Guild, Notifications Service Guild | NOTIFY-SVC-39-001..004 | Publish `/docs/notifications/rules.md`, `/docs/notifications/templates.md`, `/docs/notifications/digests.md` with examples and imposed rule line. | Docs merged; examples validated; imposed rule appended. | +| DOCS-NOTIFY-38-001 | DONE (2025-10-29) | Docs Guild, Notifications Service Guild | NOTIFY-SVC-38-001..004 | Publish `/docs/notifications/overview.md` and `/docs/notifications/architecture.md`, each ending with imposed rule reminder. | Docs merged; diagrams verified; imposed rule appended. | +| DOCS-NOTIFY-39-002 | DONE (2025-10-29) | Docs Guild, Notifications Service Guild | NOTIFY-SVC-39-001..004 | Publish `/docs/notifications/rules.md`, `/docs/notifications/templates.md`, `/docs/notifications/digests.md` with examples and imposed rule line. | Docs merged; examples validated; imposed rule appended. | | DOCS-NOTIFY-40-001 | TODO | Docs Guild, Security Guild | AUTH-NOTIFY-38-001, NOTIFY-SVC-40-001..004 | Publish `/docs/notifications/channels.md`, `/docs/notifications/escalations.md`, `/docs/notifications/api.md`, `/docs/operations/notifier-runbook.md`, `/docs/security/notifications-hardening.md`; each ends with imposed rule line. | Docs merged; accessibility checks passed; imposed rule appended. | ## CLI Parity & Task Packs @@ -359,7 +363,7 @@ |----|--------|----------|------------|-------------|---------------| | DOCS-CLI-41-001 | TODO | Docs Guild, DevEx/CLI Guild | CLI-CORE-41-001 | Publish `/docs/cli/overview.md`, `/docs/cli/configuration.md`, `/docs/cli/output-and-exit-codes.md` with imposed rule statements. | Docs merged; examples verified; imposed rule appended. | | DOCS-CLI-42-001 | TODO | Docs Guild | DOCS-CLI-41-001, CLI-PARITY-41-001 | Publish `/docs/cli/parity-matrix.md` and command guides under `/docs/cli/commands/*.md` (policy, sbom, vuln, vex, advisory, export, orchestrator, notify, aoc, auth). | Guides merged; parity automation documented; imposed rule appended. | -| DOCS-PACKS-43-001 | TODO | Docs Guild, Task Runner Guild | PACKS-REG-42-001, TASKRUN-42-001 | Publish `/docs/task-packs/spec.md`, `/docs/task-packs/authoring-guide.md`, `/docs/task-packs/registry.md`, `/docs/task-packs/runbook.md`, `/docs/security/pack-signing-and-rbac.md`, `/docs/operations/cli-release-and-packaging.md` with imposed rule statements. | Docs merged; tutorials validated; imposed rule appended; cross-links added. | +| DOCS-PACKS-43-001 | DONE (2025-10-27) | Docs Guild, Task Runner Guild | PACKS-REG-42-001, TASKRUN-42-001 | Publish `/docs/task-packs/spec.md`, `/docs/task-packs/authoring-guide.md`, `/docs/task-packs/registry.md`, `/docs/task-packs/runbook.md`, `/docs/security/pack-signing-and-rbac.md`, `/docs/operations/cli-release-and-packaging.md` with imposed rule statements. | Docs merged; tutorials validated; imposed rule appended; cross-links added. | ## Containerized Distribution (Epic 13) diff --git a/docs/api/EPIC_17_SDKS_OPENAPI.md b/docs/api/EPIC_17_SDKS_OPENAPI.md index 80fc4372..5121f4e7 100644 --- a/docs/api/EPIC_17_SDKS_OPENAPI.md +++ b/docs/api/EPIC_17_SDKS_OPENAPI.md @@ -41,8 +41,9 @@ Net result: partners and internal teams integrate quickly without reverse‑engi ### 3.1 Source of truth and layout -* Each service owns a **module‑scoped OAS** file: `src/StellaOps.Api.OpenApi//openapi.yaml`. -* An aggregate spec `src/StellaOps.Api.OpenApi/stella.yaml` is produced by build tooling that composes per‑service specs, resolves `$ref`s, and validates cross‑service schemas. +* Each service owns a **module-scoped OAS** file: `src/StellaOps.Api.OpenApi//openapi.yaml`. + * Authority authentication/token surface now lives at `src/StellaOps.Api.OpenApi/authority/openapi.yaml`, covering `/token`, `/introspect`, `/revoke`, and `/jwks` flows with examples and scope catalog metadata. +* An aggregate spec `src/StellaOps.Api.OpenApi/stella.yaml` is produced by build tooling that composes per-service specs, resolves `$ref`s, and validates cross-service schemas. * JSON Schema dialect: 2020‑12 (OpenAPI 3.1). No vendor‑specific features for core models. * Every response and error has at least one **validated example**. diff --git a/docs/api/policy.md b/docs/api/policy.md index 31f14c53..ff329383 100644 --- a/docs/api/policy.md +++ b/docs/api/policy.md @@ -10,7 +10,7 @@ This document is the canonical reference for the Policy Engine REST surface desc ## 1 · Authentication & Headers - **Auth:** Bearer tokens (`Authorization: Bearer `) with the following scopes as applicable: - - `policy:read`, `policy:write`, `policy:submit`, `policy:approve`, `policy:run`, `policy:activate`, `policy:archive`, `policy:simulate`, `policy:runs` +- `policy:read`, `policy:author`, `policy:review`, `policy:approve`, `policy:operate`, `policy:run`, `policy:activate`, `policy:archive`, `policy:simulate`, `policy:runs` - `findings:read` (for effective findings APIs) - `effective:write` (service identity only; not exposed to clients) - **Service identity:** Authority marks the Policy Engine client with `properties.serviceIdentity: policy-engine`. Tokens missing this marker cannot obtain `effective:write`. @@ -53,7 +53,7 @@ All errors use HTTP semantics plus a structured payload: ``` POST /api/policy/policies -Scopes: policy:write +Scopes: policy:author ``` **Request** @@ -106,7 +106,7 @@ Returns full DSL, metadata, provenance, simulation artefact references. ``` PUT /api/policy/policies/{policyId}/versions/{version} -Scopes: policy:write +Scopes: policy:author ``` Body identical to create. Only permitted while `status=draft`. @@ -119,7 +119,7 @@ Body identical to create. Only permitted while `status=draft`. ``` POST /api/policy/policies/{policyId}/versions/{version}:submit -Scopes: policy:submit +Scopes: policy:author ``` **Request** @@ -196,7 +196,7 @@ Request includes `reason` and optional `incidentId`. ``` POST /api/policy/policies/{policyId}/versions/{version}:compile -Scopes: policy:write +Scopes: policy:author ``` **Response 200** @@ -221,7 +221,7 @@ Scopes: policy:write ``` POST /api/policy/policies/{policyId}/lint -Scopes: policy:write +Scopes: policy:author ``` Slim wrapper used by CLI; returns 204 on success or `ERR_POL_001` payload. diff --git a/docs/architecture/console.md b/docs/architecture/console.md new file mode 100644 index 00000000..a106edf5 --- /dev/null +++ b/docs/architecture/console.md @@ -0,0 +1,210 @@ +# StellaOps Console Architecture (Sprint 23) + +> **Ownership:** Console Guild • Docs Guild +> **Delivery scope:** `StellaOps.Web` Angular workspace, Console Web Gateway routes (`/console/*`), Downloads manifest surfacing, SSE fan-out for Scheduler & telemetry. +> **Related docs:** [Console overview](../ui/console-overview.md), [Navigation](../ui/navigation.md), [Runs workspace](../ui/runs.md), [Downloads](../ui/downloads.md), [Console security posture](../security/console-security.md), [Console observability](../observability/ui-telemetry.md), [Deployment guide](../deploy/console.md) + +This dossier describes the end-to-end architecture of the StellaOps Console as delivered in Sprint 23. It covers the Angular workspace layout, API/gateway integration points, live-update channels, performance budgets, offline workflows, and observability hooks needed to keep the console deterministic and air-gap friendly. + +--- + +## 1 · Mission & Boundaries + +- Present an operator-grade UI that surfaces Concelier, Excititor, Policy Engine, Scheduler, Attestor, and SBOM Service data **without** mutating aggregation or policy state. +- Enforce Authority-issued scopes and tenant claims on every call through the Console Web Gateway. +- Deliver deterministic builds (< 1 MB initial bundle) that can be mirrored in Offline Kits, with runtime configuration loaded from `/config.json`. +- Stream live status (ingestion deltas, scheduler progress, telemetry) via SSE with graceful degradation to polling when offline or throttled. +- Maintain CLI parity by embedding `stella` commands alongside interactive actions. + +Non-goals: authoring ingestion logic, mutating Policy overlays, exposing internal Mongo collections, or performing cryptographic signing in-browser. + +--- + +## 2 · Workspace & Packages + +The console is implemented in `src/StellaOps.Web`, an Angular 17 workspace built on standalone components and Signals. + +| Path | Purpose | Highlights | +|------|---------|------------| +| `src/app/core/auth` | DPoP + PKCE authentication, Authority session store, HTTP interceptors. | WebCrypto keygen (`crypto.subtle`), session metadata persisted in `sessionStorage`, DPoP nonce replay guard. | +| `src/app/core/api` | Typed API clients for Console gateway (`/console/*`) and downstream services. | DTOs for Scanner, Notify, Concelier exporters; fetch-based clients with abort signals. | +| `src/app/core/config` | Runtime configuration loader (`/config.json`), feature flag gating. | Supports air-gap overrides and injects API base URLs, Authority issuer/client. | +| `src/app/features/*` | Route-level shells (auth bootstrap, scans detail, notifications inbox, Trivy DB settings). | Each feature is a standalone module with lazy loading and Angular Signals state. | +| `src/app/testing` | Fixtures and harnesses used in unit tests and storybook-like previews. | Deterministic data used for Playwright and Jest scenarios. | + +Workspace characteristics: + +- **Toolchain:** Node 20.11+, npm 10.2+, Angular CLI 17.3. `npm run ci:install` primes dependencies without network audits; `scripts/verify-chromium.js` ensures headless Chromium availability for Karma. +- **Build budgets:** `angular.json` enforces 500 KB warning / 1 MB error for initial bundle and 2 KB warning / 4 KB error per component stylesheet. Output hashing (`outputHashing: all`) keeps assets cache-safe. +- **Testing:** Karma + Jasmine for unit tests, Playwright for e2e with dev server autotuning. CI (`DEVOPS-CONSOLE-23-001`) runs Lighthouse against the production bundle. +- **Runtime config:** `/config.json` merged at bootstrap; gateways can rewrite it on the fly to avoid rebuilding for environment changes. + +--- + +## 3 · Runtime Topology & Data Flow + +The console SPA relies on the Console Web Gateway to proxy tenant-scoped API calls to downstream services. Tenant isolation and Aggregation-Only guardrails are enforced at every hop. + +```mermaid +graph TD + subgraph Browser["Browser (Angular SPA)"] + UI[Console Shell
(Signals, Feature Modules)] + SSE[EventSource / SSE Clients] + end + subgraph Gateway["Console Web Gateway"] + Router[Minimal API / ASP.NET Core Router] + StatusCache[Status Cache & Manifest signer] + end + Authority[Authority
(DPoP + PKCE)] + Concelier[Concelier.WebService] + Excititor[Excititor.WebService] + Scheduler[Scheduler.WebService] + Policy[Policy Engine API] + SBOM[SBOM Service] + Attestor[Attestor API] + Downloads[Downloads Manifest Store] + + UI -->|/config.json| Gateway + UI -->|/console/* (Bearer+DPoP)| Router + SSE -->|/console/status/stream| Router + Router --> Authority + Router --> Concelier + Router --> Excititor + Router --> Scheduler + Router --> Policy + Router --> SBOM + Router --> Attestor + Router --> Downloads + StatusCache -.-> Gateway + Gateway -.-> UI +``` + +Key interactions: + +- **Auth bootstrap:** UI retrieves Authority metadata and exchanges an authorization code + PKCE verifier for a DPoP-bound token (`aud=console`, `tenant=`). Tokens expire in 120 s; refresh tokens rotate, triggering new DPoP proofs. +- **Tenant switch:** Picker issues `Authority /fresh-auth` when required, then refreshes UI caches (`ui.tenant.switch` log). Gateway injects `X-Stella-Tenant` headers downstream. +- **Aggregation-only reads:** Gateway proxies `/console/advisories`, `/console/vex`, `/console/findings`, etc., without mutating Concelier or Policy data. Provenance badges and merge hashes come directly from upstream responses. +- **Downloads parity:** `/console/downloads` merges DevOps signed manifest and Offline Kit metadata; UI renders digest, signature, and CLI parity command. +- **Offline resilience:** Gateway exposes `/console/status` heartbeat. If unavailable, UI enters offline mode, disables SSE, and surfaces CLI fallbacks. + +--- + +## 4 · Live Updates & SSE Design + +Live surfaces use HTTP/1.1 SSE with heartbeat frames to keep operators informed without polling storms. + +| Endpoint | Payload | Source | Behaviour | +|----------|---------|--------|-----------| +| `/console/status/stream` | `statusChanged`, `ingestionDelta`, `attestorQueue`, `offlineBanner` events | Concelier WebService, Excititor WebService, Attestor metrics | 5 s heartbeat; gateway disables proxy buffering (`X-Accel-Buffering: no`) and sets `Cache-Control: no-store`. | +| `/console/runs/{id}/stream` | `stateChanged`, `segmentProgress`, `deltaSummary`, `log` | Scheduler WebService SSE fan-out | Event payloads carry `traceId`, `runId`, `tenant`; UI reconnects with exponential backoff and resumes using `Last-Event-ID`. | +| `/console/telemetry/stream` | `metricSample`, `alert`, `collectorStatus` | Observability aggregator | Gated by `ui.telemetry` scope; disabled when `CONSOLE_TELEMETRY_SSE_ENABLED=false`. | + +Sequence overview: + +```mermaid +sequenceDiagram + autonumber + participant UI as Console SPA + participant GW as Console Gateway + participant SCHED as Scheduler WebService + + UI->>GW: GET /console/runs/42/stream (Authorization + DPoP) + GW->>SCHED: GET /runs/42/stream (X-Stella-Tenant) + SCHED-->>GW: event: stateChanged data: {...} + GW-->>UI: event: stateChanged data: {..., traceId} + Note over UI,GW: Gateway injects retry-after + heartbeat every 15s + UI-->>GW: (disconnect) + UI->>GW: GET /console/runs/42/stream (Last-Event-ID: ) + GW->>SCHED: GET /runs/42/stream?since= +``` + +Offline behaviour: + +- If SSE fails three times within 60 s, UI falls back to polling (`/console/status`, `/console/runs/{id}`) every 30 s and shows an amber banner. +- When `console.offlineMode=true`, SSE endpoints return `204` immediately; UI suppresses auto-reconnect to preserve resources. + +--- + +## 5 · Performance & Budgets + +| Surface | Target | Enforcement | +|---------|--------|-------------| +| First meaningful paint (dashboard) | ≤ 2.5 s on 4 vCPU offline runner | Lighthouse CI gate (`DEVOPS-CONSOLE-23-001`), `ui_route_render_seconds` P95 alert. | +| Route hydration (feature shells) | ≤ 1.5 s after token acquisition | Angular Signals + lazy loading; route-level budgets tracked via custom telemetry. | +| Initial bundle size | Warn ≥ 500 KB, fail ≥ 1 MB | `angular.json` budgets; CI fails build on overflow. | +| Component stylesheet | Warn ≥ 2 KB, fail ≥ 4 KB | `angular.json` budgets; ensures Tailwind utilities stay tree-shaken. | +| SSE heartbeat | Every 15 s max | Gateway emits comment heartbeats; UI resets timers on each frame. | + +Optimisation levers: + +- Standalone components with `ChangeDetectionStrategy.OnPush` and Angular Signals avoid zone.js churn. +- `fetch` + AbortController guard double fetches. +- Assets served with immutable caching (`cache-control: public, max-age=31536000, immutable`) thanks to hashed filenames. +- Compression (gzip/brotli) enabled at gateway; offline bundles include precompressed assets. +- Command palette, tenants, and filters rely on IndexedDB caches to avoid refetching static metadata. + +--- + +## 6 · Offline & Configuration Workflows + +- **Config manifest:** `/config.json` includes Authority issuer/client ID, gateway base URL, feature flags, telemetry endpoints, and offline hints. Operators can swap config by copying `src/config/config.sample.json` and editing before build, or by rewriting the response at gateway runtime. +- **Deterministic install:** Documented in `src/StellaOps.Web/docs/DeterministicInstall.md`—`npm run ci:install` plus Chromium provisioning ensures offline runners reproduce builds. +- **Offline Kit parity:** UI validates downloads manifest signatures (cosign) and surfaces snapshot timestamps per tenant. When offline, buttons switch to CLI snippets (`stella runs export`, `stella downloads sync`). +- **Feature flags:** `CONSOLE_FEATURE_FLAGS` toggles modules (`runs`, `downloads`, `telemetry`); offline bundles include flag manifest so UI can render only supported panes. +- **Snapshot awareness:** Global banner shows snapshot timestamp and disables actions needing Authority fresh-auth when running in sealed mode. + +--- + +## 7 · Security & Tenancy + +- **DPoP + PKCE:** Every request carries `Authorization` + `DPoP` header and gateway enforces nonce replay protection. Private keys live in IndexedDB and never leave the browser. +- **Scope enforcement:** Gateway checks scope claims before proxying (`ui.read`, `runs.manage`, `downloads.read`, etc.) and propagates denials as `Problem+JSON` with `ERR_*` codes. +- **Tenant propagation:** `X-Stella-Tenant` header derived from token; downstream services reject mismatches. Tenant switches log `ui.tenant.switch` and require fresh-auth for privileged actions. +- **CSP & headers:** Default CSP forbids third-party scripts, only allows same-origin `connect-src`. HSTS, Referrer-Policy `no-referrer`, and `Permissions-Policy` configured via gateway (`deploy/console.md`). +- **Evidence handling:** Downloads never cache secrets; UI renders SHA-256 + signature references and steers users to CLI for sensitive exports. +- See [Console security posture](../security/console-security.md) for full scope table and threat model alignment. + +--- + +## 8 · Observability & Telemetry + +- **Metrics:** Prometheus scrape at `/metrics` (enabled when `CONSOLE_METRICS_ENABLED=true`). Key histograms/counters documented in [Console observability](../observability/ui-telemetry.md) (`ui_route_render_seconds`, `ui_tenant_switch_total`, `ui_download_manifest_refresh_seconds`). +- **Logs:** Structured JSON with `traceId`, `tenant`, `action`. Categories include `ui.action`, `ui.tenant.switch`, `ui.security.anomaly`. Sampled per feature flag to balance volume. +- **Traces:** Browser OTLP exporter ships spans to configured collector; gateway adds server-side spans so traces cross client/server boundary. +- **Alerts:** Burn-rate rules for route latency, telemetry batch failures, download manifest refresh, and SSE stalls integrate with Notifier. +- **Correlation:** SSE events carry `traceId` so operators can jump from UI to backend logs using shared correlation IDs. + +--- + +## 9 · Integration Points & Dependencies + +| Service | Console dependency | Notes | +|---------|-------------------|-------| +| Authority | OIDC, DPoP tokens, tenant catalog, fresh-auth | Requires client `console-ui` with scopes listed in security guide. | +| Concelier WebService | `/console/advisories`, feed health, export triggers | Gateway must enforce Aggregation-Only guardrails and surface merge hashes. | +| Excititor WebService | `/console/vex`, consensus overlays | SSE ticker shows provider deltas. | +| Policy Engine | Findings views, policy previews, simulation diffs | Console never writes overlays; uses `effective_finding_*` data via API. | +| Scheduler WebService | Runs dashboard, SSE streams, queue metrics | Heartbeat drives status ticker; cancellation actions require `runs.manage`. | +| SBOM Service | SBOM explorer tree, component lookup | Responses cached per tenant; offline bundles preload snapshots. | +| Attestor | Attestation verification, evidence links | Console displays verification status and CLI parity commands. | +| DevOps downloads pipeline | Signed manifest for `/console/downloads` | Manifest signatures validated with cosign key shipped in Offline Kit. | + +--- + +## 10 · Compliance Checklist + +- [ ] Frontend package map (core/auth/api/config + feature shells) documented with ownership and tooling details. +- [ ] Data flow diagram captures SPA ↔ Gateway ↔ downstream services with tenant & scope enforcement notes. +- [ ] SSE design documented (endpoints, payloads, heartbeat, retry/backoff, offline fallback). +- [ ] Performance budgets (< 1 MB initial bundle, route hydration ≤ 1.5 s, SSE heartbeat) stated alongside enforcement mechanisms. +- [ ] Offline workflows (`/config.json`, deterministic install, Offline Kit parity) described with operator guidance. +- [ ] Security section references DPoP, scopes, CSP, evidence handling, and tenancy propagation. +- [ ] Observability metrics/logs/traces coverage listed with alert hooks. +- [ ] Integration dependencies table links Console responsibilities to upstream services. +- [ ] Document cross-references validated (UI guides, security, observability, deployment). +- [ ] Last updated timestamp refreshed after review. + +--- + +*Last updated: 2025-10-27 (Sprint 23).* + diff --git a/docs/assets/ui/tours/README.md b/docs/assets/ui/tours/README.md new file mode 100644 index 00000000..dc8978fd --- /dev/null +++ b/docs/assets/ui/tours/README.md @@ -0,0 +1,13 @@ +# UI Tours Media Assets + +Store annotated screenshots and GIFs referenced by `/docs/examples/ui-tours.md` in this directory. Use the naming convention documented in the guide (e.g., `triage-step-01.png`, `triage-flow.gif`). + +## Contribution checklist + +- Capture at 1920×1080 resolution unless otherwise specified. +- Add annotations using the shared Docs Guild template (narrow callouts, numbered badges). +- Optimize images to stay below 2 MB (PNG) and 8 MB (GIF) while preserving legibility. +- Record GIFs at ≤30 seconds using 12–15 fps for balance between smoothness and size. +- Update the capture checklist in `docs/examples/ui-tours.md` when assets are added or replaced. +- Commit binaries using Git LFS if size exceeds repository limits; otherwise store directly. +- Include the console build hash in the asset metadata or caption, matching the Downloads manifest version. diff --git a/docs/ci/20_CI_RECIPES.md b/docs/ci/20_CI_RECIPES.md index 2023b0a6..eadc31f3 100755 --- a/docs/ci/20_CI_RECIPES.md +++ b/docs/ci/20_CI_RECIPES.md @@ -10,7 +10,7 @@ | `IMAGE` | The image you are building & scanning | `acme/backend:sha-${COMMIT_SHA}` | | `SBOM_FILE` | Immutable SBOM name – `‑YYYYMMDDThhmmssZ.sbom.json` | `acme_backend_sha‑abc123‑20250804T153050Z.sbom.json` | -> **Authority graph scopes note (2025‑10‑27):** CI stages that spin up the Authority compose profile now rely on the checked-in `etc/authority.yaml`. Before running integration smoke jobs, inject real secrets for every `etc/secrets/*.secret` file (Cartographer, Graph API, Policy Engine, Concelier, Excititor). The repository defaults contain `*-change-me` placeholders and Authority will reject tokens if those secrets are not overridden. +> **Authority graph scopes note (2025-10-27):** CI stages that spin up the Authority compose profile now rely on the checked-in `etc/authority.yaml`. Before running integration smoke jobs, inject real secrets for every `etc/secrets/*.secret` file (Cartographer, Graph API, Policy Engine, Concelier, Excititor). The repository defaults contain `*-change-me` placeholders and Authority will reject tokens if those secrets are not overridden. Reissue CI tokens that previously used `policy:write`/`policy:submit`/`policy:edit` scopes—new bundles must request `policy:read`, `policy:author`, `policy:review`, `policy:simulate`, and (`policy:approve`/`policy:operate`/`policy:activate` when pipelines promote policies). ```bash export STELLA_URL="stella-ops.ci.acme.example" @@ -264,6 +264,12 @@ python -m pip install --upgrade pip python -m pip install markdown pygments ``` +> **No `pip` available?** Some hardened Python builds (including the repo’s `tmp/docenv` +> interpreter) ship without `pip`/`ensurepip`. In that case download the pure‑Python +> sdists (e.g. `Markdown-3.x.tar.gz`, `pygments-2.x.tar.gz`) and extract their +> packages directly into the virtualenv’s `lib/python*/site-packages/` folder. +> This keeps the renderer working even when package managers are disabled. + **Offline tip.** Add the packages above to your artifact mirror (for example `ops/devops/offline-kit.json`) so runners can install them via `npm --offline` / `pip --no-index`. ### 4.2 Schema validation step diff --git a/docs/cli/cli-reference.md b/docs/cli/cli-reference.md index 18d8ce0b..8bcc0069 100644 --- a/docs/cli/cli-reference.md +++ b/docs/cli/cli-reference.md @@ -152,6 +152,8 @@ Replays the AOC guard against stored raw documents. By default it checks all adv | `--tenant ` | Overrides tenant context. Required for cross-tenant verifications when run by platform operators. | | `--no-color` | Disables ANSI colours. | +`table` mode prints a summary showing the active tenant, evaluated window, counts of checked advisories/VEX statements, the active limit, total writes/violations, and whether the page was truncated. Status is colour-coded as `ok`, `violations`, or `truncated`. When violations exist the detail table lists the code, total occurrences, first sample document (`source` + `documentId` + `contentHash`), and JSON pointer path. + ### 3.4 Report structure (JSON) ```json @@ -182,7 +184,8 @@ Replays the AOC guard against stored raw documents. By default it checks all adv "metrics": { "ingestion_write_total": 557, "aoc_violation_total": 2 - } + }, + "truncated": false } ``` @@ -262,6 +265,24 @@ Use these codes in CI to map outcomes to build statuses or alert severities. --- +## 4 · `stella vuln observations` (Overlay paging) + +`stella vuln observations` lists raw advisory observations for downstream overlays (Graph Explorer, Policy simulations, Console). Large tenants can now page through results deterministically. + +| Option | Description | +|--------|-------------| +| `--limit ` | Caps the number of observations returned in a single call. Defaults to `200`; values above `500` are clamped server-side. | +| `--cursor ` | Opaque continuation token produced by the previous page (`nextCursor` in JSON output). Pass it back to resume iteration. | + +Additional notes: + +- Table mode prints a hint when `hasMore` is `true`: + `[yellow]More observations available. Continue with --cursor [/]`. +- JSON mode returns `nextCursor` and `hasMore` alongside the observation list so automation can loop until `hasMore` is `false`. +- Supplying a non-positive limit falls back to the default (`200`). Invalid/expired cursors yield `400 Bad Request`; restart without `--cursor` to begin a fresh iteration. + +--- + ## 5 · Related references - [Aggregation-Only Contract reference](../ingestion/aggregation-only-contract.md) @@ -282,4 +303,14 @@ Use these codes in CI to map outcomes to build statuses or alert severities. --- -*Last updated: 2025-10-26 (Sprint 19).* +*Last updated: 2025-10-29 (Sprint 24).* + +## 13. Authority configuration quick reference + +| Setting | Purpose | How to set | +|---------|---------|------------| +| `StellaOps:Authority:OperatorReason` | Incident/change description recorded with `orch:operate` tokens. | CLI flag `--Authority:OperatorReason=...` or env `STELLAOPS_ORCH_REASON`. | +| `StellaOps:Authority:OperatorTicket` | Change/incident ticket reference paired with orchestrator control actions. | CLI flag `--Authority:OperatorTicket=...` or env `STELLAOPS_ORCH_TICKET`. | + +> Tokens requesting `orch:operate` will fail with `invalid_request` unless both values are present. Choose concise strings (≤256 chars for reason, ≤128 chars for ticket) and avoid sensitive data. + diff --git a/docs/cli/policy.md b/docs/cli/policy.md index a35abebb..7bed3a29 100644 --- a/docs/cli/policy.md +++ b/docs/cli/policy.md @@ -3,6 +3,7 @@ > **Audience:** Policy authors, reviewers, operators, and CI engineers using the `stella` CLI to interact with Policy Engine. > **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). --- @@ -129,6 +130,23 @@ stella policy activate P-7 --version 4 --run-now --priority high - Optional `--scheduled-at 2025-10-27T02:00:00Z`. - Requires `policy:activate` and `policy:run`. +**Options** + +- `--version ` (required) – target revision to promote. +- `--note ` – record an activation note alongside the approval. +- `--run-now` – enqueue an immediate full run after activation. +- `--scheduled-at ` – schedule activation for a specific UTC time (ISO-8601 format). +- `--priority