---
title: "Agent Context Token (ACT)"
abbrev: "ACT"
category: std
docname: draft-nennemann-act-01
submissiontype: IETF
number:
date:
v: 3
area: "SEC"
keyword:
  - agent authorization
  - execution accountability
  - JWT
  - DAG
  - delegation

author:
  -
    fullname: Christian Nennemann
    organization: Independent Researcher
    email: ietf@nennemann.de

normative:
  RFC7515:
  RFC7517:
  RFC7518:
  RFC7519:
  RFC8037:
  RFC9110:
  RFC9562:

informative:
  RFC7009:
  RFC8693:
  I-D.nennemann-wimse-ect:
    title: "Execution Context Tokens for Distributed Agentic Workflows"
    target: https://datatracker.ietf.org/doc/draft-nennemann-wimse-ect/
    seriesinfo:
      Internet-Draft: draft-nennemann-wimse-ect-02
    date: 2026
    author:
      - fullname: Christian Nennemann
  # draft-ietf-scitt-architecture is currently in AUTH48 (RFC Editor
  # queue) at version -22. To become RFC upon publication. Readers
  # should use the RFC number once assigned.
  I-D.ietf-scitt-architecture:
  I-D.oauth-transaction-tokens-for-agents:
    title: "Transaction Tokens for Agentic AI Systems"
    target: https://datatracker.ietf.org/doc/draft-oauth-transaction-tokens-for-agents/
    seriesinfo:
      Internet-Draft: draft-oauth-transaction-tokens-for-agents-06
    date: 2026
    author:
      - fullname: Fletcher, G.
  I-D.aap-oauth-profile:
    title: "Agent Authorization Profile (AAP) for OAuth 2.0"
    target: https://datatracker.ietf.org/doc/draft-aap-oauth-profile/
    seriesinfo:
      Internet-Draft: draft-aap-oauth-profile-01
    date: 2026-02
    author:
      - fullname: Cruz, A.
  I-D.helixar-hdp-agentic-delegation:
    title: "Helixar Delegation Protocol (HDP) for Agentic Delegation"
    target: https://datatracker.ietf.org/doc/draft-helixar-hdp-agentic-delegation/
    seriesinfo:
      Internet-Draft: draft-helixar-hdp-agentic-delegation-00
    date: 2026
    author:
      - org: Helixar
  I-D.emirdag-scitt-ai-agent-execution:
    title: "A SCITT Profile for AI Agent Execution Records"
    target: https://datatracker.ietf.org/doc/draft-emirdag-scitt-ai-agent-execution/
    seriesinfo:
      Internet-Draft: draft-emirdag-scitt-ai-agent-execution-00
    date: 2026-04
    author:
      - fullname: Emirdag
  AgenticJWT:
    title: "Agentic JWT: A JSON Web Token Profile for Delegated Agent Authorization"
    target: https://arxiv.org/abs/2509.13597
    date: 2025
  AIP-IBCT:
    title: "AIP: Agent Interaction Protocol with Interaction-Bound Context Tokens"
    target: https://arxiv.org/abs/2603.24775
    date: 2026-03
    author:
      - fullname: Prakash, S.
  SentinelAgent:
    title: "SentinelAgent: A Formal Delegation Chain Calculus for Verifiable Agent Authorization"
    target: https://arxiv.org/abs/2604.02767
    date: 2026-04
    author:
      - fullname: Patil
  MCP-SPEC:
    title: "Model Context Protocol Specification"
    target: https://modelcontextprotocol.io/specification/2025-11-25
    date: 2025-11-25
  OPENAI-AGENTS-SDK:
    title: "OpenAI Agents SDK"
    target: https://openai.github.io/openai-agents-python/
    date: false
    author:
      - org: OpenAI
  LANGGRAPH:
    title: "LangGraph Documentation"
    target: https://langchain-ai.github.io/langgraph/
    date: false
    author:
      - org: LangChain
  A2A-SPEC:
    title: "Agent2Agent (A2A) Protocol"
    target: https://github.com/a2aproject/A2A
    date: false
    author:
      - org: Google
  CREWAI:
    title: "CrewAI Documentation"
    target: https://docs.crewai.com/
    date: false
    author:
      - org: CrewAI
  AUTOGEN:
    title: "AutoGen Documentation"
    target: https://microsoft.github.io/autogen/
    date: false
    author:
      - org: Microsoft
  W3C-DID:
    title: "Decentralized Identifiers (DIDs) v1.0"
    target: https://www.w3.org/TR/did-core/
    date: 2022-07
    author:
      - fullname: Sporny, M.
  DID-KEY:
    title: "The did:key Method v0.7"
    target: https://w3c-ccg.github.io/did-method-key/
    date: 2021
    author:
      - fullname: Longley, D.
  DID-WEB:
    title: "did:web Method Specification"
    target: https://w3c-ccg.github.io/did-method-web/
    date: 2022
    author:
      - fullname: Steele, O.
  DORA:
    title: "Digital Operational Resilience Act (DORA), Regulation (EU) 2022/2554"
    target: https://eur-lex.europa.eu/eli/reg/2022/2554/oj
    date: 2022
    author:
      - org: European Parliament
  EUAIA:
    title: "EU Artificial Intelligence Act, Regulation (EU) 2024/1689"
    target: https://eur-lex.europa.eu/eli/reg/2024/1689/oj
    date: 2024
    author:
      - org: European Parliament
  IEC62304:
    title: "Medical device software — Software life cycle processes, IEC 62304:2006+AMD1:2015"
    target: https://www.iec.ch/
    date: 2015
    author:
      - org: IEC

--- abstract

This document defines the Agent Context Token (ACT), a self-contained
JWT-based format that captures the full invocation context of an
autonomous AI agent — its capabilities, constraints, delegation
provenance, oversight requirements, task metadata, and DAG position —
and unifies authorization and execution accountability in a single
token lifecycle. An ACT begins as a signed authorization mandate and
transitions into a tamper-evident execution record once the agent
completes its task, appending cryptographic hashes of inputs and
outputs and linking to predecessor tasks via a directed acyclic graph
(DAG). ACT requires no Authorization Server, no workload identity
infrastructure, and no transparency service for basic operation. Trust
is bootstrapped via pre-shared keys and is upgradeable to PKI or
Decentralized Identifiers (DIDs). ACT is designed for
cross-organizational agent federation in regulated and unregulated
environments alike. ACT is the general-purpose agent context
primitive; the WIMSE Execution Context Token (ECT)
{{I-D.nennemann-wimse-ect}} is a sibling profile specialized for
workload-identity-bound execution recording in WIMSE deployments.

--- middle

# Introduction

Autonomous AI agents increasingly operate across organizational
boundaries, executing multi-step workflows where individual tasks are
delegated from one agent to another. These workflows create two
distinct, inseparable compliance requirements:

1. **Authorization**: was the agent permitted to perform the action,
   under what constraints, and by whose authority?

2. **Accountability**: what did the agent actually do, with what
   inputs, producing what outputs, in what causal relationship to
   prior tasks?

Existing specifications address these requirements in isolation.
The Agent Authorization Profile (AAP) {{I-D.aap-oauth-profile}}
provides structured authorization via OAuth 2.0 but requires a
central Authorization Server. The WIMSE Execution Context Token
{{I-D.nennemann-wimse-ect}} provides execution accountability but
requires WIMSE workload identity infrastructure (SPIFFE/SPIRE).

This document defines the Agent Context Token (ACT), which addresses
both requirements in a single, self-contained token that requires no
shared infrastructure beyond the ability to verify asymmetric
signatures. The word "Context" in the name reflects what the token
carries: the complete invocation context of an agent — DAG references,
task metadata, capabilities, delegation chain, and oversight claims
— bound together in one cryptographically verifiable envelope. ACT
is positioned as the general agent context primitive, with the
WIMSE Execution Context Token (ECT) {{I-D.nennemann-wimse-ect}} as a
sibling profile specialized for workload-identity-bound execution
contexts in WIMSE deployments.

## Problem Statement

Cross-organizational agent federation today faces a bootstrapping
problem: deploying shared OAuth infrastructure or a common SPIFFE
trust domain requires organizational agreement before the first
message is exchanged. In practice this means either:

(a) agents operate without cryptographic authorization or audit
    trails, relying on application-layer access control only; or

(b) organizations adopt one party's identity infrastructure, creating
    a hub-and-spoke dependency that contradicts the decentralized
    nature of agent networks.

ACT solves this by making pre-shared keys the mandatory-to-implement
trust baseline — two agents can begin a secure, auditable interaction
with nothing more than an out-of-band key exchange — while providing
a clean upgrade path to PKI or DID-based trust without changing the
token format.

## Design Goals

- **G1 — Zero infrastructure baseline**: ACT MUST be deployable with
  no shared servers, no common identity provider, and no transparency
  service.

- **G2 — Single token lifecycle**: Authorization and accountability
  MUST be expressed in the same token format to prevent
  authorization-accountability gaps.

- **G3 — Peer-to-peer delegation**: Delegation chains MUST be
  verifiable without contacting an Authorization Server, using
  cryptographic chaining of agent signatures.

- **G4 — DAG-native causal ordering**: Workflows with parallel
  branches and fan-in dependencies MUST be expressible natively,
  without flattening to a linear chain.

- **G5 — Cross-organizational interoperability**: ACTs issued by
  agents in different trust domains MUST be verifiable by any
  participant holding the issuing agent's public key.

- **G6 — Regulatory applicability**: ACT MUST provide sufficient
  evidence for audit requirements in DORA {{DORA}}, EU AI Act
  Article 12 {{EUAIA}}, and IEC 62304 {{IEC62304}} without requiring
  additional log formats.

- **G7 — Upgrade path**: The trust model MUST support migration from
  pre-shared keys to PKI or DID without breaking existing ACT chains.

## Non-Goals

The following are explicitly out of scope:

- Defining internal AI model behavior or decision logic.
- Replacing organizational security policies or procedures.
- Defining storage formats for audit ledgers.
- Specifying token revocation infrastructure (deployments MAY use
  existing mechanisms such as {{RFC7009}} for this purpose).
- Providing non-equivocation guarantees in standalone mode (see
  {{equivocation}} for the equivocation discussion and optional
  transparency anchoring).

## Relationship to Related Work

**AAP {{I-D.aap-oauth-profile}}**: ACT addresses the same authorization
problem as AAP but does not require an Authorization Server. ACT
delegation is peer-to-peer via cryptographic signature chaining;
AAP delegation requires OAuth Token Exchange {{RFC8693}} against a
central AS. ACT is not a profile of AAP; it is an
infrastructure-independent alternative for the same problem class.

**WIMSE ECT {{I-D.nennemann-wimse-ect}}**: ACT addresses the same
execution accountability problem as the WIMSE Execution Context Token
but does not require WIMSE workload identity infrastructure. ACT is
not a profile of WIMSE; it is deployable in environments without
SPIFFE/SPIRE. In environments where WIMSE is deployed, ACT MAY be
carried alongside WIMSE tokens to augment accountability with
authorization provenance.

**SCITT {{I-D.ietf-scitt-architecture}}**: For deployments requiring
non-equivocation guarantees (see {{equivocation}}), ACT execution
records MAY be anchored to a SCITT Transparency Service as a Layer 2
mechanism. This is OPTIONAL and not required for basic ACT operation.
Note: The SCITT architecture draft is currently in AUTH48 (RFC
Editor queue) at version -22 and is about to become an RFC; readers
should use the RFC number once assigned.

### Concurrent Agent Authorization Proposals

Several concurrent proposals in the IETF and academic communities
address overlapping portions of the agent authorization problem
space. This subsection situates ACT relative to those proposals.
Protocol-layer comparison of linear versus DAG delegation
structure is deferred to {{dag-vs-linear}}; the summaries below focus
on scope and deployability.

**AIP / IBCTs {{AIP-IBCT}}**: The Agent Interaction Protocol proposes
Interaction-Bound Capability Tokens in two modes: compact signed
JWTs for single-hop invocation and Biscuit/Datalog tokens for
multi-hop delegation, motivated by a survey of approximately 2,000
Model Context Protocol servers that found no authorization
enforcement. ACT addresses the same problem class but relies
exclusively on JWT/JOSE throughout (no Biscuit or Datalog
dependency), defines an explicit two-phase lifecycle separating
authorization (Mandate) from proof-of-execution (Record), and
supports DAG delegation structure. IBCTs are modeled as append-only
chains at the protocol layer; ACT operates at the authorization
graph layer with revocable lifecycle states.

**SentinelAgent {{SentinelAgent}}**: SentinelAgent defines a formal
Delegation Chain Calculus with seven verifiable properties, a TLA+
mechanization, and reports 100% true-positive and 0% false-positive
rates against the DelegationBench v4 benchmark. It addresses the
same accountability question as ACT — namely, which principal
authorized a given chain of actions. The differentiator is
deployment substrate: SentinelAgent expresses its guarantees in a
domain-specific formal calculus, whereas ACT encodes the same
invariants in IETF-standard JWT infrastructure (RFC 7519, RFC 7515,
RFC 8032) already deployable in existing OAuth- and JOSE-aware
stacks.

**Agentic JWT {{AgenticJWT}}**: Agentic JWT derives a per-agent
identity as a one-way hash of the agent's prompt, registered tools,
and configuration, and chains delegation assertions across
invocations. It is the closest prior-art JWT-based construction
for agentic delegation. ACT differs in that it adds an explicit
two-phase lifecycle — separating the authorization mandate from
the proof-of-execution record — and expresses delegation as a DAG
via the array-valued `pred` claim rather than a strictly linear
chain.

