Files
git.stella-ops.org/docs/policy/gateway.md
root 68da90a11a
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Restructure solution layout by module
2025-10-28 15:10:40 +02:00

6.2 KiB
Raw Blame History

Policy Gateway

Delivery scope: StellaOps.Policy.Gateway minimal API service fronting Policy Engine pack CRUD + activation endpoints for UI/CLI clients. Sender-constrained with DPoP and tenant headers, suitable for online and Offline Kit deployments.

1 · Responsibilities

  • Proxy policy pack CRUD and activation requests to Policy Engine while enforcing scope policies (policy:read, policy:author, policy:review, policy:operate, policy:activate).
  • Normalise responses (DTO + ProblemDetails) so Console, CLI, and automation receive consistent payloads.
  • Guard activation actions with structured logging and metrics so approvals are auditable.
  • Support dual auth modes:
    • Forwarded caller tokens (Console/CLI) with DPoP proofs + X-Stella-Tenant header.
    • Gateway client credentials (DPoP) for service automation or Offline Kit flows when no caller token is present.

2 · Endpoints

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). Missing caller credentials now surface 401 with "Upstream authorization missing" detail.

3 · Authentication & headers

Header Source Notes
Authorization Forwarded caller token or gateway client credentials. Caller tokens must include tenant scope; gateway tokens default to DPoP scheme.
DPoP Caller or gateway. Required when Authority mandates proof-of-possession (default). Generated per request; gateway keeps ES256/ES384 key material under etc/policy-gateway-dpop.pem.
X-Stella-Tenant Caller Tenant isolation header. Forwarded unchanged; gateway automation omits it.

Gateway client credentials are configured in policy-gateway.yaml:

policyEngine:
  baseAddress: "https://policy-engine.internal"
  audience: "api://policy-engine"
  clientCredentials:
    enabled: true
    clientId: "policy-gateway"
    clientSecret: "<secret>"
    scopes:
      - policy:read
      - policy:author
      - policy:review
      - policy:operate
      - policy:activate
  dpop:
    enabled: true
    keyPath: "../etc/policy-gateway-dpop.pem"
    algorithm: "ES256"

🔐 DPoP key store the private key alongside Offline Kit secrets; rotate it whenever the gateway identity or Authority configuration changes.

4 · Metrics & logging

All activation calls emit:

  • policy_gateway_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).
  • policy_gateway_activation_latency_ms{outcome,source} histogram measuring proxy latency.

Structured logs (category StellaOps.Policy.Gateway.Activation) include PackId, Version, Outcome, Source, and upstream status code for audit trails.

5 · Sample curl workflows

Assuming you already obtained a DPoP-bound access token ($TOKEN) for tenant acme:

# Generate a DPoP proof for GET via the CLI helper
DPoP_PROOF=$(stella auth dpop proof \
  --htu https://gateway.example.com/api/policy/packs \
  --htm GET \
  --token "$TOKEN")

curl -sS https://gateway.example.com/api/policy/packs \
  -H "Authorization: DPoP $TOKEN" \
  -H "DPoP: $DPoP_PROOF" \
  -H "X-Stella-Tenant: acme"

# Draft a new revision
DPoP_PROOF=$(stella auth dpop proof \
  --htu https://gateway.example.com/api/policy/packs/policy.core/revisions \
  --htm POST \
  --token "$TOKEN")

curl -sS https://gateway.example.com/api/policy/packs/policy.core/revisions \
  -H "Authorization: DPoP $TOKEN" \
  -H "DPoP: $DPoP_PROOF" \
  -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)
DPoP_PROOF=$(stella auth dpop proof \
  --htu https://gateway.example.com/api/policy/packs/policy.core/revisions/5:activate \
  --htm POST \
  --token "$TOKEN")

curl -sS https://gateway.example.com/api/policy/packs/policy.core/revisions/5:activate \
  -H "Authorization: DPoP $TOKEN" \
  -H "DPoP: $DPoP_PROOF" \
  -H "X-Stella-Tenant: acme" \
  -H "Content-Type: application/json" \
  -d '{"comment":"Rollout baseline"}'

For air-gapped environments, bundle policy-gateway.yaml and the DPoP key in the Offline Kit (see /docs/24_OFFLINE_KIT.md §5.7).

DPoP proof helper: Use stella auth dpop proof to mint sender-constrained proofs locally. The command accepts --htu, --htm, and --token arguments and emits a ready-to-use header value. Teams maintaining alternate tooling (for example, scripts/make-dpop.sh) can substitute it as long as the inputs and output match the CLI behaviour.

6 · Offline Kit guidance

  • Include policy-gateway.yaml.sample and the resolved runtime config in the Offline Kits config/ tree.
  • Place the DPoP private key under secrets/policy-gateway-dpop.pem with restricted permissions; document rotation steps in the manifest.
  • When building verification scripts, use the gateway endpoints above instead of hitting Policy Engine directly. The Offline Kit validator now expects policy_gateway_activation_requests_total metrics in the Prometheus snapshot.

7 · Change log

  • 2025-10-27 Sprint 18.5: Initial gateway bootstrap + activation metrics + DPoP client credentials.