Day 28 限制了 fan-out、fallback 與 retry,現在接著處理路由裡的權限。假設 planner 送來 subject="alice",executor 要怎麼確認它真的代表 Alice,又被允許做哪些事?
今天沿著 orchestrator → retriever → planner → executor 傳遞委派。每一跳都驗證簽發者、接收者與業務授權,child delegation 只能保留或縮小上游給的能力。
我們先用 HMAC 作為本機測試替身,讓有匯出權限的 Fiona 重放五筆拒絕案例,再保留 Alice 合法讀取一筆費用的對照。最後會回到工具執行端,檢查委派驗證通過後,還有哪些資料需要綁在一起。
訊息中的 sender="planner" 是 caller 提供的字串,接收端還要用可信憑證確認來源。Conversation ID、session ID、message ID 與 trace ID 可以協助找回流程,不能用來代替 sender 驗證。
就算 token 簽章有效,receiver 仍然要拒絕幾種「格式正確,用途錯誤」的請求:
在這個例子裡,Zero Trust 對應到接收端的幾項工作:驗證來源、檢查 delegation,再執行自己的 policy enforcement point。即使雙方位於同一個 Project 或 VNet,這些步驟仍要保留。
先讀 D29-A01 的固定案例設定,再看它實際停在哪一個授權檢查。

Fiona 有匯出 tenant:bamboo-hq 的 root grant,orchestrator 也順利簽出第一段委派。這段委派在送往 retriever 的途中被改寫,多塞了 tenant:moon-rabbit-lab,但簽章沒有跟著重算,所以 retriever 回 DELEGATION_BAD_SIGNATURE,工具 Receipt 與副作用都為 0。下面五筆是固定資料集的路由結果,不是一張逐欄更改 issuer、audience、subject、capability、target 的 claims 矩陣;使用的 HMAC 也只是本機替身。
D29-A02 標記為 malformed,接著核對畫面真正回傳的拒絕原因。

這次送到 retriever 的不是一段可以解析的委派,rejected_attempts 記在 retriever,原因是 DELEGATION_MALFORMED。沒有任何 hop 驗證通過,工具 Receipt 與副作用都為 0。
D29-A03 設定了 resource_mismatch,這次要看它一路走到哪一跳才被擋下。

retriever 與 planner 都驗證通過;executor 收到的操作目標卻換成外租戶的 exp-moon-001,不在委派的 resource 範圍內,因此回 DELEGATION_RESOURCE_DENIED。前兩跳通過、最後一跳拒絕,正好說明每一跳都要重新驗證,不能因為上一跳驗過就放行。
接著看 D29-A04 的預算檢查,確認超額計畫是否在工具執行前停下來。

這筆要求展開 8 個分支,並讓 primary 失敗後走 fallback。預設的路由預算是 16 次模型、4 次工具與 4 個分支,投影卻需要 40、8 與 8,所以得到 ROUTE_BUDGET_EXCEEDED,並列出模型、工具與分支三種超額原因。工具 Receipt 與副作用都為 0。這張圖涵蓋的是固定路由的預算拒絕,沒有驗證真實 Agent identity 或 A2A transport。
最後看 D29-A05:前面的失敗已打開 circuit,新的路由還能不能開始。

