Some checks failed
		
		
	
	Docs CI / lint-and-preview (push) Has been cancelled
				
			- Implemented PolicyDslValidator with command-line options for strict mode and JSON output. - Created PolicySchemaExporter to generate JSON schemas for policy-related models. - Developed PolicySimulationSmoke tool to validate policy simulations against expected outcomes. - Added project files and necessary dependencies for each tool. - Ensured proper error handling and usage instructions across tools.
		
			
				
	
	
		
			403 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			403 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| # Policy Engine REST API
 | ||
| 
 | ||
| > **Audience:** Backend integrators, platform operators, and CI engineers invoking Policy Engine services programmatically.  
 | ||
| > **Base URL:** `/api/policy/*` (internal gateway route) – requires OAuth 2.0 bearer token issued by Authority with scopes listed below.
 | ||
| 
 | ||
| This document is the canonical reference for the Policy Engine REST surface described in Epic 2 (Policy Engine v2). Use it alongside the [DSL](../policy/dsl.md), [Lifecycle](../policy/lifecycle.md), and [Runs](../policy/runs.md) guides for end-to-end implementations.
 | ||
| 
 | ||
| ---
 | ||
| 
 | ||
| ## 1 · Authentication & Headers
 | ||
| 
 | ||
| - **Auth:** Bearer tokens (`Authorization: Bearer <token>`) with the following scopes as applicable:
 | ||
|   - `policy:read`, `policy:write`, `policy:submit`, `policy:approve`, `policy:run`, `policy:activate`, `policy:archive`, `policy:simulate`, `policy:runs`
 | ||
|   - `findings:read` (for effective findings APIs)
 | ||
|   - `effective:write` (service identity only; not exposed to clients)
 | ||
| - **Service identity:** Authority marks the Policy Engine client with `properties.serviceIdentity: policy-engine`. Tokens missing this marker cannot obtain `effective:write`.
 | ||
| - **Tenant:** Supply tenant context via `X-Stella-Tenant`. Tokens without multi-tenant claims default to `default`.
 | ||
| - **Idempotency:** For mutating endpoints, include `Idempotency-Key` (UUID). Retries with same key return original result.
 | ||
| - **Content type:** All request/response bodies are `application/json; charset=utf-8` unless otherwise noted.
 | ||
| 
 | ||
| ---
 | ||
| 
 | ||
| ## 2 · Error Model
 | ||
| 
 | ||
| All errors use HTTP semantics plus a structured payload:
 | ||
| 
 | ||
| ```json
 | ||
| {
 | ||
|   "code": "ERR_POL_001",
 | ||
|   "message": "Policy syntax error",
 | ||
|   "details": [
 | ||
|     {"path": "rules[0].when", "error": "Unknown function foo()"}
 | ||
|   ],
 | ||
|   "traceId": "01HDV1C4E9Z4T5G6H7J8",
 | ||
|   "timestamp": "2025-10-26T14:07:03Z"
 | ||
| }
 | ||
| ```
 | ||
| 
 | ||
| | Code | Meaning | Notes |
 | ||
| |------|---------|-------|
 | ||
| | `ERR_POL_001` | Policy syntax/compile error | Returned by `compile`, `submit`, `simulate`, `run` when DSL invalid. |
 | ||
| | `ERR_POL_002` | Policy not approved | Attempted to run or activate unapproved version. |
 | ||
| | `ERR_POL_003` | Missing inputs | Downstream service unavailable (Concelier/Excititor/SBOM). |
 | ||
| | `ERR_POL_004` | Determinism violation | Illegal API usage (wall-clock, RNG). Triggers incident mode. |
 | ||
| | `ERR_POL_005` | Unauthorized materialisation | Identity lacks `effective:write`. |
 | ||
| | `ERR_POL_006` | Run canceled or timed out | Includes cancellation metadata. |
 | ||
| 
 | ||
| ---
 | ||
| 
 | ||
| ## 3 · Policy Management
 | ||
| 
 | ||
| ### 3.1 Create Draft
 | ||
| 
 | ||
| ```
 | ||
| POST /api/policy/policies
 | ||
| Scopes: policy:write
 | ||
| ```
 | ||
| 
 | ||
| **Request**
 | ||
| 
 | ||
| ```json
 | ||
| {
 | ||
|   "policyId": "P-7",
 | ||
|   "name": "Default Org Policy",
 | ||
|   "description": "Baseline severity + VEX precedence",
 | ||
|   "dsl": {
 | ||
|     "syntax": "stella-dsl@1",
 | ||
|     "source": "policy \"Default Org Policy\" syntax \"stella-dsl@1\" { ... }"
 | ||
|   },
 | ||
|   "tags": ["baseline","vex"]
 | ||
| }
 | ||
| ```
 | ||
