The six PEP surfaces¶
Five of these are subcommands of one binary, stratium-pep. The sixth,
the Python SDK, is not a subcommand of
anything — it is a library you import into your own process and drive
yourself. Each is a different place to stand between an agent and the thing
it is about to do.
| Mode | Enforces | Vars | Session file | Blocks on approval |
|---|---|---|---|---|
session-start |
nothing — it creates the delegation | 8 | writes it | — |
hook |
a coding agent's tool calls | 8 | reads it | yes |
mcp |
MCP tools/call |
9 | reads it | yes |
proxy |
gRPC method calls | 9 | no | no |
extauthz |
HTTP requests, via Envoy | 6 | no | no |
| Python SDK | whatever you pass to Session.check() — no built-in catalog |
— (constructor args, not env) | no | yes |
Every variable count above was read off the binary, by running each mode with an
empty environment and letting it name what it wanted. Each page quotes that
output verbatim. The SDK has no such table to run: it takes its configuration
as ordinary Python arguments to Client and Session, not environment
variables, because there is no separate process to hand them to.
Which one do I want?¶
Your agent is Claude Code or Codex. hook, preceded by
session-start. This is the highest-fidelity surface: the
PDP sees the tool name and its arguments, so classification caps and
resource labels all work.
Your harness has no hook system — ChatGPT, Claude Desktop, anything that
speaks MCP and nothing else. mcp. Same fidelity, delivered by
standing in front of the MCP server instead of beside the agent.
You are policing calls to a gRPC service. proxy. No client
change beyond one address.
You are policing HTTP and already run Envoy. extauthz.
You are writing your own Python process and want the PDP client in it, not a subprocess or sidecar to configure and keep running. The Python SDK — it is the only surface here that is a library rather than a program.
Two divisions worth understanding¶
Session file or not. hook and mcp read a session file that
session-start wrote — they are companions to a delegation issued on the same
machine. proxy and extauthz take the delegation token from the caller,
in gRPC metadata or an HTTP header, so the agent carries its own authority
across the network.
Blocking or not. hook and mcp can hold a call open while a human
approves an escalation — that is what APPROVAL_POLL_INTERVAL and
APPROVAL_TIMEOUT are for. proxy and extauthz cannot: Envoy's ext_authz
budget is milliseconds, and a held-open gRPC stream is its own problem. Both
degrade an escalation to a denial carrying the approval id, for the caller to
retry.
The SDK doesn't fit either division cleanly. Like proxy and extauthz,
it has no session file — a Session object holds the delegation in memory,
in your process, for as long as that process runs. But like hook and
mcp, it blocks: Session.check() polls for approval and returns only once
it settles, on the same budget-free footing as a coding-agent hook, because
it is a library call in your own process rather than a millisecond-budgeted
sidecar.
What is the same everywhere¶
- Fail closed. A misconfigured surface denies. It does not fall through and it does not crash — run any mode with no environment and watch it refuse rather than start.
- One tenant per PDP, named at that PDP's startup. No request carries one.
- Nothing is permitted until a policy permits it. Seeing everything denied on a fresh install is usually correct.
- The delegation's
approved_toolsis checked before the catalog matters. A tool nobody has catalogued is not held, and denies — there is no fall-through for unknown tool names.
The trap that spans all of them¶
A surface that cannot run is not a broken enforcement point, it is no
enforcement point. A hook whose command does not resolve exits 127, which a
harness treats as a non-blocking error — the tool call proceeds unchecked, and
the only sign is a hook error in a transcript nobody reads. The same shape
appears as failure_mode_allow: true on Envoy's filter.
Check the surface is actually running before trusting it. deploy/stratium
doctor does this for the hook case.
See also¶
../pep.md— the operator reference: every configuration value, the decision cache, escalation, and a table decoding every denial string.../quickstart.md— a scripted path to a workinghookinstall, if you would rather not do it by hand.../architecture.md— how the PEP, PDP and PAP fit together.../../example/— all five subcommands as runnable programs (or, forhook, a configuration plus a real captured transcript): a real local model, real enforcement, a passing or denied run you can reproduce.../../sdk/python/README.md— the Python SDK: install, the two calls, the four safety properties it exists to provide, and what it deliberately does not do (DPoP).