- Move 24 gateway source files (endpoints, services, contracts) into engine under Endpoints/Gateway/, Services/Gateway/, Contracts/Gateway/ namespaces - Add gateway DI registrations and endpoint mappings to engine Program.cs - Add missing project references (StellaOps.Policy.Scoring, DeltaVerdict, Localization) - Remove HTTP proxy layer (PolicyEngineClient, DPoP, forwarding context not copied) - Update gateway routes in router appsettings to point to policy-engine - Comment out policy service in docker-compose, add backwards-compat network alias - Update services-matrix (gateway build line commented out) - Update all codebase references: AdvisoryAI, JobEngine, CLI, router tests, helm - Update docs: OFFLINE_KIT, configuration-migration, gateway guide, port-registry - Deprecate etc/policy-gateway.yaml.sample with notice - Eliminates 1 container, 9 HTTP round-trips, DPoP token flow Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
104 lines
6.3 KiB
Markdown
104 lines
6.3 KiB
Markdown
# Policy Gateway (Merged into Policy Engine)
|
|
|
|
> **Status:** The `StellaOps.Policy.Gateway` service has been **merged into
|
|
> `StellaOps.Policy.Engine`**. All gateway endpoints (exceptions, deltas,
|
|
> gates, governance, tool-lattice, pack CRUD, activation) are now served
|
|
> directly by the policy-engine process. The separate gateway container,
|
|
> HTTP proxy layer, DPoP token flow, and `PolicyEngineClient` have been
|
|
> removed.
|
|
|
|
## 1 - Responsibilities (now handled by Policy Engine)
|
|
|
|
- Policy pack CRUD and activation endpoints enforcing scope policies (`policy:read`, `policy:author`, `policy:review`, `policy:operate`, `policy:activate`).
|
|
- Normalised responses (DTO + `ProblemDetails`) so Console, CLI, and automation receive consistent payloads.
|
|
- Structured logging and metrics for activation actions so approvals are auditable.
|
|
- Exception management, delta computation, gate evaluation, governance, and tool-lattice endpoints.
|
|
|
|
## 2 - Endpoints
|
|
|
|
All endpoints below are served by the **policy-engine** service.
|
|
|
|
| Route | Method | Description | Required scope(s) |
|
|
|-------|--------|-------------|-------------------|
|
|
| `/api/policy/packs` | `GET` | List policy packs and revisions for the active tenant. | `policy:read` |
|
|
| `/api/policy/packs` | `POST` | Create a policy pack shell or upsert display metadata. | `policy:author` |
|
|
| `/api/policy/packs/{packId}/revisions` | `POST` | Create or update a policy revision (draft/approved). | `policy:author` |
|
|
| `/api/policy/packs/{packId}/revisions/{version}:activate` | `POST` | Activate a revision, enforcing single/two-person approvals. | `policy:operate`, `policy:activate` |
|
|
|
|
### Response shapes
|
|
|
|
- Successful responses return camel-case DTOs matching `PolicyPackDto`, `PolicyRevisionDto`, or `PolicyRevisionActivationDto` as described in the Policy Engine API doc (`/docs/api/policy.md`).
|
|
- Errors always return RFC 7807 `ProblemDetails` with deterministic fields (`title`, `detail`, `status`).
|
|
|
|
### Dual-control activation
|
|
|
|
- **Config-driven.** Set `PolicyEngine.activation.forceTwoPersonApproval=true` when every activation must collect two distinct `policy:activate` approvals. When false, operators can opt into dual-control per revision (`requiresTwoPersonApproval: true`).
|
|
- **Defaults.** `PolicyEngine.activation.defaultRequiresTwoPersonApproval` feeds the default when callers omit the checkbox/flag.
|
|
- **Statuses.** First approval on a dual-control revision returns `202 pending_second_approval`; duplicate actors get `400 duplicate_approval`; the second distinct approver receives the usual `200 activated`.
|
|
- **Audit trail.** With `PolicyEngine.activation.emitAuditLogs` on, Policy Engine emits structured `policy.activation.*` scopes (pack id, revision, tenant, approver IDs, comments).
|
|
|
|
#### Activation configuration wiring
|
|
|
|
- **Helm ConfigMap.** `devops/helm/stellaops/values*.yaml` now include a `policy-engine-activation` ConfigMap. The chart automatically injects it via `envFrom` into the Policy Engine pod, so overriding the ConfigMap data updates the service with no manifest edits.
|
|
- **Type safety.** Quote ConfigMap values (e.g., `"true"`, `"false"`) because Kubernetes ConfigMaps carry string data.
|
|
- **File-based overrides (optional).** The Policy Engine host probes `/config/policy-engine/activation.yaml`, `../etc/policy-engine.activation.yaml`, and ambient `policy-engine.activation.yaml` files beside the binary.
|
|
- **Offline/Compose.** Compose/offline bundles can continue exporting `STELLAOPS_POLICY_ENGINE__ACTIVATION__*` variables directly; the ConfigMap wiring simply mirrors those keys for Kubernetes clusters.
|
|
|
|
## 3 - Authentication & headers
|
|
|
|
| Header | Source | Notes |
|
|
|--------|--------|-------|
|
|
| `Authorization` | Caller token. | Caller tokens must include tenant scope. |
|
|
| `X-Stella-Tenant` | Caller | Tenant isolation header. |
|
|
|
|
> **Note:** The previous DPoP proxy layer (gateway client credentials, `PolicyEngineClient`,
|
|
> `PolicyGatewayDpopHandler`) has been removed. Callers authenticate directly with Policy Engine
|
|
> using standard StellaOps resource server authentication.
|
|
|
|
## 4 - Metrics & logging
|
|
|
|
All activation calls emit:
|
|
|
|
- `policy_engine_activation_requests_total{outcome,source}` -- counter labelled with `outcome` (`activated`, `pending_second_approval`, `already_active`, `bad_request`, `not_found`, `unauthorized`, `forbidden`, `error`) and `source` (`caller`, `service`).
|
|
|
|
Structured logs (category `StellaOps.Policy.Engine.Activation`) include `PackId`, `Version`, `Outcome`, `Source`, and status code for audit trails.
|
|
|
|
## 5 - Sample `curl` workflows
|
|
|
|
Assuming you already obtained an access token (`$TOKEN`) for tenant `acme`:
|
|
|
|
```bash
|
|
curl -sS https://gateway.example.com/api/policy/packs \
|
|
-H "Authorization: Bearer $TOKEN" \
|
|
-H "X-Stella-Tenant: acme"
|
|
|
|
# Draft a new revision
|
|
curl -sS https://gateway.example.com/api/policy/packs/policy.core/revisions \
|
|
-H "Authorization: Bearer $TOKEN" \
|
|
-H "X-Stella-Tenant: acme" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"version":5,"requiresTwoPersonApproval":true,"initialStatus":"Draft"}'
|
|
|
|
# Activate revision 5 (returns 202 when awaiting the second approver)
|
|
curl -sS https://gateway.example.com/api/policy/packs/policy.core/revisions/5:activate \
|
|
-H "Authorization: Bearer $TOKEN" \
|
|
-H "X-Stella-Tenant: acme" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"comment":"Rollout baseline"}'
|
|
```
|
|
|
|
## 6 - Offline Kit guidance
|
|
|
|
- Include `policy-engine.yaml.sample` and the resolved runtime config in the Offline Kit's `config/` tree.
|
|
- When building verification scripts, use the policy-engine endpoints above. The Offline Kit validator expects `policy_engine_activation_requests_total` metrics in the Prometheus snapshot.
|
|
|
|
## 7 - Backwards compatibility
|
|
|
|
- The `policy-gateway.stella-ops.local` hostname is preserved as a network alias on the policy-engine container in both docker-compose and hosts files. Existing configurations referencing `policy-gateway.stella-ops.local` will continue to work.
|
|
- The `STELLAOPS_POLICY_GATEWAY_URL` environment variable has been removed from service defaults. Services that previously used it should use `STELLAOPS_POLICY_ENGINE_URL` instead.
|
|
|
|
## 8 - Change log
|
|
|
|
- **2026-04-08 -- Gateway merge**: Gateway merged into Policy Engine. Separate container, HTTP proxy layer, DPoP flow, and `PolicyEngineClient` removed. All endpoints served directly by policy-engine.
|
|
- **2025-10-27 -- Sprint 18.5**: Initial gateway bootstrap + activation metrics + DPoP client credentials.
|