Restructure solution layout by module
	
		
			
	
		
	
	
		
	
		
			Some checks failed
		
		
	
	
		
			
				
	
				Docs CI / lint-and-preview (push) Has been cancelled
				
			
		
		
	
	
				
					
				
			
		
			Some checks failed
		
		
	
	Docs CI / lint-and-preview (push) Has been cancelled
				
			This commit is contained in:
		| @@ -1,261 +1,261 @@ | ||||
| # Authority Scopes & Tenancy — AOC Update | ||||
|  | ||||
| > **Audience:** Authority Core, platform security engineers, DevOps owners.   | ||||
| > **Scope:** Scope taxonomy, tenancy enforcement, rollout guidance for the Aggregation-Only Contract (Sprint 19). | ||||
|  | ||||
| Authority issues short-lived tokens bound to tenants and scopes. Sprint 19 introduces new scopes to support the AOC guardrails in Concelier and Excititor. This document lists the canonical scope catalogue, describes tenancy propagation, and outlines operational safeguards. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 1 · Scope catalogue (post AOC) | ||||
|  | ||||
| | Scope | Surface | Purpose | Notes | | ||||
| |-------|---------|---------|-------| | ||||
| | `advisory:ingest` | Concelier ingestion APIs | Append-only writes to `advisory_raw` collections. | Requires tenant claim; blocked for global clients. | | ||||
| | `advisory:read` | `/aoc/verify`, Concelier dashboards, CLI | Read-only access to stored advisories and guard results. | Must be requested with `aoc:verify`; Authority rejects tokens missing the pairing. | | ||||
| | `vex:ingest` | Excititor ingestion APIs | Append-only writes to `vex_raw`. | Mirrors `advisory:ingest`; tenant required. | | ||||
| | `vex:read` | `/aoc/verify`, Excititor dashboards, CLI | Read-only access to stored VEX material. | Must be requested with `aoc:verify`; Authority rejects tokens missing the pairing. | | ||||
| | `aoc:verify` | CLI/CI pipelines, Console verification jobs | Execute Aggregation-Only Contract guard runs. | Always issued with tenant; required whenever requesting `advisory:read`, `vex:read`, or any `signals:*` scope. | | ||||
| | `signals:read` | Signals API, reachability dashboards | Read-only access to stored reachability signals. | Tenant and `aoc:verify` required; missing pairing returns `invalid_scope`. | | ||||
| | `signals:write` | Signals ingestion APIs | Append-only writes for reachability signals. | Requires tenant and `aoc:verify`; Authority logs `authority.aoc_scope_violation` on mismatch. | | ||||
| | `signals:admin` | Signals administration tooling | Rotate credentials, manage reachability sensors, purge stale data. | Reserved for automation; `aoc:verify` + tenant mandatory; violations are audited. | | ||||
| | `graph:write` | Cartographer pipeline | Enqueue graph build/overlay jobs. | Reserved for Cartographer service identity; tenant required. | | ||||
| | `graph:read` | Graph API, Scheduler overlays, UI | Read graph projections/overlays. | Tenant required; granted to Cartographer, Graph API, Scheduler. | | ||||
| | `graph:export` | Graph export endpoints | Stream GraphML/JSONL artefacts. | UI/gateway automation only; tenant required. | | ||||
| | `graph:simulate` | Policy simulation overlays | Trigger what-if overlays on graphs. | Restricted to automation; tenant required. | | ||||
| | `effective:write` | Policy Engine | Create/update `effective_finding_*` collections. | **Only** the Policy Engine service client may hold this scope; tenant required. | | ||||
| | `findings:read` | Console, CLI, exports | Read derived findings materialised by Policy Engine. | Shared across tenants with RBAC; tenant claim still enforced. | | ||||
| | `policy:author` | Policy Studio (Console, CLI) | Author drafts, run lint, execute quick simulations. | Tenant required; typically granted via `role/policy-author`. | | ||||
| | `policy:review` | Policy Studio review panes | Review drafts, leave comments, request changes. | Tenant required; pair with `policy:simulate` for diff previews. | | ||||
| | `policy:approve` | Policy Studio approvals | Approve or reject policy drafts. | Tenant required; fresh-auth enforced by Console UI. | | ||||
| | `policy:operate` | Policy Studio promotion controls | Trigger batch simulations, promotions, and canary runs. | Tenant required; combine with `policy:run`/`policy:activate`. | | ||||
| | `policy:audit` | Policy audit exports | Access immutable policy history, comments, and signatures. | Tenant required; read-only access. | | ||||
| | `policy:simulate` | Policy Studio / CLI simulations | Run simulations against tenant inventories. | Tenant required; available to authors, reviewers, operators. | | ||||
| | `vuln:read` | Vuln Explorer API/UI | Read normalized vulnerability data. | Tenant required. | | ||||
| | `export.viewer` | Export Center APIs | List export profiles/runs, fetch manifests and bundles. | Tenant required; read-only access. | | ||||
| | `export.operator` | Export Center APIs | Trigger export runs, manage schedules, request verifications. | Tenant required; pair with `export.admin` for retention/encryption changes. | | ||||
| | `export.admin` | Export Center administrative APIs | Configure retention policies, encryption keys, and scheduling defaults. | Tenant required; token requests must include `export_reason` + `export_ticket`; Authority audits denials. | | ||||
| | `orch:read` | Orchestrator dashboards/API | Read queued jobs, worker state, and rate-limit telemetry. | Tenant required; never grants mutation rights. | | ||||
| | `orch:operate` | Orchestrator control actions | Execute pause/resume, retry, sync-now, and backfill operations. Requires tenant assignment **and** `operator_reason`/`operator_ticket` parameters when requesting tokens. | | ||||
| | `exceptions:read` | Exception service APIs, Console | Enumerate exception definitions, routing templates, and approval state. | Tenant and approval routing metadata required for audit replay. | | ||||
| | `exceptions:write` | Policy Engine → Authority bridge | Persist exception evaluations, lifecycle events, and status changes. | Tenant required; only service principals should hold this scope. | | ||||
| | `exceptions:approve` | Console fresh-auth flows, delegated admins | Approve or reject exception requests routed through Authority. | Tenant required; Authority enforces MFA when any bound routing template has `requireMfa=true`. | | ||||
| | `ui.read` | Console base APIs | Retrieve tenant catalog, profile metadata, and token introspection results. | Tenant header required; responses are DPoP-bound and audit logged. | | ||||
| | `authority:tenants.read` | Console admin workspace | Enumerate configured tenants, default roles, and isolation metadata. | Tenant claim must match header; access audited via `authority.console.tenants.read`. | | ||||
| | Existing scopes | (e.g., `policy:*`, `concelier.jobs.trigger`) | Unchanged. | `concelier.merge` is retired — clients must request `advisory:ingest`/`advisory:read`; requests continue to fail with `invalid_client`. Review `/docs/security/policy-governance.md` for policy-specific scopes. | | ||||
|  | ||||
| ### 1.1 Scope bundles (roles) | ||||
|  | ||||
| - **`role/concelier-ingest`** → `advisory:ingest`, `advisory:read`. | ||||
| - **`role/excititor-ingest`** → `vex:ingest`, `vex:read`. | ||||
| - **`role/signals-uploader`** → `signals:write`, `signals:read`, `aoc:verify`. | ||||
| - **`role/aoc-operator`** → `aoc:verify`, `advisory:read`, `vex:read`. | ||||
| - **`role/policy-engine`** → `effective:write`, `findings:read`. | ||||
| - **`role/cartographer-service`** → `graph:write`, `graph:read`. | ||||
| - **`role/graph-gateway`** → `graph:read`, `graph:export`, `graph:simulate`. | ||||
| - **`role/console`** → `ui.read`, `advisory:read`, `vex:read`, `exceptions:read`, `aoc:verify`, `findings:read`, `orch:read`, `vuln:read`. | ||||
| - **`role/ui-console-admin`** → `ui.read`, `authority:tenants.read`, `authority:roles.read`, `authority:tokens.read`, `authority:clients.read` (paired with write scopes where required).   | ||||
| - **`role/orch-viewer`** *(Authority role: `Orch.Viewer`)* → `orch:read`. | ||||
| - **`role/orch-operator`** *(Authority role: `Orch.Operator`)* → `orch:read`, `orch:operate`. | ||||
| - **`role/policy-author`** → `policy:author`, `policy:read`, `policy:simulate`, `findings:read`. | ||||
| - **`role/policy-reviewer`** → `policy:review`, `policy:read`, `policy:simulate`, `findings:read`. | ||||
| - **`role/policy-approver`** → `policy:approve`, `policy:review`, `policy:read`, `policy:simulate`, `findings:read`. | ||||
| - **`role/policy-operator`** → `policy:operate`, `policy:run`, `policy:activate`, `policy:read`, `policy:simulate`, `findings:read`. | ||||
| - **`role/policy-auditor`** → `policy:audit`, `policy:read`, `policy:simulate`, `findings:read`. | ||||
| - **`role/export-viewer`** *(Authority role: `Export.Viewer`)* → `export.viewer`. | ||||
| - **`role/export-operator`** *(Authority role: `Export.Operator`)* → `export.viewer`, `export.operator`. | ||||
| - **`role/export-admin`** *(Authority role: `Export.Admin`)* → `export.viewer`, `export.operator`, `export.admin`. | ||||
| - **`role/exceptions-service`** → `exceptions:read`, `exceptions:write`. | ||||
| - **`role/exceptions-approver`** → `exceptions:read`, `exceptions:approve`. | ||||
|  | ||||
| Roles are declared per tenant in `authority.yaml`: | ||||
|  | ||||
| ```yaml | ||||
| tenants: | ||||
|   - name: default | ||||
|     roles: | ||||
|       concelier-ingest: | ||||
|         scopes: [advisory:ingest, advisory:read] | ||||
|       signals-uploader: | ||||
|         scopes: [signals:write, signals:read, aoc:verify] | ||||
|       aoc-operator: | ||||
|         scopes: [aoc:verify, advisory:read, vex:read] | ||||
|       orch-viewer: | ||||
|         scopes: [orch:read] | ||||
|       orch-operator: | ||||
|         scopes: [orch:read, orch:operate] | ||||
|       policy-author: | ||||
|         scopes: [policy:author, policy:read, policy:simulate, findings:read] | ||||
|       policy-reviewer: | ||||
|         scopes: [policy:review, policy:read, policy:simulate, findings:read] | ||||
|       policy-approver: | ||||
|         scopes: [policy:approve, policy:review, policy:read, policy:simulate, findings:read] | ||||
|       policy-operator: | ||||
|         scopes: [policy:operate, policy:run, policy:activate, policy:read, policy:simulate, findings:read] | ||||
|       policy-auditor: | ||||
|         scopes: [policy:audit, policy:read, policy:simulate, findings:read] | ||||
|       policy-engine: | ||||
|         scopes: [effective:write, findings:read] | ||||
|       exceptions-service: | ||||
|         scopes: [exceptions:read, exceptions:write] | ||||
|       exceptions-approver: | ||||
|         scopes: [exceptions:read, exceptions:approve] | ||||
| ``` | ||||
|  | ||||
| > **MFA requirement:** When any `exceptions.routingTemplates` entry sets `requireMfa: true`, Authority refuses to mint tokens containing `exceptions:approve` unless the authenticating identity provider advertises MFA support. Password/OIDC flows produce `authority.password.grant` audit events with `reason="Exception approval scope requires an MFA-capable identity provider."` when the requirement is violated. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 2 · Tenancy enforcement | ||||
|  | ||||
| ### 2.1 Token claims | ||||
|  | ||||
| Tokens now include: | ||||
|  | ||||
| - `tenant` claim (string) — required for all ingestion and verification scopes. | ||||
| - `service_identity` (optional) — e.g., `policy-engine`, `cartographer`. Required when requesting `effective:write` or `graph:write`. | ||||
| - `delegation_allowed` (boolean) — defaults `false`. Prevents console tokens from delegating ingest scopes. | ||||
|  | ||||
| Authority rejects requests when: | ||||
|  | ||||
| - `tenant` is missing while requesting `advisory:ingest`, `advisory:read`, `vex:ingest`, `vex:read`, or `aoc:verify` scopes. | ||||
| - `aoc:verify` is absent while tokens request `advisory:read`, `vex:read`, or any `signals:*` scope (`invalid_scope` with deterministic message). | ||||
| - `service_identity != policy-engine` but `effective:write` is present (`ERR_AOC_006` enforcement). | ||||
| - `service_identity != cartographer` but `graph:write` is present (graph pipeline enforcement). | ||||
| - Tokens attempt to combine `advisory:ingest` with `effective:write` (separation of duties). | ||||
| - `exceptions:approve` is requested by a client without a tenant assignment or via an identity provider lacking MFA when `RequireMfaForApprovals=true`. | ||||
|  | ||||
| ### 2.2 Propagation | ||||
|  | ||||
| - API Gateway forwards `tenant` claim as header (`X-Stella-Tenant`). Services refuse requests lacking the header. | ||||
| - Concelier/Excititor stamp tenant into raw documents and structured logs. | ||||
| - Policy Engine copies `tenant` from tokens into `effective_finding_*` collections. | ||||
| - Exception lifecycle services persist tenant and the selected routing template identifier alongside approval decisions. Authority audit events (`authority.password.grant`, `authority.client_credentials.grant`) surface `audit.scopes` and, on denials, a `scope.invalid` metadata entry so operators can trace exception approval attempts without inspecting downstream services. | ||||
|  | ||||
| ### 2.3 Cross-tenant scenarios | ||||
|  | ||||
| - Platform operators with `tenant:admin` can assume other tenants via `/authority/tenant/switch` if explicitly permitted. | ||||
| - CLI commands accept `--tenant <id>` to override environment default; Authority logs tenant switch events (`authority.tenant.switch`). | ||||
| - Console tenant picker uses delegated token exchange (`/token/exchange`) to obtain scoped tenant tokens without exposing raw credentials. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 3 · Configuration changes | ||||
|  | ||||
| ### 3.1 Authority configuration (`authority.yaml`) | ||||
|  | ||||
| Add new scopes and optional claims transformations: | ||||
|  | ||||
| ```yaml | ||||
| security: | ||||
|   scopes: | ||||
|     - name: advisory:ingest | ||||
|       description: Concelier raw ingestion (append-only) | ||||
|     - name: advisory:read | ||||
|       description: Read Concelier advisories and guard verdicts | ||||
|     - name: vex:ingest | ||||
|       description: Excititor raw ingestion | ||||
|     - name: vex:read | ||||
|       description: Read Excititor VEX records | ||||
|     - name: aoc:verify | ||||
|       description: Run AOC verification | ||||
|     - name: effective:write | ||||
|       description: Policy Engine materialisation | ||||
|     - name: findings:read | ||||
|       description: Read derived findings | ||||
|     - name: graph:write | ||||
|       description: Cartographer build submissions | ||||
|     - name: graph:read | ||||
|       description: Read graph overlays | ||||
|     - name: graph:export | ||||
|       description: Export graph artefacts | ||||
|     - name: graph:simulate | ||||
|       description: Run graph what-if simulations | ||||
|     - name: vuln:read | ||||
|       description: Read Vuln Explorer data | ||||
|   claimTransforms: | ||||
|     - match: { scope: "effective:write" } | ||||
|       require: | ||||
|         serviceIdentity: policy-engine | ||||
|     - match: { scope: "graph:write" } | ||||
|       require: | ||||
|         serviceIdentity: cartographer | ||||
| ``` | ||||
|  | ||||
| ### 3.2 Client registration | ||||
|  | ||||
| Update service clients: | ||||
|  | ||||
| - `Concelier.WebService` → request `advisory:ingest`, `advisory:read`. | ||||
| - `Excititor.WebService` → request `vex:ingest`, `vex:read`. | ||||
| - `Policy.Engine` → request `effective:write`, `findings:read`; set `properties.serviceIdentity=policy-engine`. | ||||
| - `Cartographer.Service` → request `graph:write`, `graph:read`; set `properties.serviceIdentity=cartographer`. | ||||
| - `Graph API Gateway` → request `graph:read`, `graph:export`, `graph:simulate`; tenant hint required. | ||||
| - `Console` → request `advisory:read`, `vex:read`, `aoc:verify`, `findings:read`, `vuln:read` plus existing UI scopes. | ||||
| - `CLI automation` → request `aoc:verify`, `advisory:read`, `vex:read` as needed. | ||||
|  | ||||
| Client definition snippet: | ||||
|  | ||||
| ```yaml | ||||
| clients: | ||||
|   - clientId: concelier-web | ||||
|     grantTypes: [client_credentials] | ||||
|     scopes: [advisory:ingest, advisory:read] | ||||
|     tenants: [default] | ||||
|   - clientId: policy-engine | ||||
|     grantTypes: [client_credentials] | ||||
|     scopes: [effective:write, findings:read] | ||||
|     properties: | ||||
|       serviceIdentity: policy-engine | ||||
|   - clientId: cartographer-service | ||||
|     grantTypes: [client_credentials] | ||||
|     scopes: [graph:write, graph:read] | ||||
|     properties: | ||||
|       serviceIdentity: cartographer | ||||
| ``` | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 4 · Operational safeguards | ||||
|  | ||||
| - **Audit events:** Authority emits `authority.scope.granted` and `authority.scope.revoked` events with `scope` and `tenant`. Monitor for unexpected grants. | ||||
| - **Rate limiting:** Apply stricter limits on `/token` endpoints for clients requesting `advisory:ingest` or `vex:ingest` to mitigate brute-force ingestion attempts. | ||||
| - **Incident response:** Link AOC alerts to Authority audit logs to confirm whether violations come from expected identities. | ||||
| - **Rotation:** Rotate ingest client secrets alongside guard deployments; add rotation steps to `ops/authority-key-rotation.md`. | ||||
| - **Testing:** Integration tests must fail if tokens lacking `tenant` attempt ingestion; add coverage in Concelier/Excititor smoke suites (see `CONCELIER-CORE-AOC-19-013`). | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 5 · Offline & air-gap notes | ||||
|  | ||||
| - Offline Kit bundles include tenant-scoped service credentials. Ensure ingest bundles ship without `advisory:ingest` scopes unless strictly required. | ||||
| - CLI verification in offline environments uses pre-issued `aoc:verify` tokens; document expiration and renewal processes. | ||||
| - Authority replicas in air-gapped environments should restrict scope issuance to known tenants and log all `/token` interactions for later replay. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 6 · References | ||||
|  | ||||
| - [Aggregation-Only Contract reference](../ingestion/aggregation-only-contract.md) | ||||
| - [Architecture overview](../architecture/overview.md) | ||||
| - [Concelier architecture](../ARCHITECTURE_CONCELIER.md) | ||||
| - [Excititor architecture](../ARCHITECTURE_EXCITITOR.md) | ||||
| - [Policy governance](policy-governance.md) | ||||
| - [Authority key rotation playbook](../ops/authority-key-rotation.md) | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 7 · Compliance checklist | ||||
|  | ||||
| - [ ] Scope catalogue updated in Authority configuration templates. | ||||
| - [ ] Role mappings documented for each tenant profile. | ||||
| - [ ] Claim transforms enforce `serviceIdentity` for `effective:write`. | ||||
| - [ ] Claim transforms enforce `serviceIdentity` for `graph:write`. | ||||
| - [ ] Concelier/Excititor smoke tests cover missing tenant rejection. | ||||
| - [ ] Offline kit credentials reviewed for least privilege. | ||||
| - [ ] Audit/monitoring guidance validated with Observability Guild. | ||||
| - [ ] Authority Core sign-off recorded (owner: @authority-core, due 2025-10-28). | ||||
|  | ||||
| --- | ||||
|  | ||||
| *Last updated: 2025-10-27 (Sprint 19).*  | ||||
| # Authority Scopes & Tenancy — AOC Update | ||||
|  | ||||
| > **Audience:** Authority Core, platform security engineers, DevOps owners.   | ||||
| > **Scope:** Scope taxonomy, tenancy enforcement, rollout guidance for the Aggregation-Only Contract (Sprint 19). | ||||
|  | ||||
| Authority issues short-lived tokens bound to tenants and scopes. Sprint 19 introduces new scopes to support the AOC guardrails in Concelier and Excititor. This document lists the canonical scope catalogue, describes tenancy propagation, and outlines operational safeguards. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 1 · Scope catalogue (post AOC) | ||||
|  | ||||
| | Scope | Surface | Purpose | Notes | | ||||
| |-------|---------|---------|-------| | ||||
| | `advisory:ingest` | Concelier ingestion APIs | Append-only writes to `advisory_raw` collections. | Requires tenant claim; blocked for global clients. | | ||||
| | `advisory:read` | `/aoc/verify`, Concelier dashboards, CLI | Read-only access to stored advisories and guard results. | Must be requested with `aoc:verify`; Authority rejects tokens missing the pairing. | | ||||
| | `vex:ingest` | Excititor ingestion APIs | Append-only writes to `vex_raw`. | Mirrors `advisory:ingest`; tenant required. | | ||||
| | `vex:read` | `/aoc/verify`, Excititor dashboards, CLI | Read-only access to stored VEX material. | Must be requested with `aoc:verify`; Authority rejects tokens missing the pairing. | | ||||
| | `aoc:verify` | CLI/CI pipelines, Console verification jobs | Execute Aggregation-Only Contract guard runs. | Always issued with tenant; required whenever requesting `advisory:read`, `vex:read`, or any `signals:*` scope. | | ||||
| | `signals:read` | Signals API, reachability dashboards | Read-only access to stored reachability signals. | Tenant and `aoc:verify` required; missing pairing returns `invalid_scope`. | | ||||
| | `signals:write` | Signals ingestion APIs | Append-only writes for reachability signals. | Requires tenant and `aoc:verify`; Authority logs `authority.aoc_scope_violation` on mismatch. | | ||||
| | `signals:admin` | Signals administration tooling | Rotate credentials, manage reachability sensors, purge stale data. | Reserved for automation; `aoc:verify` + tenant mandatory; violations are audited. | | ||||
| | `graph:write` | Cartographer pipeline | Enqueue graph build/overlay jobs. | Reserved for Cartographer service identity; tenant required. | | ||||
| | `graph:read` | Graph API, Scheduler overlays, UI | Read graph projections/overlays. | Tenant required; granted to Cartographer, Graph API, Scheduler. | | ||||
| | `graph:export` | Graph export endpoints | Stream GraphML/JSONL artefacts. | UI/gateway automation only; tenant required. | | ||||
| | `graph:simulate` | Policy simulation overlays | Trigger what-if overlays on graphs. | Restricted to automation; tenant required. | | ||||
| | `effective:write` | Policy Engine | Create/update `effective_finding_*` collections. | **Only** the Policy Engine service client may hold this scope; tenant required. | | ||||
| | `findings:read` | Console, CLI, exports | Read derived findings materialised by Policy Engine. | Shared across tenants with RBAC; tenant claim still enforced. | | ||||
| | `policy:author` | Policy Studio (Console, CLI) | Author drafts, run lint, execute quick simulations. | Tenant required; typically granted via `role/policy-author`. | | ||||
| | `policy:review` | Policy Studio review panes | Review drafts, leave comments, request changes. | Tenant required; pair with `policy:simulate` for diff previews. | | ||||
| | `policy:approve` | Policy Studio approvals | Approve or reject policy drafts. | Tenant required; fresh-auth enforced by Console UI. | | ||||
| | `policy:operate` | Policy Studio promotion controls | Trigger batch simulations, promotions, and canary runs. | Tenant required; combine with `policy:run`/`policy:activate`. | | ||||
| | `policy:audit` | Policy audit exports | Access immutable policy history, comments, and signatures. | Tenant required; read-only access. | | ||||
| | `policy:simulate` | Policy Studio / CLI simulations | Run simulations against tenant inventories. | Tenant required; available to authors, reviewers, operators. | | ||||
| | `vuln:read` | Vuln Explorer API/UI | Read normalized vulnerability data. | Tenant required. | | ||||
| | `export.viewer` | Export Center APIs | List export profiles/runs, fetch manifests and bundles. | Tenant required; read-only access. | | ||||
| | `export.operator` | Export Center APIs | Trigger export runs, manage schedules, request verifications. | Tenant required; pair with `export.admin` for retention/encryption changes. | | ||||
| | `export.admin` | Export Center administrative APIs | Configure retention policies, encryption keys, and scheduling defaults. | Tenant required; token requests must include `export_reason` + `export_ticket`; Authority audits denials. | | ||||
| | `orch:read` | Orchestrator dashboards/API | Read queued jobs, worker state, and rate-limit telemetry. | Tenant required; never grants mutation rights. | | ||||
| | `orch:operate` | Orchestrator control actions | Execute pause/resume, retry, sync-now, and backfill operations. Requires tenant assignment **and** `operator_reason`/`operator_ticket` parameters when requesting tokens. | | ||||
| | `exceptions:read` | Exception service APIs, Console | Enumerate exception definitions, routing templates, and approval state. | Tenant and approval routing metadata required for audit replay. | | ||||
| | `exceptions:write` | Policy Engine → Authority bridge | Persist exception evaluations, lifecycle events, and status changes. | Tenant required; only service principals should hold this scope. | | ||||
| | `exceptions:approve` | Console fresh-auth flows, delegated admins | Approve or reject exception requests routed through Authority. | Tenant required; Authority enforces MFA when any bound routing template has `requireMfa=true`. | | ||||
| | `ui.read` | Console base APIs | Retrieve tenant catalog, profile metadata, and token introspection results. | Tenant header required; responses are DPoP-bound and audit logged. | | ||||
| | `authority:tenants.read` | Console admin workspace | Enumerate configured tenants, default roles, and isolation metadata. | Tenant claim must match header; access audited via `authority.console.tenants.read`. | | ||||
| | Existing scopes | (e.g., `policy:*`, `concelier.jobs.trigger`) | Unchanged. | `concelier.merge` is retired — clients must request `advisory:ingest`/`advisory:read`; requests continue to fail with `invalid_client`. Review `/docs/security/policy-governance.md` for policy-specific scopes. | | ||||
|  | ||||
| ### 1.1 Scope bundles (roles) | ||||
|  | ||||
| - **`role/concelier-ingest`** → `advisory:ingest`, `advisory:read`. | ||||
| - **`role/excititor-ingest`** → `vex:ingest`, `vex:read`. | ||||
| - **`role/signals-uploader`** → `signals:write`, `signals:read`, `aoc:verify`. | ||||
| - **`role/aoc-operator`** → `aoc:verify`, `advisory:read`, `vex:read`. | ||||
| - **`role/policy-engine`** → `effective:write`, `findings:read`. | ||||
| - **`role/cartographer-service`** → `graph:write`, `graph:read`. | ||||
| - **`role/graph-gateway`** → `graph:read`, `graph:export`, `graph:simulate`. | ||||
| - **`role/console`** → `ui.read`, `advisory:read`, `vex:read`, `exceptions:read`, `aoc:verify`, `findings:read`, `orch:read`, `vuln:read`. | ||||
| - **`role/ui-console-admin`** → `ui.read`, `authority:tenants.read`, `authority:roles.read`, `authority:tokens.read`, `authority:clients.read` (paired with write scopes where required).   | ||||
| - **`role/orch-viewer`** *(Authority role: `Orch.Viewer`)* → `orch:read`. | ||||
| - **`role/orch-operator`** *(Authority role: `Orch.Operator`)* → `orch:read`, `orch:operate`. | ||||
| - **`role/policy-author`** → `policy:author`, `policy:read`, `policy:simulate`, `findings:read`. | ||||
| - **`role/policy-reviewer`** → `policy:review`, `policy:read`, `policy:simulate`, `findings:read`. | ||||
| - **`role/policy-approver`** → `policy:approve`, `policy:review`, `policy:read`, `policy:simulate`, `findings:read`. | ||||
| - **`role/policy-operator`** → `policy:operate`, `policy:run`, `policy:activate`, `policy:read`, `policy:simulate`, `findings:read`. | ||||
| - **`role/policy-auditor`** → `policy:audit`, `policy:read`, `policy:simulate`, `findings:read`. | ||||
| - **`role/export-viewer`** *(Authority role: `Export.Viewer`)* → `export.viewer`. | ||||
| - **`role/export-operator`** *(Authority role: `Export.Operator`)* → `export.viewer`, `export.operator`. | ||||
| - **`role/export-admin`** *(Authority role: `Export.Admin`)* → `export.viewer`, `export.operator`, `export.admin`. | ||||
| - **`role/exceptions-service`** → `exceptions:read`, `exceptions:write`. | ||||
| - **`role/exceptions-approver`** → `exceptions:read`, `exceptions:approve`. | ||||
|  | ||||
| Roles are declared per tenant in `authority.yaml`: | ||||
|  | ||||
| ```yaml | ||||
| tenants: | ||||
|   - name: default | ||||
|     roles: | ||||
|       concelier-ingest: | ||||
|         scopes: [advisory:ingest, advisory:read] | ||||
|       signals-uploader: | ||||
|         scopes: [signals:write, signals:read, aoc:verify] | ||||
|       aoc-operator: | ||||
|         scopes: [aoc:verify, advisory:read, vex:read] | ||||
|       orch-viewer: | ||||
|         scopes: [orch:read] | ||||
|       orch-operator: | ||||
|         scopes: [orch:read, orch:operate] | ||||
|       policy-author: | ||||
|         scopes: [policy:author, policy:read, policy:simulate, findings:read] | ||||
|       policy-reviewer: | ||||
|         scopes: [policy:review, policy:read, policy:simulate, findings:read] | ||||
|       policy-approver: | ||||
|         scopes: [policy:approve, policy:review, policy:read, policy:simulate, findings:read] | ||||
|       policy-operator: | ||||
|         scopes: [policy:operate, policy:run, policy:activate, policy:read, policy:simulate, findings:read] | ||||
|       policy-auditor: | ||||
|         scopes: [policy:audit, policy:read, policy:simulate, findings:read] | ||||
|       policy-engine: | ||||
|         scopes: [effective:write, findings:read] | ||||
|       exceptions-service: | ||||
|         scopes: [exceptions:read, exceptions:write] | ||||
|       exceptions-approver: | ||||
|         scopes: [exceptions:read, exceptions:approve] | ||||
| ``` | ||||
|  | ||||
| > **MFA requirement:** When any `exceptions.routingTemplates` entry sets `requireMfa: true`, Authority refuses to mint tokens containing `exceptions:approve` unless the authenticating identity provider advertises MFA support. Password/OIDC flows produce `authority.password.grant` audit events with `reason="Exception approval scope requires an MFA-capable identity provider."` when the requirement is violated. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 2 · Tenancy enforcement | ||||
|  | ||||
| ### 2.1 Token claims | ||||
|  | ||||
| Tokens now include: | ||||
|  | ||||
| - `tenant` claim (string) — required for all ingestion and verification scopes. | ||||
| - `service_identity` (optional) — e.g., `policy-engine`, `cartographer`. Required when requesting `effective:write` or `graph:write`. | ||||
| - `delegation_allowed` (boolean) — defaults `false`. Prevents console tokens from delegating ingest scopes. | ||||
|  | ||||
| Authority rejects requests when: | ||||
|  | ||||
| - `tenant` is missing while requesting `advisory:ingest`, `advisory:read`, `vex:ingest`, `vex:read`, or `aoc:verify` scopes. | ||||
| - `aoc:verify` is absent while tokens request `advisory:read`, `vex:read`, or any `signals:*` scope (`invalid_scope` with deterministic message). | ||||
| - `service_identity != policy-engine` but `effective:write` is present (`ERR_AOC_006` enforcement). | ||||
| - `service_identity != cartographer` but `graph:write` is present (graph pipeline enforcement). | ||||
| - Tokens attempt to combine `advisory:ingest` with `effective:write` (separation of duties). | ||||
| - `exceptions:approve` is requested by a client without a tenant assignment or via an identity provider lacking MFA when `RequireMfaForApprovals=true`. | ||||
|  | ||||
| ### 2.2 Propagation | ||||
|  | ||||
| - API Gateway forwards `tenant` claim as header (`X-Stella-Tenant`). Services refuse requests lacking the header. | ||||
| - Concelier/Excititor stamp tenant into raw documents and structured logs. | ||||
| - Policy Engine copies `tenant` from tokens into `effective_finding_*` collections. | ||||
| - Exception lifecycle services persist tenant and the selected routing template identifier alongside approval decisions. Authority audit events (`authority.password.grant`, `authority.client_credentials.grant`) surface `audit.scopes` and, on denials, a `scope.invalid` metadata entry so operators can trace exception approval attempts without inspecting downstream services. | ||||
|  | ||||
| ### 2.3 Cross-tenant scenarios | ||||
|  | ||||
| - Platform operators with `tenant:admin` can assume other tenants via `/authority/tenant/switch` if explicitly permitted. | ||||
| - CLI commands accept `--tenant <id>` to override environment default; Authority logs tenant switch events (`authority.tenant.switch`). | ||||
| - Console tenant picker uses delegated token exchange (`/token/exchange`) to obtain scoped tenant tokens without exposing raw credentials. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 3 · Configuration changes | ||||
|  | ||||
| ### 3.1 Authority configuration (`authority.yaml`) | ||||
|  | ||||
| Add new scopes and optional claims transformations: | ||||
|  | ||||
| ```yaml | ||||
| security: | ||||
|   scopes: | ||||
|     - name: advisory:ingest | ||||
|       description: Concelier raw ingestion (append-only) | ||||
|     - name: advisory:read | ||||
|       description: Read Concelier advisories and guard verdicts | ||||
|     - name: vex:ingest | ||||
|       description: Excititor raw ingestion | ||||
|     - name: vex:read | ||||
|       description: Read Excititor VEX records | ||||
|     - name: aoc:verify | ||||
|       description: Run AOC verification | ||||
|     - name: effective:write | ||||
|       description: Policy Engine materialisation | ||||
|     - name: findings:read | ||||
|       description: Read derived findings | ||||
|     - name: graph:write | ||||
|       description: Cartographer build submissions | ||||
|     - name: graph:read | ||||
|       description: Read graph overlays | ||||
|     - name: graph:export | ||||
|       description: Export graph artefacts | ||||
|     - name: graph:simulate | ||||
|       description: Run graph what-if simulations | ||||
|     - name: vuln:read | ||||
|       description: Read Vuln Explorer data | ||||
|   claimTransforms: | ||||
|     - match: { scope: "effective:write" } | ||||
|       require: | ||||
|         serviceIdentity: policy-engine | ||||
|     - match: { scope: "graph:write" } | ||||
|       require: | ||||
|         serviceIdentity: cartographer | ||||
| ``` | ||||
|  | ||||
| ### 3.2 Client registration | ||||
|  | ||||
| Update service clients: | ||||
|  | ||||
| - `Concelier.WebService` → request `advisory:ingest`, `advisory:read`. | ||||
| - `Excititor.WebService` → request `vex:ingest`, `vex:read`. | ||||
| - `Policy.Engine` → request `effective:write`, `findings:read`; set `properties.serviceIdentity=policy-engine`. | ||||
| - `Cartographer.Service` → request `graph:write`, `graph:read`; set `properties.serviceIdentity=cartographer`. | ||||
| - `Graph API Gateway` → request `graph:read`, `graph:export`, `graph:simulate`; tenant hint required. | ||||
| - `Console` → request `advisory:read`, `vex:read`, `aoc:verify`, `findings:read`, `vuln:read` plus existing UI scopes. | ||||
| - `CLI automation` → request `aoc:verify`, `advisory:read`, `vex:read` as needed. | ||||
|  | ||||
| Client definition snippet: | ||||
|  | ||||
| ```yaml | ||||
| clients: | ||||
|   - clientId: concelier-web | ||||
|     grantTypes: [client_credentials] | ||||
|     scopes: [advisory:ingest, advisory:read] | ||||
|     tenants: [default] | ||||
|   - clientId: policy-engine | ||||
|     grantTypes: [client_credentials] | ||||
|     scopes: [effective:write, findings:read] | ||||
|     properties: | ||||
|       serviceIdentity: policy-engine | ||||
|   - clientId: cartographer-service | ||||
|     grantTypes: [client_credentials] | ||||
|     scopes: [graph:write, graph:read] | ||||
|     properties: | ||||
|       serviceIdentity: cartographer | ||||
| ``` | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 4 · Operational safeguards | ||||
|  | ||||
| - **Audit events:** Authority emits `authority.scope.granted` and `authority.scope.revoked` events with `scope` and `tenant`. Monitor for unexpected grants. | ||||
| - **Rate limiting:** Apply stricter limits on `/token` endpoints for clients requesting `advisory:ingest` or `vex:ingest` to mitigate brute-force ingestion attempts. | ||||
| - **Incident response:** Link AOC alerts to Authority audit logs to confirm whether violations come from expected identities. | ||||
| - **Rotation:** Rotate ingest client secrets alongside guard deployments; add rotation steps to `ops/authority-key-rotation.md`. | ||||
| - **Testing:** Integration tests must fail if tokens lacking `tenant` attempt ingestion; add coverage in Concelier/Excititor smoke suites (see `CONCELIER-CORE-AOC-19-013`). | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 5 · Offline & air-gap notes | ||||
|  | ||||
| - Offline Kit bundles include tenant-scoped service credentials. Ensure ingest bundles ship without `advisory:ingest` scopes unless strictly required. | ||||
| - CLI verification in offline environments uses pre-issued `aoc:verify` tokens; document expiration and renewal processes. | ||||
| - Authority replicas in air-gapped environments should restrict scope issuance to known tenants and log all `/token` interactions for later replay. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 6 · References | ||||
|  | ||||
| - [Aggregation-Only Contract reference](../ingestion/aggregation-only-contract.md) | ||||
| - [Architecture overview](../architecture/overview.md) | ||||
| - [Concelier architecture](../ARCHITECTURE_CONCELIER.md) | ||||
| - [Excititor architecture](../ARCHITECTURE_EXCITITOR.md) | ||||
| - [Policy governance](policy-governance.md) | ||||
| - [Authority key rotation playbook](../ops/authority-key-rotation.md) | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 7 · Compliance checklist | ||||
|  | ||||
| - [ ] Scope catalogue updated in Authority configuration templates. | ||||
| - [ ] Role mappings documented for each tenant profile. | ||||
| - [ ] Claim transforms enforce `serviceIdentity` for `effective:write`. | ||||
| - [ ] Claim transforms enforce `serviceIdentity` for `graph:write`. | ||||
| - [ ] Concelier/Excititor smoke tests cover missing tenant rejection. | ||||
| - [ ] Offline kit credentials reviewed for least privilege. | ||||
| - [ ] Audit/monitoring guidance validated with Observability Guild. | ||||
| - [ ] Authority Core sign-off recorded (owner: @authority-core, due 2025-10-28). | ||||
|  | ||||
| --- | ||||
|  | ||||
| *Last updated: 2025-10-27 (Sprint 19).*  | ||||
|   | ||||
| @@ -1,106 +1,106 @@ | ||||
| # Authority Threat Model (STRIDE) | ||||
|  | ||||
| > Prepared by Security Guild — 2025-10-12. Scope covers Authority host, Standard plug-in, CLI, bootstrap workflow, and offline revocation distribution. | ||||
|  | ||||
| ## 1. Scope & Method | ||||
|  | ||||
| - Methodology: STRIDE applied to primary Authority surfaces (token issuance, bootstrap, revocation, operator tooling, plug-in extensibility). | ||||
| - Assets in scope: identity credentials, OAuth tokens (access/refresh), bootstrap invites, revocation manifests, signing keys, audit telemetry. | ||||
| - Out of scope: Third-party IdPs federated via OpenIddict (tracked separately in SEC6 backlog). | ||||
|  | ||||
| ## 2. Assets & Entry Points | ||||
|  | ||||
| | Asset / Surface | Description | Primary Actors | | ||||
| |-----------------|-------------|----------------| | ||||
| | Token issuance APIs (`/token`, `/authorize`) | OAuth/OIDC endpoints mediated by OpenIddict | CLI, UI, automation agents | | ||||
| | Bootstrap channel | Initial admin invite + bootstrap CLI workflow | Platform operators | | ||||
| | Revocation bundle | Offline JSON + detached JWS consumed by agents | Concelier, Agents, Zastava | | ||||
| | Plug-in manifests | Standard plug-in configuration and password policy overrides | Operators, DevOps | | ||||
| | Signing keys | ES256 signing keys backing tokens and revocation manifests | Security Guild, HSM/KeyOps | | ||||
| | Audit telemetry | Structured login/audit stream persisted to Mongo/observability stack | SOC, SecOps | | ||||
|  | ||||
| ## 3. Trust Boundaries | ||||
|  | ||||
| | Boundary | Rationale | Controls | | ||||
| |----------|-----------|----------| | ||||
| | TB1 — Public network ↔️ Authority ingress | Internet/extranet exposure for `/token`, `/authorize`, `/bootstrap` | TLS 1.3, reverse proxy ACLs, rate limiting (SEC3.A / CORE8.RL) | | ||||
| | TB2 — Authority host ↔️ Mongo storage | Credential store, revocation state, audit log persistence | Authenticated Mongo, network segmentation, deterministic serializers | | ||||
| | TB3 — Authority host ↔️ Plug-in sandbox | Plug-ins may override password policy and bootstrap flows | Code signing, manifest validation, restart-time loading only | | ||||
| | TB4 — Operator workstation ↔️ CLI | CLI holds bootstrap secrets and revocation bundles | OS keychain storage, MFA on workstations, offline kit checksum | | ||||
| | TB5 — Authority ↔️ Downstream agents | Revocation bundle consumption, token validation | Mutual TLS (planned), detached JWS signatures, bundle freshness checks | | ||||
|  | ||||
| ## 4. Data Flow Diagrams | ||||
|  | ||||
| ### 4.1 Runtime token issuance | ||||
|  | ||||
| ```mermaid | ||||
| flowchart LR | ||||
|     subgraph Client Tier | ||||
|         CLI[StellaOps CLI] | ||||
|         UI[UI / Automation] | ||||
|     end | ||||
|     subgraph Perimeter | ||||
|         RP[Reverse Proxy / WAF] | ||||
|     end | ||||
|     subgraph Authority | ||||
|         AUTH[Authority Host] | ||||
|         PLGIN[Standard Plug-in] | ||||
|         STORE[(Mongo Credential Store)] | ||||
|     end | ||||
|     CLI -->|OAuth password / client creds| RP --> AUTH | ||||
|     UI -->|OAuth flows| RP | ||||
|     AUTH -->|PasswordHashOptions + Secrets| PLGIN | ||||
|     AUTH -->|Verify / Persist hashes| STORE | ||||
|     STORE -->|Rehash needed| AUTH | ||||
|     AUTH -->|Access / refresh token| RP --> Client Tier | ||||
| ``` | ||||
|  | ||||
| ### 4.2 Bootstrap & revocation | ||||
|  | ||||
| ```mermaid | ||||
| flowchart LR | ||||
|     subgraph Operator | ||||
|         OPS[Operator Workstation] | ||||
|     end | ||||
|     subgraph Authority | ||||
|         AUTH[Authority Host] | ||||
|         STORE[(Mongo)] | ||||
|     end | ||||
|     subgraph Distribution | ||||
|         OFFKIT[Offline Kit Bundle] | ||||
|         AGENT[Authorized Agent / Concelier] | ||||
|     end | ||||
|     OPS -->|Bootstrap CLI (`stellaops auth bootstrap`)| AUTH | ||||
|     AUTH -->|One-time invite + Argon2 hash| STORE | ||||
|     AUTH -->|Revocation export (`stellaops auth revoke export`)| OFFKIT | ||||
|     OFFKIT -->|Signed JSON + .jws| AGENT | ||||
|     AGENT -->|Revocation ACK / telemetry| AUTH | ||||
| ``` | ||||
|  | ||||
| ## 5. STRIDE Analysis | ||||
|  | ||||
| | Threat | STRIDE Vector | Surface | Risk (L×I) | Existing Controls | Gaps / Actions | Owner | | ||||
| |--------|---------------|---------|------------|-------------------|----------------|-------| | ||||
| | Spoofed revocation bundle | Spoofing | TB5 — Authority ↔️ Agents | Med×High | Detached JWS signature (planned), offline kit checksums | Finalise signing key registry & verification script (SEC4.B/SEC4.HOST); add bundle freshness requirement | Security Guild (follow-up: **SEC5.B**) | | ||||
| | Parameter tampering on `/token` | Tampering | TB1 — Public ingress | Med×High | ASP.NET model validation, OpenIddict, rate limiter (CORE8.RL) | Tampered requests emit `authority.token.tamper` audit events (`request.tampered`, unexpected parameter names) correlating with `/token` outcomes (SEC5.C) | Security Guild + Authority Core (follow-up: **SEC5.C**) | | ||||
| | Bootstrap invite replay | Repudiation | TB4 — Operator CLI ↔️ Authority | Low×High | One-time bootstrap tokens, Argon2id hashing on creation | Invites expire automatically and emit audit events on consumption/expiration (SEC5.D) | Security Guild | | ||||
| | Token replay by stolen agent | Information Disclosure | TB5 | Med×High | Signed revocation bundles, device fingerprint heuristics, optional mTLS | Monitor revocation acknowledgement latency via Zastava and tune replay alerting thresholds | Security Guild + Zastava (follow-up: **SEC5.E**) | | ||||
| | Privilege escalation via plug-in override | Elevation of Privilege | TB3 — Plug-in sandbox | Med×High | Signed plug-ins, restart-only loading, configuration validation | Add static analysis on manifest overrides + runtime warning when policy weaker than host | Security Guild + DevOps (follow-up: **SEC5.F**) | | ||||
| | Offline bundle tampering | Tampering | Distribution | Low×High | SHA256 manifest, signed bundles (planned) | Add supply-chain attestation for Offline Kit, publish verification CLI in docs | Security Guild + Ops (follow-up: **SEC5.G**) | | ||||
| | Failure to log denied tokens | Repudiation | TB2 — Authority ↔️ Mongo | Med×Med | Serilog structured events (partial), Mongo persistence path (planned) | Finalise audit schema (SEC2.A) and ensure `/token` denies include subject/client/IP fields | Security Guild + Authority Core (follow-up: **SEC5.H**) | | ||||
|  | ||||
| Risk scoring uses qualitative scale (Low/Med/High) for likelihood × impact; mitigation priority follows High > Med > Low. | ||||
|  | ||||
| ## 6. Follow-up Backlog Hooks | ||||
|  | ||||
| | Backlog ID | Linked Threat | Summary | Target Owners | | ||||
| |------------|---------------|---------|---------------| | ||||
| | SEC5.B | Spoofed revocation bundle | Complete libsodium/Core signing integration and ship revocation verification script. | Security Guild + Authority Core | | ||||
| | SEC5.C | Parameter tampering on `/token` | Finalise audit contract (`SEC2.A`) and add request tamper logging. | Security Guild + Authority Core | | ||||
| | SEC5.D | Bootstrap invite replay | Implement expiry enforcement + audit coverage for unused bootstrap invites. | Security Guild | | ||||
| | SEC5.E | Token replay by stolen agent | Coordinate Zastava alerting with the new device fingerprint heuristics and surface stale revocation acknowledgements. | Security Guild + Zastava | | ||||
| | SEC5.F | Plug-in override escalation | Static analysis of plug-in manifests; warn on weaker password policy overrides. | Security Guild + DevOps | | ||||
| | SEC5.G | Offline bundle tampering | Extend Offline Kit build to include attested manifest + verification CLI sample. | Security Guild + Ops | | ||||
| | SEC5.H | Failure to log denied tokens | Ensure audit persistence for all `/token` denials with correlation IDs. | Security Guild + Authority Core | | ||||
|  | ||||
| Update `src/StellaOps.Cryptography/TASKS.md` (Security Guild board) with the above backlog entries to satisfy SEC5.A exit criteria. | ||||
| # Authority Threat Model (STRIDE) | ||||
|  | ||||
| > Prepared by Security Guild — 2025-10-12. Scope covers Authority host, Standard plug-in, CLI, bootstrap workflow, and offline revocation distribution. | ||||
|  | ||||
| ## 1. Scope & Method | ||||
|  | ||||
| - Methodology: STRIDE applied to primary Authority surfaces (token issuance, bootstrap, revocation, operator tooling, plug-in extensibility). | ||||
| - Assets in scope: identity credentials, OAuth tokens (access/refresh), bootstrap invites, revocation manifests, signing keys, audit telemetry. | ||||
| - Out of scope: Third-party IdPs federated via OpenIddict (tracked separately in SEC6 backlog). | ||||
|  | ||||
| ## 2. Assets & Entry Points | ||||
|  | ||||
| | Asset / Surface | Description | Primary Actors | | ||||
| |-----------------|-------------|----------------| | ||||
| | Token issuance APIs (`/token`, `/authorize`) | OAuth/OIDC endpoints mediated by OpenIddict | CLI, UI, automation agents | | ||||
| | Bootstrap channel | Initial admin invite + bootstrap CLI workflow | Platform operators | | ||||
| | Revocation bundle | Offline JSON + detached JWS consumed by agents | Concelier, Agents, Zastava | | ||||
| | Plug-in manifests | Standard plug-in configuration and password policy overrides | Operators, DevOps | | ||||
| | Signing keys | ES256 signing keys backing tokens and revocation manifests | Security Guild, HSM/KeyOps | | ||||
| | Audit telemetry | Structured login/audit stream persisted to Mongo/observability stack | SOC, SecOps | | ||||
|  | ||||
| ## 3. Trust Boundaries | ||||
|  | ||||
| | Boundary | Rationale | Controls | | ||||
| |----------|-----------|----------| | ||||
| | TB1 — Public network ↔️ Authority ingress | Internet/extranet exposure for `/token`, `/authorize`, `/bootstrap` | TLS 1.3, reverse proxy ACLs, rate limiting (SEC3.A / CORE8.RL) | | ||||
| | TB2 — Authority host ↔️ Mongo storage | Credential store, revocation state, audit log persistence | Authenticated Mongo, network segmentation, deterministic serializers | | ||||
| | TB3 — Authority host ↔️ Plug-in sandbox | Plug-ins may override password policy and bootstrap flows | Code signing, manifest validation, restart-time loading only | | ||||
| | TB4 — Operator workstation ↔️ CLI | CLI holds bootstrap secrets and revocation bundles | OS keychain storage, MFA on workstations, offline kit checksum | | ||||
| | TB5 — Authority ↔️ Downstream agents | Revocation bundle consumption, token validation | Mutual TLS (planned), detached JWS signatures, bundle freshness checks | | ||||
|  | ||||
| ## 4. Data Flow Diagrams | ||||
|  | ||||
| ### 4.1 Runtime token issuance | ||||
|  | ||||
| ```mermaid | ||||
| flowchart LR | ||||
|     subgraph Client Tier | ||||
|         CLI[StellaOps CLI] | ||||
|         UI[UI / Automation] | ||||
|     end | ||||
|     subgraph Perimeter | ||||
|         RP[Reverse Proxy / WAF] | ||||
|     end | ||||
|     subgraph Authority | ||||
|         AUTH[Authority Host] | ||||
|         PLGIN[Standard Plug-in] | ||||
|         STORE[(Mongo Credential Store)] | ||||
|     end | ||||
|     CLI -->|OAuth password / client creds| RP --> AUTH | ||||
|     UI -->|OAuth flows| RP | ||||
|     AUTH -->|PasswordHashOptions + Secrets| PLGIN | ||||
|     AUTH -->|Verify / Persist hashes| STORE | ||||
|     STORE -->|Rehash needed| AUTH | ||||
|     AUTH -->|Access / refresh token| RP --> Client Tier | ||||
| ``` | ||||
|  | ||||
| ### 4.2 Bootstrap & revocation | ||||
|  | ||||
| ```mermaid | ||||
| flowchart LR | ||||
|     subgraph Operator | ||||
|         OPS[Operator Workstation] | ||||
|     end | ||||
|     subgraph Authority | ||||
|         AUTH[Authority Host] | ||||
|         STORE[(Mongo)] | ||||
|     end | ||||
|     subgraph Distribution | ||||
|         OFFKIT[Offline Kit Bundle] | ||||
|         AGENT[Authorized Agent / Concelier] | ||||
|     end | ||||
|     OPS -->|Bootstrap CLI (`stellaops auth bootstrap`)| AUTH | ||||
|     AUTH -->|One-time invite + Argon2 hash| STORE | ||||
|     AUTH -->|Revocation export (`stellaops auth revoke export`)| OFFKIT | ||||
|     OFFKIT -->|Signed JSON + .jws| AGENT | ||||
|     AGENT -->|Revocation ACK / telemetry| AUTH | ||||
| ``` | ||||
|  | ||||
| ## 5. STRIDE Analysis | ||||
|  | ||||
| | Threat | STRIDE Vector | Surface | Risk (L×I) | Existing Controls | Gaps / Actions | Owner | | ||||
| |--------|---------------|---------|------------|-------------------|----------------|-------| | ||||
| | Spoofed revocation bundle | Spoofing | TB5 — Authority ↔️ Agents | Med×High | Detached JWS signature (planned), offline kit checksums | Finalise signing key registry & verification script (SEC4.B/SEC4.HOST); add bundle freshness requirement | Security Guild (follow-up: **SEC5.B**) | | ||||
| | Parameter tampering on `/token` | Tampering | TB1 — Public ingress | Med×High | ASP.NET model validation, OpenIddict, rate limiter (CORE8.RL) | Tampered requests emit `authority.token.tamper` audit events (`request.tampered`, unexpected parameter names) correlating with `/token` outcomes (SEC5.C) | Security Guild + Authority Core (follow-up: **SEC5.C**) | | ||||
| | Bootstrap invite replay | Repudiation | TB4 — Operator CLI ↔️ Authority | Low×High | One-time bootstrap tokens, Argon2id hashing on creation | Invites expire automatically and emit audit events on consumption/expiration (SEC5.D) | Security Guild | | ||||
| | Token replay by stolen agent | Information Disclosure | TB5 | Med×High | Signed revocation bundles, device fingerprint heuristics, optional mTLS | Monitor revocation acknowledgement latency via Zastava and tune replay alerting thresholds | Security Guild + Zastava (follow-up: **SEC5.E**) | | ||||
| | Privilege escalation via plug-in override | Elevation of Privilege | TB3 — Plug-in sandbox | Med×High | Signed plug-ins, restart-only loading, configuration validation | Add static analysis on manifest overrides + runtime warning when policy weaker than host | Security Guild + DevOps (follow-up: **SEC5.F**) | | ||||
| | Offline bundle tampering | Tampering | Distribution | Low×High | SHA256 manifest, signed bundles (planned) | Add supply-chain attestation for Offline Kit, publish verification CLI in docs | Security Guild + Ops (follow-up: **SEC5.G**) | | ||||
| | Failure to log denied tokens | Repudiation | TB2 — Authority ↔️ Mongo | Med×Med | Serilog structured events (partial), Mongo persistence path (planned) | Finalise audit schema (SEC2.A) and ensure `/token` denies include subject/client/IP fields | Security Guild + Authority Core (follow-up: **SEC5.H**) | | ||||
|  | ||||
| Risk scoring uses qualitative scale (Low/Med/High) for likelihood × impact; mitigation priority follows High > Med > Low. | ||||
|  | ||||
| ## 6. Follow-up Backlog Hooks | ||||
|  | ||||
| | Backlog ID | Linked Threat | Summary | Target Owners | | ||||
| |------------|---------------|---------|---------------| | ||||
| | SEC5.B | Spoofed revocation bundle | Complete libsodium/Core signing integration and ship revocation verification script. | Security Guild + Authority Core | | ||||
| | SEC5.C | Parameter tampering on `/token` | Finalise audit contract (`SEC2.A`) and add request tamper logging. | Security Guild + Authority Core | | ||||
| | SEC5.D | Bootstrap invite replay | Implement expiry enforcement + audit coverage for unused bootstrap invites. | Security Guild | | ||||
| | SEC5.E | Token replay by stolen agent | Coordinate Zastava alerting with the new device fingerprint heuristics and surface stale revocation acknowledgements. | Security Guild + Zastava | | ||||
| | SEC5.F | Plug-in override escalation | Static analysis of plug-in manifests; warn on weaker password policy overrides. | Security Guild + DevOps | | ||||
| | SEC5.G | Offline bundle tampering | Extend Offline Kit build to include attested manifest + verification CLI sample. | Security Guild + Ops | | ||||
| | SEC5.H | Failure to log denied tokens | Ensure audit persistence for all `/token` denials with correlation IDs. | Security Guild + Authority Core | | ||||
|  | ||||
| Update `src/__Libraries/StellaOps.Cryptography/TASKS.md` (Security Guild board) with the above backlog entries to satisfy SEC5.A exit criteria. | ||||
|   | ||||
| @@ -1,183 +1,183 @@ | ||||
| # StellaOps Console Security Posture | ||||
|  | ||||
| > **Audience:** Security Guild, Console & Authority teams, deployment engineers.   | ||||
| > **Scope:** OIDC/DPoP flows, scope model, session controls, CSP and transport headers, evidence handling, offline posture, and monitoring expectations for the StellaOps Console (Sprint 23). | ||||
|  | ||||
| The console is an Angular SPA fronted by the StellaOps Web gateway. It consumes Authority for identity, Concelier/Excititor for aggregation data, Policy Engine for findings, and Attestor for evidence bundles. This guide captures the security guarantees and required hardening so that the console can ship alongside the Aggregation-Only Contract (AOC) without introducing new attack surface. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 1 · Identity & Authentication | ||||
|  | ||||
| ### 1.1 Authorization sequence | ||||
|  | ||||
| 1. Browser→Authority uses **OAuth 2.1 Authorization Code + PKCE** (`S256`).   | ||||
| 2. Upon code exchange the console requests a **DPoP-bound access token** (`aud=console`, `tenant=<id>`) with **120 s TTL** and optional **rotating refresh token** (`rotate=true`).   | ||||
| 3. Authority includes `cnf.jkt` for the ephemeral WebCrypto keypair; console stores the private key in **IndexedDB** (non-exportable) and keeps the public JWK in memory.   | ||||
| 4. All API calls attach `Authorization: Bearer <token>` + `DPoP` proof header. Nonces from the gateway are replay-protected (`dpopt-nonce` header).   | ||||
| 5. Tenanted API calls flow through the Web gateway which forwards `X-Stella-Tenant` and enforces tenancy headers. Missing or mismatched tenants trigger `403` with `ERR_TENANT_MISMATCH`. | ||||
|  | ||||
| ### 1.2 Fresh-auth gating | ||||
|  | ||||
| - Sensitive actions (tenant edits, token revocation, policy promote, signing key rotation) call `Authority /fresh-auth` using `prompt=login` + `max_age=300`.   | ||||
| - Successful fresh-auth yields a **300 s** scoped token (`fresh_auth=true`) stored only in memory; the UI disables guarded buttons when the timer expires.   | ||||
| - Audit events: `authority.fresh_auth.start`, `authority.fresh_auth.success`, `authority.fresh_auth.expired` (link to correlation IDs for the gated action). | ||||
|  | ||||
| ### 1.3 Offline & sealed mode | ||||
|  | ||||
| - When `console.offlineMode=true` the console presents an offline banner and suppresses fresh-auth prompts, replacing them with CLI guidance (`stella auth fresh-auth --offline`).   | ||||
| - Offline mode requires pre-issued tenant-scoped tokens bundled with the Offline Kit; tokens must include `offline=true` claim and 15 m TTL.   | ||||
| - Authority availability health is polled via `/api/console/status`. HTTP failures raise the offline banner and switch to read-only behaviour. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 2 · Session & Device Binding | ||||
|  | ||||
| - Access and refresh tokens live in memory; metadata (subject, tenant, expiry) persists in `sessionStorage` for reload continuity. **Never** store raw JWTs in `localStorage`.   | ||||
| - Inactivity timeout defaults to **15 minutes**. Idle sessions trigger silent refresh; on failure the UI shows a modal requiring re-auth.   | ||||
| - Tokens are device-bound through DPoP; if a new device logs in, Authority revokes the previous DPoP key and emits `authority.token.binding_changed`.   | ||||
| - CSRF mitigations: bearer tokens plus DPoP remove cookie reliance. If cookies are required (e.g., same-origin analytics) they must be `HttpOnly`, `SameSite=Lax`, `Secure`.   | ||||
| - Browser hardening: enforce `Strict-Transport-Security`, `X-Content-Type-Options: nosniff`, `Referrer-Policy: no-referrer`, `Permissions-Policy: camera=(), microphone=(), geolocation=()`. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 3 · Authorization & Scope Model | ||||
|  | ||||
| The console client is registered in Authority as `console-ui` with scopes: | ||||
|  | ||||
| | Feature area | Required scopes | Notes | | ||||
| |--------------|----------------|-------| | ||||
| | Base navigation (Dashboard, Findings, SBOM, Runs) | `ui.read`, `findings:read`, `advisory:read`, `vex:read`, `aoc:verify` | `findings:read` enables Policy Engine overlays; `advisory:read`/`vex:read` load ingestion panes; `aoc:verify` allows on-demand guard runs. | | ||||
| | Admin workspace | `ui.admin`, `authority:tenants.read`, `authority:tenants.write`, `authority:roles.read`, `authority:roles.write`, `authority:tokens.read`, `authority:tokens.revoke`, `authority:clients.read`, `authority:clients.write`, `authority:audit.read` | Scope combinations are tenant constrained. Role changes require fresh-auth. | | ||||
| | Policy approvals | `policy:read`, `policy:review`, `policy:approve`, `policy:operate`, `policy:simulate` | `policy:operate` (promote/activate/run) requires fresh-auth. | | ||||
| | Observability panes (status ticker, telemetry) | `ui.telemetry`, `scheduler:runs.read`, `advisory:read`, `vex:read` | `ui.telemetry` drives OTLP export toggles. | | ||||
| | Orchestrator dashboard (queues, workers, rate limits) | `orch:read` | Provision via `Orch.Viewer` role; read-only access to job state and telemetry. | | ||||
| | Orchestrator control actions (pause/resume, retry, sync-now, backfill) | `orch:operate` (plus `orch:read`) | CLI/Console must request tokens with `operator_reason` and `operator_ticket`; Authority denies issuance when either value is missing. | | ||||
| | Downloads parity (SBOM, attestation) | `downloads:read`, `attestation:verify`, `sbom:export` | Console surfaces digests only; download links require CLI parity for write operations. | | ||||
|  | ||||
| Guidance: | ||||
|  | ||||
| - **Role mapping**: Provision Authority role `role/ui-console-admin` encapsulating the admin scopes above.   | ||||
| - **Orchestrator viewers**: Assign Authority role `role/orch-viewer` (Authority role string `Orch.Viewer`) to consoles that require read-only access to Orchestrator telemetry.   | ||||
| - **Orchestrator operators**: Assign Authority role `role/orch-operator` (Authority role string `Orch.Operator`) to identities allowed to pause/resume or backfill. Tokens must include `operator_reason` (≤256 chars) and `operator_ticket` (≤128 chars); Authority records the values in audit logs. | ||||
| - **Tenant enforcement**: Gateway injects `X-Stella-Tenant` from token claims. Requests missing the header must be rejected by downstream services (Concelier, Excititor, Policy Engine) and logged.   | ||||
| - **Separation of duties**: Never grant `ui.admin` and `policy:approve`/`policy:operate` to the same human role without SOC sign-off; automation accounts should use least-privilege dedicated clients. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ### 3.1 Console Authority endpoints | ||||
|  | ||||
| Console uses dedicated Authority endpoints scoped under `/console/*`. All requests must include the tenant header injected by the gateway (`X-Stella-Tenant`); calls without the header fail with `tenant_header_missing` and emit a structured audit event. Keep reverse proxies configured to pass the header end-to-end. | ||||
|  | ||||
| | Endpoint | Required scopes | Purpose | Notes | | ||||
| |----------|-----------------|---------|-------| | ||||
| | `GET /console/tenants` | `authority:tenants.read` | Returns the tenant catalogue for the authenticated principal. | Validates `X-Stella-Tenant`; rejects tenants not configured in Authority. | | ||||
| | `GET /console/profile` | `ui.read` | Surfaces subject metadata (roles, scopes, session id, fresh-auth state). | Response includes `freshAuth` (bool) based on a 300 s window since `auth_time`. | | ||||
| | `POST /console/token/introspect` | `ui.read` | Introspects the access token currently in use and reports expiry + tenant. | Console polls this endpoint to drive session inactivity prompts; intended for SPA usage via fetch POST. | | ||||
|  | ||||
| **Fresh-auth & session inactivity:** Authority stamps `auth_time` on issued tokens and considers privileged actions “fresh” for five minutes. When `/console/profile` returns `freshAuth: false`, the UI must require an interactive re-authentication before allowing admin operations (`ui.admin`, `authority:*` mutations, `policy:activate`, `exceptions:approve`). Access tokens remain short-lived (`00:02:00` by default); pair this with Console session timeouts so idle dashboards prompt the user before two minutes of inactivity. | ||||
|  | ||||
| **DPoP + tenant binding:** All `/console/*` endpoints require DPoP-bound access tokens. Audit events include `tenant.resolved`, `scope`, `correlationId`, and (when applicable) `token.expires_at`. Staple the same headers into downstream services so cross-component troubleshooting uses the same correlation identifiers. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 4 · Transport, CSP & Browser Hardening | ||||
|  | ||||
| ### 4.1 Gateway requirements | ||||
|  | ||||
| - TLS 1.2+ with modern cipher suites; enable HTTP/2 for SSE streams.   | ||||
| - Terminate TLS at the reverse proxy (Traefik, NGINX) and forward `X-Forwarded-*` headers (`ASPNETCORE_FORWARDEDHEADERS_ENABLED=true`).   | ||||
| - Rate-limit `/authorize` and `/token` according to [Authority rate-limit guidance](rate-limits.md). | ||||
|  | ||||
| ### 4.2 Content Security Policy | ||||
|  | ||||
| Default CSP served by the console container: | ||||
|  | ||||
| ``` | ||||
| default-src 'self'; | ||||
| connect-src 'self' https://*.stella-ops.local; | ||||
| img-src 'self' data:; | ||||
| script-src 'self'; | ||||
| style-src 'self' 'unsafe-inline'; | ||||
| font-src 'self'; | ||||
| frame-ancestors 'none'; | ||||
| ``` | ||||
|  | ||||
| Recommendations: | ||||
|  | ||||
| - Extend `connect-src` only for known internal APIs (e.g., telemetry collector). Use `console.config.cspOverrides` instead of editing NGINX directly.   | ||||
| - Enable **COOP/COEP** (`Cross-Origin-Opener-Policy: same-origin`, `Cross-Origin-Embedder-Policy: require-corp`) to support WASM policy previews.   | ||||
| - Use **Subresource Integrity (SRI)** hashes when adding third-party fonts or scripts.   | ||||
| - For embedded screenshots/GIFs sourced from Offline Kit, use `img-src 'self' data: blob:` and verify assets during build.   | ||||
| - Enforce `X-Frame-Options: DENY`, `X-XSS-Protection: 0`, and `Cache-Control: no-store` on JSON API responses (HTML assets remain cacheable). | ||||
|  | ||||
| ### 4.3 SSE & WebSocket hygiene | ||||
|  | ||||
| - SSE endpoints (`/console/status/stream`, `/console/runs/{id}/events`) must set `Cache-Control: no-store` and disable proxy buffering.   | ||||
| - Gate SSE behind the same DPoP tokens; reject without `Authorization`.   | ||||
| - Proxy timeouts ≥ 60 s to avoid disconnect storms; clients use exponential backoff with jitter. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 5 · Evidence & Data Handling | ||||
|  | ||||
| - **Evidence bundles**: Download links trigger `attestor.verify` or `downloads.manifest` APIs. The UI never caches bundle contents; it only surfaces SHA-256 digests and cosign signatures. Operators must use CLI to fetch the signed artefact.   | ||||
| - **Secrets**: UI redacts tokens, emails, and attachment paths in logs. Structured logs include only `subject`, `tenant`, `action`, `correlationId`.   | ||||
| - **Aggregation data**: Console honours Aggregation-Only contract—no client-side rewriting of Concelier/Excititor precedence. Provenance badges display source IDs and merge-event hashes.   | ||||
| - **PII minimisation**: User lists show minimal identity (display name, email hash). Full email addresses require `ui.admin` + fresh-auth.   | ||||
| - **Downloads parity**: Every downloadable artefact includes a CLI parity link (e.g., `stella downloads fetch --artifact <id>`). If CLI parity fails, the console displays a warning banner and links to troubleshooting docs. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 6 · Logging, Monitoring & Alerts | ||||
|  | ||||
| - Structured logs: `ui.action`, `tenantId`, `subject`, `scope`, `correlationId`, `dpop.jkt`. Log level `Information` for key actions; `Warning` for security anomalies (failed DPoP, tenant mismatch).   | ||||
| - Metrics (Prometheus): `ui_request_duration_seconds`, `ui_dpop_failure_total`, `ui_fresh_auth_prompt_total`, `ui_tenant_switch_total`, `ui_offline_banner_seconds`.   | ||||
| - Alerts: | ||||
|   1. **Fresh-auth failures** > 5 per minute per tenant → security review.   | ||||
|   2. **DPoP mismatches** sustained > 1 % of requests → potential replay attempt.   | ||||
|   3. **Tenant mismatches** > 0 triggers an audit incident (could indicate scope misconfiguration).   | ||||
| - Correlate with Authority audit events (`authority.scope.granted`, `authority.token.revoked`) and Concelier/Excititor ingestion logs to trace user impact. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 7 · Offline & Air-Gapped Posture | ||||
|  | ||||
| - Offline deployments require mirrored container images and Offline Kit manifest verification (see `/docs/deploy/console.md` §7).   | ||||
| - Console reads `offlineManifest.json` at boot to validate asset digests; mismatches block startup until the manifest is refreshed.   | ||||
| - Tenant and role edits queue change manifests for export; UI instructs operators to run `stella auth apply --bundle <file>` on the offline Authority host.   | ||||
| – Evidence viewing remains read-only; download buttons provide scripts to export from local Attestor snapshots.   | ||||
| - Fresh-auth prompts display instructions for hardware-token usage on bastion hosts; system logs mark actions executed under offline fallback. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 8 · Threat Model Alignment | ||||
|  | ||||
| | Threat (Authority TM §5) | Console control | | ||||
| |--------------------------|-----------------| | ||||
| | Spoofed revocation bundle | Console verifies manifest signatures before showing revocation status; links to `stella auth revoke verify`. | | ||||
| | Parameter tampering on `/token` | PKCE + DPoP enforced; console propagates correlation IDs so Authority logs can link anomalies. | | ||||
| | Bootstrap invite replay | Admin UI surfaces invite status with expiry; fresh-auth required before issuing new invites. | | ||||
| | Token replay by stolen agent | DPoP binding prevents reuse; console surfaces revocation latency warnings sourced from Zastava metrics. | | ||||
| | Offline bundle tampering | Console refuses unsigned Offline Kit assets; prompts operators to re-import verified bundles. | | ||||
| | Privilege escalation via plug-in overrides | Plug-in manifest viewer warns when a plug-in downgrades password policy; UI restricts plug-in activation to fresh-auth + `ui.admin` scoped users. | | ||||
|  | ||||
| Document gaps and remediation hooks in `SEC5.*` backlog as they are addressed. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 9 · Compliance checklist | ||||
|  | ||||
| - [x] Authority client `console-ui` registered with PKCE, DPoP, tenant claim requirement, and scopes from §3. (see [console security sign-off](../updates/2025-10-27-console-security-signoff.md#authority-client-validation))   | ||||
| - [x] CSP enforced per §4 with overrides documented in deployment manifests. (see [console security sign-off](../updates/2025-10-27-console-security-signoff.md#csp-enforcement))   | ||||
| - [x] Fresh-auth timer (300 s) validated for admin and policy actions; audit events captured. (see [console security sign-off](../updates/2025-10-27-console-security-signoff.md#fresh-auth-timer))   | ||||
| - [x] DPoP binding tested (replay attempt blocked; logs show `ui_dpop_failure_total` increment). (see [console security sign-off](../updates/2025-10-27-console-security-signoff.md#dpop-binding-test))   | ||||
| - [x] Offline mode exercises performed (banner, CLI guidance, manifest verification). (see [console security sign-off](../updates/2025-10-27-console-security-signoff.md#offline-mode-exercise))   | ||||
| - [x] Evidence download parity verified with CLI scripts; console never caches sensitive artefacts. (see [console security sign-off](../updates/2025-10-27-console-security-signoff.md#evidence-parity))   | ||||
| - [x] Monitoring dashboards show metrics and alerts outlined in §6; alert runbooks reviewed with Security Guild. (see [console security sign-off](../updates/2025-10-27-console-security-signoff.md#monitoring--alerts))   | ||||
| - [x] Security review sign-off recorded in sprint log with links to Authority threat model references. (see [console security sign-off](../updates/2025-10-27-console-security-signoff.md#sign-off)) | ||||
| - [x] `/console` Authority endpoints validated for tenant header enforcement, fresh-auth prompts, and introspection flows (Audit IDs `authority.console.tenants.read`, `authority.console.profile.read`, `authority.console.token.introspect`). (see [console security sign-off](../updates/2025-10-31-console-security-refresh.md)) | ||||
|  | ||||
| --- | ||||
|  | ||||
| *Last updated: 2025-10-31 (Sprint 23).*  | ||||
| # StellaOps Console Security Posture | ||||
|  | ||||
| > **Audience:** Security Guild, Console & Authority teams, deployment engineers.   | ||||
| > **Scope:** OIDC/DPoP flows, scope model, session controls, CSP and transport headers, evidence handling, offline posture, and monitoring expectations for the StellaOps Console (Sprint 23). | ||||
|  | ||||
| The console is an Angular SPA fronted by the StellaOps Web gateway. It consumes Authority for identity, Concelier/Excititor for aggregation data, Policy Engine for findings, and Attestor for evidence bundles. This guide captures the security guarantees and required hardening so that the console can ship alongside the Aggregation-Only Contract (AOC) without introducing new attack surface. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 1 · Identity & Authentication | ||||
|  | ||||
| ### 1.1 Authorization sequence | ||||
|  | ||||
| 1. Browser→Authority uses **OAuth 2.1 Authorization Code + PKCE** (`S256`).   | ||||
| 2. Upon code exchange the console requests a **DPoP-bound access token** (`aud=console`, `tenant=<id>`) with **120 s TTL** and optional **rotating refresh token** (`rotate=true`).   | ||||
| 3. Authority includes `cnf.jkt` for the ephemeral WebCrypto keypair; console stores the private key in **IndexedDB** (non-exportable) and keeps the public JWK in memory.   | ||||
| 4. All API calls attach `Authorization: Bearer <token>` + `DPoP` proof header. Nonces from the gateway are replay-protected (`dpopt-nonce` header).   | ||||
| 5. Tenanted API calls flow through the Web gateway which forwards `X-Stella-Tenant` and enforces tenancy headers. Missing or mismatched tenants trigger `403` with `ERR_TENANT_MISMATCH`. | ||||
|  | ||||
| ### 1.2 Fresh-auth gating | ||||
|  | ||||
| - Sensitive actions (tenant edits, token revocation, policy promote, signing key rotation) call `Authority /fresh-auth` using `prompt=login` + `max_age=300`.   | ||||
| - Successful fresh-auth yields a **300 s** scoped token (`fresh_auth=true`) stored only in memory; the UI disables guarded buttons when the timer expires.   | ||||
| - Audit events: `authority.fresh_auth.start`, `authority.fresh_auth.success`, `authority.fresh_auth.expired` (link to correlation IDs for the gated action). | ||||
|  | ||||
| ### 1.3 Offline & sealed mode | ||||
|  | ||||
| - When `console.offlineMode=true` the console presents an offline banner and suppresses fresh-auth prompts, replacing them with CLI guidance (`stella auth fresh-auth --offline`).   | ||||
| - Offline mode requires pre-issued tenant-scoped tokens bundled with the Offline Kit; tokens must include `offline=true` claim and 15 m TTL.   | ||||
| - Authority availability health is polled via `/api/console/status`. HTTP failures raise the offline banner and switch to read-only behaviour. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 2 · Session & Device Binding | ||||
|  | ||||
| - Access and refresh tokens live in memory; metadata (subject, tenant, expiry) persists in `sessionStorage` for reload continuity. **Never** store raw JWTs in `localStorage`.   | ||||
| - Inactivity timeout defaults to **15 minutes**. Idle sessions trigger silent refresh; on failure the UI shows a modal requiring re-auth.   | ||||
| - Tokens are device-bound through DPoP; if a new device logs in, Authority revokes the previous DPoP key and emits `authority.token.binding_changed`.   | ||||
| - CSRF mitigations: bearer tokens plus DPoP remove cookie reliance. If cookies are required (e.g., same-origin analytics) they must be `HttpOnly`, `SameSite=Lax`, `Secure`.   | ||||
| - Browser hardening: enforce `Strict-Transport-Security`, `X-Content-Type-Options: nosniff`, `Referrer-Policy: no-referrer`, `Permissions-Policy: camera=(), microphone=(), geolocation=()`. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 3 · Authorization & Scope Model | ||||
|  | ||||
| The console client is registered in Authority as `console-ui` with scopes: | ||||
|  | ||||
| | Feature area | Required scopes | Notes | | ||||
| |--------------|----------------|-------| | ||||
| | Base navigation (Dashboard, Findings, SBOM, Runs) | `ui.read`, `findings:read`, `advisory:read`, `vex:read`, `aoc:verify` | `findings:read` enables Policy Engine overlays; `advisory:read`/`vex:read` load ingestion panes; `aoc:verify` allows on-demand guard runs. | | ||||
| | Admin workspace | `ui.admin`, `authority:tenants.read`, `authority:tenants.write`, `authority:roles.read`, `authority:roles.write`, `authority:tokens.read`, `authority:tokens.revoke`, `authority:clients.read`, `authority:clients.write`, `authority:audit.read` | Scope combinations are tenant constrained. Role changes require fresh-auth. | | ||||
| | Policy approvals | `policy:read`, `policy:review`, `policy:approve`, `policy:operate`, `policy:simulate` | `policy:operate` (promote/activate/run) requires fresh-auth. | | ||||
| | Observability panes (status ticker, telemetry) | `ui.telemetry`, `scheduler:runs.read`, `advisory:read`, `vex:read` | `ui.telemetry` drives OTLP export toggles. | | ||||
| | Orchestrator dashboard (queues, workers, rate limits) | `orch:read` | Provision via `Orch.Viewer` role; read-only access to job state and telemetry. | | ||||
| | Orchestrator control actions (pause/resume, retry, sync-now, backfill) | `orch:operate` (plus `orch:read`) | CLI/Console must request tokens with `operator_reason` and `operator_ticket`; Authority denies issuance when either value is missing. | | ||||
| | Downloads parity (SBOM, attestation) | `downloads:read`, `attestation:verify`, `sbom:export` | Console surfaces digests only; download links require CLI parity for write operations. | | ||||
|  | ||||
| Guidance: | ||||
|  | ||||
| - **Role mapping**: Provision Authority role `role/ui-console-admin` encapsulating the admin scopes above.   | ||||
| - **Orchestrator viewers**: Assign Authority role `role/orch-viewer` (Authority role string `Orch.Viewer`) to consoles that require read-only access to Orchestrator telemetry.   | ||||
| - **Orchestrator operators**: Assign Authority role `role/orch-operator` (Authority role string `Orch.Operator`) to identities allowed to pause/resume or backfill. Tokens must include `operator_reason` (≤256 chars) and `operator_ticket` (≤128 chars); Authority records the values in audit logs. | ||||
| - **Tenant enforcement**: Gateway injects `X-Stella-Tenant` from token claims. Requests missing the header must be rejected by downstream services (Concelier, Excititor, Policy Engine) and logged.   | ||||
| - **Separation of duties**: Never grant `ui.admin` and `policy:approve`/`policy:operate` to the same human role without SOC sign-off; automation accounts should use least-privilege dedicated clients. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ### 3.1 Console Authority endpoints | ||||
|  | ||||
| Console uses dedicated Authority endpoints scoped under `/console/*`. All requests must include the tenant header injected by the gateway (`X-Stella-Tenant`); calls without the header fail with `tenant_header_missing` and emit a structured audit event. Keep reverse proxies configured to pass the header end-to-end. | ||||
|  | ||||
| | Endpoint | Required scopes | Purpose | Notes | | ||||
| |----------|-----------------|---------|-------| | ||||
| | `GET /console/tenants` | `authority:tenants.read` | Returns the tenant catalogue for the authenticated principal. | Validates `X-Stella-Tenant`; rejects tenants not configured in Authority. | | ||||
| | `GET /console/profile` | `ui.read` | Surfaces subject metadata (roles, scopes, session id, fresh-auth state). | Response includes `freshAuth` (bool) based on a 300 s window since `auth_time`. | | ||||
| | `POST /console/token/introspect` | `ui.read` | Introspects the access token currently in use and reports expiry + tenant. | Console polls this endpoint to drive session inactivity prompts; intended for SPA usage via fetch POST. | | ||||
|  | ||||
| **Fresh-auth & session inactivity:** Authority stamps `auth_time` on issued tokens and considers privileged actions “fresh” for five minutes. When `/console/profile` returns `freshAuth: false`, the UI must require an interactive re-authentication before allowing admin operations (`ui.admin`, `authority:*` mutations, `policy:activate`, `exceptions:approve`). Access tokens remain short-lived (`00:02:00` by default); pair this with Console session timeouts so idle dashboards prompt the user before two minutes of inactivity. | ||||
|  | ||||
| **DPoP + tenant binding:** All `/console/*` endpoints require DPoP-bound access tokens. Audit events include `tenant.resolved`, `scope`, `correlationId`, and (when applicable) `token.expires_at`. Staple the same headers into downstream services so cross-component troubleshooting uses the same correlation identifiers. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 4 · Transport, CSP & Browser Hardening | ||||
|  | ||||
| ### 4.1 Gateway requirements | ||||
|  | ||||
| - TLS 1.2+ with modern cipher suites; enable HTTP/2 for SSE streams.   | ||||
| - Terminate TLS at the reverse proxy (Traefik, NGINX) and forward `X-Forwarded-*` headers (`ASPNETCORE_FORWARDEDHEADERS_ENABLED=true`).   | ||||
| - Rate-limit `/authorize` and `/token` according to [Authority rate-limit guidance](rate-limits.md). | ||||
|  | ||||
| ### 4.2 Content Security Policy | ||||
|  | ||||
| Default CSP served by the console container: | ||||
|  | ||||
| ``` | ||||
| default-src 'self'; | ||||
| connect-src 'self' https://*.stella-ops.local; | ||||
| img-src 'self' data:; | ||||
| script-src 'self'; | ||||
| style-src 'self' 'unsafe-inline'; | ||||
| font-src 'self'; | ||||
| frame-ancestors 'none'; | ||||
| ``` | ||||
|  | ||||
| Recommendations: | ||||
|  | ||||
| - Extend `connect-src` only for known internal APIs (e.g., telemetry collector). Use `console.config.cspOverrides` instead of editing NGINX directly.   | ||||
| - Enable **COOP/COEP** (`Cross-Origin-Opener-Policy: same-origin`, `Cross-Origin-Embedder-Policy: require-corp`) to support WASM policy previews.   | ||||
| - Use **Subresource Integrity (SRI)** hashes when adding third-party fonts or scripts.   | ||||
| - For embedded screenshots/GIFs sourced from Offline Kit, use `img-src 'self' data: blob:` and verify assets during build.   | ||||
| - Enforce `X-Frame-Options: DENY`, `X-XSS-Protection: 0`, and `Cache-Control: no-store` on JSON API responses (HTML assets remain cacheable). | ||||
|  | ||||
| ### 4.3 SSE & WebSocket hygiene | ||||
|  | ||||
| - SSE endpoints (`/console/status/stream`, `/console/runs/{id}/events`) must set `Cache-Control: no-store` and disable proxy buffering.   | ||||
| - Gate SSE behind the same DPoP tokens; reject without `Authorization`.   | ||||
| - Proxy timeouts ≥ 60 s to avoid disconnect storms; clients use exponential backoff with jitter. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 5 · Evidence & Data Handling | ||||
|  | ||||
| - **Evidence bundles**: Download links trigger `attestor.verify` or `downloads.manifest` APIs. The UI never caches bundle contents; it only surfaces SHA-256 digests and cosign signatures. Operators must use CLI to fetch the signed artefact.   | ||||
| - **Secrets**: UI redacts tokens, emails, and attachment paths in logs. Structured logs include only `subject`, `tenant`, `action`, `correlationId`.   | ||||
| - **Aggregation data**: Console honours Aggregation-Only contract—no client-side rewriting of Concelier/Excititor precedence. Provenance badges display source IDs and merge-event hashes.   | ||||
| - **PII minimisation**: User lists show minimal identity (display name, email hash). Full email addresses require `ui.admin` + fresh-auth.   | ||||
| - **Downloads parity**: Every downloadable artefact includes a CLI parity link (e.g., `stella downloads fetch --artifact <id>`). If CLI parity fails, the console displays a warning banner and links to troubleshooting docs. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 6 · Logging, Monitoring & Alerts | ||||
|  | ||||
| - Structured logs: `ui.action`, `tenantId`, `subject`, `scope`, `correlationId`, `dpop.jkt`. Log level `Information` for key actions; `Warning` for security anomalies (failed DPoP, tenant mismatch).   | ||||
| - Metrics (Prometheus): `ui_request_duration_seconds`, `ui_dpop_failure_total`, `ui_fresh_auth_prompt_total`, `ui_tenant_switch_total`, `ui_offline_banner_seconds`.   | ||||
| - Alerts: | ||||
|   1. **Fresh-auth failures** > 5 per minute per tenant → security review.   | ||||
|   2. **DPoP mismatches** sustained > 1 % of requests → potential replay attempt.   | ||||
|   3. **Tenant mismatches** > 0 triggers an audit incident (could indicate scope misconfiguration).   | ||||
| - Correlate with Authority audit events (`authority.scope.granted`, `authority.token.revoked`) and Concelier/Excititor ingestion logs to trace user impact. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 7 · Offline & Air-Gapped Posture | ||||
|  | ||||
| - Offline deployments require mirrored container images and Offline Kit manifest verification (see `/docs/deploy/console.md` §7).   | ||||
| - Console reads `offlineManifest.json` at boot to validate asset digests; mismatches block startup until the manifest is refreshed.   | ||||
| - Tenant and role edits queue change manifests for export; UI instructs operators to run `stella auth apply --bundle <file>` on the offline Authority host.   | ||||
| – Evidence viewing remains read-only; download buttons provide scripts to export from local Attestor snapshots.   | ||||
| - Fresh-auth prompts display instructions for hardware-token usage on bastion hosts; system logs mark actions executed under offline fallback. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 8 · Threat Model Alignment | ||||
|  | ||||
| | Threat (Authority TM §5) | Console control | | ||||
| |--------------------------|-----------------| | ||||
| | Spoofed revocation bundle | Console verifies manifest signatures before showing revocation status; links to `stella auth revoke verify`. | | ||||
| | Parameter tampering on `/token` | PKCE + DPoP enforced; console propagates correlation IDs so Authority logs can link anomalies. | | ||||
| | Bootstrap invite replay | Admin UI surfaces invite status with expiry; fresh-auth required before issuing new invites. | | ||||
| | Token replay by stolen agent | DPoP binding prevents reuse; console surfaces revocation latency warnings sourced from Zastava metrics. | | ||||
| | Offline bundle tampering | Console refuses unsigned Offline Kit assets; prompts operators to re-import verified bundles. | | ||||
| | Privilege escalation via plug-in overrides | Plug-in manifest viewer warns when a plug-in downgrades password policy; UI restricts plug-in activation to fresh-auth + `ui.admin` scoped users. | | ||||
|  | ||||
| Document gaps and remediation hooks in `SEC5.*` backlog as they are addressed. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 9 · Compliance checklist | ||||
|  | ||||
| - [x] Authority client `console-ui` registered with PKCE, DPoP, tenant claim requirement, and scopes from §3. (see [console security sign-off](../updates/2025-10-27-console-security-signoff.md#authority-client-validation))   | ||||
| - [x] CSP enforced per §4 with overrides documented in deployment manifests. (see [console security sign-off](../updates/2025-10-27-console-security-signoff.md#csp-enforcement))   | ||||
| - [x] Fresh-auth timer (300 s) validated for admin and policy actions; audit events captured. (see [console security sign-off](../updates/2025-10-27-console-security-signoff.md#fresh-auth-timer))   | ||||
| - [x] DPoP binding tested (replay attempt blocked; logs show `ui_dpop_failure_total` increment). (see [console security sign-off](../updates/2025-10-27-console-security-signoff.md#dpop-binding-test))   | ||||
| - [x] Offline mode exercises performed (banner, CLI guidance, manifest verification). (see [console security sign-off](../updates/2025-10-27-console-security-signoff.md#offline-mode-exercise))   | ||||
| - [x] Evidence download parity verified with CLI scripts; console never caches sensitive artefacts. (see [console security sign-off](../updates/2025-10-27-console-security-signoff.md#evidence-parity))   | ||||
| - [x] Monitoring dashboards show metrics and alerts outlined in §6; alert runbooks reviewed with Security Guild. (see [console security sign-off](../updates/2025-10-27-console-security-signoff.md#monitoring--alerts))   | ||||
| - [x] Security review sign-off recorded in sprint log with links to Authority threat model references. (see [console security sign-off](../updates/2025-10-27-console-security-signoff.md#sign-off)) | ||||
| - [x] `/console` Authority endpoints validated for tenant header enforcement, fresh-auth prompts, and introspection flows (Audit IDs `authority.console.tenants.read`, `authority.console.profile.read`, `authority.console.token.introspect`). (see [console security sign-off](../updates/2025-10-31-console-security-refresh.md)) | ||||
|  | ||||
| --- | ||||
|  | ||||
| *Last updated: 2025-10-31 (Sprint 23).*  | ||||
|   | ||||
| @@ -1,165 +1,165 @@ | ||||
| > **Imposed rule:** Work of this type or tasks of this type on this component must also be applied everywhere else it should be applied. | ||||
|  | ||||
| # Pack Signing & RBAC Controls | ||||
|  | ||||
| This document defines signing, verification, and authorization requirements for Task Packs across the CLI, Packs Registry, Task Runner, and Offline Kit. It aligns with Authority sprint tasks (`AUTH-PACKS-41-001`, `AUTH-PACKS-43-001`) and security guild expectations. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 1 · Threat Model Highlights | ||||
|  | ||||
| | Threat | Mitigation | | ||||
| |--------|------------| | ||||
| | Unsigned or tampered pack uploaded to registry | Mandatory cosign/DSSE verification before acceptance. | | ||||
| | Unauthorized user publishing or promoting packs | Authority scopes (`Packs.Write`) + registry policy checks. | | ||||
| | Privilege escalation during approvals | Approval gates require `Packs.Approve` + audit logging; fresh-auth recommended. | | ||||
| | Secret exfiltration via pack steps | Secrets injection sandbox with redaction, sealed-mode network guardrails, evidence review. | | ||||
| | Replay of old approval tokens | Approval payloads carry plan hash + expiry; Task Runner rejects mismatches. | | ||||
| | Malicious pack in Offline Kit | Mirror verification using signed manifest and DSSE provenance. | | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 2 · Signing Requirements | ||||
|  | ||||
| - **Cosign** signatures required for all bundles. Keys can be: | ||||
|   - Keyless (Fulcio OIDC). | ||||
|   - KMS-backed (HSM, cloud KMS). | ||||
|   - Offline keys stored in secure vault (air-gapped mode). | ||||
| - **DSSE Attestations** recommended to embed: | ||||
|   - Manifest digest. | ||||
|   - Build metadata (repo, commit, CI run). | ||||
|   - CLI version (`stella/pack`). | ||||
| - Signatures stored alongside bundle in registry object storage. | ||||
| - `stella pack push` refuses to publish without signature (unless `--insecure-publish` used in dev). | ||||
| - Registry enforces trust policy: | ||||
|  | ||||
| | Policy | Description | | ||||
| |--------|-------------| | ||||
| | `anyOf` | Accepts any key in configured trust store. | | ||||
| | `keyRef` | Accepts specific key ID (`kid`). | | ||||
| | `oidcIssuer` | Accepts Fulcio certificates from allowed issuers (e.g., `https://fulcio.sigstore.dev`). | | ||||
| | `threshold` | Requires N-of-M signatures (future release). | | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 3 · RBAC & Scopes | ||||
|  | ||||
| Authority exposes pack-related scopes: | ||||
|  | ||||
| | Scope | Description | | ||||
| |-------|-------------| | ||||
| | `Packs.Read` | View packs, download manifests/bundles. | | ||||
| | `Packs.Write` | Publish, promote, deprecate packs. | | ||||
| | `Packs.Run` | Execute packs (Task Runner, CLI). | | ||||
| | `Packs.Approve` | Approve pack gates, override tenant visibility. | | ||||
|  | ||||
| ### 3.1 Role Mapping | ||||
|  | ||||
| | Role | Scopes | Use Cases | | ||||
| |------|--------|-----------| | ||||
| | `pack.viewer` | `Packs.Read` | Inspect packs, plan runs. | | ||||
| | `pack.publisher` | `Packs.Read`, `Packs.Write` | Publish new versions, manage channels. | | ||||
| | `pack.operator` | `Packs.Read`, `Packs.Run` | Execute packs, monitor runs. | | ||||
| | `pack.approver` | `Packs.Read`, `Packs.Approve` | Fulfil approvals, authorize promotions. | | ||||
| | `pack.admin` | All | Full lifecycle management (rare). | | ||||
|  | ||||
| Roles are tenant-scoped; cross-tenant access requires explicit addition. | ||||
|  | ||||
| ### 3.2 CLI Enforcement | ||||
|  | ||||
| - CLI requests scopes based on command: | ||||
|   - `stella pack plan` → `Packs.Read`. | ||||
|   - `stella pack run` → `Packs.Run`. | ||||
|   - `stella pack push` → `Packs.Write`. | ||||
|   - `stella pack approve` → `Packs.Approve`. | ||||
| - Offline tokens must include same scopes; CLI warns if missing. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 4 · Approvals & Fresh Auth | ||||
|  | ||||
| - Approval commands require recent fresh-auth (< 5 minutes). CLI prompts automatically; Console enforces via Authority. | ||||
| - Approval payload includes: | ||||
|   - `runId` | ||||
|   - `gateId` | ||||
|   - `planHash` | ||||
|   - `approver` | ||||
|   - `timestamp` | ||||
| - Task Runner logs approval event and verifies plan hash to prevent rerouting. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 5 · Secret Management | ||||
|  | ||||
| - Secrets defined in pack manifest map to Authority secret providers (e.g., HSM, Vault). | ||||
| - Task Runner obtains secrets using service account with scoped access; CLI may prompt or read from profile. | ||||
| - Secret audit trail: | ||||
|   - `secretRequested` event with reason, pack, step. | ||||
|   - `secretDelivered` event omitted (only aggregate metrics) to avoid leakage. | ||||
|   - Evidence bundle includes hashed secret metadata (no values). | ||||
|  | ||||
| Sealed mode requires secrets to originate from sealed vault; external endpoints blocked. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 6 · Audit & Evidence | ||||
|  | ||||
| - Registry, Task Runner, and Authority emit audit events to central timeline. | ||||
| - Required events: | ||||
|   - `pack.version.published` | ||||
|   - `pack.version.promoted` | ||||
|   - `pack.run.started/completed` | ||||
|   - `pack.approval.requested/granted` | ||||
|   - `pack.secret.requested` | ||||
| - Evidence Locker stores DSSE attestations and run bundles for 90 days (configurable). | ||||
| - Auditors can use `stella pack audit --run <id>` to retrieve audit trail. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 7 · Offline / Air-Gap Policies | ||||
|  | ||||
| - Offline Kit includes: | ||||
|   - Pack bundles + signatures. | ||||
|   - Trusted key store (`trust-bundle.pem`). | ||||
|   - Approval workflow instructions for manual signing. | ||||
| - Air-gapped approvals: | ||||
|   - CLI generates approval request file (`.approval-request.json`). | ||||
|   - Approver uses offline CLI to sign with offline key. | ||||
|   - Response imported to Task Runner. | ||||
| - Mirror process verifies signatures prior to import; failure aborts import with `ERR_PACK_SIGNATURE_INVALID`. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 8 · Incident Response | ||||
|  | ||||
| - Compromised pack signature: | ||||
|   - Revoke key via Authority trust store. | ||||
|   - Deprecate affected versions (`registry deprecate`). | ||||
|   - Notify consumers via Notifier (`pack.security.alert`). | ||||
|   - Forensically review run evidence for impacted tenants. | ||||
| - Unauthorized approval: | ||||
|   - Review audit log for `Packs.Approve` events. | ||||
|   - Trigger `pack.run.freeze` (pauses run pending investigation). | ||||
|   - Rotate approver credentials and require fresh-auth. | ||||
| - Secret leak suspicion: | ||||
|   - Quarantine evidence bundles. | ||||
|   - Rotate secrets referenced by pack. | ||||
|   - Run sealed-mode audit script to confirm guardrails. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 9 · Compliance Checklist | ||||
|  | ||||
| - [ ] Signing requirements (cosign/DSSE, trust policies) documented.   | ||||
| - [ ] Authority scope mapping and CLI enforcement captured.   | ||||
| - [ ] Approval workflow + fresh-auth expectations defined.   | ||||
| - [ ] Secret lifecycle (request, injection, audit) described.   | ||||
| - [ ] Audit/evidence integration noted (timeline, Evidence Locker).   | ||||
| - [ ] Offline/air-gap controls outlined.   | ||||
| - [ ] Incident response playbook provided.   | ||||
| - [ ] Imposed rule reminder retained at top. | ||||
|  | ||||
| --- | ||||
|  | ||||
| *Last updated: 2025-10-27 (Sprint 43).*  | ||||
|  | ||||
| > **Imposed rule:** Work of this type or tasks of this type on this component must also be applied everywhere else it should be applied. | ||||
|  | ||||
| # Pack Signing & RBAC Controls | ||||
|  | ||||
| This document defines signing, verification, and authorization requirements for Task Packs across the CLI, Packs Registry, Task Runner, and Offline Kit. It aligns with Authority sprint tasks (`AUTH-PACKS-41-001`, `AUTH-PACKS-43-001`) and security guild expectations. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 1 · Threat Model Highlights | ||||
|  | ||||
| | Threat | Mitigation | | ||||
| |--------|------------| | ||||
| | Unsigned or tampered pack uploaded to registry | Mandatory cosign/DSSE verification before acceptance. | | ||||
| | Unauthorized user publishing or promoting packs | Authority scopes (`Packs.Write`) + registry policy checks. | | ||||
| | Privilege escalation during approvals | Approval gates require `Packs.Approve` + audit logging; fresh-auth recommended. | | ||||
| | Secret exfiltration via pack steps | Secrets injection sandbox with redaction, sealed-mode network guardrails, evidence review. | | ||||
| | Replay of old approval tokens | Approval payloads carry plan hash + expiry; Task Runner rejects mismatches. | | ||||
| | Malicious pack in Offline Kit | Mirror verification using signed manifest and DSSE provenance. | | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 2 · Signing Requirements | ||||
|  | ||||
| - **Cosign** signatures required for all bundles. Keys can be: | ||||
|   - Keyless (Fulcio OIDC). | ||||
|   - KMS-backed (HSM, cloud KMS). | ||||
|   - Offline keys stored in secure vault (air-gapped mode). | ||||
| - **DSSE Attestations** recommended to embed: | ||||
|   - Manifest digest. | ||||
|   - Build metadata (repo, commit, CI run). | ||||
|   - CLI version (`stella/pack`). | ||||
| - Signatures stored alongside bundle in registry object storage. | ||||
| - `stella pack push` refuses to publish without signature (unless `--insecure-publish` used in dev). | ||||
| - Registry enforces trust policy: | ||||
|  | ||||
| | Policy | Description | | ||||
| |--------|-------------| | ||||
| | `anyOf` | Accepts any key in configured trust store. | | ||||
| | `keyRef` | Accepts specific key ID (`kid`). | | ||||
| | `oidcIssuer` | Accepts Fulcio certificates from allowed issuers (e.g., `https://fulcio.sigstore.dev`). | | ||||
| | `threshold` | Requires N-of-M signatures (future release). | | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 3 · RBAC & Scopes | ||||
|  | ||||
| Authority exposes pack-related scopes: | ||||
|  | ||||
| | Scope | Description | | ||||
| |-------|-------------| | ||||
| | `Packs.Read` | View packs, download manifests/bundles. | | ||||
| | `Packs.Write` | Publish, promote, deprecate packs. | | ||||
| | `Packs.Run` | Execute packs (Task Runner, CLI). | | ||||
| | `Packs.Approve` | Approve pack gates, override tenant visibility. | | ||||
|  | ||||
| ### 3.1 Role Mapping | ||||
|  | ||||
| | Role | Scopes | Use Cases | | ||||
| |------|--------|-----------| | ||||
| | `pack.viewer` | `Packs.Read` | Inspect packs, plan runs. | | ||||
| | `pack.publisher` | `Packs.Read`, `Packs.Write` | Publish new versions, manage channels. | | ||||
| | `pack.operator` | `Packs.Read`, `Packs.Run` | Execute packs, monitor runs. | | ||||
| | `pack.approver` | `Packs.Read`, `Packs.Approve` | Fulfil approvals, authorize promotions. | | ||||
| | `pack.admin` | All | Full lifecycle management (rare). | | ||||
|  | ||||
| Roles are tenant-scoped; cross-tenant access requires explicit addition. | ||||
|  | ||||
| ### 3.2 CLI Enforcement | ||||
|  | ||||
| - CLI requests scopes based on command: | ||||
|   - `stella pack plan` → `Packs.Read`. | ||||
|   - `stella pack run` → `Packs.Run`. | ||||
|   - `stella pack push` → `Packs.Write`. | ||||
|   - `stella pack approve` → `Packs.Approve`. | ||||
| - Offline tokens must include same scopes; CLI warns if missing. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 4 · Approvals & Fresh Auth | ||||
|  | ||||
| - Approval commands require recent fresh-auth (< 5 minutes). CLI prompts automatically; Console enforces via Authority. | ||||
| - Approval payload includes: | ||||
|   - `runId` | ||||
|   - `gateId` | ||||
|   - `planHash` | ||||
|   - `approver` | ||||
|   - `timestamp` | ||||
| - Task Runner logs approval event and verifies plan hash to prevent rerouting. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 5 · Secret Management | ||||
|  | ||||
| - Secrets defined in pack manifest map to Authority secret providers (e.g., HSM, Vault). | ||||
| - Task Runner obtains secrets using service account with scoped access; CLI may prompt or read from profile. | ||||
| - Secret audit trail: | ||||
|   - `secretRequested` event with reason, pack, step. | ||||
|   - `secretDelivered` event omitted (only aggregate metrics) to avoid leakage. | ||||
|   - Evidence bundle includes hashed secret metadata (no values). | ||||
|  | ||||
| Sealed mode requires secrets to originate from sealed vault; external endpoints blocked. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 6 · Audit & Evidence | ||||
|  | ||||
| - Registry, Task Runner, and Authority emit audit events to central timeline. | ||||
| - Required events: | ||||
|   - `pack.version.published` | ||||
|   - `pack.version.promoted` | ||||
|   - `pack.run.started/completed` | ||||
|   - `pack.approval.requested/granted` | ||||
|   - `pack.secret.requested` | ||||
| - Evidence Locker stores DSSE attestations and run bundles for 90 days (configurable). | ||||
| - Auditors can use `stella pack audit --run <id>` to retrieve audit trail. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 7 · Offline / Air-Gap Policies | ||||
|  | ||||
| - Offline Kit includes: | ||||
|   - Pack bundles + signatures. | ||||
|   - Trusted key store (`trust-bundle.pem`). | ||||
|   - Approval workflow instructions for manual signing. | ||||
| - Air-gapped approvals: | ||||
|   - CLI generates approval request file (`.approval-request.json`). | ||||
|   - Approver uses offline CLI to sign with offline key. | ||||
|   - Response imported to Task Runner. | ||||
| - Mirror process verifies signatures prior to import; failure aborts import with `ERR_PACK_SIGNATURE_INVALID`. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 8 · Incident Response | ||||
|  | ||||
| - Compromised pack signature: | ||||
|   - Revoke key via Authority trust store. | ||||
|   - Deprecate affected versions (`registry deprecate`). | ||||
|   - Notify consumers via Notifier (`pack.security.alert`). | ||||
|   - Forensically review run evidence for impacted tenants. | ||||
| - Unauthorized approval: | ||||
|   - Review audit log for `Packs.Approve` events. | ||||
|   - Trigger `pack.run.freeze` (pauses run pending investigation). | ||||
|   - Rotate approver credentials and require fresh-auth. | ||||
| - Secret leak suspicion: | ||||
|   - Quarantine evidence bundles. | ||||
|   - Rotate secrets referenced by pack. | ||||
|   - Run sealed-mode audit script to confirm guardrails. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 9 · Compliance Checklist | ||||
|  | ||||
| - [ ] Signing requirements (cosign/DSSE, trust policies) documented.   | ||||
| - [ ] Authority scope mapping and CLI enforcement captured.   | ||||
| - [ ] Approval workflow + fresh-auth expectations defined.   | ||||
| - [ ] Secret lifecycle (request, injection, audit) described.   | ||||
| - [ ] Audit/evidence integration noted (timeline, Evidence Locker).   | ||||
| - [ ] Offline/air-gap controls outlined.   | ||||
| - [ ] Incident response playbook provided.   | ||||
| - [ ] Imposed rule reminder retained at top. | ||||
|  | ||||
| --- | ||||
|  | ||||
| *Last updated: 2025-10-27 (Sprint 43).*  | ||||
|  | ||||
|   | ||||
| @@ -1,112 +1,112 @@ | ||||
| # Policy Governance & Least Privilege | ||||
|  | ||||
| > **Audience:** Security Guild, Policy Guild, Authority Core, auditors.   | ||||
| > **Scope:** Scopes, RBAC, approval controls, tenancy, auditing, and compliance requirements for Policy Engine v2. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 1 · Governance Principles | ||||
|  | ||||
| 1. **Least privilege by scope** – API clients receive only the `policy:*` scopes required for their role; `effective:write` reserved for service identity. | ||||
| 2. **Immutable history** – All policy changes, approvals, runs, and suppressions produce audit artefacts retrievable offline. | ||||
| 3. **Separation of duties** – Authors cannot approve their own submissions; approvers require distinct scope and should not have deployment rights. | ||||
| 4. **Deterministic verification** – Simulations, determinism checks, and incident replay bundles provide reproducible evidence for auditors. | ||||
| 5. **Tenant isolation** – Policies, runs, and findings scoped to tenants; cross-tenant access requires explicit admin scopes and is logged. | ||||
| 6. **Offline parity** – Air-gapped sites follow the same governance workflow with sealed-mode safeguards and signed bundles. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 2 · Authority Scopes & Role Mapping | ||||
|  | ||||
| | Scope | Description | Recommended role | | ||||
| |-------|-------------|------------------| | ||||
| | `policy:read` | View policies, revisions, runs, findings. | Readers, auditors. | | ||||
| | `policy:author` | Create/edit drafts, lint/compile, quick simulate. | `role/policy-author`. | | ||||
| | `policy:review` | Comment, request changes, approve in-progress drafts. | `role/policy-reviewer`. | | ||||
| | `policy:approve` | Final approval; archive decisions. | `role/policy-approver`. | | ||||
| | `policy:operate` | Promote revisions, trigger runs, manage rollouts. | `role/policy-operator`, automation bots. | | ||||
| | `policy:audit` | Access immutable history and evidence bundles. | `role/policy-auditor`, compliance teams. | | ||||
| | `policy:simulate` | Execute simulations via API/CLI. | Authors, reviewers, CI. | | ||||
| | `policy:run` | Trigger runs, inspect live status. | Operators, automation bots. | | ||||
| | `policy:activate` | Promote approved version, schedule activation. | Runtime operators / release managers. | | ||||
| | `findings:read` | View effective findings/explain. | Analysts, auditors, CLI. | | ||||
| | `effective:write` | **Service only** – materialise findings. | Policy Engine service principal. | | ||||
|  | ||||
| > Map organisation roles to scopes via Authority issuer config (`authority.tenants[].roles`). Document assignments in tenant onboarding checklist. | ||||
|  | ||||
| > **Authority configuration tip:** the Policy Engine service client must include `properties.serviceIdentity: policy-engine` and a tenant hint in `authority.yaml`. Authority rejects `effective:write` tokens that lack this marker. See [Authority scopes](authority-scopes.md) for the full scope catalogue. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 3 · Workflow Controls | ||||
|  | ||||
| - **Submit gate:** CLI/UI require fresh lint + simulation artefacts (<24 h). Submissions store reviewer list and diff attachments. | ||||
| - **Review quorum:** Authority policy enforces minimum reviewers (e.g., 2) and optional separation between functional/security domains. | ||||
| - **Approval guard:** Approvers must acknowledge simulation + determinism check completion. CLI enforces `--note` and `--attach` fields. | ||||
| - **Activation guard:** Policy Engine refuses activation when latest full run status ≠ success or incremental backlog aged > SLA. | ||||
| - **Rollback policy:** Rollbacks require incident reference and produce `policy.rollback` audit events. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 4 · Tenancy & Data Access | ||||
|  | ||||
| - Policies stored per tenant; `tenant-global` used for shared baselines. | ||||
| - API filters all requests by `X-Stella-Tenant` (default from token). Cross-tenant requests require `policy:tenant-admin`. | ||||
| - Effective findings collections include `tenant` field and unique indexes preventing cross-tenant writes. | ||||
| - CLI/Console display tenant context prominently; switching tenant triggers warnings when active policy differs. | ||||
| - Offline bundles encode tenant metadata; import commands validate compatibility before applying. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 5 · Audit & Evidence | ||||
|  | ||||
| - **Collections:** `policies`, `policy_reviews`, `policy_history`, `policy_runs`, `policy_run_events`, `effective_finding_*_history`. | ||||
| - **Events:** `policy.submitted`, `policy.review.requested`, `policy.approved`, `policy.activated`, `policy.archived`, `policy.run.*`, `policy.incident.*`. | ||||
| - **Explain traces:** Stored for critical findings (sampled); available via CLI/UI for auditors (requires `findings:read`). | ||||
| - **Offline evidence:** `stella policy bundle export` produces DSSE-signed packages containing DSL, IR digest, simulations, approval notes, run summaries, trace metadata. | ||||
| - **Retention:** Default 365 days for run history, extendable per compliance requirements; incident mode extends to 30 days minimum. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 6 · Secrets & Configuration Hygiene | ||||
|  | ||||
| - Policy Engine configuration loaded from environment/secret stores; no secrets in repo. | ||||
| - CLI profiles should store tokens encrypted (`stella profile set --secret`). | ||||
| - UI/CLI logs redact tokens, reviewer emails, and attachments. | ||||
| - Rotating tokens/keys: Authority exposes `policy scopes` in discovery docs; follow `/docs/security/authority-scopes.md` for rotation. | ||||
| - Use `policy:operate` to disable self-service simulation temporarily during incident response if needed. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 7 · Incident Response | ||||
|  | ||||
| - Trigger incident mode for determinism violations, backlog surges, or suspected policy abuse. | ||||
| - Capture replay bundles and run `stella policy run replay` for affected runs. | ||||
| - Coordinate with Observability dashboards (see `/docs/observability/policy.md`) to monitor queue depth, failures. | ||||
| - After resolution, document remediation in Lifecycle guide (§8) and attach to approval history. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 8 · Offline / Air-Gapped Governance | ||||
|  | ||||
| - Same scopes apply; tokens issued by local Authority. | ||||
| - Approvers must use offline UI/CLI to sign submissions; attachments stored locally. | ||||
| - Bundle import/export must be signed (DSSE + cosign). CLI warns if signatures missing. | ||||
| - Sealed-mode banner reminds operators to refresh bundles when staleness thresholds exceeded. | ||||
| - Offline audits rely on evidence bundles and local `policy_runs` snapshot. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 9 · Compliance Checklist | ||||
|  | ||||
| - [ ] **Scope mapping reviewed:** Authority issuer config updated; RBAC matrix stored with change request. | ||||
| - [ ] **Separation enforced:** Automated checks block self-approval; review quorum satisfied. | ||||
| - [ ] **Activation guard documented:** Operators trained on run health checks before promoting. | ||||
| - [ ] **Audit exports tested:** Evidence bundles verified (hash/signature) and stored per compliance policy. | ||||
| - [ ] **Incident drills rehearsed:** Replay/rollback procedures executed and logged. | ||||
| - [ ] **Offline parity confirmed:** Air-gapped site executes submit/approve flow with sealed-mode guidance. | ||||
| - [ ] **Documentation cross-links:** References to lifecycle, runs, observability, CLI, and API docs validated. | ||||
|  | ||||
| --- | ||||
|  | ||||
| *Last updated: 2025-10-26 (Sprint 20).* | ||||
| # Policy Governance & Least Privilege | ||||
|  | ||||
| > **Audience:** Security Guild, Policy Guild, Authority Core, auditors.   | ||||
| > **Scope:** Scopes, RBAC, approval controls, tenancy, auditing, and compliance requirements for Policy Engine v2. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 1 · Governance Principles | ||||
|  | ||||
| 1. **Least privilege by scope** – API clients receive only the `policy:*` scopes required for their role; `effective:write` reserved for service identity. | ||||
| 2. **Immutable history** – All policy changes, approvals, runs, and suppressions produce audit artefacts retrievable offline. | ||||
| 3. **Separation of duties** – Authors cannot approve their own submissions; approvers require distinct scope and should not have deployment rights. | ||||
| 4. **Deterministic verification** – Simulations, determinism checks, and incident replay bundles provide reproducible evidence for auditors. | ||||
| 5. **Tenant isolation** – Policies, runs, and findings scoped to tenants; cross-tenant access requires explicit admin scopes and is logged. | ||||
| 6. **Offline parity** – Air-gapped sites follow the same governance workflow with sealed-mode safeguards and signed bundles. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 2 · Authority Scopes & Role Mapping | ||||
|  | ||||
| | Scope | Description | Recommended role | | ||||
| |-------|-------------|------------------| | ||||
| | `policy:read` | View policies, revisions, runs, findings. | Readers, auditors. | | ||||
| | `policy:author` | Create/edit drafts, lint/compile, quick simulate. | `role/policy-author`. | | ||||
| | `policy:review` | Comment, request changes, approve in-progress drafts. | `role/policy-reviewer`. | | ||||
| | `policy:approve` | Final approval; archive decisions. | `role/policy-approver`. | | ||||
| | `policy:operate` | Promote revisions, trigger runs, manage rollouts. | `role/policy-operator`, automation bots. | | ||||
| | `policy:audit` | Access immutable history and evidence bundles. | `role/policy-auditor`, compliance teams. | | ||||
| | `policy:simulate` | Execute simulations via API/CLI. | Authors, reviewers, CI. | | ||||
| | `policy:run` | Trigger runs, inspect live status. | Operators, automation bots. | | ||||
| | `policy:activate` | Promote approved version, schedule activation. | Runtime operators / release managers. | | ||||
| | `findings:read` | View effective findings/explain. | Analysts, auditors, CLI. | | ||||
| | `effective:write` | **Service only** – materialise findings. | Policy Engine service principal. | | ||||
|  | ||||
| > Map organisation roles to scopes via Authority issuer config (`authority.tenants[].roles`). Document assignments in tenant onboarding checklist. | ||||
|  | ||||
| > **Authority configuration tip:** the Policy Engine service client must include `properties.serviceIdentity: policy-engine` and a tenant hint in `authority.yaml`. Authority rejects `effective:write` tokens that lack this marker. See [Authority scopes](authority-scopes.md) for the full scope catalogue. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 3 · Workflow Controls | ||||
|  | ||||
| - **Submit gate:** CLI/UI require fresh lint + simulation artefacts (<24 h). Submissions store reviewer list and diff attachments. | ||||
| - **Review quorum:** Authority policy enforces minimum reviewers (e.g., 2) and optional separation between functional/security domains. | ||||
| - **Approval guard:** Approvers must acknowledge simulation + determinism check completion. CLI enforces `--note` and `--attach` fields. | ||||
| - **Activation guard:** Policy Engine refuses activation when latest full run status ≠ success or incremental backlog aged > SLA. | ||||
| - **Rollback policy:** Rollbacks require incident reference and produce `policy.rollback` audit events. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 4 · Tenancy & Data Access | ||||
|  | ||||
| - Policies stored per tenant; `tenant-global` used for shared baselines. | ||||
| - API filters all requests by `X-Stella-Tenant` (default from token). Cross-tenant requests require `policy:tenant-admin`. | ||||
| - Effective findings collections include `tenant` field and unique indexes preventing cross-tenant writes. | ||||
| - CLI/Console display tenant context prominently; switching tenant triggers warnings when active policy differs. | ||||
| - Offline bundles encode tenant metadata; import commands validate compatibility before applying. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 5 · Audit & Evidence | ||||
|  | ||||
| - **Collections:** `policies`, `policy_reviews`, `policy_history`, `policy_runs`, `policy_run_events`, `effective_finding_*_history`. | ||||
| - **Events:** `policy.submitted`, `policy.review.requested`, `policy.approved`, `policy.activated`, `policy.archived`, `policy.run.*`, `policy.incident.*`. | ||||
| - **Explain traces:** Stored for critical findings (sampled); available via CLI/UI for auditors (requires `findings:read`). | ||||
| - **Offline evidence:** `stella policy bundle export` produces DSSE-signed packages containing DSL, IR digest, simulations, approval notes, run summaries, trace metadata. | ||||
| - **Retention:** Default 365 days for run history, extendable per compliance requirements; incident mode extends to 30 days minimum. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 6 · Secrets & Configuration Hygiene | ||||
|  | ||||
| - Policy Engine configuration loaded from environment/secret stores; no secrets in repo. | ||||
| - CLI profiles should store tokens encrypted (`stella profile set --secret`). | ||||
| - UI/CLI logs redact tokens, reviewer emails, and attachments. | ||||
| - Rotating tokens/keys: Authority exposes `policy scopes` in discovery docs; follow `/docs/security/authority-scopes.md` for rotation. | ||||
| - Use `policy:operate` to disable self-service simulation temporarily during incident response if needed. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 7 · Incident Response | ||||
|  | ||||
| - Trigger incident mode for determinism violations, backlog surges, or suspected policy abuse. | ||||
| - Capture replay bundles and run `stella policy run replay` for affected runs. | ||||
| - Coordinate with Observability dashboards (see `/docs/observability/policy.md`) to monitor queue depth, failures. | ||||
| - After resolution, document remediation in Lifecycle guide (§8) and attach to approval history. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 8 · Offline / Air-Gapped Governance | ||||
|  | ||||
| - Same scopes apply; tokens issued by local Authority. | ||||
| - Approvers must use offline UI/CLI to sign submissions; attachments stored locally. | ||||
| - Bundle import/export must be signed (DSSE + cosign). CLI warns if signatures missing. | ||||
| - Sealed-mode banner reminds operators to refresh bundles when staleness thresholds exceeded. | ||||
| - Offline audits rely on evidence bundles and local `policy_runs` snapshot. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## 9 · Compliance Checklist | ||||
|  | ||||
| - [ ] **Scope mapping reviewed:** Authority issuer config updated; RBAC matrix stored with change request. | ||||
| - [ ] **Separation enforced:** Automated checks block self-approval; review quorum satisfied. | ||||
| - [ ] **Activation guard documented:** Operators trained on run health checks before promoting. | ||||
| - [ ] **Audit exports tested:** Evidence bundles verified (hash/signature) and stored per compliance policy. | ||||
| - [ ] **Incident drills rehearsed:** Replay/rollback procedures executed and logged. | ||||
| - [ ] **Offline parity confirmed:** Air-gapped site executes submit/approve flow with sealed-mode guidance. | ||||
| - [ ] **Documentation cross-links:** References to lifecycle, runs, observability, CLI, and API docs validated. | ||||
|  | ||||
| --- | ||||
|  | ||||
| *Last updated: 2025-10-26 (Sprint 20).* | ||||
|   | ||||
		Reference in New Issue
	
	Block a user