| 
 | ||
| **Response 201**
 | ||
| 
 | ||
| ```json
 | ||
| {
 | ||
|   "policyId": "P-7",
 | ||
|   "version": 1,
 | ||
|   "status": "draft",
 | ||
|   "digest": "sha256:7e1d…",
 | ||
|   "createdBy": "user:ali",
 | ||
|   "createdAt": "2025-10-26T13:40:00Z"
 | ||
| }
 | ||
| ```
 | ||
| 
 | ||
| ### 3.2 List Policies
 | ||
| 
 | ||
| ```
 | ||
| GET /api/policy/policies?status=approved&tenant=default&page=1&pageSize=25
 | ||
| Scopes: policy:read
 | ||
| ```
 | ||
| 
 | ||
| Returns paginated list with `X-Total-Count` header.
 | ||
| 
 | ||
| ### 3.3 Fetch Version
 | ||
| 
 | ||
| ```
 | ||
| GET /api/policy/policies/{policyId}/versions/{version}
 | ||
| Scopes: policy:read
 | ||
| ```
 | ||
| 
 | ||
| Returns full DSL, metadata, provenance, simulation artefact references.
 | ||
| 
 | ||
| ### 3.4 Update Draft Version
 | ||
| 
 | ||
| ```
 | ||
| PUT /api/policy/policies/{policyId}/versions/{version}
 | ||
| Scopes: policy:write
 | ||
| ```
 | ||
| 
 | ||
| Body identical to create. Only permitted while `status=draft`.
 | ||
| 
 | ||
| ---
 | ||
| 
 | ||
| ## 4 · Lifecycle Transitions
 | ||
| 
 | ||
| ### 4.1 Submit for Review
 | ||
| 
 | ||
| ```
 | ||
| POST /api/policy/policies/{policyId}/versions/{version}:submit
 | ||
| Scopes: policy:submit
 | ||
| ```
 | ||
| 
 | ||
| **Request**
 | ||
| 
 | ||
| ```json
 | ||
| {
 | ||
|   "reviewers": ["user:kay","group:sec-reviewers"],
 | ||
|   "notes": "Simulated on golden SBOM set (diff attached)",
 | ||
|   "simulationArtifacts": [
 | ||
|     "blob://policy/P-7/v3/simulations/2025-10-26.json"
 | ||
|   ]
 | ||
| }
 | ||
| ```
 | ||
| 
 | ||
| **Response 202** – submission recorded. `Location` header points to review resource.
 | ||
| 
 | ||
| ### 4.2 Review Feedback
 | ||
| 
 | ||
| ```
 | ||
| POST /api/policy/policies/{policyId}/versions/{version}/reviews
 | ||
| Scopes: policy:review
 | ||
| ```
 | ||
| 
 | ||
| **Request**
 | ||
| 
 | ||
| ```json
 | ||
| {
 | ||
|   "decision": "approve",     // approve | request_changes | comment
 | ||
|   "note": "Looks good, ensure incident playbook covers reachability data.",
 | ||
|   "blocking": false
 | ||
| }
 | ||
| ```
 | ||
| 
 | ||
| ### 4.3 Approve
 | ||
| 
 | ||
| ```
 | ||
| POST /api/policy/policies/{policyId}/versions/{version}:approve
 | ||
| Scopes: policy:approve
 | ||
| ```
 | ||
| 
 | ||
| Body requires approval note and confirmation of compliance gates:
 | ||
| 
 | ||
| ```json
 | ||
| {
 | ||
|   "note": "All simulations and determinism checks passed.",
 | ||
|   "acknowledgeDeterminism": true,
 | ||
|   "acknowledgeSimulation": true
 | ||
| }
 | ||
| ```
 | ||
| 
 | ||
| ### 4.4 Activate
 | ||
| 
 | ||
| ```
 | ||
| POST /api/policy/policies/{policyId}/versions/{version}:activate
 | ||
| Scopes: policy:activate, policy:run
 | ||
| ```
 | ||
| 
 | ||
| Marks version as active for tenant; triggers optional immediate full run (`"runNow": true`).
 | ||
| 
 | ||
| ### 4.5 Archive
 | ||
| 
 | ||
| ```
 | ||
| POST /api/policy/policies/{policyId}/versions/{version}:archive
 | ||
| Scopes: policy:archive
 | ||
| ```
 | ||
| 
 | ||
| Request includes `reason` and optional `incidentId`.
 | ||
| 
 | ||
| ---
 | ||
| 
 | ||
| ## 5 · Compilation & Validation
 | ||
| 
 | ||
| ### 5.1 Compile
 | ||
| 
 | ||