前兩次請求都帶著被改過的委派,各得到一次 DELEGATION_BAD_SIGNATURE,把 circuit 打開;第三次才得到 ROUTE_CIRCUIT_OPEN,工具 Receipt 與副作用都為 0。這是固定資料集的本機結果,不能代替真實 Agent identity 或 A2A transport 的驗證。
D29-A01~A05 都由 Fiona 提出同一句業務請求:
月底結帳要用,麻煩把竹林總部本月的費用明細匯出來。
五筆都使用同一句話,只改測試 metadata:bad_signature、malformed、resource_mismatch、budget_exceeded 與 circuit_open。其中 D29-A04 另外把分支數設成 8。
選 Fiona 是有原因的。她本來就能匯出竹林總部的費用,root grant 會放行,所以每一筆都能走到情境要測的那道檢查。這幾筆要看的不是「Fiona 能不能匯出」,而是委派在路上被竄改、壞掉、被換目標,或整條路由太大時,系統能不能在工具執行前停下來。
先前的固定資料用的是 Alice。她只有讀取的 root grant,五筆裡有三筆在 orchestrator 簽發第一段委派時就被擋下,圖上寫著簽章錯誤、格式錯誤、資源不符,實際卻一道都沒測到。外部審查指出後,改成現在的資料。
這一版 DelegationClaims 實際只有下列欄位:
root_grant_id
root_issuer_agent + root_policy_version
issuer_agent
subject
tenant_id
audience_agent
actions + resources
max_depth + current_depth
expires_at
nonce
目前 claims 沒有 subject_source、plan_digest 或 revocation version。讀程式時先依上面列出的欄位追流程;後續若需要把計畫版本或撤權狀態綁入執行,還要擴充這份契約。
從 day29/ 執行下面的 helper。它讓 Fiona 要求匯出 tenant:bamboo-hq,預算情境另外把分支數設成 8:
from magic_panda_agent.stages.day29 import replay_delegated_route
for scenario in (
"bad_signature",
"malformed",
"resource_mismatch",
"budget_exceeded",
"circuit_open",
):
wide_route = scenario == "budget_exceeded"
result = replay_delegated_route(
scenario=scenario,
subject="fiona",
action="export_expenses",
resource_id="tenant:bamboo-hq",
high_cost_route=True,
fan_out=8 if wide_route else 1,
force_primary_failure=wide_route,
)
print(
scenario,
result["allowed"],
result["reason"],
len(result["tool_receipts"]),
result["side_effect_count"],
)
五筆依序得到 DELEGATION_BAD_SIGNATURE、DELEGATION_MALFORMED、DELEGATION_RESOURCE_DENIED、ROUTE_BUDGET_EXCEEDED 與 ROUTE_CIRCUIT_OPEN,而且都要滿足:
allowed == False
tool_receipts == []
side_effect_count == 0
預算情境沒有另外收緊上限。先前的程式看到 budget_exceeded 這個情境名稱,就把上限改成 0 個分支,等於由測試名稱決定防禦的設定;現在所有情境共用同一組預設預算,超額是因為這筆要求本身展開得太大。
circuit_open 案例共用一個 RouteCircuitBreaker(threshold=2)。前兩次 invocation 帶著被改過的委派,各得到一次 DELEGATION_BAD_SIGNATURE。累積到 failures=2、open=true 後,第三次雖改用 valid 情境,仍在 projection、materialization 與工具執行前收到 ROUTE_CIRCUIT_OPEN。這能確認 breaker 狀態跨 request 延續。
實作與 acceptance 實際使用的 reason code 如下:
| 情境 | Reason code |
|---|---|
| 簽章被修改/token 無法解析 | DELEGATION_BAD_SIGNATURE/DELEGATION_MALFORMED |
| Receiver 不是 token audience | DELEGATION_WRONG_AUDIENCE |
| Expected issuer、subject 或 tenant 不符 | DELEGATION_WRONG_ISSUER/DELEGATION_WRONG_SUBJECT/DELEGATION_WRONG_TENANT |
| Issuer 不受信/issuer 不能代表該 tenant | DELEGATION_UNTRUSTED_ISSUER/DELEGATION_ISSUER_TENANT_DENIED |
| Receiver 要求 token 未列的 action 或 resource | DELEGATION_ACTION_DENIED/DELEGATION_RESOURCE_DENIED |
| Child 擴大 action 或 resource | DELEGATION_CAPABILITY_AMPLIFICATION/DELEGATION_RESOURCE_AMPLIFICATION |
| Parent audience 與次跳 issuer 不同 | DELEGATION_ISSUER_AUDIENCE_MISMATCH |
| 超過 depth/token 過期/nonce 再用 | DELEGATION_DEPTH_EXCEEDED/DELEGATION_EXPIRED/DELEGATION_REPLAY |
| 完整 route 超出預算/circuit 已打開 | ROUTE_BUDGET_EXCEEDED/ROUTE_CIRCUIT_OPEN |
現在看獨立 helper 的 resource_mismatch 結果,確認拒絕原因、停止位置與接收 Agent。

這份資料的 scenario=resource_mismatch、reason=DELEGATION_RESOURCE_DENIED,流程停在 executor verification,拒絕的 Agent 是 executor。它只列出這次 helper 的結果,沒有逐步 input digest,也沒有驗證正式環境的金鑰清冊、撤權、A2A 憑證或 transport 綁定。
看圖時沿著已通過的 hop 往下找拒絕位置,五筆都應在 tool_executor 前停止。MICROSOFT_ENTRA_AND_A2A_CREDENTIALS_NOT_EXERCISED 則表示這組測試沒有使用 Entra 或 A2A 憑證。
再走一次合法路由,從第一跳的能力交集開始看。