**OAuth Transaction Tokens for Agents
{{I-D.oauth-transaction-tokens-for-agents}}**: This draft
extends OAuth Transaction Tokens with an `actchain` claim (an
ordered delegation array), an `agentic_ctx` claim conveying intent
and constraints, and flow-type markers distinguishing interactive
from autonomous invocations. It is complementary to ACT at the
OAuth layer. The primary differentiators are topology and
infrastructure dependency: Transaction Tokens for Agents presume
an OAuth Authorization Server and use a linear `actchain`, whereas
ACT operates peer-to-peer without any AS and uses a DAG-valued
`pred`. A detailed differencing document is referenced in
{{security-considerations}}.

**Helixar Delegation Protocol (HDP)
{{I-D.helixar-hdp-agentic-delegation}}**: HDP specifies
Ed25519 signatures over RFC 8785-canonicalized JSON, an
append-only linear delegation chain with session binding, and
offline verification. ACT addresses the same problem but is
encoded in JWT/JOSE (aligning with the broader IETF token
ecosystem) rather than raw canonical JSON, and its `pred` claim
admits DAG topologies rather than strictly linear chains.

**SCITT Profile for AI Agent Execution Records
{{I-D.emirdag-scitt-ai-agent-execution}}**: This draft
defines a SCITT profile in which AgentInteractionRecord (AIR)
payloads are carried as COSE_Sign1 statements anchored to a
SCITT Transparency Service. It is highly complementary to ACT:
where ACT defines the two-phase lifecycle token issued and
consumed by agents at runtime, the SCITT AI Agent Execution
draft defines the payload format suitable for long-term anchoring.
Implementations that anchor Phase 2 ACTs to SCITT
({{security-considerations}}) SHOULD consider the AIR payload
structure defined in that draft as the canonical encoding for
anchored records.

## Applicability

ACT is designed as a general-purpose primitive for AI agent
authorization and execution accountability. While a sibling
specification {{I-D.nennemann-wimse-ect}} profiles execution context
tokens specifically for the WIMSE working group's workload identity
infrastructure, ACT operates without any shared identity plane. This
section identifies deployment contexts where ACT applies independently
of WIMSE, and clarifies how ACT complements — rather than competes
with — ecosystem-specific agent protocols.

### Model Context Protocol (MCP) Tool-Use Flows

The Model Context Protocol {{MCP-SPEC}} defines a client-server
interface by which LLM hosts invoke external tools via structured
JSON-RPC calls. MCP 2025-11-25 mandates OAuth 2.1 for transport-layer
authentication, but provides no mechanism for carrying per-invocation
authorization constraints or for producing a tamper-evident record
of what arguments were passed and what result was returned.

ACT addresses this gap as follows: when an MCP host is about to
dispatch a tool call on behalf of an agent, it SHOULD issue a Phase 1
ACT Mandate encoding the permitted tool name (e.g., as a capability
constraint), the declaring scope, and any parameter-level constraints
applicable to that invocation. The MCP server, upon receiving the
request, MAY validate the ACT Mandate and, upon completing the tool
execution, SHOULD transition the token to Phase 2 by appending
SHA-256 hashes of the serialized input arguments and the JSON
response, then re-sign. The resulting Phase 2 ACT constitutes an
unforgeable record that a specific tool was called with specific
arguments and returned a specific result, independently of MCP's
OAuth layer.

This integration requires no modification to MCP transport; the ACT
SHOULD be carried in the `ACT-Mandate` and `ACT-Record` HTTP headers
defined in {{http-header-transport}} of this document.

### OpenAI Agents SDK and Function Calling

The OpenAI Agents SDK {{OPENAI-AGENTS-SDK}} enables composition of
agents via handoffs — structured transfers of control from one agent
to another, each potentially invoking registered function tools. The
SDK provides no built-in mechanism for a receiving agent to verify
that the handoff was authorized by a named principal, nor for the
invoking agent to produce a verifiable record of what functions it
called.

ACT is applicable at the handoff boundary: the orchestrating agent
SHOULD issue a Phase 1 ACT Mandate to the receiving agent at the
moment of handoff, encoding the permitted function set as
capability constraints and the maximum privilege the receiving agent
MAY exercise. The receiving agent SHOULD attach its Phase 2 ACT
Record to any callback or downstream response, providing the
orchestrator with cryptographic evidence of the actions taken. In
multi-turn chains involving multiple handoffs, the DAG linkage
({{dag-structure}}) allows each handoff to be expressed as a
parent-child edge, preserving the full causal ordering of the agent
invocation sequence.

Implementations that use the OpenAI function calling API directly,
without the Agents SDK, MAY apply ACT at the application layer: the
calling process issues a Phase 1 ACT before the function call
parameter block is finalized, and the receiving function handler
returns a Phase 2 ACT alongside its JSON result.

### LangGraph and LangChain Agent Graphs

LangGraph {{LANGGRAPH}} models agent workflows as typed StateGraphs in
which nodes represent agent invocations or tool calls and edges
represent conditional transitions. The DAG structure of ACT
({{dag-structure}}) is a natural fit for this model: each LangGraph
node that performs an observable action corresponds to exactly one
ACT task identifier (`tid`), and directed edges in the LangGraph
correspond to `pred` (predecessor) references in successor ACTs.

ACT is applicable at the node boundary: when a LangGraph node
dispatches a sub-agent or invokes a tool with side effects, it SHOULD
issue a Phase 1 ACT Mandate encoding the node's permitted actions
before any external call is made. Upon transition out of the node,
a Phase 2 ACT Record SHOULD be produced and attached to the
LangGraph state object alongside the node's output. Downstream nodes
that fan-in from multiple predecessors MAY retrieve the set of parent
ACT identifiers from the shared state to populate their `pred` array,
thereby expressing LangGraph's fan-in semantics within the ACT DAG
without any additional infrastructure.

In contrast to LangGraph's built-in state audit trail, which is
mutable in-process memory, Phase 2 ACTs are cryptographically signed
and portable: they can be exported from a LangGraph run and
submitted to an external audit ledger, satisfying compliance
requirements that cannot be met by in-process logging alone.

### Google Agent2Agent (A2A) Protocol

The Agent2Agent protocol {{A2A-SPEC}} defines a task-oriented JSON-RPC
interface for inter-agent communication, with authentication
delegated to OAuth 2.0 or API key schemes declared in each agent's
Agent Card. A2A provides no mechanism for a receiving agent to
verify the authorization provenance of a task request beyond the
transport-layer credential, and produces no token that represents
the execution of the task in a verifiable, portable form.

ACT is applicable as a session-layer accountability complement to
A2A: a client agent SHOULD include a Phase 1 ACT Mandate in the
`metadata` field of the A2A Task object, encoding the task type as
a capability constraint and the delegating agent's identity as the
ACT issuer. The receiving agent SHOULD validate the Mandate before
beginning task execution and SHOULD return a Phase 2 ACT Record
as an artifact in the A2A TaskResult, enabling the client agent to
retain cryptographic proof of what was executed on its behalf.

This integration does not require modification to A2A's transport or
authentication scheme; ACT and A2A's OAuth credentials operate at
independent layers and are not redundant. A2A's credential answers
"is this client permitted to contact this server?"; the ACT Mandate
answers "is this agent permitted to request this specific task
under these constraints?".

### Enterprise Orchestration Without WIMSE (CrewAI, AutoGen)

Enterprise orchestration frameworks such as CrewAI {{CREWAI}} and
AutoGen {{AUTOGEN}} deploy multi-agent systems within a single
organizational boundary, typically without SPIFFE/SPIRE workload
identity infrastructure. In these environments, OAuth Authorization
Servers are often unavailable or impractical to deploy for
intra-process agent communication.

ACT is applicable in this context via its Tier 1 (pre-shared key)
trust model ({{tier1}}): each agent role in a CrewAI Crew or
AutoGen ConversableAgent graph is assigned an Ed25519 keypair at
instantiation time. The orchestrating agent issues Phase 1 Mandates
to worker agents before delegating tasks, constraining each worker
to only the tools and actions relevant to its role. Worker agents
produce Phase 2 Records on task completion. The resulting ACT chain
is exportable as a structured audit trail that satisfies the
per-action logging requirements of DORA {{DORA}} and EU AI Act
Article 12 {{EUAIA}} without requiring shared infrastructure beyond
the ability to exchange public keys at deployment time.

Implementations SHOULD NOT use ACT's self-assertion mode (where an
agent issues and records its own mandate without external sign-off)
in regulated workflows; at minimum, the orchestrating agent MUST
sign the initial Mandate so that accountability is anchored to a
principal outside the executing agent.

### Relationship to WIMSE ECT

Where WIMSE infrastructure is deployed, ACT and the WIMSE Execution
Context Token {{I-D.nennemann-wimse-ect}} serve complementary and
non-overlapping functions. The ECT records workload-level execution
in WIMSE terms — which SPIFFE workload executed, in which trust
domain, against which service. ACT records the authorization
provenance — which agent was permitted to request which action,
under what capability constraints, by whose authority — and
transitions that authorization record into an execution record upon
task completion.

In mixed environments, both tokens SHOULD be carried simultaneously:
the `Workload-Identity` header carries the WIMSE ECT; the
`ACT-Record` header carries the ACT. Verifiers MAY correlate the
two by matching the ACT `tid` claim against application-layer
identifiers present in the ECT's task context. Neither token is a
profile or extension of the other; they operate at different
abstraction layers and their co-presence is additive.

# Conventions and Definitions

{::boilerplate bcp14-tagged}

**Agent**: An autonomous software entity that executes tasks, issues
ACTs as mandates for sub-agents, and produces ACTs as execution
records of its own actions.

**Authorization Mandate**: An ACT in Phase 1, encoding what an agent
is permitted to do, under what constraints, and by whose authority.

**Execution Record**: An ACT in Phase 2, encoding what an agent
actually did, including cryptographic hashes of inputs and outputs
and causal links to predecessor tasks.

**Directed Acyclic Graph (DAG)**: A graph structure representing task
dependency ordering where edges are directed and no cycles exist. Used
by ACT to model causal relationships between tasks in a workflow.

**Delegation Chain**: A cryptographically verifiable sequence of ACT
issuances from a root authority through one or more agents, each
signing a new ACT that reduces privileges relative to the one it
received.

**Trust Tier**: A level of key management infrastructure used to
establish the public key of an ACT issuer. Tiers range from
pre-shared keys (Tier 1, mandatory) to PKI (Tier 2) and DIDs
(Tier 3).

**Workflow**: A set of related tasks, identified by a shared `wid`
claim, forming a single logical unit of work.

# ACT Lifecycle

An ACT has a two-phase lifecycle. The same token format is used in
both phases; the presence or absence of execution claims determines
which phase a token represents.

A token is a **Phase 2 Execution Record** if and only if the claim
`exec_act` is present. A token that does not contain `exec_act` is
a **Phase 1 Authorization Mandate**. Verifiers MUST determine the
phase before applying verification rules, and MUST reject a token
that is presented in the wrong phase for the operation being
performed.

## Phase 1: Authorization Mandate

In Phase 1, an ACT is created by a delegating agent (or a human
operator) to authorize a target agent to perform a specific task. The
token carries:

- The identity of the issuing agent and the target agent.
- The capabilities granted, with associated constraints.
- Human oversight requirements for high-impact actions.
- The delegation provenance (who authorized the issuer to delegate).
- A task identifier and declared purpose.

The Phase 1 ACT is signed by the issuing agent using its private key.
The target agent receives the ACT and uses it as a bearer mandate —
evidence that it is authorized to proceed.

Phase 1 ACTs are short-lived. Implementations SHOULD set expiration
(`exp`) to no more than 15 minutes after issuance (`iat`) for
automated agent-to-agent workflows. Longer lifetimes MAY be used for
human-initiated mandates where the agent may not act immediately.

## Phase 2: Execution Record

Upon completing the authorized task, the executing agent MUST
transition the ACT to Phase 2 by:

1. Adding the `exec_act` claim describing the action performed.
2. Optionally adding `inp_hash` and/or `out_hash` SHA-256 hashes
   of task inputs and outputs (RECOMMENDED for regulated environments).
3. Adding the `pred` array referencing predecessor task identifiers (DAG
   dependencies).
4. Adding `exec_ts` and `status` claims.
5. Re-signing the complete token with its own private key.

The re-signing is critical: it produces a new signature over the
combined authorization + execution claims, binding the executing
agent's cryptographic identity to both the mandate it received and
the execution it performed. This creates a single, non-repudiable
record that answers both "was this agent authorized?" and "what
did it do?"

Note on issuer signature preservation: re-signing replaces the
Phase 1 signature produced by the issuing agent (`iss`). The
integrity of the original mandate is preserved through the
`del.chain` mechanism: the chain entry's `sig` field is the `iss`
agent's signature over the Phase 1 ACT, and this signature remains
intact and verifiable in the Phase 2 token. For root mandates where
`del.chain` is empty, the issuer's signature is not independently
preserved in Phase 2. Deployments requiring independent
verifiability of the original mandate SHOULD retain the Phase 1
ACT separately alongside the Phase 2 record.

The resulting Phase 2 ACT SHOULD be submitted to an audit ledger
({{audit-ledger}}) and MAY be sent to the next agent in the workflow
as evidence of completed prerequisites.

