« Phase 08 · Warmup · Track Overview
Staff Notes — Judgment, Review Signal & Seniority
Table of Contents
- 1. Build vs buy
- 2. A decision framework for a credential request
- 3. Review red flags
- 4. Production war stories
- 5. The interview signal
- 6. Mentoring notes
1. Build vs buy
| Concern | Default | Why |
|---|---|---|
| Authorization server | Buy — Entra ID | you will not out-build an IdP, and the enterprise already has one |
| JWT sign/verify | Buy — a library, with explicit algorithms and audience | hand-rolled verifiers are where the CVEs are |
| Workload identity | Buy — SPIRE, or managed identity | attestation plugins are the product |
| mTLS | Buy — the service mesh | transparent, and it removes application code |
| Dynamic secrets | Buy — Vault | and it is the only thing here with real revocation |
| The platform STS | Build, thin | act chains, per-task scopes and second-scale TTLs are not what an enterprise IdP issues |
| The exchange policy | Build | which agent may exchange for what is your control model |
| The NHI registry | Build | lifecycle, ownership and the scope ceiling are yours |
| The JIT broker | Build | it is the composition of the above |
The line is sharper here than elsewhere: buy every cryptographic primitive and every protocol implementation; build only the composition and the policy. A hand-rolled JWT verifier is a liability with a long CVE history behind it; a hand-rolled narrowing rule is fifteen lines you must own because nobody else knows your control model.
And a caution about the thin STS: it is genuinely thin — verify, narrow, chain, sign — and it must stay that way. The moment it grows authorization logic it needs the policy engine on its critical path, and a policy outage becomes a total outage.
2. A decision framework for a credential request
A team needs an agent to call something. Seven questions, in order:
- Is a human involved? If yes, the credential must carry them — which means an exchange from the user's token, not client credentials. This question alone resolves most designs.
- What is the audience? One specific service. "Several" means several credentials.
- What scopes does this task need? Not what the agent might ever need, and not what the user holds. If the answer is a list of five, ask which the task actually uses.
- How long? Start at 60 seconds and justify anything longer. A number nobody can justify is a number that will grow.
- What happens when it expires mid-task? If the task can park for hours, you need the re-mint-on-resume shape, not a longer credential.
- Can it be bound to a key? If the workload already has an mTLS identity, binding is nearly free and removes theft-is-sufficient.
- Who owns the identity, and what happens when they leave? A name that is not linked to the HR feed is a name that will be stale within a year.
If someone asks for a long-lived credential, the question underneath is almost always (5). Answer that and the request usually dissolves.
3. Review red flags
In a design document
- A service account for the agent fleet, or "the platform identity".
- Tokens forwarded between services rather than exchanged.
- No mention of
audvalidation. - Credential lifetimes in hours, or unspecified.
- No answer to "what happens to a task parked for four hours?"
- Impersonation chosen "because delegation is complicated".
- A revocation claim of "instant" with no deny-list design.
- No NHI owner field, or a free-text one.
- Symmetric signing, or a signing key in configuration.
- Scopes described as "the agent's permissions" rather than the task's.
- A SPIRE registration keyed on one selector.
- Cross-domain trust assumed rather than federated.
- The STS doing authorization as well as authentication.
In code
# Red flag: algorithm from the token
jwt.decode(token, key) # no algorithms= → confusion attacks
# Red flag: no audience check
jwt.decode(token, key, algorithms=["RS256"]) # no audience= → confused deputy
# Red flag: forwarding
headers = {"Authorization": request.headers["Authorization"]} # exchange, don't forward
# Red flag: signature compared with ==
if computed == provided: ... # timing oracle
# Red flag: re-serializing before verifying
payload = json.loads(decode(parts[1]))
recomputed = sign(json.dumps(payload)) # key order changed; verify the bytes received
# Red flag: the chain from the request
chain = body["delegation_chain"] # asserted, not derived
# Red flag: silently narrowing
granted = set(requested) & set(held) # caller believes it got what it asked for
# Red flag: a lifetime that ignores the parent
exp = now + 3600 # can outlive the token it was derived from
# Red flag: any-selector attestation
if any(s in attested for s in entry.selectors): ...
# Red flag: verify=False
jwt.decode(token, options={"verify_signature": False})
In an incident review
- "We couldn't tell who authorized it" → service account, or the chain was dropped.
- "The credential still worked after we revoked it" → revocation was never instant; say the number.
- "It broke an hour after deploy" → an SVID cached at startup and never re-read.
- "It fails intermittently on some hosts" → clock skew.
4. Production war stories
"The platform released the payment." One service account for the agent fleet. Six weeks of
actions attributed to svc-ai-platform. Remediation meant reconstructing user context from
application logs and correlating by timestamp — and for a subset it was not possible at all.
The forwarded token. An agent passed its incoming token to core banking, which did not validate
aud. It worked for eight months. A penetration test found that any service holding a platform
token could act against any other service in the estate.
alg: none. A hand-rolled verifier read the algorithm from the header and dispatched on it. A
token with no signature verified successfully. The code had been reviewed twice.
The credential that outlived the session. An exchange minted a one-hour token from a token with four minutes remaining. The user logged out; the derived credential kept working for fifty-six minutes, and the actions it took were attributed to a user who was not there.
The invisible loop. Agent A delegated to B; B, doing its job honestly, delegated back to A. No cycle check. Six hours, two teams, neither able to see the whole thing, and the token bill was the first symptom.
The subset selector. A SPIRE registration keyed only on namespace. Every workload in that namespace could obtain the payments agent's identity — including a debugging pod someone had left running for a week.
The empty selector set. A registration entry created with no selectors by a templating bug.
all() over an empty sequence is True, so it matched every workload. Every pod on the cluster
could obtain that identity for two days.
The NHI nobody owned. Created during a proof of concept, still active two years later with production permissions, belonging to a team that had been reorganized twice. Found by an access review, not by monitoring.
The SVID cached at startup. A long-running service read its SVID once and cached it. It worked perfectly for an hour after every deploy, then failed — which made it look like a load problem for three days.
The impersonation shortcut. Delegation was "too complicated for the timeline", so the platform impersonated the user. Every downstream record showed the user acting directly, and when Internal Audit asked which actions were human and which were agent, there was no way to tell.
5. The interview signal
Signal 1 — you lead with the audit record. Not "service accounts are bad" but "here is the log line it produces, and here is why nobody can answer 'who asked?'" Concrete beats principled.
Signal 2 — the four words. Derived, narrowed, chained, short-lived — offered as a structure rather than recited. Then each one as a control you can demonstrate.
Signal 3 — "exchange, don't forward", with the confused deputy. And the observation that forwarding is the obvious implementation, which is why every platform builds it first.
Signal 4 — "derived from a verified assertion, never asserted". The single sentence that makes the chain unforgeable, and the one that connects this phase to Phase 03.
Signal 5 — you volunteer the parked-task problem. A four-hour approval outlives every sensible credential, and the answer is re-mint on resume with policy re-evaluated — not a longer token. Very few candidates raise this, and it demonstrates that they have run one of these rather than designed one.
Signal 6 — honest revocation. "A signed assertion cannot be recalled; revocation latency is the TTL, which is why I run sixty seconds; a deny list buys seconds and costs an availability dependency." Claiming instant revocation is the anti-signal.
Signal 7 — the containment argument. When asked whether all this complexity is worth it: "a compromised agent has one audience, one task's scopes, for sixty seconds, and cannot pivot — versus a service account with the union of everything, permanently."
Anti-signals:
- A service account, unremarked.
- "We use JWTs" as an identity model.
- No mention of
aud. - Impersonation because delegation is complicated.
- "We can revoke instantly."
- Treating workload identity and user identity as the same layer.
- Credential lifetimes chosen by convention rather than by a stated revocation-latency target.
The question to ask them: "When an agent calls core banking on a user's behalf, what does core banking see in the token?" The answer is the whole phase in one sentence, and it separates the platforms that have solved this from the ones that have a service account and a plan.
6. Mentoring notes
Three exercises, in order of how much they change behaviour:
- Show them two audit lines. The service-account one and the delegated one, side by side, and ask which they would want to hand an examiner. Ten seconds, and it reframes the whole topic from "security ceremony" to "the thing that makes the platform defensible."
- Have them attack their own verifier. Give them a token with
alg: none, one for a different audience, and one expired by 29 seconds. Most hand-rolled verifiers fail at least one. The exercise teaches the checklist far better than reading it. - Draw the three-hop flow on a whiteboard, and at each arrow ask: what narrowed, what was appended, and what is the lifetime now? The moment someone says "it can't be longer than the parent's remaining life" without prompting, they have it.
And the framing for the platform team: this is the phase where "we'll tighten it later" is most expensive. Every agent built against a service account has to be re-plumbed when the model changes — scopes, audiences, the chain, the call sites. Building the exchange path first costs a sprint; retrofitting it across forty agents costs a quarter, and it happens under audit pressure.
The argument that gets it funded is not security in the abstract. It is: "today, every action our agents take is attributed to the platform. An examiner will ask who authorized a payment, and our answer is a service account. That is a finding, and the remediation is this."