| ```
 | ||
| POST /api/policy/policies/{policyId}/versions/{version}:compile
 | ||
| Scopes: policy:write
 | ||
| ```
 | ||
| 
 | ||
| **Response 200**
 | ||
| 
 | ||
| ```json
 | ||
| {
 | ||
|   "digest": "sha256:7e1d…",
 | ||
|   "warnings": [],
 | ||
|   "rules": {
 | ||
|     "count": 24,
 | ||
|     "actions": {
 | ||
|       "block": 5,
 | ||
|       "warn": 4,
 | ||
|       "ignore": 3,
 | ||
|       "requireVex": 2
 | ||
|     }
 | ||
|   }
 | ||
| }
 | ||
| ```
 | ||
| 
 | ||
| ### 5.2 Lint / Simulate Quick Check
 | ||
| 
 | ||
| ```
 | ||
| POST /api/policy/policies/{policyId}/lint
 | ||
| Scopes: policy:write
 | ||
| ```
 | ||
| 
 | ||
| Slim wrapper used by CLI; returns 204 on success or `ERR_POL_001` payload.
 | ||
| 
 | ||
| ---
 | ||
| 
 | ||
| ## 6 · Run & Simulation APIs
 | ||
| 
 | ||
| > Schema reference: canonical policy run request/status/diff payloads ship with the Scheduler Models guide (`src/StellaOps.Scheduler.Models/docs/SCHED-MODELS-20-001-POLICY-RUNS.md`) and JSON fixtures under `samples/api/scheduler/policy-*.json`.
 | ||
| 
 | ||
| ### 6.1 Trigger Run
 | ||
| 
 | ||
| ```
 | ||
| POST /api/policy/policies/{policyId}/runs
 | ||
| Scopes: policy:run
 | ||
| ```
 | ||
| 
 | ||
| **Request**
 | ||
| 
 | ||
| ```json
 | ||
| {
 | ||
|   "mode": "incremental",           // full | incremental
 | ||
|   "runId": "run:P-7:2025-10-26:auto",   // optional idempotency key
 | ||
|   "sbomSet": ["sbom:S-42","sbom:S-318"],
 | ||
|   "env": {"exposure": "internet"},
 | ||
|   "priority": "normal"             // normal | high | emergency
 | ||
| }
 | ||
| ```
 | ||
| 
 | ||
| **Response 202**
 | ||
| 
 | ||
| ```json
 | ||
| {
 | ||
|   "runId": "run:P-7:2025-10-26:auto",
 | ||
|   "status": "queued",
 | ||
|   "queuedAt": "2025-10-26T14:05:00Z"
 | ||
| }
 | ||
| ```
 | ||
| 
 | ||
| ### 6.2 Get Run Status
 | ||
| 
 | ||
| ```
 | ||
| GET /api/policy/policies/{policyId}/runs/{runId}
 | ||
| Scopes: policy:runs
 | ||
| ```
 | ||
| 
 | ||
| Includes status, stats, determinism hash, failure diagnostics.
 | ||
| 
 | ||
| ### 6.3 List Runs
 | ||
| 
 | ||
| ```
 | ||
| GET /api/policy/policies/{policyId}/runs?mode=incremental&status=failed&page=1&pageSize=20
 | ||
| Scopes: policy:runs
 | ||
| ```
 | ||
| 
 | ||
| Supports filtering by `mode`, `status`, `from`/`to` timestamps, `tenant`.
 | ||
| 
 | ||
| ### 6.4 Simulate
 | ||
| 
 | ||
| ```
 | ||
| POST /api/policy/policies/{policyId}/simulate
 | ||
| Scopes: policy:simulate
 | ||
| ```
 | ||
| 
 | ||
| **Request**
 | ||
| 
 | ||
| ```json
 | ||
| {
 | ||
|   "baseVersion": 3,
 | ||
|   "candidateVersion": 4,
 | ||
|   "sbomSet": ["sbom:S-42","sbom:S-318"],
 | ||
|   "env": {"sealed": false},
 | ||
|   "explain": true
 | ||
| }
 | ||
| ```
 | ||
| 
 | ||
| **Response 200**
 | ||
| 
 | ||
| ```json
 | ||
| {
 | ||
|   "diff": {
 | ||
|     "added": 12,
 | ||
|     "removed": 8,
 | ||
|     "unchanged": 657,
 | ||
|     "bySeverity": {
 | ||
|       "Critical": {"up": 1, "down": 0},
 | ||
|       "High": {"up": 3, "down": 4}
 | ||
|     }
 | ||
|   },
 | ||
|   "explainUri": "blob://policy/P-7/simulations/2025-10-26-4-vs-3.json"
 | ||
| }
 | ||
| ```
 | ||
