Skip to content

Delegation and HITL approvals

A2A delegation chain signing, verification, and HITL approval records. See Tutorial: A2A delegation chains and Tutorial: HITL approval workflows.

Delegation chain

DelegationHopSigner dataclass

Signs a single delegation hop.

sign_hop

sign_hop(*, hop: int, principal_id: str, principal_type: str, delegated_at: str, scope_grant: dict[str, Any], manifest_id: str) -> str

Return base64url-encoded signature over the hop's canonical pre-image.

verify_delegation_chain

verify_delegation_chain(delegation_chain: list[dict[str, Any]], public_keys: dict[str, bytes], manifest_id: str, manifest_issuer: str | None = None) -> None

Verify all hops in a delegation chain.

Checks
  • The chain root is bound to the manifest's signing identity (when manifest_issuer is supplied).
  • Each hop signature is valid for its principal's key.
  • Hop indices are sequential starting from 0.
  • Scope at each hop is not broader than the previous hop's grant (tools, data_classifications, constraints, ttl_seconds, depth).
  • Chain depth does not exceed root hop's max_delegation_depth.
  • No hop carries a non-empty constraints array, since this verifier does not implement Cedar and cannot evaluate what such constraints actually permit (spec 3.4.1 / 5.2). Structural scope-narrowing on the constraint strings (see _check_scope_narrowing) is still enforced first and can independently fail the chain with ValueError; only a chain that is otherwise fully valid raises DelegationUnverifiable for the unresolved constraints.

Parameters:

Name Type Description Default
delegation_chain list[dict[str, Any]]

List of hop dicts from the manifest.

required
public_keys dict[str, bytes]

Map of principal_id -> raw Ed25519 public key bytes.

required
manifest_id str

Manifest ID to include in pre-image (replay protection).

required
manifest_issuer str | None

The manifest's signing identity (issuer or agent_id). When provided, the root hop's principal MUST equal this identity; otherwise the chain is rejected. A chain whose root is not the manifest signer could be grafted onto an unrelated manifest, so this binding is fail-closed when an issuer is known.

None

Raises:

Type Description
InvalidSignature

If any hop signature is invalid.

ValueError

If scope laundering is detected or chain is malformed.

DelegationUnverifiable

If every other check passes but one or more hops carry a non-empty constraints array that this verifier cannot parse/evaluate as Cedar. Callers MUST map this to UNVERIFIABLE, never VALID (spec 3.4.1 / 5.2).

HITL approvals

HitlApprovalSigner dataclass

Signs a HITL approval record.

In production, the keypair should be backed by a hardware security key (FIDO2/passkey or HSM). The signature proves the approver deliberately approved this exact scope at this exact time for this exact manifest.

sign_approval

sign_approval(*, manifest_id: str, approved_at: str, approved_scope: dict[str, Any], approver_id: str, approval_method: Optional[str] = None) -> str

Return base64url-encoded approval signature.

Pass approval_method whenever the approval record will carry one, so the signature covers the strength claim the verifier reads.

verify_hitl_approval

verify_hitl_approval(approval: dict[str, Any], manifest_id: str, approver_public_key: bytes) -> None

Verify a single HITL approval signature.

Parameters:

Name Type Description Default
approval dict[str, Any]

The approval dict from hitl_record.approvals.

required
manifest_id str

Manifest ID to bind the approval.

required
approver_public_key bytes

Raw Ed25519 public key bytes of the approver.

required

Raises:

Type Description
InvalidSignature

If the approval signature is invalid.

ValueError

If required fields are missing, malformed, or the approval has expired.