« Phase 09 · Warmup · Track Overview

Lab 01 — The Control Plane

The problem

There are four hundred agents in production. An examiner asks four questions:

  1. Which of them can move money?
  2. Who owns the one that made this decision, and when was it last evaluated?
  3. Which policy allowed it — and can you show me that version of the policy?
  4. If you suspend one right now, how long until it stops acting?

A platform without a control plane answers the first with a spreadsheet, the second with a Slack archaeology exercise, the third with "the rules are in the prompt", and the fourth with a shrug. Those four answers are the difference between a platform and a collection of teams with API keys.

You build the layer that answers all four in milliseconds — and the one property that is harder than it sounds: it must keep answering when it is itself unreachable.

What you build

#ComponentWhat it does
1Subject, Resource, Environment, Requestthe four decision inputs — a blended principal, and posture in the environment
2Rule, _action_matchesABAC matching with a wildcard, where empty means "any"
3PolicyEnginedefault-deny, deny-overrides — the two combining rules a bank cannot negotiate
4PolicyBundleversioned, HMAC-signed, structurally validated
5BundleDistributor, Postureatomic activation, staleness alarm, hard stop — and fail-static
6AgentRegistry, AgentStatethe KYA database and its lifecycle, with a mandatory human owner
7ToolRegistry, ControlPlane.discoverauthorization-aware capability discovery
8PostureFinding, .posture_checks, .authorizeKYA at request time — categorical vs graduated — then policy, then the record
9ContinuousAuthorizer, Leasedecision TTLs, mid-task re-evaluation, a kill switch that beats the TTL
10EvaluationPipelinegolden sets and safety suites — wired into authorization, not into a dashboard
11Tracer, lineageagent- and tool-granular spans carrying identity, decision and version

Key concepts

ConceptWhereWhy it matters
Default-denyPolicyEngine.evaluatea policy set that fails open when someone forgets a case is not a control
Deny-overridesPolicyEngine.evaluatemakes the rule set order-independent, and therefore reviewable
Every matching rule is recordedDecision.matched_rules"what else fired?" is the first question in a policy incident
A decision is an artifactDecisiona boolean cannot be shown to an examiner; a policy version can
Signed bundlesPolicyBundle.sign/verifythe mechanism that makes atomic activation trustworthy
Allow-everything is a bugPolicyBundle.validatean ALLOW with every facet empty is never intended, and is silent
Rollback resistanceBundleDistributor.offera replayed older bundle is a policy downgrade attack
Fail-staticPosturethe third option: not fail-open (a hole), not fail-shut (a self-inflicted outage)
The hard stopBundleDistributor.enginea bundle hours old in a bank is worse than an outage — say the number
Rejection ≠ refreshoffer on a bad bundlea rejected push must not reset the staleness clock, or staleness never fires
Every agent has a human ownerAgentRegistry.registerthe standard audit finding, and the reorg problem underneath it
A pinned model versionAgentRecord.model_versionan unpinned model changes under you, silently, between evaluations
KYA is a runtime propertyposture_checksonboarding checks describe the agent that was approved, not the one running
Categorical vs graduatedPostureFinding.blocks_readsa control that downs the fleet on a late eval job is a control operators disable
Discovery is authorizationControlPlane.discovera tool a model can see is a tool it will eventually try to call
Posture degrades discoverydiscoveroffering a tool the next call would refuse is worse than not offering it
Leases and revocation latencyLease.ttl_ticksthe TTL is the revocation SLA, and it is a number you will be asked for
A new bundle invalidates leasesContinuousAuthorizerotherwise a policy push takes effect one TTL from now
High-impact is never leasedhigh_impact=Truethe actions worth caching are exactly the ones not worth caching
The kill switch has two halvesrevoke_agentdropping leases makes it fast; the revoked set keeps it fast
Safety failures are disqualifyingEvaluationPipeline.gatean aggregate that averages away a safety failure is a gate that does not gate
Evaluation feeds authorizationposture_checksrecord_evaluationthe single wire that turns quality from a report into a control
Lineage is a queryTracer.lineageonly answerable because every span carries identity, policy and model version

Files

FileRole
lab.pyyour implementation
solution.pyreference; python solution.py runs an eight-part worked session
test_lab.py119 tests
requirements.txtpytest

Run

pip install -r requirements.txt
pytest test_lab.py -v
LAB_MODULE=solution pytest test_lab.py -v
python solution.py

