- 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>
6.3 KiB
Policy Gateway (Merged into Policy Engine)
Status: The
StellaOps.Policy.Gatewayservice has been merged intoStellaOps.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, andPolicyEngineClienthave 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, orPolicyRevisionActivationDtoas described in the Policy Engine API doc (/docs/api/policy.md). - Errors always return RFC 7807
ProblemDetailswith deterministic fields (title,detail,status).
Dual-control activation
- Config-driven. Set
PolicyEngine.activation.forceTwoPersonApproval=truewhen every activation must collect two distinctpolicy:activateapprovals. When false, operators can opt into dual-control per revision (requiresTwoPersonApproval: true). - Defaults.
PolicyEngine.activation.defaultRequiresTwoPersonApprovalfeeds the default when callers omit the checkbox/flag. - Statuses. First approval on a dual-control revision returns
202 pending_second_approval; duplicate actors get400 duplicate_approval; the second distinct approver receives the usual200 activated. - Audit trail. With
PolicyEngine.activation.emitAuditLogson, Policy Engine emits structuredpolicy.activation.*scopes (pack id, revision, tenant, approver IDs, comments).
Activation configuration wiring
- Helm ConfigMap.
devops/helm/stellaops/values*.yamlnow include apolicy-engine-activationConfigMap. The chart automatically injects it viaenvFrominto 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 ambientpolicy-engine.activation.yamlfiles 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 withoutcome(activated,pending_second_approval,already_active,bad_request,not_found,unauthorized,forbidden,error) andsource(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:
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.sampleand the resolved runtime config in the Offline Kit'sconfig/tree. - When building verification scripts, use the policy-engine endpoints above. The Offline Kit validator expects
policy_engine_activation_requests_totalmetrics in the Prometheus snapshot.
7 - Backwards compatibility
- The
policy-gateway.stella-ops.localhostname is preserved as a network alias on the policy-engine container in both docker-compose and hosts files. Existing configurations referencingpolicy-gateway.stella-ops.localwill continue to work. - The
STELLAOPS_POLICY_GATEWAY_URLenvironment variable has been removed from service defaults. Services that previously used it should useSTELLAOPS_POLICY_ENGINE_URLinstead.
8 - Change log
- 2026-04-08 -- Gateway merge: Gateway merged into Policy Engine. Separate container, HTTP proxy layer, DPoP flow, and
PolicyEngineClientremoved. All endpoints served directly by policy-engine. - 2025-10-27 -- Sprint 18.5: Initial gateway bootstrap + activation metrics + DPoP client credentials.