## Lifecycle State Machine

~~~
  [Issuer creates Phase 1 ACT]
           |
           | sign(issuer_key)
           v
    +------------------+
    |  MANDATE         |  Phase 1: Authorization Mandate
    |  (unsigned by    |  Carried as bearer token by target agent
    |   target agent)  |
    +------------------+
           |
           | Target agent executes task
           | adds exec_act, inp_hash, out_hash, pred
           | re-signs with target_agent_key
           v
    +------------------+
    |  RECORD          |  Phase 2: Execution Record
    |  (signed by      |  Submitted to ledger, passed to next agent
    |   target agent)  |
    +------------------+
           |
           | (optional) anchor to SCITT Transparency Service
           v
    +------------------+
    |  ANCHORED        |  Phase 2 + external non-equivocation
    +------------------+
~~~

# ACT Token Format

An ACT is a JSON Web Token {{RFC7519}} signed as a JSON Web Signature
{{RFC7515}} using JWS Compact Serialization. All ACTs MUST use JWS
Compact Serialization to ensure they can be carried in a single HTTP
header value.

## JOSE Header

The ACT JOSE header MUST contain:

~~~ json
{
  "alg": "ES256",
  "typ": "act+jwt",
  "kid": "agent-a-key-2026-03"
}
~~~

**alg** (REQUIRED): The digital signature algorithm. Implementations
MUST support ES256 {{RFC7518}}. EdDSA (Ed25519) {{RFC8037}} is
RECOMMENDED for new deployments due to smaller signatures and
resistance to side-channel attacks. Symmetric algorithms (HS256,
HS384, HS512) MUST NOT be used. The "alg" value MUST NOT be "none".

**typ** (REQUIRED): MUST be "act+jwt" to distinguish ACTs from other
JWT types.

**kid** (REQUIRED): An identifier for the signing key. In Tier 1
deployments (pre-shared keys), this is an opaque string agreed
out-of-band. In Tier 2 deployments (PKI), this is the X.509
certificate thumbprint. In Tier 3 deployments (DID), this is the
DID key fragment (e.g., `did:key:z6Mk...#key-1`).

**x5c** (OPTIONAL): In Tier 2 deployments, the X.509 certificate
chain MAY be included to enable verification without out-of-band
key distribution.

**did** (OPTIONAL): In Tier 3 deployments, the full DID of the
issuing agent MAY be included for resolution.

## JWT Claims: Authorization Phase

### Standard JWT Claims

**iss** (REQUIRED): The identifier of the agent issuing the mandate.
Format depends on trust tier: an opaque string (Tier 1), an X.509
Subject DN (Tier 2), or a DID (Tier 3).

**sub** (REQUIRED): The identifier of the agent authorized to act.
MUST use the same format convention as `iss`.

**aud** (REQUIRED): The intended recipient(s). MUST include the
identifier of the target agent (`sub`). When an audit ledger is
deployed, MUST also include the ledger's identifier. When multiple
recipients are present, MUST be an array. Verifiers that are audit
ledgers MUST verify that their own identifier appears in `aud`.

**iat** (REQUIRED): Issuance time as a NumericDate {{RFC7519}}.

**exp** (REQUIRED): Expiration time. Implementations SHOULD set to
no more than 15 minutes after `iat` for automated workflows.

**jti** (REQUIRED): A UUID {{RFC9562}} uniquely identifying this ACT
and, in Phase 2, the task it records. Used as the task identifier
for DAG predecessor references in `pred`.

### ACT Authorization Claims

**wid** (OPTIONAL): A UUID identifying the workflow to which this
task belongs. When present, groups related ACTs and scopes `jti`
uniqueness to the workflow.

**task** (REQUIRED): An object describing the authorized task:

~~~ json
{
  "task": {
    "purpose": "validate_patient_dosage",
    "data_sensitivity": "restricted",
    "created_by": "operator:clinical-admin-01",
    "expires_at": 1772064750
  }
}
~~~

- `purpose` (REQUIRED): A string describing the intended task.
  Implementations SHOULD use a controlled vocabulary or reverse-
  domain notation (e.g., "com.example.validate_dosage") to enable
  semantic consistency checking by the receiving agent.
- `data_sensitivity` (OPTIONAL): One of "public", "internal",
  "confidential", "restricted". Receiving agents MUST NOT perform
  actions that would expose data above this classification.
- `created_by` (OPTIONAL): An identifier for the human or system
  that initiated the workflow. SHOULD be pseudonymous (see
  {{privacy-considerations}}).
- `expires_at` (OPTIONAL): A NumericDate after which the task
  mandate is no longer valid, independent of `exp`.

**cap** (REQUIRED): An array of capability objects, each specifying
an action the agent is authorized to perform and the constraints
under which it may do so:

~~~ json
{
  "cap": [
    {
      "action": "read.patient_record",
      "constraints": {
        "patient_id_scope": "current_task_only",
        "max_records": 1,
        "data_classification_max": "restricted"
      }
    },
    {
      "action": "write.dosage_recommendation",
      "constraints": {
        "status": "draft_only"
      }
    }
  ]
}
~~~

Action names MUST conform to the ABNF grammar:

~~~
action-name = component *( "." component )
component   = ALPHA *( ALPHA / DIGIT / "-" / "_" )
~~~

Receiving agents MUST perform exact string matching on action names.
Wildcard matching is NOT part of this specification.

When multiple capabilities match the same action, OR semantics
apply: if ANY capability grants the action, the request is
authorized subject to that capability's constraints. When multiple
constraints exist within a single capability, AND semantics apply:
ALL constraints MUST be satisfied. When the same constraint key
appears in both a capability-level and a policy-level context, the
more restrictive value applies: lower numeric limits, narrower
allow-lists (intersection), broader block-lists (union), and
narrower time windows.

**oversight** (OPTIONAL): Human oversight requirements:

~~~ json
{
  "oversight": {
    "requires_approval_for": ["write.publish", "execute.payment"],
    "approval_ref": "https://approval.example.com/workflow/w-123"
  }
}
~~~

When `requires_approval_for` lists an action, the receiving agent
MUST NOT execute that action autonomously. The approval mechanism
is out of scope for this specification.

**del** (OPTIONAL): Delegation provenance, establishing the chain
of authority from the root mandate to this ACT. If `del` is absent,
the ACT MUST be treated as a root mandate with `depth` = 0 and
further delegation is not permitted (i.e., the receiving agent MUST
NOT issue sub-mandates based on this ACT).

~~~ json
{
  "del": {
    "depth": 1,
    "max_depth": 3,
    "chain": [
      {
        "delegator": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
        "jti": "550e8400-e29b-41d4-a716-446655440000",
        "sig": "base64url-encoded-signature-of-parent-act-hash"
      }
    ]
  }
}
~~~

- `depth`: The current delegation depth. 0 means this is a root
  mandate issued by a human or root authority.
- `max_depth`: The maximum permitted delegation depth. Receiving
  agents MUST NOT issue sub-mandates that would exceed this depth.
- `chain`: An array of delegation provenance records ordered from
  root to immediate parent (`chain[0]` is the root authority,
  `chain[depth-1]` is the direct parent of this ACT). Each entry
  contains:
  - `delegator`: The identifier of the agent that authorized this
    delegation step (i.e., the `iss` of the parent ACT at that
    depth).
  - `jti`: The `jti` of the parent ACT that authorized this
    delegation step.
  - `sig`: The delegating agent's signature over the SHA-256 hash
    of that parent ACT, providing cryptographic linkage without
    requiring the full parent ACT to be transmitted.

The `sig` field in each chain entry is the critical departure from
AAP's delegation model: rather than requiring a central AS to
validate the chain, any verifier holding the delegating agent's
public key can independently verify each step by recomputing the
hash and checking the signature.

## JWT Claims: Execution Phase

The following claims are added by the executing agent when
transitioning to Phase 2. Their presence distinguishes an Execution
Record from an Authorization Mandate.

**exec_act** (REQUIRED in Phase 2): A string identifying the action
actually performed. MUST conform to the same ABNF grammar as
capability action names. MUST match one of the `action` values in
the `cap` array of the Phase 1 claims.

**pred** (REQUIRED in Phase 2): An array of `jti` values of predecessor
tasks in the DAG. An empty array indicates a root task. Each value
MUST be the `jti` of a previously verified ACT (Phase 2) within
the same workflow (same `wid`) or the global ACT store if `wid` is
absent.

**inp_hash** (OPTIONAL): The base64url encoding (without padding) of
the SHA-256 hash of the task's input data, computed over the raw
octets of the serialized input. Provides cryptographic evidence of
what data the agent processed.

**out_hash** (OPTIONAL): The base64url encoding (without padding) of
the SHA-256 hash of the task's output data, using the same format
as `inp_hash`. Provides cryptographic evidence of what data the
agent produced.

**exec_ts** (REQUIRED in Phase 2): A NumericDate recording the
actual time of task execution. MAY differ from `iat` when the agent
queued the mandate before execution. MUST be greater than or equal
to `iat`. SHOULD be less than or equal to `exp`; execution after
mandate expiry is possible when tasks are long-running and MUST NOT
cause automatic rejection, but implementors SHOULD log a warning.

**status** (REQUIRED in Phase 2): One of "completed", "failed",
"partial". Allows audit systems to distinguish successful execution
from partial or failed attempts, which is essential for regulated
environments where failed attempts must be recorded.

**err** (OPTIONAL, present when `status` is "failed" or "partial"):
An object providing error context:

~~~ json
{
  "err": {
    "code": "constraint_violation",
    "detail": "data_classification_max exceeded"
  }
}
~~~

Error detail SHOULD NOT reveal internal system state beyond what is
necessary for audit purposes.

## Complete Examples

### Example: Phase 1 — Authorization Mandate

~~~
{
  "alg": "ES256",
  "typ": "act+jwt",
  "kid": "agent-clinical-key-2026-03"
}
.
{
  "iss": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
  "sub": "did:key:z6MknGc3omCyas4b1GmEn4xySHgLuSHxrKrUBnrhJekxZHFz",
  "aud": [
    "did:key:z6MknGc3omCyas4b1GmEn4xySHgLuSHxrKrUBnrhJekxZHFz",
    "https://ledger.hospital.example.com"
  ],
  "iat": 1772064000,
  "exp": 1772064900,
  "jti": "550e8400-e29b-41d4-a716-446655440001",

  "wid": "a0b1c2d3-e4f5-6789-abcd-ef0123456789",

  "task": {
    "purpose": "validate_treatment_recommendation",
    "data_sensitivity": "restricted",
    "created_by": "operator:clinical-admin-01"
  },

  "cap": [
    {
      "action": "read.patient_record",
      "constraints": {
        "patient_id_scope": "current_task_only",
        "max_records": 1
      }
    },
    {
      "action": "write.safety_assessment",
      "constraints": {
        "status": "draft_only"
      }
    }
  ],

  "oversight": {
    "requires_approval_for": ["write.publish_assessment"]
  },

  "del": {
    "depth": 0,
    "max_depth": 2,
    "chain": []
  }
}
~~~

### Example: Phase 2 — Execution Record (same token, re-signed by target agent)

~~~
{
  "alg": "EdDSA",
  "typ": "act+jwt",
  "kid": "agent-safety-key-2026-03"
}
.
{
  "iss": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
  "sub": "did:key:z6MknGc3omCyas4b1GmEn4xySHgLuSHxrKrUBnrhJekxZHFz",
  "aud": [
    "did:key:z6MknGc3omCyas4b1GmEn4xySHgLuSHxrKrUBnrhJekxZHFz",
    "https://ledger.hospital.example.com"
  ],
  "iat": 1772064000,
  "exp": 1772064900,
  "jti": "550e8400-e29b-41d4-a716-446655440001",

  "wid": "a0b1c2d3-e4f5-6789-abcd-ef0123456789",

  "task": {
    "purpose": "validate_treatment_recommendation",
    "data_sensitivity": "restricted",
    "created_by": "operator:clinical-admin-01"
  },

  "cap": [
    {
      "action": "read.patient_record",
      "constraints": {
        "patient_id_scope": "current_task_only",
        "max_records": 1
      }
    },
    {
      "action": "write.safety_assessment",
      "constraints": {
        "status": "draft_only"
      }
    }
  ],

  "oversight": {
    "requires_approval_for": ["write.publish_assessment"]
  },

  "del": {
    "depth": 0,
    "max_depth": 2,
    "chain": []
  },

  "exec_act": "write.safety_assessment",
  "pred": ["550e8400-e29b-41d4-a716-446655440000"],
  "inp_hash": "n4bQgYhMfWWaL-qgxVrQFaO_TxsrC4Is0V1sFbDwCgg",
  "out_hash": "LCa0a2j_xo_5m0U8HTBBNBNCLXBkg7-g-YpeiGJm564",
  "exec_ts": 1772064300,
  "status": "completed"
}
~~~

# Trust Model

ACT defines four trust tiers. Tier 1 is mandatory-to-implement; all
others are optional upgrades. An ACT verifier MUST be able to process
ACTs from any tier it has configured. The trust tier in use is
determined by the `kid` format and the presence of `x5c` or `did`
header parameters.

## Tier 0: Bootstrap (TOFU — Trust On First Use)

Tier 0 is NOT part of the normative trust model and MUST NOT be used
in regulated environments. It is defined here for documentation
purposes only, to describe the common bootstrapping scenario.