| 
 | ||
| ### 6.5 Replay Run
 | ||
| 
 | ||
| ```
 | ||
| POST /api/policy/policies/{policyId}/runs/{runId}:replay
 | ||
| Scopes: policy:runs, policy:simulate
 | ||
| ```
 | ||
| 
 | ||
| Produces sealed bundle for determinism verification; returns location of bundle.
 | ||
| 
 | ||
| ---
 | ||
| 
 | ||
| ## 7 · Effective Findings APIs
 | ||
| 
 | ||
| ### 7.1 List Findings
 | ||
| 
 | ||
| ```
 | ||
| GET /api/policy/findings/{policyId}?sbomId=S-42&status=affected&severity=High,Critical&page=1&pageSize=100
 | ||
| Scopes: findings:read
 | ||
| ```
 | ||
| 
 | ||
| Response includes cursor-based pagination:
 | ||
| 
 | ||
| ```json
 | ||
| {
 | ||
|   "items": [
 | ||
|     {
 | ||
|       "findingId": "P-7:S-42:pkg:npm/lodash@4.17.21:CVE-2021-23337",
 | ||
|       "status": "affected",
 | ||
|       "severity": {"normalized": "High", "score": 7.5},
 | ||
|       "sbomId": "sbom:S-42",
 | ||
|       "advisoryIds": ["CVE-2021-23337"],
 | ||
|       "vex": {"winningStatementId": "VendorX-123"},
 | ||
|       "policyVersion": 4,
 | ||
|       "updatedAt": "2025-10-26T14:06:01Z"
 | ||
|     }
 | ||
|   ],
 | ||
|   "nextCursor": "eyJwYWdlIjoxfQ=="
 | ||
| }
 | ||
| ```
 | ||
| 
 | ||
| ### 7.2 Fetch Explain Trace
 | ||
| 
 | ||
| ```
 | ||
| GET /api/policy/findings/{policyId}/{findingId}/explain?mode=verbose
 | ||
| Scopes: findings:read
 | ||
| ```
 | ||
| 
 | ||
| Returns rule hit sequence:
 | ||
| 
 | ||
| ```json
 | ||
| {
 | ||
|   "findingId": "P-7:S-42:pkg:npm/lodash@4.17.21:CVE-2021-23337",
 | ||
|   "policyVersion": 4,
 | ||
|   "steps": [
 | ||
|     {"rule": "vex_precedence", "status": "not_affected", "inputs": {"statementId": "VendorX-123"}},
 | ||
|     {"rule": "severity_baseline", "severity": {"normalized": "Low", "score": 3.4}}
 | ||
|   ],
 | ||
|   "sealedHints": [{"message": "Using cached EPSS percentile from bundle 2025-10-20"}]
 | ||
| }
 | ||
| ```
 | ||
| 
 | ||
| ---
 | ||
| 
 | ||
| ## 8 · Events & Webhooks
 | ||
| 
 | ||
| - `policy.run.completed` – emitted with `runId`, `policyId`, `mode`, `stats`, `determinismHash`.
 | ||
| - `policy.run.failed` – includes error code, retry count, guidance.
 | ||
| - `policy.lifecycle.*` – mirrored from lifecycle APIs (see [Lifecycle guide](../policy/lifecycle.md)).
 | ||
| - Webhook registration occurs via `/api/policy/webhooks` (future work, reserved). For now, integrate with Notifier streams documented in `/docs/notifications/*`.
 | ||
| 
 | ||
| ---
 | ||
| 
 | ||
| ## 9 · Compliance Checklist
 | ||
| 
 | ||
| - [ ] **Scopes enforced:** Endpoint access requires correct Authority scope mapping (see `/src/StellaOps.Authority/TASKS.md`).
 | ||
| - [ ] **Schemas current:** JSON examples align with Scheduler Models (`SCHED-MODELS-20-001`) and Policy Engine DTOs; update when contracts change.
 | ||
| - [ ] **Error codes mapped:** `ERR_POL_*` table reflects implementation and CI tests cover edge cases.
 | ||
| - [ ] **Pagination documented:** List endpoints specify page/size and cursor semantics; responses include `X-Total-Count` or `nextCursor`.
 | ||
| - [ ] **Idempotency described:** Mutating endpoints mandate `Idempotency-Key`.
 | ||
| - [ ] **Offline parity noted:** Simulate/run endpoints explain `--sealed` behaviour and bundle generation.
 | ||
| - [ ] **Cross-links added:** References to lifecycle, runs, DSL, and observability docs verified.
 | ||
| 
 | ||
| ---
 | ||
| 
 | ||
| *Last updated: 2025-10-26 (Sprint 20).*
 |