Success criteria

  • All 119 tests green against your lab.py.
  • No matching rule denies, and the decision still names a policy version.
  • Any matching DENY beats every ALLOW, and reversing the rule order changes nothing.
  • The decision lists every rule that matched, sorted — not only the winner.
  • An unsigned, tampered, structurally invalid or older bundle leaves the previous one live.
  • An unconditional allow-everything rule is rejected; an unconditional deny-everything is not.
  • A rejected push raises an alarm and does not reset the staleness clock.
  • A stale bundle keeps serving until the hard stop; past it, the evaluator refuses.
  • An agent with no human owner, or no pinned model version, cannot be registered.
  • draft → active is refused; retirement is terminal.
  • Discovery hides a tool for five distinct reasons, and returns nothing under a categorical posture failure — but degrades to reads only under a graduated one.
  • A stale evaluation blocks a high-impact action and not a read; several failures are all reported.
  • A repeated read inside the TTL is served from a lease; a high-impact action never is.
  • Activating a new bundle invalidates every live lease.
  • A suspension alone is not seen until the lease expires — and the kill switch beats it.
  • A safety failure blocks promotion even with min_score=0.0.
  • Span durations are never negative, and span ids are identical across two runs.

How this maps to the real stack

This labThe real thingWhat we simplified
Rule + Condition callablesOPA/Rego, AWS Cedar, or Entra CAE policiesno policy language, no parser, no partial evaluation
PolicyEnginean OPA sidecar, Cedar embedded, or a PDP serviceno data documents, no bundle-scoped data, no query API
PolicyBundle + HMACOPA bundles signed with Cosign/Notary, served from OCIsymmetric signing; the digest covers structure, not condition source
BundleDistributorOPA's bundle plugin with polling, signing and status reportingno HTTP, no ETags, no persistent disk cache across restarts
AgentRegistryan internal service over Postgres, plus Entra app registrationsno approval workflow, no attestation, no discovery of shadow agents
ControlPlane.discoveran MCP server filtering tools/list per principal (Phase 02)no protocol; the filtering is the point
ContinuousAuthorizerEntra Continuous Access Evaluation, or an in-house lease cacheno push channel; revocation here is in-process
EvaluationPipelineAzure AI Foundry evaluations, Promptfoo, DeepEval, an internal harnessno LLM judge, no statistical significance, no drift detection
TracerOpenTelemetry with GenAI semantic conventions (Phase 14)no context propagation, no sampling, no exporter

Honest limits. The bundle digest covers rule structure, not condition source — because conditions here are Python callables, and a callable cannot be hashed meaningfully. That is exactly why production policy lives in Rego or Cedar text: the text is what you sign, review, diff and attest. The kill switch is in-process, so it says nothing about the genuinely hard part — propagating a revocation to fifty PEP instances across three regions faster than their lease TTL. There is no policy test framework, and a policy set without unit tests is a policy set that will be changed by someone who does not know what rule seven does. And the anomaly score arrives as a number with no provenance; in production, deciding what feeds it is a larger design than everything in this file.

Extensions

  1. Swap the callables for Rego. Run a real OPA sidecar, express the same rule set in Rego, and sign the bundle with Cosign. Then hash the source and watch the digest become meaningful.
  2. Policy unit tests. Give every rule a fixture set — one request it must allow, one it must deny. Then break a rule and confirm the suite catches it before the bundle ships.
  3. A push-based revocation channel. Replace the in-process kill switch with a fan-out to N PEP instances. Measure the p99 propagation, then decide honestly whether your TTL can be raised.
  4. Decision-log streaming. Ship every decision to an append-only store with a hash chain (Phase 10), and answer "show me every denial for agent A last Tuesday" without a grep.
  5. Break-glass. Add an emergency-override path that is itself a policy decision, requires two approvers, expires in fifteen minutes, and pages someone. Emergency access nobody reviews is permanent access with a story attached.
  6. Shadow evaluation. Run a candidate bundle alongside the active one, record where they disagree, and ship only when the disagreements are all intended. This is how you change policy in a bank without an incident.
  7. Multi-region staleness. Two distributors, one source, a partition. Which region hard-stops first, and is that the behaviour you want?

Interview / resume bullets

  • "Built the platform's control plane: a policy engine with default-deny and deny-overrides evaluating ABAC over subject, action, resource and environment, driven by signed versioned bundles with atomic activation — so every agent decision carries the policy version that produced it."
  • "Designed for fail-static: when the control plane is unreachable, the data plane keeps enforcing the last known-good bundle, alarms on staleness, and hard-stops at a stated threshold — which avoided making control-plane availability a multiplier on the platform's."
  • "Made capability discovery an authorization decision rather than a lookup, so an agent is never shown a tool it cannot use — removing a whole class of prompt-injection target."
  • "Implemented continuous authorization with decision leases and a kill switch that beats the lease TTL, so suspending an agent stops in-flight work in under a second instead of at the next refresh."
  • "Wired the evaluation pipeline into the authorization path: an agent whose safety suite is stale or failing cannot act, which turned model quality from a dashboard into a control."