In Tier 0, the first ACT received from an agent establishes its
public key. This is equivalent to SSH TOFU behavior: an attacker
who intercepts the first message can substitute their own key. Tier 0
deployments MUST transition to Tier 1 or higher before exchanging
ACTs that carry sensitive capabilities.

## Tier 1: Pre-Shared Keys (Mandatory-to-Implement) {#tier1}

In Tier 1, both parties exchange public keys out-of-band prior to
the first ACT exchange. The `kid` is an opaque string agreed during
the key exchange. Implementations MUST support Tier 1.

Key exchange MAY occur via any out-of-band mechanism: manual
configuration, a configuration management system, or a prior
authenticated channel. This specification does not mandate a
specific key exchange protocol.

Tier 1 public keys MUST be Ed25519 {{RFC8037}} or P-256 (ES256)
{{RFC7518}} keys. RSA keys SHOULD NOT be used in Tier 1 deployments due to
key size. Key rotation MUST be performed out-of-band using the same
mechanism as the initial exchange.

## Tier 2: PKI / X.509

In Tier 2, agent identity is bound to an X.509 certificate issued
by a mutually trusted Certificate Authority (CA). The `kid` is the
certificate thumbprint (SHA-256 of the DER-encoded certificate).

Cross-organizational ACT exchange in Tier 2 requires either:

(a) a mutually trusted root CA, or
(b) cross-certification between the organizations' CAs, or
(c) explicit trust anchoring (one organization's CA is added to
    the other's trust store).

The `x5c` JOSE header parameter {{RFC7515}} MAY carry the full
certificate chain to enable verification without out-of-band trust
store configuration.

## Tier 3: Decentralized Identifiers (DID)

In Tier 3, agent identity is expressed as a DID {{W3C-DID}}. The
`kid` is a DID key fragment. The `did` JOSE header parameter carries
the full DID for resolution.

Implementations SHOULD support at minimum `did:key` {{DID-KEY}} for
self-contained key distribution without external resolution, and
`did:web` {{DID-WEB}} for organizations that prefer DNS-anchored
identity.

DID resolution latency introduces a dependency on external
infrastructure. To preserve the zero-infrastructure baseline,
implementations using Tier 3 MAY cache DID Documents and MUST
specify a maximum cache TTL in their configuration.

## Cross-Tier Interoperability

A delegation chain MAY include agents operating at different trust
tiers. Each step in the chain is verified using the trust tier of
the signing agent at that step. Verifiers MUST NOT reject a chain
solely because it mixes trust tiers, but MAY apply stricter
policy for chains that include Tier 0 or Tier 1 steps when
exchanging sensitive capabilities.

# Delegation Chain

ACT delegation is peer-to-peer: no Authorization Server is involved.
Delegation is expressed as a cryptographically verifiable chain of
ACT issuances, where each step reduces privileges relative to the
previous step.

## Peer-to-Peer Delegation

When Agent A authorizes Agent B to perform a sub-task, Agent A:

1. Creates a new ACT with `sub` set to Agent B's identifier.
2. Sets `cap` to a subset of A's own authorized capabilities,
   with constraints at least as restrictive as those in A's mandate.
3. Sets `del.depth` to A's own `del.depth + 1`.
4. Sets `del.max_depth` to no more than the `del.max_depth` value
   in A's own mandate.
5. Adds a chain entry containing A's identifier as `delegator`,
   the `jti` of A's own mandate, and a `sig` value computed as:

   ~~~
   sig = Sign(A.private_key, SHA-256(canonical_ACT_phase1_bytes))
   ~~~

   where `canonical_ACT_phase1_bytes` is the UTF-8 encoded bytes
   of the JWS Compact Serialization of A's Phase 1 ACT.

6. Signs the new ACT with A's private key.

## Privilege Reduction Requirements

When issuing a delegated ACT, the issuing agent MUST reduce
privileges by one or more of:

- Removing capabilities (sub-set of parent capabilities only).
- Adding stricter constraints (lower rate limits, narrower domains,
  shorter time windows, lower data classification ceiling).
- Reducing token lifetime (`exp` closer to `iat`).
- Reducing `del.max_depth`.

The issuing agent MUST NOT grant capabilities not present in its
own mandate. Capability escalation via delegation is prohibited and
MUST be detected and rejected by verifiers.

For well-known numeric constraints (e.g., `max_records`,
`max_requests_per_hour`), "more restrictive" means a numerically
lower or equal value. For well-known enumerated constraints
(e.g., `data_sensitivity`), "more restrictive" means a value that
is equal or higher in the defined ordering
("public" < "internal" < "confidential" < "restricted").
For unknown or domain-specific constraint keys, verifiers MUST
treat the constraint as non-comparable and MUST reject the
delegation unless the delegated constraint value is byte-for-byte
identical to the parent constraint value.

## Delegation Verification

A verifier receiving a delegated ACT MUST:

1. Verify the ACT's own signature ({{auth-verification}}).
2. For each entry in `del.chain`, in order from index 0 to
   `del.depth - 1`:
   a. Retrieve the public key for `entry.delegator`.
   b. Verify that `entry.sig` is a valid signature over the SHA-256
      hash of the referenced parent ACT (identified by `entry.jti`).
   c. Verify that the capabilities in the current ACT are a subset
      of the capabilities in the parent ACT, per the constraint
      comparison rules in {{privilege-reduction-requirements}}.
3. Verify that `del.depth` does not exceed `del.max_depth`.
4. Verify that `del.chain` length equals `del.depth`.

If any step fails, the ACT MUST be rejected.

# DAG Structure and Causal Ordering {#dag-structure}

ACTs in Phase 2 form a DAG over the `pred` (predecessor) claim. The DAG
encodes causal dependencies: a task MAY NOT begin before all its
parent tasks are completed.

## DAG Validation {#dag-validation}

When processing a Phase 2 ACT, implementations MUST:

1. **Uniqueness**: Verify the `jti` is unique within the workflow
   (`wid`) or globally if `wid` is absent.

2. **Predecessor Existence**: Verify every `jti` in `pred` corresponds to
   a Phase 2 ACT available in the ACT store or audit ledger.

3. **Temporal Ordering**: Verify that for each parent:
   `parent.exec_ts < child.exec_ts + clock_skew_tolerance`
   (RECOMMENDED tolerance: 30 seconds). Causal ordering is
   primarily enforced by DAG structure, not timestamps.

4. **Acyclicity**: Following parent references MUST NOT lead back
   to the current ACT's `jti`. Implementations MUST enforce a
   maximum ancestor traversal limit (RECOMMENDED: 10,000 nodes).

5. **Capability Consistency**: Verify that `exec_act` matches one
   of the `action` values in the `cap` array from Phase 1.

## Root Tasks and Fan-in

A root task has `pred = []`. A workflow MAY have multiple root tasks
representing parallel branches with no shared predecessor.

Fan-in — a task with multiple parents — is expressed naturally:

~~~ json
{
  "pred": [
    "550e8400-e29b-41d4-a716-446655440001",
    "550e8400-e29b-41d4-a716-446655440002"
  ]
}
~~~

This indicates the current task depends on the completion of both
referenced parent tasks, which MAY have been executed in parallel
by different agents.

## DAG vs Linear Delegation Chains {#dag-vs-linear}

Several concurrent proposals for agent authorization model delegation
as an ordered, linear chain of tokens or principals. Examples include
the `actchain` claim of
{{I-D.oauth-transaction-tokens-for-agents}}, the Agentic JWT
construction of {{AgenticJWT}}, the AIP / Interaction-Bound Context Token
(IBCT) model of {{AIP-IBCT}}, and the delegation record defined in
{{I-D.helixar-hdp-agentic-delegation}}. In each of these
designs, the trail from the originator to the final executor is
represented as an ordered array recording one predecessor per hop.

### What Linear Chains Express Well

Linear chains are a natural fit for simple sequential delegation:
agent A delegates to agent B, which delegates to agent C. The chain
records the history of that single hand-off in order, and verifiers
can walk from the current holder back to the originator without
branching. For interactive user-to-agent-to-service flows, where each
step has exactly one predecessor, a linear chain is both sufficient
and compact.

### Limitations of Linear Chains

Agentic workflows in practice are rarely purely linear. Planner
agents dispatch parallel sub-tasks; synthesizer agents consume
results from multiple independent branches; tool calls execute
concurrently and their outputs are merged. A linear chain cannot
faithfully represent the following common topologies:

- **Fork**: A single task spawns multiple independent sub-tasks. A
  linear chain cannot express that two concurrent sub-executions
  share a common parent authorization but are otherwise
  independent; each sub-task would either omit its siblings or
  fabricate a false ordering between them.

- **Join (fan-in)**: A task whose output depends on results from
  several predecessors has no single prior hop. Linear chains
  cannot express multiple-parent relationships without either
  collapsing parallel branches into an arbitrary order or
  duplicating records.

- **Diamond dependencies**: A planner dispatches parallel work and
  later synthesizes the results. The synthesis step depends on
  every branch, and all branches depend on the same planner.
  This diamond shape requires a DAG; a linear chain forces the
  verifier to pick one branch and discard the others.

- **Cross-chain references**: When two independently authorized
  chains produce outputs that are later combined (e.g., a shared
  cache lookup and a fresh retrieval), linear chains force a
  single history and cannot record that the combined result has
  two distinct provenances.

### ACT's DAG Approach

As specified in {{jwt-claims-execution-phase}}, the `pred` claim is
an array of parent `jti` values rather than a single scalar. This
allows an ACT to record:

- Zero parents (a root task, `pred = []`);
- Exactly one parent (a linear chain, equivalent to the
  single-predecessor designs referenced above);
- Multiple parents (fan-in from parallel branches); and
- Any acyclic shape that matches the actual execution structure.

The following example illustrates a diamond workflow. A research
agent (A) dispatches a web-search agent (B) and a code-analysis
agent (C) in parallel; both complete, and their outputs are
combined by a writer agent (D):

~~~
        +-----+
        |  A  |   pred = []
        +-----+
         /   \
        v     v
     +---+   +---+
     | B |   | C |   pred = [A.jti]
     +---+   +---+
        \   /
         v v
        +-----+
        |  D  |   pred = [B.jti, C.jti]
        +-----+
~~~

A linear `actchain` representation cannot express that D depends on
both B and C. At best, it can record one of the two parents and lose
the other, or serialize B and C into a false sequential order.

### Verifiability Implications

With a DAG representation, an auditor holding the set of Phase 2
ACTs for a workflow can reconstruct the full execution graph, not
just one chain per final record. This matters for:

- **Debugging**: identifying which branch contributed an erroneous
  input to a downstream synthesis.
- **Compliance**: demonstrating that every input to a regulated
  decision was itself authorized, not only the most recent hop.
- **Tamper-evidence**: detecting that a branch has been omitted,
  since the surviving siblings' `pred` arrays name the missing
  predecessor by `jti`.

### Interoperability with Linear-Chain Designs

ACT's DAG reduces to a linear chain in the degenerate case where
every `pred` array has length zero or one. An implementation that
requires linear-chain semantics MAY treat such ACTs as equivalent
to `actchain`-style records and ignore the fork/join capability.
The reverse reduction is not available: a linear-chain-only design
cannot represent ACT DAG topologies without loss of information.

ACT therefore takes the linear chain as a strict subset of its
model rather than as a competing approach. The DAG generalization
is deliberate and is motivated by the concurrent, branching nature
of real agentic executions rather than by any deficiency in the
linear-chain designs for the sequential cases they target.

# Verification Procedure

## Authorization Phase Verification {#auth-verification}

A receiving agent MUST verify a Phase 1 ACT as follows:

1. Parse JWS Compact Serialization per {{RFC7515}}.
2. Verify `typ` is "act+jwt".
3. Verify `alg` is in the verifier's algorithm allowlist. The
   allowlist MUST NOT include "none" or any symmetric algorithm.
4. Retrieve the public key for `kid` per the applicable trust tier
   ({{trust-model}}).
5. Verify the JWS signature.
6. Verify `exp` has not passed (with clock skew tolerance:
   RECOMMENDED maximum 5 minutes).
7. Verify `iat` is not unreasonably in the future (RECOMMENDED:
   no more than 30 seconds ahead).
8. Verify `aud` contains the verifier's own identifier.
9. Verify `iss` is a trusted agent identity per local policy.
10. Verify `sub` matches the verifier's own identifier (the agent
    is the intended recipient of this mandate).
11. Verify all required claims are present and well-formed.
12. Verify delegation chain ({{delegation-verification}}) if
    `del.chain` is non-empty.
13. Verify capabilities are within policy limits.

## Execution Phase Verification

In addition to all Phase 1 verification steps, a verifier processing
a Phase 2 ACT MUST:

1. Verify `exec_act` is present and matches an `action` in `cap`.
2. Verify `pred` is present and perform DAG validation
   ({{dag-validation}}).
3. Verify `exec_ts` is present and is greater than or equal to
   `iat`. If `exec_ts` is after `exp`, implementations SHOULD log
   a warning but MUST NOT reject the record solely on this basis.
4. Verify `status` is present and has a valid value.
5. Verify the re-signature was produced by the `sub` agent (the
   executing agent), not the `iss` agent (the mandating agent).
   This is verified by checking that the `kid` in the Phase 2 JOSE
   header corresponds to the `sub` agent's public key.
6. If `inp_hash` or `out_hash` are present, verify them against
   locally available input/output data when possible.

