When One Agent Becomes Ten: Parallel Sub-Agent Fan-Out Breaks Credential Attribution
8 min read Fullmakt Team
- agents
- credentials
- traceability
- observability
- governance
- authentication
Modern agent frameworks don’t run one call at a time anymore. An orchestrator gets a task, decides it splits cleanly into ten independent pieces, and spins up ten sub-agents in parallel — each one calling external APIs, each one finishing whenever it finishes, none of them waiting on the others. It’s the obvious way to get a fan-out task done in seconds instead of minutes. It’s also a governance model most credential systems were never built to describe, because they were designed around one agent making one call at a time, not one identity forking into ten simultaneous actors that all reach for the same key at once.
The failure: one credential, ten actors, no way to tell them apart
Give a parallel fan-out task a single task-scoped credential — the pattern most orchestration frameworks default to, because it’s the simplest thing that works — and every sub-agent authenticates as the same principal. That’s fine for the API on the other end; it has no way to know or care that “the agent” is actually ten concurrent branches right now. It’s not fine for whoever has to reconstruct what happened afterward. An audit log built for one caller at a time now shows forty API calls landing within the same three-second window, under the same identity, with no field that says which branch of the task made which call. If branch 3 wrote bad data and branch 7 read it before the mistake was caught, there’s no record that ties either action back to which sub-agent, which prompt, or which piece of the original task was actually responsible.
The sharper version of this failure isn’t just unattributable logs — it’s a race. Say the task has a budget: “spend at most $50 across all the calls this task makes.” A single-threaded agent can check that budget before every call and never overspend. Ten sub-agents checking the same budget concurrently can all read “we’re at $40, plenty of room” in the same instant, each greenlight a $15 call, and the task ends $95 over where a serialized version of the same check would have stopped it cold. This is a textbook time-of-check-to-time-of-use gap, except the “time” between check and use isn’t a processing delay — it’s ten branches checking the same fact at once and none of them seeing each other’s answer.
Why a session ID doesn’t fix it
The instinctive patch is to tag every call with a shared session or task ID so the logs can at least be grouped after the fact. That helps observability — you can filter to “everything this task did” — but it doesn’t touch authorization, and it doesn’t solve attribution within the group. A session ID tells you ten calls belonged to the same task; it doesn’t tell you which of the ten sub-agents made call #6, and it does nothing to stop two of those sub-agents from independently clearing the same budget or rate-limit check because neither one’s evaluation knew about the other’s in-flight request. Observability built at the task level is necessary but not sufficient here — the unit that needs its own identity, its own policy check, and its own line in the audit trail is the branch, not just the task it was spawned from.
It’s the same shape of gap as on-behalf-of delegation turned sideways: that problem is about a chain of agents acting for each other one link at a time and losing the “who’s ultimately responsible” thread. This is about one identity forking into many simultaneous links at once, where even a perfect delegation chain doesn’t help if all ten branches are, technically, the same link.
Where the fix actually has to live
- Every sub-agent branch needs its own credential, minted at fork time — not a copy of the parent task’s key, but a distinct, short-lived grant that traces back to the parent and can be revoked or scoped independently of its siblings. If branch 3 goes rogue, killing its credential shouldn’t require killing the other nine.
- Policy checks that involve shared state — budgets, rate limits, write locks on the same resource — have to be evaluated atomically at a single choke point, not independently inside each branch. A budget check that ten branches can each pass concurrently isn’t a budget check; it’s ten separate, uncoordinated guesses.
- The audit trail needs a call tree, not a call list. Every entry should carry the parent task, the branch that made it, and where that branch sits in the fan-out — so “reconstruct what happened” means walking a tree instead of grepping a flat log for a shared session ID and hoping the ordering tells you something it can’t.
- Idempotency and conflict detection need to account for siblings, not just retries. The duplicate-action problem usually gets framed as one agent retrying itself — fan-out adds a second version: two different branches independently deciding to take the same action, neither one aware the other already did.
- Fan-out has to be visible to the kill switch, not just the top-level task. Stopping “the task” needs to mean stopping every live branch, immediately — not stopping the parent and leaving nine already-dispatched calls to finish on credentials nobody’s watching anymore.
The business case: the broker is the choke point fan-out needs
This is exactly the shape of problem a broker sitting between every agent and every credentialed call is built to solve, because it sees the fork, not just the task:
- Fullmakt mints a scoped, short-lived credential per call, which means a parallel sub-agent branch never inherits a standing task-wide key — each branch’s calls are individually authorized and individually revocable, down to the same decision-token model that governs single-agent calls, just applied across every concurrent branch instead of one at a time.
- Shared limits are enforced where the broker sees all the traffic, so a budget or rate cap is checked against the real, current total across every branch of a task — not against what any single branch believes the total is, which closes the exact race a per-branch, uncoordinated check can’t.
- Every call in the audit trail carries its branch and parent task, so a fan-out incident can be reconstructed as the tree it actually was — which sub-agent, spawned from which task, made which call, in what order relative to its siblings — instead of a flat list of identical-looking calls from one identity.
- Revocation and the kill switch reach every branch at once, because there’s no per-branch credential living outside the broker’s view for a stop command to miss.
Parallel fan-out isn’t a fringe pattern — it’s how orchestration frameworks get agentic tasks done fast, and it’s only going to get more common as agents take on bigger, more decomposable work. A governance model built around “one agent, one credential, one call at a time” was never going to describe it. One built around brokering every call, regardless of how many branches are asking at once, already does.
FAQ
Isn’t this just a logging problem — can’t we fix it by logging more detail per call? Logging more detail helps reconstruct what happened after the fact, but it doesn’t fix the budget-race or the shared-credential problem, which happen during execution. You need per-branch authorization, not just per-branch log fields.
Do all agent frameworks even support parallel sub-agent fan-out today? Support varies, but the pattern — spawn N independent workers for a decomposable task, let them run concurrently, join the results — is already common in orchestration frameworks and gets more common as tasks scale, so the credential model needs to be ready before it becomes the default rather than after.
Does giving every branch its own credential slow down the fan-out? It adds one authorization step per branch, but that step is what the budget and rate-limit checks depend on to be accurate. A fan-out that skips it can run marginally faster and still overspend its own budget by the time anyone notices.
How is this different from the idempotency problem you’ve written about before? Idempotency usually covers one agent retrying its own action. This is a sibling problem: two different, concurrently running branches of the same task independently taking the same or conflicting action, neither aware the other exists.
What should an incident review be able to answer after a fan-out task goes wrong? Which branch made which call, in what order relative to its siblings, under what credential, and whether any shared limit was actually enforced across the whole set — not just “this task made forty calls across three seconds.”
A single agent making one call at a time was never the hard case for credential governance — it’s the case every framework already handles. The hard case is the one agentic AI is increasingly built around: one task forking into many actors that all reach for authorization at once. Attribution, budget enforcement, and revocation all have to hold at the branch level, not just the task level, or “one agent” quietly becomes “ten agents nobody can tell apart.”