畫面先列出 route、depths、actions、resource_ids 與 capability_rule,再展開 capability_steps/0。第一跳把收到的能力與本地政策取交集,決定可以往下交多少。以下三張圖只測固定的本機政策與合成 HMAC,沒有驗證 Entra、A2A 身分、動態信任或 federation。
第二個 Agent 收到能力後,再和自己的政策取一次交集。

capability_steps/1 保留第二跳的 actions、resources 與 outgoing_matches_intersection。上一跳已移除的能力,不能到了這裡又被加回來。
最後看第三跳,再核對整條路由是否曾放大能力。

capability_steps/2 列出最後一跳,all_outgoing_match_intersection=true、amplification_detected=false 則確認這條固定路由的交集結果一致,沒有擴大能力。
DelegationService.issue() 先用 trusted_issuer_tenants 檢查簽發者能否代表這個 tenant,再找政策預先核發的 DelegationRootGrant。只有受信任的 issuer,還不足以自行增加權限。
目前 root grant 只有兩份:Alice 可以對 exp-bamboo-001 執行 read_expense,Fiona 可以對 tenant:bamboo-hq 執行 export_expenses。簽發者不能新增未列出的 subject、action、resource、audience 或 depth;每一跳還會重驗 root grant ID、root issuer 與 policy version。
這裡的 authenticated_issuer_agent 由 lab 呼叫端提供,尚未經 transport authentication 驗證。正式服務需要從已驗證的 workload 或 delegated credential 導出 issuer,不能直接採信 request body;本機結果也不能拿來證明 Entra access token 或 A2A profile 已通過測試。
合法 route 的 attenuation 如下:
orchestrator → retriever audience=retriever, depth=0
retriever → planner audience=planner, depth=1
planner → executor audience=executor, depth=2
三跳都保留:
subject = alice
tenant = bamboo-hq
action = read_expense
resource = exp-bamboo-001
attenuate() 先確認 parent 的 audience 就是下一位 issuer,subject 與 tenant 沒變,child 的 actions/resources 也是 parent 的子集,再檢查 depth 與尚未使用的 nonce。Nonce 的檢查和登記在接收端同一把 lock 裡完成,32-thread 測試只能有一次成功。Child 沿用 parent 的 expiry,不能延長期限,audience 也仍受 root grant 限制。
Breaker admission 在 projection 之前;通過後才投影並檢查 Day 28 的 model、tool、token、cost proxy 與 fan-out limits,而且必須在 materialization/executor 前完成。這是兩個不同的 oracle:delegation 問「能不能做」,route budget 問「這次最多可以展開多大」。
replay_delegated_route() 會沿用 caller 注入的 breaker instance,所以 A05 的第三次請求讀得到前兩次失敗。若每次 request 都建立新的 instance,狀態便會歸零,無法驗證這個跨請求條件。
actor 是當前送出呼叫的 Agent workload。subject 是這份工作代表的使用者。背景工作若沒有代表任何使用者,subject 就明確留空,依 application authority 授權。需要使用者權限的流程則延續 OBO/delegated context;若途中遺失,應拒絕該操作,不能改用較大權限的 app-only identity。
五條拒絕路徑之外,也保留一條使用相同本機協定、能正常讀取的路由。