# Transport

## HTTP Header Transport {#http-header-transport}

This specification defines two HTTP {{RFC9110}} header fields for ACT
transport:

**ACT-Mandate**: Carries a Phase 1 ACT issued by an upstream agent
or operator. Value is the JWS Compact Serialization of the ACT.

~~~
GET /api/safety-check HTTP/1.1
Host: safety-agent.example.com
ACT-Mandate: eyJhbGci...Phase1ACT...
~~~

**ACT-Record**: Carries a Phase 2 ACT from a predecessor agent,
serving as evidence of completed prerequisites.

~~~
POST /api/downstream HTTP/1.1
Host: downstream-agent.example.com
ACT-Mandate: eyJhbGci...Phase1ACT...
ACT-Record: eyJhbGci...Phase2ACT...
~~~

Multiple `ACT-Record` header lines MAY be included when a task has
multiple completed predecessors (DAG fan-in). If any single ACT-Record
fails verification, the receiver MUST reject the entire request.

## Non-HTTP Transports

For non-HTTP transports (MCP stdio, A2A message queues, AMQP, etc.),
ACTs SHOULD be carried as a dedicated field in the transport's
metadata envelope. The field name SHOULD be `act_mandate` for Phase 1
ACTs and `act_record` for Phase 2 ACTs. Implementations MUST use the
JWS Compact Serialization form in all transports.

# Audit Ledger Interface {#audit-ledger}

Phase 2 ACTs SHOULD be submitted to an immutable audit ledger.
A ledger is RECOMMENDED for regulated environments but is not
required for basic ACT operation. This specification does not
mandate a specific storage technology.

When an audit ledger is deployed, the implementation MUST provide:

1. **Append-only semantics**: Once an ACT is recorded, it MUST NOT
   be modified or deleted.

2. **Ordering**: A monotonically increasing sequence number per
   recorded ACT.

3. **Lookup**: Efficient retrieval by `jti` value.

4. **Integrity**: A cryptographic commitment scheme over recorded
   ACTs (e.g., hash-chaining, Merkle tree anchoring, or SCITT
   registration per {{I-D.ietf-scitt-architecture}}).

# Security Considerations {#security-considerations}

## Threat Model

ACT assumes an adversarial environment where:

- Individual agents may be compromised.
- Network paths may be intercepted (mitigated by transport security).
- Attackers may attempt to replay valid ACTs from prior interactions.
- Colluding agents may attempt to fabricate execution records.
- Agents may attempt privilege escalation via manipulated delegation
  chains.

ACT does NOT assume:

- A trusted central authority (by design).
- Synchronized clocks beyond the stated skew tolerance.
- Availability of external network services during verification.

## Self-Assertion Limitation

Phase 2 ACTs are self-asserted: an executing agent signs its own
execution record. A compromised agent with an intact private key can
produce Phase 2 ACTs claiming arbitrary inputs, outputs, and action
types, as long as the claimed `exec_act` matches an authorized
capability.

This is a fundamental limitation of self-sovereign attestation. It
is the same limitation affecting WIMSE ECT
{{I-D.nennemann-wimse-ect}}.

Mitigations:

- **Cross-agent corroboration**: A receiving agent that processes
  an ACT-Record as a prerequisite independently verifies that the
  claimed `out_hash` matches the data it actually received.
- **Ledger sequencing**: An append-only ledger with monotonic
  sequence numbers prevents retroactive insertion of fabricated
  records.
- **SCITT anchoring**: For high-assurance deployments, Phase 2 ACTs
  SHOULD be anchored to a SCITT Transparency Service, providing
  external witness that the record was submitted at a claimed time.

## Key Compromise

If an agent's private key is compromised, an attacker can issue
arbitrary Phase 1 mandates (impersonating the agent as an issuer)
and fabricate Phase 2 records (impersonating the agent as an
executor).

Key compromise response:

1. The compromised agent's identifier MUST be added to all
   verifiers' deny lists.
2. In Tier 2 (PKI) deployments, the certificate MUST be revoked
   via CRL or OCSP.
3. In Tier 3 (DID) deployments, the DID Document MUST be updated
   to revoke the compromised key.
4. In Tier 1 (pre-shared key) deployments, both parties MUST
   perform an out-of-band key rotation.

ACT chains that include records signed by a compromised key MUST
be treated as potentially tainted from the point of compromise.
Audit systems MUST flag all ACTs signed after the estimated
compromise time.

## Replay Attack Prevention

`jti` uniqueness within the applicable scope (workflow or global)
provides replay detection. Verifiers MUST reject ACTs whose `jti`
has already been seen and processed.

`exp` provides a time-bounded replay window. Verifiers MUST reject
expired ACTs. The combination of `jti` and `exp` means that replay
detection state only needs to be maintained for the duration of token
lifetimes.

## Equivocation {#equivocation}

In standalone deployment (no audit ledger, no SCITT anchoring), ACT
does NOT provide non-equivocation guarantees. A compromised agent
can maintain two valid ACT chains — presenting Phase 2 records with
different `out_hash` values to different verifiers — and both will
pass independent verification.

**Deployments claiming DORA {{DORA}} Article 10/11 compliance or EU AI
Act {{EUAIA}} Article 12 compliance MUST use one of:**

(a) A shared append-only audit ledger visible to all relevant parties,
    with cryptographic integrity (hash chaining or Merkle trees).

(b) SCITT anchoring {{I-D.ietf-scitt-architecture}} providing external
    Transparency Service receipts.

Standalone ACT provides tamper detection (a verifier can detect
modification of a record it has seen) but not split-view prevention
(a verifier cannot detect a different record shown to another
verifier).

## Privilege Escalation

Verifiers MUST check that each step in `del.chain` reduces or
maintains (never increases) the capabilities relative to the
preceding step. Implementations MUST reject ACTs where:

- `del.depth` exceeds `del.max_depth`.
- `cap` contains actions not present in any referenced parent ACT.
- Constraints in `cap` are less restrictive than those in the parent.

## Denial of Service

ACT verification is more computationally expensive than standard JWT
validation due to delegation chain verification and DAG traversal.

Mitigations:

- Reject ACTs larger than 64KB before parsing.
- Enforce maximum `del.chain` length (RECOMMENDED: 10 entries).
- Enforce maximum DAG ancestor traversal depth (RECOMMENDED: 10,000
  nodes, {{dag-validation}}).
- Cache verification results for recently seen `jti` values within
  the token lifetime window.

# Privacy Considerations {#privacy-considerations}

ACT tokens and audit ledger records may contain information that
identifies agents, organizations, or individuals. Implementations
SHOULD apply data minimization principles:

- `task.created_by` SHOULD use a pseudonymous identifier rather
  than a personal email address or real name.
- `task.purpose` SHOULD use a controlled vocabulary code rather
  than free-text descriptions that may contain personal data.
- `del.chain` entries reveal organizational structure. Cross-
  organizational delegation chains SHOULD use Tier 3 (DID)
  identifiers that do not reveal organizational affiliation.
- `inp_hash` and `out_hash` are hashes of data, not the data
  itself, and do not constitute personal data under GDPR
  Article 4(1) provided the underlying data is not trivially
  reversible (e.g., hashes of very short strings).

For GDPR Article 17 (right to erasure) compliance, audit ledgers
SHOULD store only ACT tokens (which contain hashes, not raw data)
and SHOULD implement crypto-shredding for any associated encrypted
payloads.

# IANA Considerations

## Media Type Registration

This document requests registration of the following media type:

- Type name: application
- Subtype name: act+jwt
- Required parameters: none
- Encoding considerations: binary (base64url-encoded JWT)
- Security considerations: See {{security-considerations}}.
- Interoperability considerations: See {{auth-verification}}.
- Specification: This document.

## HTTP Header Field Registration

This document requests registration of the following HTTP header
fields in the "Hypertext Transfer Protocol (HTTP) Field Name
Registry":

- Header field name: ACT-Mandate
- Applicable protocol: HTTP
- Status: permanent
- Specification: This document, {{http-header-transport}}.

- Header field name: ACT-Record
- Applicable protocol: HTTP
- Status: permanent
- Specification: This document, {{http-header-transport}}.

## JWT Claims Registration

This document requests registration of the following claims in the
IANA "JSON Web Token Claims" registry:

| Claim Name | Description                        | Reference       |
|------------|------------------------------------|-----------------|
| wid        | Workflow identifier                | This document   |
| task       | Task authorization context         | This document   |
| cap        | Capabilities with constraints      | This document   |
| oversight  | Human oversight requirements       | This document   |
| del        | Delegation provenance chain        | This document   |
| exec_act   | Executed action identifier         | This document   |
| pred       | Predecessor task identifiers (DAG) | This document   |
| inp_hash   | SHA-256 hash of task input         | This document   |
| out_hash   | SHA-256 hash of task output        | This document   |
| exec_ts    | Actual execution timestamp         | This document   |
| status     | Execution status                   | This document   |
| err        | Execution error context            | This document   |

--- back

# Appendix A: Complete JSON Schema
{:numbered="false"}

The following JSON Schema (2020-12) describes the claim set of ACT
Phase 1 (Authorization Mandate) and Phase 2 (Execution Record)
tokens. A token is a Phase 2 record if and only if it contains the
`exec_act`, `exec_ts`, `pred`, and `status` claims; otherwise it is
a Phase 1 mandate. The schema validates structure only; semantic
rules (capability subset on delegation, `exec_act` membership in
`cap`, DAG acyclicity, depth bounds) are specified normatively in
the body of this document and cannot be expressed in JSON Schema.

~~~ json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "urn:ietf:params:act:schema:token",
  "title": "Agent Context Token (ACT)",
  "type": "object",
  "oneOf": [
    { "$ref": "#/$defs/phase1" },
    { "$ref": "#/$defs/phase2" }
  ],
  "$defs": {
    "actionName": {
      "type": "string",
      "pattern": "^[A-Za-z][A-Za-z0-9_-]*(\\.[A-Za-z][A-Za-z0-9_-]*)*$"
    },
    "numericDate": { "type": "integer", "minimum": 0 },
    "uuid": {
      "type": "string",
      "format": "uuid"
    },
    "audience": {
      "oneOf": [
        { "type": "string" },
        {
          "type": "array",
          "items": { "type": "string" },
          "minItems": 1
        }
      ]
    },
    "task": {
      "type": "object",
      "required": ["purpose"],
      "properties": {
        "purpose": { "type": "string", "minLength": 1 },
        "data_sensitivity": {
          "type": "string",
          "enum": [
            "public", "internal", "confidential", "restricted"
          ]
        },
        "created_by": { "type": "string" },
        "expires_at": { "$ref": "#/$defs/numericDate" }
      },
      "additionalProperties": true
    },
    "capability": {
      "type": "object",
      "required": ["action"],
      "properties": {
        "action": { "$ref": "#/$defs/actionName" },
        "constraints": { "type": "object" }
      },
      "additionalProperties": false
    },
    "oversight": {
      "type": "object",
      "properties": {
        "requires_approval_for": {
          "type": "array",
          "items": { "$ref": "#/$defs/actionName" }
        },
        "approval_ref": { "type": "string" }
      },
      "additionalProperties": false
    },
    "delegationEntry": {
      "type": "object",
      "required": ["delegator", "jti", "sig"],
      "properties": {
        "delegator": { "type": "string" },
        "jti": { "$ref": "#/$defs/uuid" },
        "sig": { "type": "string" }
      },
      "additionalProperties": false
    },
    "delegation": {
      "type": "object",
      "required": ["depth", "max_depth", "chain"],
      "properties": {
        "depth": { "type": "integer", "minimum": 0 },
        "max_depth": { "type": "integer", "minimum": 0 },
        "chain": {
          "type": "array",
          "items": { "$ref": "#/$defs/delegationEntry" }
        }
      },
      "additionalProperties": false
    },
    "error": {
      "type": "object",
      "required": ["code"],
      "properties": {
        "code": { "type": "string" },
        "detail": { "type": "string" }
      },
      "additionalProperties": false
    },
    "commonClaims": {
      "type": "object",
      "required": [
        "iss", "sub", "aud", "iat", "exp", "jti", "task", "cap"
      ],
      "properties": {
        "iss": { "type": "string" },
        "sub": { "type": "string" },
        "aud": { "$ref": "#/$defs/audience" },
        "iat": { "$ref": "#/$defs/numericDate" },
        "exp": { "$ref": "#/$defs/numericDate" },
        "jti": { "$ref": "#/$defs/uuid" },
        "wid": { "$ref": "#/$defs/uuid" },
        "task": { "$ref": "#/$defs/task" },
        "cap": {
          "type": "array",
          "minItems": 1,
          "items": { "$ref": "#/$defs/capability" }
        },
        "oversight": { "$ref": "#/$defs/oversight" },
        "del": { "$ref": "#/$defs/delegation" }
      }
    },
    "phase1": {
      "allOf": [{ "$ref": "#/$defs/commonClaims" }],
      "not": {
        "anyOf": [
          { "required": ["exec_act"] },
          { "required": ["exec_ts"] },
          { "required": ["status"] }
        ]
      }
    },
    "phase2": {
      "allOf": [{ "$ref": "#/$defs/commonClaims" }],
      "required": ["exec_act", "pred", "exec_ts", "status"],
      "properties": {
        "exec_act": { "$ref": "#/$defs/actionName" },
        "pred": {
          "type": "array",
          "items": { "$ref": "#/$defs/uuid" }
        },
        "inp_hash": { "type": "string" },
        "out_hash": { "type": "string" },
        "exec_ts": { "$ref": "#/$defs/numericDate" },
        "status": {
          "type": "string",
          "enum": ["completed", "failed", "partial"]
        },
        "err": { "$ref": "#/$defs/error" }
      }
    }
  }
}
~~~

