Skip to content

Vault as PEP with external PDP

This content is for v1.0. Switch to the latest version for up-to-date documentation.

Stop writing thousands of Sentinel policies per agent. Vault stays the enforcement point closest to secrets and dynamic credentials; OPA (or your existing policy engine) stays the decision point. One Endpoint Governing Policy, one HTTP call, binary outcome — issue the token or don’t.

This is the upstream seam in credential patterns: Vault attests and issues; Starfly mints WIMSE downstream when the fabric needs revocation, audience binding, and federation.

  • Policy complexity is the blocker — building blocks exist; per-agent Sentinel sprawl does not scale as agent adoption grows.
  • Enforcement stays at the vault — decisions happen where secrets are issued, not through a distant gateway that adds latency and a new SPOF.
  • Agents must not hold long-lived credentials — short-lived, scope-bound tokens with full audit attribution; credential separation via sidecar or gateway injection.
  • Delegation depth is infrastructure — multi-hop act chains enforced at issuance, not trusted because the application said so.
Agent identity (AppRole / JWT / K8s auth)
┌───────────────────┐
│ Vault auth │ bind principal, scope, delegation metadata
└─────────┬─────────┘
┌───────────────────┐
│ Sentinel EGP │ single rule — http import to external PDP
│ (one rule) │
└─────────┬─────────┘
┌───────────────────┐
│ OPA / policy engine │ evaluate payload → 200 + allow, or deny
└─────────┬─────────┘
┌───────────────────┐
│ Vault issues │ short-lived token + metadata (principal, intent, chain)
│ or denies │ full audit log on the PEP
└───────────────────┘

Vault = PEP. It performs the irreversible act.
OPA = PDP. It returns allow/deny (and optional constraints).
Policy growth happens in Rego, not in Vault configuration churn.

Sentinel’s job is orchestration, not encoding every agent scenario:

import "http"
import "json"
payload = json.marshal({
"agent_id": request.data.agent_id,
"requested_scope": request.data.requested_scope,
"principal": identity.entity.metadata.principal,
"delegation_chain": identity.entity.metadata.delegation_chain,
"auth_method": request.auth.accessor,
"timestamp": time.now,
})
resp = http.post("https://policy-engine.internal/evaluate", {
"body": payload,
"headers": {"Content-Type": "application/json"},
})
main = rule { resp.status_code == 200 and resp.body.decision == "allow" }
ResponseVault behavior
200 + decision: allowIssue scoped token / secret
Anything elseDeny — fail closed by default

Include a correlation ID in the payload and response so Vault audit logs and PDP evaluation logs stitch together.

These run regardless of PDP outcome — non-negotiable boundaries:

GuardrailWhat it enforces
Agent TTL ceilingAgent tokens cannot exceed org max (e.g. 15 min)
Delegation depth capdelegation_chain cannot exceed N hops
Scope monotonicityScope may narrow per hop, never widen
Jurisdiction / recordingAgent sessions meet compliance constraints (e.g. Boundary recording)

The PDP handles intent and context. Sentinel guardrails handle physics.

Multi-hop on-behalf-of uses nested act claims — audit provenance in the token, flattened delegation_chain in the PDP payload:

{
"sub": "<user-entity-id>",
"aud": "<target-service>",
"scope": "resource:read",
"act": {
"client_id": "agent-b",
"sub": "<agent-b-entity>",
"act": {
"client_id": "agent-a",
"sub": "<agent-a-entity>"
}
}
}

Only the top-level actor is validated against the subject’s may_act claim. Nested act carries lineage for forensics. Starfly exchange uses the same RFC 8693 vocabulary when minting WIMSE — the wire shape stays consistent across vault and fabric.

Token type URIs: id_token (subject), jwt (actor), access_token (delegated output).

Two PEPs, one policy vocabulary:

LayerQuestionOutput
Vault PEPMay this agent receive vault material / IdP token?Scoped secret or OIDC token
Starfly PEPMay this credential leave the fabric as WIMSE?Audience-bound JWT + revocation
Agent ──► Vault (Sentinel → OPA) ──► dynamic cred / IdP token
└──► Starfly exchange (OPA) ──► WIMSE JWT

Your in-house token exchange or Starfly can be the PDP Vault calls — same team, same Rego, new architecture. Vault becomes the PEP you already operate at scale.

The OIDC plugin adds IdP bridge mode when downstream APIs require Azure/Okta/Google tokens. FIAM preflight runs before the IdP request — role-level context gates, analogous to PDP input shaping.

Behavioral signals — Reflector wire truth, SSF Relay CAEP fan-out, SIEM alerts — feed the PDP as policy information, not as enforcement. Discovery → observation → policy refinement → Vault enforces on the next issuance. That is the entitlement development lifecycle without LLM guesswork at the seam.

Reasoner answers a different question: does runtime match declared architecture? PDP + Reasoner + graphs share typed evidence when CALM Forge leaf nodes federate with Starfly Graph.

ConcernGuidance
PDP availabilityDeploy HA; default fail-closed on engine timeout
LatencyCo-locate PDP with Vault; cache repeated decisions where safe
Engine compromisemTLS between Vault and PDP; engine behind network policy
AuditCorrelate Vault audit + PDP eval logs via shared request ID
StandardFit
IETF agentic auth draftsPolicy out of scope for standardization — PEP/PDP split matches
WIMSE workload credentialsVault-issued scoped tokens + Starfly exchange for fabric JWTs
RFC 8693 token exchangeDelegation chain wire format
NIST ABACClassic PEP / PDP / PIP separation
OpenID SSF / CAEPContinuous evaluation signals feed PDP input

Preview — Sentinel http import and OPA PDP pattern documented; OIDC plugin export stub: providers/oidc-engine/.