各跳驗證通過,目標 PEP 放行,最後只留下 1 張唯讀 Receipt,副作用為 0。這筆合法對照沒有驗證 Foundry A2A 協定、外部 Agent 信任,或委派與最終工具之間的完整綁定。
從 day29/ 執行:
env PYTHONPATH=src:. PYTHONDONTWRITEBYTECODE=1 \
PYTHONNOUSERSITE=1 PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 \
python -m pytest tests/stages/day29/test_acceptance.py -q -p no:cacheprovider
目前 suite 實際驗證:
read_expense 與 exp-bamboo-001。get_expense receipt,side_effect=false;D29-B01 是 renderer 綁定的 ID。ROUTE_CIRCUIT_OPEN,ledger 仍是零 receipt/零 effect。ROUTE_BUDGET_EXCEEDED,不再由情境名稱收緊上限。approve_expense)直接得到 DELEGATION_ACTION_UNSUPPORTED,不會被當成匯出。bad_signature 情境確實把 tenant:moon-rabbit-lab 塞進委派的 resource,簽章驗證因此失敗。這組本機驗收的保存結果是 15 passed。
合法的 legal_three_hop_flow() 依序走 retriever→planner→executor,depth 為 0→1→2,最後只留下 read_expense 與 exp-bamboo-001。這是本機 HMAC 流程,可以用來核對每一跳的能力是否維持或縮小。
把今天的 root grant 與 child grant 放到真正的 A2A 路徑時,要分開看兩張證據:Foundry endpoint 先驗誰可以呼叫這個 Agent,工程司的 PEP 再驗這一跳能否替 Alice 讀取指定費用。若中途改用 Agent identity,接收端看到的 actor 可能變了;subject、capability、audience 與 expiry 仍要由應用程式逐跳核對。本篇十張圖只展示本機 grant 流程,沒有 A2A 雲端封包。
Foundry Agents core 在官方 readiness 表列為 GA,A2A 則要按協定版本與整合路徑分開看:incoming v1.0 與 outgoing a2a tool 已是 GA;v0.3 與舊的 a2a_preview tool 仍是 Preview。Preview 路徑沒有 GA SLA,不能因另一個版本已 GA 就一起視為正式支援。官方文件目前列出的邊界包含:
a2a/A2ATool,對應 GA v1.0;既有的 a2a_preview/A2APreviewTool 對應 Preview v0.3。Outgoing 的 authentication 選項不能反推 incoming endpoint 也接受相同方式。這些平台條件提供 authentication 與 protocol surface,不會替大魔術熊貓工程司判斷 Alice 能否讀取 exp-bamboo-001,也不會自動產生 capability attenuation、nonce replay store 與 route budget。
本機沒有驗證 Entra token、Foundry role、OBO 或 A2A agent card。平台部分還要分開看 Agents core、managed hosting、session API 與 incoming/outgoing A2A;不同文件仍各有 GA、Preview 或 prerelease 範圍。Python self-host 的 agent-framework-a2a 與 agent-framework-hosting-a2a 也仍標為 prerelease。
NIST SP 800-207/207A 只支撐 resource-centric、service identity 與 granular policy 的設計理由;「每一跳重驗、child 能力只能是 parent 子集」是本 repo 的 invariant,不是 Zero Trust conformance 測試。ISO/IEC 27001:2022 是 ISMS requirements standard,27002:2022 才是 control guidance;兩者都不會替 A2A 產生 token schema 或認證證據。
這個 lab 已經有 root grant、policy version 與封閉的 action 對照表,還沒有 VerifiedDelegationProof、可單次消耗的 ExecutionGrant、plan digest 或 revocation version。
目前 replay_delegated_route() 在同一個函式裡,用相同 action/resource 驗證後呼叫 router.execute()。這讓我們能追蹤一條固定路徑,但還沒有把驗證結果做成和實際工具原子綁定、只能消耗一次的執行授權。
原本的 fixture 把 read_expense 對應到 get_expense,其餘 action 一律對應到 export_expenses,新 action 可能因此被綁到匯出工具。現在改成只有兩列的對照表:read_expense 對 get_expense、export_expenses 對 export_expenses,其他 action 在建立路由前就得到 DELEGATION_ACTION_UNSUPPORTED。後續仍要將 issuer、subject、tenant、audience、action、resource、expiry、nonce、plan digest 與 exact tool 綁進可原子消耗的 grant。
Nonce check 在單 process 內已用 lock 原子化,但 nonce store 與 circuit breaker 都是 instance/process-local,而且只有 caller 重用或注入同一 instance 才能跨 request 延續。Breaker 目前只是單一未分 tenant/route 的 counter,沒有時間窗、cooldown、half-open 或 distributed atomicity;若錯誤地全域共用,兩筆壞簽章就可能替其他租戶製造 DoS。它也把所有委派拒絕都算成失敗,連「沒有 root grant」這種業務拒絕也算在內,一位員工連續要求兩次沒權限的匯出,就可能把 circuit 打開。比較合理的做法是只計入簽章錯誤、格式錯誤與重放這類完整性失敗。Production 需要 durable、partitioned replay/breaker state,並處理併發 consume、TTL、clock skew、key rotation、revocation 與 tool transaction。把 HMAC 字串換成 JWT 不會自動補齊這些生命週期。
最後一天會把每日驗收與固定案例的指標分開整理,先確認分母和版本,再檢查這一版是否具備發布條件。