# Appendix B: Test Vectors
{:numbered="false"}

All vectors in this appendix are produced by the reference implementation using fixed Ed25519 seeds and fixed identifiers, and are therefore reproducible. The base timestamp (iat) is 1772064000 (2026-02-26T00:00:00Z). Long Base64url values are wrapped for display only; line breaks are not part of the value.

## B.0. Key Material
{:numbered="false"}

Three Ed25519 key pairs are used, identified by kid. Keys are given in JWK {{RFC7517}} form using the Ed25519 OKP encoding {{RFC8037}}; "d" is the private seed and "x" is the public key, both Base64url-encoded. These seeds are recognizable test patterns and MUST NOT be used in production.

~~~ json
{
  "kty": "OKP",
  "crv": "Ed25519",
  "kid": "iss-key",
  "d": "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8",
  "x": "A6EHv_POEL4dcN0Y50vAmWfk1jCbpQ1fHdyGZBJVMbg"
}
~~~

~~~ json
{
  "kty": "OKP",
  "crv": "Ed25519",
  "kid": "sub-key",
  "d": "ICEiIyQlJicoKSorLC0uLzAxMjM0NTY3ODk6Ozw9Pj8",
  "x": "Kay64UG8yvCyLhqU000LxzYeUm0L_hLIl5S8kyKWbdc"
}
~~~

~~~ json
{
  "kty": "OKP",
  "crv": "Ed25519",
  "kid": "agent-c-key",
  "d": "QEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaW1xdXl8",
  "x": "JUO5L_EJVRFHatyDadtt3JM2ZaEZeN2hQE7hBmypVZ0"
}
~~~

## B.1. Valid Phase 1 ACT: root mandate, Tier 1 (Ed25519), no delegation
{:numbered="false"}

JOSE Header:

~~~ json
{
  "alg": "EdDSA",
  "typ": "act+jwt",
  "kid": "iss-key"
}
~~~

JWT Claims:

~~~ json
{
  "iss": "agent-issuer",
  "sub": "agent-subject",
  "aud": [
    "agent-subject",
    "https://ledger.example.com"
  ],
  "iat": 1772064000,
  "exp": 1772064900,
  "jti": "550e8400-e29b-41d4-a716-446655440001",
  "task": {
    "purpose": "validate_data",
    "data_sensitivity": "restricted"
  },
  "cap": [
    {
      "action": "read.data",
      "constraints": {
        "max_records": 10
      }
    },
    {
      "action": "write.result"
    }
  ],
  "wid": "a0b1c2d3-e4f5-6789-abcd-ef0123456789",
  "del": {
    "depth": 0,
    "max_depth": 2,
    "chain": []
  }
}
~~~

JWS Compact Serialization:

~~~
eyJhbGciOiJFZERTQSIsInR5cCI6ImFjdCtqd3QiLCJraWQiOiJpc3Mta2V5In0.
eyJpc3MiOiJhZ2VudC1pc3N1ZXIiLCJzdWIiOiJhZ2VudC1zdWJqZWN0IiwiYXVk
IjpbImFnZW50LXN1YmplY3QiLCJodHRwczovL2xlZGdlci5leGFtcGxlLmNvbSJd
LCJpYXQiOjE3NzIwNjQwMDAsImV4cCI6MTc3MjA2NDkwMCwianRpIjoiNTUwZTg0
MDAtZTI5Yi00MWQ0LWE3MTYtNDQ2NjU1NDQwMDAxIiwidGFzayI6eyJwdXJwb3Nl
IjoidmFsaWRhdGVfZGF0YSIsImRhdGFfc2Vuc2l0aXZpdHkiOiJyZXN0cmljdGVk
In0sImNhcCI6W3siYWN0aW9uIjoicmVhZC5kYXRhIiwiY29uc3RyYWludHMiOnsi
bWF4X3JlY29yZHMiOjEwfX0seyJhY3Rpb24iOiJ3cml0ZS5yZXN1bHQifV0sIndp
ZCI6ImEwYjFjMmQzLWU0ZjUtNjc4OS1hYmNkLWVmMDEyMzQ1Njc4OSIsImRlbCI6
eyJkZXB0aCI6MCwibWF4X2RlcHRoIjoyLCJjaGFpbiI6W119fQ._nVgyp4X0zYvy
ylbyBgE_CWKAVdEMFgHjHIUkhxEsr83PQw_rzxNryDd179_KdkCo_kxWDbfmEQV-
JA1-pknBA
~~~

Expected result: Accept. The signature verifies under kid "iss-key" and all required Phase 1 claims are present.

## B.2. Valid Phase 2 ACT: completed execution (transition from B.1)
{:numbered="false"}

JOSE Header:

~~~ json
{
  "alg": "EdDSA",
  "typ": "act+jwt",
  "kid": "sub-key"
}
~~~

JWT Claims:

~~~ json
{
  "iss": "agent-issuer",
  "sub": "agent-subject",
  "aud": [
    "agent-subject",
    "https://ledger.example.com"
  ],
  "iat": 1772064000,
  "exp": 1772064900,
  "jti": "550e8400-e29b-41d4-a716-446655440001",
  "task": {
    "purpose": "validate_data",
    "data_sensitivity": "restricted"
  },
  "cap": [
    {
      "action": "read.data",
      "constraints": {
        "max_records": 10
      }
    },
    {
      "action": "write.result"
    }
  ],
  "exec_act": "read.data",
  "pred": [],
  "exec_ts": 1772064300,
  "status": "completed",
  "wid": "a0b1c2d3-e4f5-6789-abcd-ef0123456789",
  "del": {
    "depth": 0,
    "max_depth": 2,
    "chain": []
  },
  "inp_hash": "DHooOQxm8XoCuW4a2BOMC2ZP8VyzFYj1gRvKzRdfX_g",
  "out_hash": "n24FIk7teYuF6yXfVBqJ9THKIWuU9C0M5qlGlhLtx2U"
}
~~~

JWS Compact Serialization:

~~~
eyJhbGciOiJFZERTQSIsInR5cCI6ImFjdCtqd3QiLCJraWQiOiJzdWIta2V5In0.
eyJpc3MiOiJhZ2VudC1pc3N1ZXIiLCJzdWIiOiJhZ2VudC1zdWJqZWN0IiwiYXVk
IjpbImFnZW50LXN1YmplY3QiLCJodHRwczovL2xlZGdlci5leGFtcGxlLmNvbSJd
LCJpYXQiOjE3NzIwNjQwMDAsImV4cCI6MTc3MjA2NDkwMCwianRpIjoiNTUwZTg0
MDAtZTI5Yi00MWQ0LWE3MTYtNDQ2NjU1NDQwMDAxIiwidGFzayI6eyJwdXJwb3Nl
IjoidmFsaWRhdGVfZGF0YSIsImRhdGFfc2Vuc2l0aXZpdHkiOiJyZXN0cmljdGVk
In0sImNhcCI6W3siYWN0aW9uIjoicmVhZC5kYXRhIiwiY29uc3RyYWludHMiOnsi
bWF4X3JlY29yZHMiOjEwfX0seyJhY3Rpb24iOiJ3cml0ZS5yZXN1bHQifV0sImV4
ZWNfYWN0IjoicmVhZC5kYXRhIiwicHJlZCI6W10sImV4ZWNfdHMiOjE3NzIwNjQz
MDAsInN0YXR1cyI6ImNvbXBsZXRlZCIsIndpZCI6ImEwYjFjMmQzLWU0ZjUtNjc4
OS1hYmNkLWVmMDEyMzQ1Njc4OSIsImRlbCI6eyJkZXB0aCI6MCwibWF4X2RlcHRo
IjoyLCJjaGFpbiI6W119LCJpbnBfaGFzaCI6IkRIb29PUXhtOFhvQ3VXNGEyQk9N
QzJaUDhWeXpGWWoxZ1J2S3pSZGZYX2ciLCJvdXRfaGFzaCI6Im4yNEZJazd0ZVl1
RjZ5WGZWQnFKOVRIS0lXdVU5QzBNNXFsR2xoTHR4MlUifQ.ByEeEiMp2MwLHEeiF
iaV-fRjJw37VdIWahoaGdtR3EnlRMxI01jN9-TuPnUSDqIAf8yfzbri3FTjb02zX
h0LBA
~~~

Expected result: Accept. Phase 2 record; the signature verifies under kid "sub-key" and exec_act "read.data" is present in cap.

## B.3. Valid Phase 2 ACT: fan-in with two predecessor tasks
{:numbered="false"}

JOSE Header:

~~~ json
{
  "alg": "EdDSA",
  "typ": "act+jwt",
  "kid": "sub-key"
}
~~~

JWT Claims:

~~~ json
{
  "iss": "agent-issuer",
  "sub": "agent-subject",
  "aud": "agent-subject",
  "iat": 1772064000,
  "exp": 1772064900,
  "jti": "550e8400-e29b-41d4-a716-446655440005",
  "task": {
    "purpose": "merge_results"
  },
  "cap": [
    {
      "action": "compute.result"
    }
  ],
  "exec_act": "compute.result",
  "pred": [
    "550e8400-e29b-41d4-a716-446655440003",
    "550e8400-e29b-41d4-a716-446655440004"
  ],
  "exec_ts": 1772064200,
  "status": "completed",
  "wid": "a0b1c2d3-e4f5-6789-abcd-ef0123456789",
  "del": {
    "depth": 0,
    "max_depth": 1,
    "chain": []
  }
}
~~~

JWS Compact Serialization:

~~~
eyJhbGciOiJFZERTQSIsInR5cCI6ImFjdCtqd3QiLCJraWQiOiJzdWIta2V5In0.
eyJpc3MiOiJhZ2VudC1pc3N1ZXIiLCJzdWIiOiJhZ2VudC1zdWJqZWN0IiwiYXVk
IjoiYWdlbnQtc3ViamVjdCIsImlhdCI6MTc3MjA2NDAwMCwiZXhwIjoxNzcyMDY0
OTAwLCJqdGkiOiI1NTBlODQwMC1lMjliLTQxZDQtYTcxNi00NDY2NTU0NDAwMDUi
LCJ0YXNrIjp7InB1cnBvc2UiOiJtZXJnZV9yZXN1bHRzIn0sImNhcCI6W3siYWN0
aW9uIjoiY29tcHV0ZS5yZXN1bHQifV0sImV4ZWNfYWN0IjoiY29tcHV0ZS5yZXN1
bHQiLCJwcmVkIjpbIjU1MGU4NDAwLWUyOWItNDFkNC1hNzE2LTQ0NjY1NTQ0MDAw
MyIsIjU1MGU4NDAwLWUyOWItNDFkNC1hNzE2LTQ0NjY1NTQ0MDAwNCJdLCJleGVj
X3RzIjoxNzcyMDY0MjAwLCJzdGF0dXMiOiJjb21wbGV0ZWQiLCJ3aWQiOiJhMGIx
YzJkMy1lNGY1LTY3ODktYWJjZC1lZjAxMjM0NTY3ODkiLCJkZWwiOnsiZGVwdGgi
OjAsIm1heF9kZXB0aCI6MSwiY2hhaW4iOltdfX0.Koikd5apdJ1LLfdl0n0-arQ4
nDWbrygFImdE8QMKtWFwE35bIjIlr8ILXcr-0jDOBt0IJa8b_0yZROkUPlEgCA
~~~

Expected result: Accept. Phase 2 fan-in; pred lists the two predecessor jti values from the parallel branches.

## B.4. Valid Phase 1 ACT: delegated mandate (depth 1)
{:numbered="false"}

JOSE Header:

~~~ json
{
  "alg": "EdDSA",
  "typ": "act+jwt",
  "kid": "iss-key"
}
~~~

JWT Claims:

~~~ json
{
  "iss": "agent-issuer",
  "sub": "agent-c",
  "aud": "agent-c",
  "iat": 1772064010,
  "exp": 1772064600,
  "jti": "550e8400-e29b-41d4-a716-446655440006",
  "task": {
    "purpose": "delegated_read"
  },
  "cap": [
    {
      "action": "read.data",
      "constraints": {
        "max_records": 5
      }
    }
  ],
  "wid": "a0b1c2d3-e4f5-6789-abcd-ef0123456789",
  "del": {
    "depth": 1,
    "max_depth": 2,
    "chain": [
      {
        "delegator": "agent-issuer",
        "jti": "550e8400-e29b-41d4-a716-446655440001",
        "sig": "cYYbPuSiYxFV1D45qBKkzUo2GZMjkVkN4IEnJt4433CN8MJ6sAEVy4Huqr4ebuF7fCKpvGijOidrOkfY-BJFCQ"
      }
    ]
  }
}
~~~

JWS Compact Serialization:

~~~
eyJhbGciOiJFZERTQSIsInR5cCI6ImFjdCtqd3QiLCJraWQiOiJpc3Mta2V5In0.
eyJpc3MiOiJhZ2VudC1pc3N1ZXIiLCJzdWIiOiJhZ2VudC1jIiwiYXVkIjoiYWdl
bnQtYyIsImlhdCI6MTc3MjA2NDAxMCwiZXhwIjoxNzcyMDY0NjAwLCJqdGkiOiI1
NTBlODQwMC1lMjliLTQxZDQtYTcxNi00NDY2NTU0NDAwMDYiLCJ0YXNrIjp7InB1
cnBvc2UiOiJkZWxlZ2F0ZWRfcmVhZCJ9LCJjYXAiOlt7ImFjdGlvbiI6InJlYWQu
ZGF0YSIsImNvbnN0cmFpbnRzIjp7Im1heF9yZWNvcmRzIjo1fX1dLCJ3aWQiOiJh
MGIxYzJkMy1lNGY1LTY3ODktYWJjZC1lZjAxMjM0NTY3ODkiLCJkZWwiOnsiZGVw
dGgiOjEsIm1heF9kZXB0aCI6MiwiY2hhaW4iOlt7ImRlbGVnYXRvciI6ImFnZW50
LWlzc3VlciIsImp0aSI6IjU1MGU4NDAwLWUyOWItNDFkNC1hNzE2LTQ0NjY1NTQ0
MDAwMSIsInNpZyI6ImNZWWJQdVNpWXhGVjFENDVxQktrelVvMkdaTWprVmtONElF
bkp0NDQzM0NOOE1KNnNBRVZ5NEh1cXI0ZWJ1RjdmQ0twdkdpak9pZHJPa2ZZLUJK
RkNRIn1dfX0.k4SGmgQrAdUN_f-LsJVAL5A3Q-2NHllGaSx1WOmdbcRzvW2uOfct
1K0e_zxnd_7UHvLClTE01kKiQ3zU1imtCQ
~~~

Expected result: Accept. Delegated mandate at depth 1; the delegated cap (max_records=5) is a subset of the parent cap (max_records=10).

## B.5. Valid Phase 2 ACT: delegated execution record
{:numbered="false"}

JOSE Header:

~~~ json
{
  "alg": "EdDSA",
  "typ": "act+jwt",
  "kid": "agent-c-key"
}
~~~

JWT Claims:

~~~ json
{
  "iss": "agent-issuer",
  "sub": "agent-c",
  "aud": "agent-c",
  "iat": 1772064010,
  "exp": 1772064600,
  "jti": "550e8400-e29b-41d4-a716-446655440006",
  "task": {
    "purpose": "delegated_read"
  },
  "cap": [
    {
      "action": "read.data",
      "constraints": {
        "max_records": 5
      }
    }
  ],
  "exec_act": "read.data",
  "pred": [],
  "exec_ts": 1772064350,
  "status": "completed",
  "wid": "a0b1c2d3-e4f5-6789-abcd-ef0123456789",
  "del": {
    "depth": 1,
    "max_depth": 2,
    "chain": [
      {
        "delegator": "agent-issuer",
        "jti": "550e8400-e29b-41d4-a716-446655440001",
        "sig": "cYYbPuSiYxFV1D45qBKkzUo2GZMjkVkN4IEnJt4433CN8MJ6sAEVy4Huqr4ebuF7fCKpvGijOidrOkfY-BJFCQ"
      }
    ]
  }
}
~~~

JWS Compact Serialization:

~~~
eyJhbGciOiJFZERTQSIsInR5cCI6ImFjdCtqd3QiLCJraWQiOiJhZ2VudC1jLWtl
eSJ9.eyJpc3MiOiJhZ2VudC1pc3N1ZXIiLCJzdWIiOiJhZ2VudC1jIiwiYXVkIjo
iYWdlbnQtYyIsImlhdCI6MTc3MjA2NDAxMCwiZXhwIjoxNzcyMDY0NjAwLCJqdGk
iOiI1NTBlODQwMC1lMjliLTQxZDQtYTcxNi00NDY2NTU0NDAwMDYiLCJ0YXNrIjp
7InB1cnBvc2UiOiJkZWxlZ2F0ZWRfcmVhZCJ9LCJjYXAiOlt7ImFjdGlvbiI6InJ
lYWQuZGF0YSIsImNvbnN0cmFpbnRzIjp7Im1heF9yZWNvcmRzIjo1fX1dLCJleGV
jX2FjdCI6InJlYWQuZGF0YSIsInByZWQiOltdLCJleGVjX3RzIjoxNzcyMDY0MzU
wLCJzdGF0dXMiOiJjb21wbGV0ZWQiLCJ3aWQiOiJhMGIxYzJkMy1lNGY1LTY3ODk
tYWJjZC1lZjAxMjM0NTY3ODkiLCJkZWwiOnsiZGVwdGgiOjEsIm1heF9kZXB0aCI
6MiwiY2hhaW4iOlt7ImRlbGVnYXRvciI6ImFnZW50LWlzc3VlciIsImp0aSI6IjU
1MGU4NDAwLWUyOWItNDFkNC1hNzE2LTQ0NjY1NTQ0MDAwMSIsInNpZyI6ImNZWWJ
QdVNpWXhGVjFENDVxQktrelVvMkdaTWprVmtONElFbkp0NDQzM0NOOE1KNnNBRVZ
5NEh1cXI0ZWJ1RjdmQ0twdkdpak9pZHJPa2ZZLUJKRkNRIn1dfX0.dCUyOsyvrRA
vM7m0WO6LBnIhijckknlbX3kVGxmkKlzekQmKTAJQbM5H2WbACOh3LpB9LW3hgCE
luEjhd4laAw
~~~

Expected result: Accept. Phase 2 record for the delegated mandate; the signature verifies under kid "agent-c-key".

## B.6. Invalid: delegation depth exceeds maximum
{:numbered="false"}

JOSE Header:

~~~ json
{
  "alg": "EdDSA",
  "typ": "act+jwt",
  "kid": "iss-key"
}
~~~

JWT Claims:

~~~ json
{
  "iss": "agent-issuer",
  "sub": "agent-subject",
  "aud": "agent-subject",
  "iat": 1772064000,
  "exp": 1772064900,
  "jti": "550e8400-e29b-41d4-a716-446655440008",
  "task": {
    "purpose": "bad_depth"
  },
  "cap": [
    {
      "action": "read.data"
    }
  ],
  "del": {
    "depth": 3,
    "max_depth": 2,
    "chain": [
      {
        "delegator": "a",
        "jti": "j1",
        "sig": "sig1"
      },
      {
        "delegator": "b",
        "jti": "j2",
        "sig": "sig2"
      },
      {
        "delegator": "c",
        "jti": "j3",
        "sig": "sig3"
      }
    ]
  }
}
~~~

JWS Compact Serialization:

~~~
eyJhbGciOiJFZERTQSIsInR5cCI6ImFjdCtqd3QiLCJraWQiOiJpc3Mta2V5In0.
eyJpc3MiOiJhZ2VudC1pc3N1ZXIiLCJzdWIiOiJhZ2VudC1zdWJqZWN0IiwiYXVk
IjoiYWdlbnQtc3ViamVjdCIsImlhdCI6MTc3MjA2NDAwMCwiZXhwIjoxNzcyMDY0
OTAwLCJqdGkiOiI1NTBlODQwMC1lMjliLTQxZDQtYTcxNi00NDY2NTU0NDAwMDgi
LCJ0YXNrIjp7InB1cnBvc2UiOiJiYWRfZGVwdGgifSwiY2FwIjpbeyJhY3Rpb24i
OiJyZWFkLmRhdGEifV0sImRlbCI6eyJkZXB0aCI6MywibWF4X2RlcHRoIjoyLCJj
aGFpbiI6W3siZGVsZWdhdG9yIjoiYSIsImp0aSI6ImoxIiwic2lnIjoic2lnMSJ9
LHsiZGVsZWdhdG9yIjoiYiIsImp0aSI6ImoyIiwic2lnIjoic2lnMiJ9LHsiZGVs
ZWdhdG9yIjoiYyIsImp0aSI6ImozIiwic2lnIjoic2lnMyJ9XX19.bhZ7ZaXG6GN
q4vqquQPcxImFlu50rV8Ae16D06N_X9bRCu9pCiMcY2LTK9XRYWcKlRjUyBRVNJX
02bFXJ5wGAA
~~~

Expected result: Reject. del.depth (3) exceeds del.max_depth (2).

## B.7. Invalid: capability escalation in delegated mandate
{:numbered="false"}

Expected result: Reject. The delegated cap (max_records=100) is not a subset of the parent cap (max_records=10); privilege reduction is violated. This vector is a delegation-creation check; no token is issued.

## B.8. Invalid: exec_act not present in cap
{:numbered="false"}

JOSE Header:

~~~ json
{
  "alg": "EdDSA",
  "typ": "act+jwt",
  "kid": "sub-key"
}
~~~

JWT Claims:

~~~ json
{
  "iss": "agent-issuer",
  "sub": "agent-subject",
  "aud": "agent-subject",
  "iat": 1772064000,
  "exp": 1772064900,
  "jti": "550e8400-e29b-41d4-a716-446655440009",
  "task": {
    "purpose": "bad_exec"
  },
  "cap": [
    {
      "action": "read.data"
    }
  ],
  "exec_act": "delete.everything",
  "pred": [],
  "exec_ts": 1772064100,
  "status": "completed"
}
~~~

JWS Compact Serialization:

~~~
eyJhbGciOiJFZERTQSIsInR5cCI6ImFjdCtqd3QiLCJraWQiOiJzdWIta2V5In0.
eyJpc3MiOiJhZ2VudC1pc3N1ZXIiLCJzdWIiOiJhZ2VudC1zdWJqZWN0IiwiYXVk
IjoiYWdlbnQtc3ViamVjdCIsImlhdCI6MTc3MjA2NDAwMCwiZXhwIjoxNzcyMDY0
OTAwLCJqdGkiOiI1NTBlODQwMC1lMjliLTQxZDQtYTcxNi00NDY2NTU0NDAwMDki
LCJ0YXNrIjp7InB1cnBvc2UiOiJiYWRfZXhlYyJ9LCJjYXAiOlt7ImFjdGlvbiI6
InJlYWQuZGF0YSJ9XSwiZXhlY19hY3QiOiJkZWxldGUuZXZlcnl0aGluZyIsInBy
ZWQiOltdLCJleGVjX3RzIjoxNzcyMDY0MTAwLCJzdGF0dXMiOiJjb21wbGV0ZWQi
fQ.AI-K0abyWHyAUTxIUS22kqhmSdgfOVyLMHaOm6vauEd3YpoRQ8vdzJ2Oblk07
qyXwR5pAlDewYOwCJTSmIK0Cg
~~~

Expected result: Reject. exec_act "delete.everything" does not match any cap.action.

## B.9. Invalid: DAG cycle (pred references own jti)
{:numbered="false"}

JOSE Header:

~~~ json
{
  "alg": "EdDSA",
  "typ": "act+jwt",
  "kid": "sub-key"
}
~~~

JWT Claims:

~~~ json
{
  "iss": "agent-issuer",
  "sub": "agent-subject",
  "aud": "agent-subject",
  "iat": 1772064000,
  "exp": 1772064900,
  "jti": "550e8400-e29b-41d4-a716-44665544000a",
  "task": {
    "purpose": "cycle_test"
  },
  "cap": [
    {
      "action": "read.data"
    }
  ],
  "exec_act": "read.data",
  "pred": [
    "550e8400-e29b-41d4-a716-44665544000a"
  ],
  "exec_ts": 1772064100,
  "status": "completed"
}
~~~

JWS Compact Serialization:

~~~
eyJhbGciOiJFZERTQSIsInR5cCI6ImFjdCtqd3QiLCJraWQiOiJzdWIta2V5In0.
eyJpc3MiOiJhZ2VudC1pc3N1ZXIiLCJzdWIiOiJhZ2VudC1zdWJqZWN0IiwiYXVk
IjoiYWdlbnQtc3ViamVjdCIsImlhdCI6MTc3MjA2NDAwMCwiZXhwIjoxNzcyMDY0
OTAwLCJqdGkiOiI1NTBlODQwMC1lMjliLTQxZDQtYTcxNi00NDY2NTU0NDAwMGEi
LCJ0YXNrIjp7InB1cnBvc2UiOiJjeWNsZV90ZXN0In0sImNhcCI6W3siYWN0aW9u
IjoicmVhZC5kYXRhIn1dLCJleGVjX2FjdCI6InJlYWQuZGF0YSIsInByZWQiOlsi
NTUwZTg0MDAtZTI5Yi00MWQ0LWE3MTYtNDQ2NjU1NDQwMDBhIl0sImV4ZWNfdHMi
OjE3NzIwNjQxMDAsInN0YXR1cyI6ImNvbXBsZXRlZCJ9.9V4Pd5QoKBXLnBCAJne
PR_QArbIdLAEYFkM0az0ATr_A8BqjQdCQm-zDxExszLBnSdVclHo1Ns25FSpzrDj
VDg
~~~

Expected result: Reject. pred references the record's own jti, forming a cycle.

## B.10. Invalid: missing predecessor task in DAG
{:numbered="false"}

JOSE Header:

~~~ json
{
  "alg": "EdDSA",
  "typ": "act+jwt",
  "kid": "sub-key"
}
~~~

JWT Claims:

~~~ json
{
  "iss": "agent-issuer",
  "sub": "agent-subject",
  "aud": "agent-subject",
  "iat": 1772064000,
  "exp": 1772064900,
  "jti": "550e8400-e29b-41d4-a716-44665544000b",
  "task": {
    "purpose": "missing_parent"
  },
  "cap": [
    {
      "action": "read.data"
    }
  ],
  "exec_act": "read.data",
  "pred": [
    "nonexistent-parent-jti"
  ],
  "exec_ts": 1772064100,
  "status": "completed"
}
~~~

JWS Compact Serialization:

~~~
eyJhbGciOiJFZERTQSIsInR5cCI6ImFjdCtqd3QiLCJraWQiOiJzdWIta2V5In0.
eyJpc3MiOiJhZ2VudC1pc3N1ZXIiLCJzdWIiOiJhZ2VudC1zdWJqZWN0IiwiYXVk
IjoiYWdlbnQtc3ViamVjdCIsImlhdCI6MTc3MjA2NDAwMCwiZXhwIjoxNzcyMDY0
OTAwLCJqdGkiOiI1NTBlODQwMC1lMjliLTQxZDQtYTcxNi00NDY2NTU0NDAwMGIi
LCJ0YXNrIjp7InB1cnBvc2UiOiJtaXNzaW5nX3BhcmVudCJ9LCJjYXAiOlt7ImFj
dGlvbiI6InJlYWQuZGF0YSJ9XSwiZXhlY19hY3QiOiJyZWFkLmRhdGEiLCJwcmVk
IjpbIm5vbmV4aXN0ZW50LXBhcmVudC1qdGkiXSwiZXhlY190cyI6MTc3MjA2NDEw
MCwic3RhdHVzIjoiY29tcGxldGVkIn0.PSxajLuiL_b2cAZT2Eab5UR-nZDMs2BE
jEkhzeId8vQzYkldQb9h5xrvvRD9lsjglzda4wscpU-IoW4m8Kw4Cw
~~~

Expected result: Reject. pred references a jti that is not a previously verified task.

## B.11. Invalid: tampered payload
{:numbered="false"}

JOSE Header:

~~~ json
{
  "alg": "EdDSA",
  "typ": "act+jwt",
  "kid": "iss-key"
}
~~~

JWT Claims:

~~~ json
{
  "iss": "agent-issuer",
  "sub": "agent-subject",
  "aud": [
    "agent-subject",
    "https://ledger.example.com"
  ],
  "iat": 1772064000,
  "exp": 1772064900,
  "jti": "550e8400-e29b-41d4-a716-446655440001",
  "task": {
    "purpose": "validate_data",
    "tata_sensitivity": "restricted"
  },
  "cap": [
    {
      "action": "read.data",
      "constraints": {
        "max_records": 10
      }
    },
    {
      "action": "write.result"
    }
  ],
  "wid": "a0b1c2d3-e4f5-6789-abcd-ef0123456789",
  "del": {
    "depth": 0,
    "max_depth": 2,
    "chain": []
  }
}
~~~

JWS Compact Serialization:

~~~
eyJhbGciOiJFZERTQSIsInR5cCI6ImFjdCtqd3QiLCJraWQiOiJpc3Mta2V5In0.
eyJpc3MiOiJhZ2VudC1pc3N1ZXIiLCJzdWIiOiJhZ2VudC1zdWJqZWN0IiwiYXVk
IjpbImFnZW50LXN1YmplY3QiLCJodHRwczovL2xlZGdlci5leGFtcGxlLmNvbSJd
LCJpYXQiOjE3NzIwNjQwMDAsImV4cCI6MTc3MjA2NDkwMCwianRpIjoiNTUwZTg0
MDAtZTI5Yi00MWQ0LWE3MTYtNDQ2NjU1NDQwMDAxIiwidGFzayI6eyJwdXJwb3Nl
IjoidmFsaWRhdGVfZGF0YSIsInRhdGFfc2Vuc2l0aXZpdHkiOiJyZXN0cmljdGVk
In0sImNhcCI6W3siYWN0aW9uIjoicmVhZC5kYXRhIiwiY29uc3RyYWludHMiOnsi
bWF4X3JlY29yZHMiOjEwfX0seyJhY3Rpb24iOiJ3cml0ZS5yZXN1bHQifV0sIndp
ZCI6ImEwYjFjMmQzLWU0ZjUtNjc4OS1hYmNkLWVmMDEyMzQ1Njc4OSIsImRlbCI6
eyJkZXB0aCI6MCwibWF4X2RlcHRoIjoyLCJjaGFpbiI6W119fQ._nVgyp4X0zYvy
ylbyBgE_CWKAVdEMFgHjHIUkhxEsr83PQw_rzxNryDd179_KdkCo_kxWDbfmEQV-
JA1-pknBA
~~~

Expected result: Reject. The signature does not verify; one byte of the claims segment was altered after signing.

## B.12. Invalid: expired token
{:numbered="false"}

JOSE Header:

~~~ json
{
  "alg": "EdDSA",
  "typ": "act+jwt",
  "kid": "iss-key"
}
~~~

JWT Claims:

~~~ json
{
  "iss": "agent-issuer",
  "sub": "agent-subject",
  "aud": "agent-subject",
  "iat": 1772060400,
  "exp": 1772061300,
  "jti": "550e8400-e29b-41d4-a716-44665544000c",
  "task": {
    "purpose": "expired_test"
  },
  "cap": [
    {
      "action": "read.data"
    }
  ]
}
~~~

JWS Compact Serialization:

~~~
eyJhbGciOiJFZERTQSIsInR5cCI6ImFjdCtqd3QiLCJraWQiOiJpc3Mta2V5In0.
eyJpc3MiOiJhZ2VudC1pc3N1ZXIiLCJzdWIiOiJhZ2VudC1zdWJqZWN0IiwiYXVk
IjoiYWdlbnQtc3ViamVjdCIsImlhdCI6MTc3MjA2MDQwMCwiZXhwIjoxNzcyMDYx
MzAwLCJqdGkiOiI1NTBlODQwMC1lMjliLTQxZDQtYTcxNi00NDY2NTU0NDAwMGMi
LCJ0YXNrIjp7InB1cnBvc2UiOiJleHBpcmVkX3Rlc3QifSwiY2FwIjpbeyJhY3Rp
b24iOiJyZWFkLmRhdGEifV19.mK6-ML3hkOlAVvHeG5PPnvr3do9KT-1g4OISjlB
WrcHac-JPWnzOSZmMiESIKA6mXNSYVxnTDLZyFbAPglrCAQ
~~~

Expected result: Reject. exp is in the past (expired 45 minutes before the verification time).

## B.13. Invalid: audience mismatch
{:numbered="false"}

JOSE Header:

~~~ json
{
  "alg": "EdDSA",
  "typ": "act+jwt",
  "kid": "iss-key"
}
~~~

JWT Claims:

~~~ json
{
  "iss": "agent-issuer",
  "sub": "wrong-agent",
  "aud": "wrong-agent",
  "iat": 1772064000,
  "exp": 1772064900,
  "jti": "550e8400-e29b-41d4-a716-44665544000d",
  "task": {
    "purpose": "wrong_aud_test"
  },
  "cap": [
    {
      "action": "read.data"
    }
  ]
}
~~~

JWS Compact Serialization:

~~~
eyJhbGciOiJFZERTQSIsInR5cCI6ImFjdCtqd3QiLCJraWQiOiJpc3Mta2V5In0.
eyJpc3MiOiJhZ2VudC1pc3N1ZXIiLCJzdWIiOiJ3cm9uZy1hZ2VudCIsImF1ZCI6
Indyb25nLWFnZW50IiwiaWF0IjoxNzcyMDY0MDAwLCJleHAiOjE3NzIwNjQ5MDAs
Imp0aSI6IjU1MGU4NDAwLWUyOWItNDFkNC1hNzE2LTQ0NjY1NTQ0MDAwZCIsInRh
c2siOnsicHVycG9zZSI6Indyb25nX2F1ZF90ZXN0In0sImNhcCI6W3siYWN0aW9u
IjoicmVhZC5kYXRhIn1dfQ.8vP0564UjixScto2vo0MX5wt5TX_dP9ilDB8aPEpl
WnOovQgQUHywL0_BqSaqgM7NrtjGnITmVoJ6RtUrjV8CA
~~~

Expected result: Reject. aud does not include the verifier identity.

## B.14. Invalid: Phase 2 record signed by wrong key
{:numbered="false"}

JOSE Header:

~~~ json
{
  "alg": "EdDSA",
  "typ": "act+jwt",
  "kid": "sub-key"
}
~~~

JWT Claims:

~~~ json
{
  "iss": "agent-issuer",
  "sub": "agent-subject",
  "aud": [
    "agent-subject",
    "https://ledger.example.com"
  ],
  "iat": 1772064000,
  "exp": 1772064900,
  "jti": "550e8400-e29b-41d4-a716-446655440001",
  "task": {
    "purpose": "validate_data",
    "data_sensitivity": "restricted"
  },
  "cap": [
    {
      "action": "read.data",
      "constraints": {
        "max_records": 10
      }
    },
    {
      "action": "write.result"
    }
  ],
  "exec_act": "read.data",
  "pred": [],
  "exec_ts": 1772064300,
  "status": "completed",
  "wid": "a0b1c2d3-e4f5-6789-abcd-ef0123456789",
  "del": {
    "depth": 0,
    "max_depth": 2,
    "chain": []
  }
}
~~~

JWS Compact Serialization:

~~~
eyJhbGciOiJFZERTQSIsInR5cCI6ImFjdCtqd3QiLCJraWQiOiJzdWIta2V5In0.
eyJpc3MiOiJhZ2VudC1pc3N1ZXIiLCJzdWIiOiJhZ2VudC1zdWJqZWN0IiwiYXVk
IjpbImFnZW50LXN1YmplY3QiLCJodHRwczovL2xlZGdlci5leGFtcGxlLmNvbSJd
LCJpYXQiOjE3NzIwNjQwMDAsImV4cCI6MTc3MjA2NDkwMCwianRpIjoiNTUwZTg0
MDAtZTI5Yi00MWQ0LWE3MTYtNDQ2NjU1NDQwMDAxIiwidGFzayI6eyJwdXJwb3Nl
IjoidmFsaWRhdGVfZGF0YSIsImRhdGFfc2Vuc2l0aXZpdHkiOiJyZXN0cmljdGVk
In0sImNhcCI6W3siYWN0aW9uIjoicmVhZC5kYXRhIiwiY29uc3RyYWludHMiOnsi
bWF4X3JlY29yZHMiOjEwfX0seyJhY3Rpb24iOiJ3cml0ZS5yZXN1bHQifV0sImV4
ZWNfYWN0IjoicmVhZC5kYXRhIiwicHJlZCI6W10sImV4ZWNfdHMiOjE3NzIwNjQz
MDAsInN0YXR1cyI6ImNvbXBsZXRlZCIsIndpZCI6ImEwYjFjMmQzLWU0ZjUtNjc4
OS1hYmNkLWVmMDEyMzQ1Njc4OSIsImRlbCI6eyJkZXB0aCI6MCwibWF4X2RlcHRo
IjoyLCJjaGFpbiI6W119fQ.rTp6oHlLzG32Ex6P08rAafhmjzRdwQTjwgU1Cod1a
kFy3x5KpaX5fkfeCiK5i0D6HAtwUYR61_HlBWCcJj5_BA
~~~

Expected result: Reject. The Phase 2 record claims kid "sub-key" but was signed with the issuer's private key; the signature does not verify.

## B.15. Invalid: unsigned token (alg "none")
{:numbered="false"}

JWS Compact Serialization:

~~~
eyJhbGciOiJub25lIiwidHlwIjoiYWN0K2p3dCIsImtpZCI6ImsifQ.eyJpc3MiO
iJhIiwic3ViIjoiYiJ9.
~~~

Expected result: Reject. The JOSE header alg is "none"; unsigned tokens MUST NOT be accepted.

# Appendix C: Deployment Scenarios
{:numbered="false"}

## C.1. Minimal Deployment (Zero Infrastructure)
{:numbered="false"}

Two organizations exchange pre-shared public keys via secure email.
Each agent signs Phase 1 mandates and Phase 2 records with its
Ed25519 key. No ledger, no external services. Suitable for
development and low-risk workflows.

Limitation: No non-equivocation ({{equivocation}}).

## C.2. Regulated Deployment with Hash-Chained Ledger
{:numbered="false"}

Phase 2 ACTs are submitted to a shared append-only ledger with
hash-chaining. Each recorded ACT extends a cryptographic chain,
providing tamper evidence for each ACT and the chain as a whole.
The ledger is shared between all regulated parties participating
in the workflow. Suitable for DORA compliance.

## C.3. High-Assurance Cross-Organizational Deployment
{:numbered="false"}

Phase 2 ACTs are anchored to a SCITT Transparency Service. SCITT
receipts are attached to the audit record as non-equivocation proofs.
DID-based agent identities (Tier 3) enable self-sovereign key
management without shared CA infrastructure.

## C.4. WIMSE Environment Integration
{:numbered="false"}

In environments where WIMSE is already deployed, ACT-Mandate and
ACT-Record headers are carried alongside the WIMSE Workload-Identity
header. The ECT and ACT serve different purposes: the ECT records
workload-level execution in WIMSE terms; the ACT records the
authorization provenance and capability constraints that governed
the action.

# Acknowledgments
{:numbered="false"}

The author thanks the IETF WIMSE, OAuth, and SCITT working groups
for foundational work on workload identity, delegated authorization,
and transparent supply chain records that informs this specification.
