---
eip: 8410
title: Portable Execution Plan Artifact
description: Verifiable references for passing execution plans from agent tools to wallets without relaying calldata
author: Moody Salem (@moodysalem)
discussions-to: https://ethereum-magicians.org/t/erc-8410-portable-execution-plan-artifact/29587
status: Draft
type: Standards Track
category: ERC
created: 2026-09-04
requires: 155, 712
---

## Abstract

This ERC defines a format for passing prepared execution plans from tools to
wallets through an AI agent. A producer stores a plan and returns an *artifact
reference* containing its location, byte length, and integrity digest. The agent
passes the reference to the wallet, which retrieves and validates the plan
directly. The plan body need not enter the agent's context or tool-call arguments.

An *execution plan* contains an ordered sequence of calls from one account on
one chain and any required execution capabilities. The same reference format
also supports requests to sign an [EIP-712](./eip-712.md) message. The wallet
checks the retrieved bytes and their source and independently authorizes signing
or execution. The format is independent of the agent framework and tool protocol.

## Motivation

An agent arranging a swap may call one tool to prepare it and another to execute
it. If the preparation tool returns calldata, the model consumes that data as
input tokens and reproduces it as output tokens in the wallet tool call. Large
or multi-step transactions increase context usage, latency, and cost. Copying
the payload through the model also introduces opportunities for omission,
truncation, or modification, even though the agent does not need to interpret
the bytes to select the operation.

A reference allows the producer and wallet to exchange the payload directly:

```text
Producer -> Agent:  summary and artifact reference
Agent    -> Wallet: artifact reference
Wallet   -> Producer: retrieve artifact
Wallet: verify source and integrity, validate, simulate, authorize, execute
```

This provides five benefits:

- **Lower token usage.** The agent reads and passes a small reference instead of
  the full payload. Calldata size does not determine the size of the agent's
  tool response or subsequent tool-call arguments.
- **Faster tool handoffs.** The model does not have to process the full payload
  or generate it again before invoking the signing tool. The wallet can fetch
  the bytes directly once it receives the reference, reducing model processing
  and generation latency for large payloads.
- **Exact payload transfer.** The wallet checks the retrieved byte length and
  digest before parsing. The model does not need to copy or reconstruct calldata.
- **Verifiable source.** When retrieving over HTTPS, the wallet verifies the
  serving origin itself and can apply policy to that origin, rather than relying
  on the agent's claim about which tool produced the calls.
- **Independent validation.** The wallet receives the complete call sequence,
  including approvals, execution, cleanup, and atomicity requirements. It can
  validate and simulate that sequence and present it for authorization without
  relying on the agent's description.

The producer can return a concise summary for the agent to compare alternatives
or explain an operation. The wallet evaluates the retrieved artifact, and the
summary does not authorize it. HTTPS establishes the serving origin, not that
its content is trustworthy; integrity establishes agreement with the reference,
not the user's consent.

These benefits apply when the agent passes a reference to an externally stored
artifact. An inline document or a `data:` URI still carries the payload through
the agent if it appears in the model's context.

This ERC specifies the reference, document formats, and validation rules.
Producer APIs, agent orchestration, and wallet authorization policies are
outside its scope.

## Specification

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD",
"SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be
interpreted as described in RFC 2119 and RFC 8174.

Machine-readable schemas are provided for
[the execution plan](../assets/eip-8410/execution-plan.schema.json),
[the typed-data signature request](../assets/eip-8410/typed-data-signature-request.schema.json),
and [the artifact reference](../assets/eip-8410/artifact-reference.schema.json).

A *producer* prepares a document. A *consumer* is the wallet that validates it
and decides whether to sign or execute it. A *relay* passes a document or
reference from the producer to the consumer.

### Quantity encoding

A *quantity* is a JSON string holding a canonical unsigned decimal integer: the
single character `0`, or an ASCII digit string with no leading zero. A quantity
MUST fit in a `uint256`.

A *byte string* is a JSON string of the form `0x` followed by an even number of
hexadecimal digits. An *address* is a byte string of exactly 20 bytes. Producers
MUST emit both in lowercase. Consumers MUST normalize both to lowercase before
computing the digest, and MAY additionally reject input that is not already
lowercase.

### Execution plan

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `schema_version` | string | yes | MUST be `"1"` for this revision |
| `chain_id` | quantity | yes | [EIP-155](./eip-155.md) chain identifier the plan executes on |
| `caip2_chain_id` | string | yes | MUST equal `eip155:` concatenated with `chain_id` |
| `sender` | address | yes | Account expected to send every step |
| `ordered_steps` | array | yes | 1 to 4096 steps, in execution order |
| `required_capabilities` | array of string | no | Behaviors the consumer must implement |
| `simulation_failure_policy` | object | no | What the producer advises on failure |
| `extensions` | object | no | Producer-defined data the consumer ignores |

A document containing a member not listed here MUST be rejected. Consumers MUST
reject unsupported `schema_version` values and inconsistent chain identifiers.

### Steps

Each entry of `ordered_steps` is an object:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `step` | integer | yes | 1-indexed position; entry *n* MUST have `step` equal to *n* |
| `kind` | string | yes | One of the step kinds below |
| `transaction` | object | yes | The call to make |
| `revert_decode` | object | no | ABI for rendering a revert from this step |

`transaction` has the following members:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `chain_id` | quantity | yes | MUST equal the plan's `chain_id` |
| `from` | address | yes | MUST equal the plan's `sender` |
| `to` | address | yes | Call destination |
| `data` | byte string | yes | Calldata |
| `value` | quantity | yes | Native token value, in wei |
| `gas` | quantity | no | Suggested gas limit |

Unknown step and transaction members MUST be rejected. A transaction without
`to` cannot be represented; the format does not directly support contract
creation transactions. Consumers MUST reject a plan containing inconsistent
chain or sender values rather than execute it partially.

`gas` is advisory. A consumer MUST NOT broadcast a producer-supplied gas limit
without its own estimation.

`kind` explains why a step is in the plan, and MUST be one of:

- `execution` — a call the user asked for.
- `approval` — grants a spending allowance a later step needs.
- `allowance_cleanup` — revokes an allowance an earlier step granted.
- `signature_dependent_execution` — spends a signature approved earlier.
- `other` — none of the above.

`kind` is a producer-supplied label for display. Consumers MUST NOT derive
authority from it or assume that the calldata implements the labeled operation.

`revert_decode`, when present, is an object with `kind` equal to `error_result`,
an `abi` array holding a JSON ABI fragment declaring at least one error, and an
OPTIONAL boolean `required` defaulting to false. `required` indicates whether
the producer considers decoding necessary to explain the failure; consumers
MAY ignore it. Consumers MUST treat the decoded result as display text only and
MUST bound the fragment they accept. The schema permits at most 128 ABI entries;
the reference implementation also limits the fragment to 65536 bytes.

### Required capabilities

`required_capabilities` lists behaviors required to execute the plan. A consumer
MUST reject a plan naming any capability it does not implement.

This revision defines one capability:

- `atomic_batch` — every step executes in a single transaction that reverts as a
  whole if any step fails.

Consumers can satisfy `atomic_batch` through an account-abstraction batch
executor or an [EIP-7702](./eip-7702.md) delegation to a batching implementation.

A multi-step plan that does not list `atomic_batch` MAY be executed as
separate transactions. A consumer doing so MUST execute steps in order, MUST NOT
begin a step until the previous one has been mined successfully, and MUST stop
at the first failure. A consumer that can execute atomically MAY do so
regardless of whether the plan asks for it.

Capability names MUST contain 1 to 64 ASCII characters in the range `0x21` to
`0x7e`. A plan MUST NOT list more than 32 capabilities. Additional capabilities
may be defined by later proposals.

### Simulation failure policy

When present, `simulation_failure_policy` MUST contain all three of
`rpc_error`, `execution_reverted`, and `simulation_setup_error`. Each is an
object with an `action` and a human-readable `instruction` of 1 to 2000
characters.

`action` MUST be one of:

- `retry_same_plan` — the failure is transient; the identical document may be
  retried.
- `reprepare_plan` — the document is stale; ask the producer for a new one.
- `user_review` — do not retry automatically; surface it to a human.

`execution_reverted` and `simulation_setup_error` MUST NOT specify
`retry_same_plan`.

The policy is advisory. Consumers MAY ignore it and MUST NOT treat it as
authorization to broadcast.

### Extensions

`extensions` is an object reserved for producer-defined data. Consumers MUST
ignore its contents and MUST bound its serialized size; 65536 bytes is
RECOMMENDED. It is excluded from the digest, so a producer MUST NOT place in it
anything that changes what executes or what a reviewer needs to see.

### Size limits

A consumer MUST enforce limits on any plan it accepts and MUST reject rather
than truncate. Plans MUST contain at most 4096 steps. Additional RECOMMENDED
limits are 8 MiB of `data` summed across steps and 16 MiB of serialized document.

### Plan digest

The *plan digest* is the keccak256 hash of the UTF-8 serialization of a canonical
JSON object built from the plan:

```json
{
  "schema_version": "1",
  "chain_id": "1",
  "sender": "0x...",
  "ordered_steps": [
    {
      "step": 1,
      "kind": "approval",
      "transaction": {
        "chain_id": "1",
        "from": "0x...",
        "to": "0x...",
        "data": "0x...",
        "value": "0"
      }
    }
  ]
}
```

The serialization MUST place members in exactly the order shown, MUST contain no
whitespace between tokens, and MUST encode addresses and `data` as lowercase byte
strings. `step` MUST be serialized as a JSON number and every other scalar as a
JSON string. Strings MUST be emitted without optional JSON escapes; `step`
MUST use decimal integer notation without a fractional or exponent part.

This member order is normative and is **not** lexicographic. A generic
canonicalizer such as RFC 8785 sorts members by name and would order the outer
object `chain_id`, `ordered_steps`, `schema_version`, `sender`, producing
different bytes and a different digest. Implementations MUST emit the order given
here rather than delegating to a general-purpose canonicalization routine.

The digest covers `schema_version`, `chain_id`, `sender`, and each step's `step`,
`kind`, and transaction `chain_id`, `from`, `to`, `data`, and `value`. It excludes
`caip2_chain_id` (derivable from `chain_id`), `gas`, `revert_decode`,
`simulation_failure_policy`, `required_capabilities`, and `extensions`.

The plan digest identifies this projection, not the complete document or a
signed transaction. Plans with different gas hints or capability requirements
can have the same digest. The artifact reference's integrity digest, defined
below, covers the complete serialized document instead.

Consumers SHOULD display the digest wherever they display a plan for approval,
SHOULD record it against the resulting approval, and SHOULD record it against the
broadcast transaction hash.

The digest does not authenticate the producer or establish authorization.

### Typed-data signature requests

A `typed_data_signature_request` is a separate document carrying one concrete
EIP-712 message and the account expected to sign it. The
[typed-data signature request specification](../assets/eip-8410/typed-data-signature-requests.md)
is normative for this document type. It defines the request and result shapes,
the request digest, and optional delivery of the signature to the producer.

Supporting signature requests is OPTIONAL. A consumer that supports execution
plans only MUST reject signature requests. A signature request MUST NOT be
encoded as an execution-plan step or placed in its ignored `extensions` field.

Producers MAY return several signature requests, receive their results, and
then return another concrete signature request or an execution plan. Each new
document requires its own validation and authorization. No execution plan is
required when the workflow ends with an offchain signature and producer relay.

### Delivery by reference

A plan or typed-data signature request MAY be supplied inline or by an
*artifact reference*. The reference identifies a document for the consumer to
retrieve and verify.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `kind` | string | yes | MUST be `"artifact_reference"` |
| `artifact_type` | string | yes | `"execution_plan"` or `"typed_data_signature_request"` |
| `url` | string | yes | `https` URL, `data:` URI, or `file` URI locating the bytes |
| `integrity` | object | conditional | Digest over the exact stored bytes |
| `bytes` | integer | conditional | Nonnegative exact byte length of the stored bytes |
| `instruction` | string | no | Human-readable note from the producer |

`integrity` and `bytes` are REQUIRED when `url` is an `https` or `file`
URL, and OPTIONAL when it is a `data:` URI. When either is supplied for a
`data:` URI it MUST still be checked. Consumers MUST ignore unrecognized
top-level members of the envelope.

`integrity` is an object with `algorithm`, which MUST be `"keccak256"` for this
revision, and `value`, `0x` followed by 64 hexadecimal digits. A consumer MUST
reject an `integrity` object containing any other member.

`artifact_type` MUST be exactly `execution_plan` or
`typed_data_signature_request`. No other artifact types are defined or accepted
by this revision. A consumer MUST reject a type it does not implement, and MUST
reject a reference whose `artifact_type` differs from the one expected at the
point of use.

In the retrieval rules below, the maximum document size and validation rules
are those of the selected type. Fetching a signature request MUST NOT use the
larger execution-plan size limit merely because the envelope is shared.

`instruction` is producer-supplied display text and MUST NOT influence
any authorization decision.

#### Retrieval

Given an envelope, a consumer MUST, in this order:

1. Reject the envelope if its serialized length exceeds the consumer's limit,
   before parsing the URL.
2. Reject the reference if `bytes` is present and exceeds the document type's
   size limit, before opening any connection or reading any file.
3. Retrieve the bytes according to the transport rules below. A consumer that
   does not implement the `file` transport MUST reject a `file` envelope.
4. Reject the result if `bytes` is present and the retrieved length differs.
5. Reject the result if `integrity` is present and the digest of the retrieved
   bytes differs.
6. Parse and validate the bytes exactly as it would a document of that type
   supplied inline.

Where the transport applies a content encoding, length and integrity checks
apply to the decoded bytes. Successful verification establishes that the bytes
match the reference, not that the document is authorized.

#### File transport

A `file` URI identifies a document on the consumer's local filesystem. Support
for this transport is OPTIONAL.

A consumer fetching a `file` URL MUST enforce all of the following and MUST
reject the reference if any condition cannot be met:

- The URI has an empty host or `localhost`, and names an absolute path.
- `integrity` and `bytes` are present; a `file` reference without them is
  rejected before any read.
- The bytes are read once, bounded by the document type's size limit during
  reading, and the length-then-digest checks above are applied before parsing.
- Missing, oversized, mismatched, and unreadable files fail indistinguishably,
  and error messages MUST NOT include any part of the file contents.
- No network access is performed for the read.

Consumers SHOULD additionally restrict `file` reads to one or more
user-configured directories and treat anything outside them as failure.

#### HTTPS transport

A consumer fetching an `https` URL MUST enforce all of the following and MUST
reject the reference if any condition cannot be met:

- The scheme is `https` on the default port.
- The URL carries no userinfo component and no fragment.
- The host resolves, and every address it resolves to is a public unicast
  address. Loopback, link-local, unique-local, and other private ranges MUST be
  refused.
- Redirects are not followed.
- No credentials, cookies, or ambient authorization are attached.
- The response body is bounded by the document type's size limit, enforced during
  reading, and the bound applies to the decoded stream where a content encoding
  is in use.
- Connection and total timeouts are enforced, and the number of concurrent
  retrievals is bounded.

Error messages MUST NOT include any part of the response body.

#### Data URI transport

A `data:` URI MUST have media type `application/json`, MAY use the `;base64`
parameter, and MUST decode to at most the document type's size limit. Consumers
MUST bound the encoded length before decoding and MUST NOT perform network
access for a `data:` reference.

For a decoded limit of *N* bytes, padded base64 requires at most
`4 × ceil(N / 3)` characters before percent-encoding. Percent-encoding can use
three characters per encoded byte. The encoded-length bound must account for
the URI prefix and the encoding used; the decoded-size limit still applies.

#### Provenance

A consumer reporting provenance MUST distinguish the HTTPS host from which it
retrieved a document from a `data:` URI, which provides no producer provenance.
A caller-supplied `file` path MUST NOT be presented as provenance beyond the
local machine. Authorization policies MAY use the observed HTTPS host, but TLS
does not establish the trustworthiness of documents hosted there.

### Relationship to `wallet_sendCalls`

A consumer or producer supporting [EIP-5792](./eip-5792.md) MAY convert a plan's
calls to `wallet_sendCalls` parameters:

| Plan | `wallet_sendCalls` |
| --- | --- |
| `chain_id` (decimal) | `chainId` (hexadecimal) |
| `sender` | `from` |
| `ordered_steps[n].transaction.to` / `data` | `calls[n].to` / `data` |
| `ordered_steps[n].transaction.value` (decimal) | `calls[n].value` (hexadecimal) |
| `atomic_batch` in `required_capabilities` | `atomicRequired: true` |
| `kind`, `revert_decode`, `simulation_failure_policy`, `extensions` | no equivalent; dropped |

When converting to a plan, `caip2_chain_id` is derived from `chainId`, the resolved
`from` account becomes `sender`, steps are numbered consecutively, and every
step's `kind` is `execution`. Transaction quantities are converted to decimal
strings, omitted `data` and `value` become `"0x"` and `"0"`, and
`atomicRequired: true` becomes `atomic_batch`. The resulting plan is subject to
the same validation as any other plan. Calls without a destination cannot be
represented.

This mapping does not preserve all metadata. In particular, changing a step's
`kind` changes the plan digest. A round trip preserves the digest only if all
digest-covered fields are preserved. Capabilities without an equivalent in the
destination format cannot be preserved by this mapping.

The mapping alone does not establish execution compatibility. EIP-5792 does not
require an atomic batch to use a single transaction, whereas `atomic_batch`
does. Non-atomic execution of a plan also requires waiting for each preceding
step to be mined successfully and stopping on failure. Consumers executing a
converted plan remain subject to these requirements.

## Rationale

### Document format

EIP-5792 specifies a wallet interface for submitting calls. This ERC defines a
document the wallet can retrieve independently of the agent's tool calls. The
agent selects a prepared operation by its reference; the producer supplies the
call sequence and the wallet performs validation and authorization. The same
format can also be used by non-agent applications.

Passing a reference keeps bulk data transfer outside model inference. This
reduces both token usage and the time spent processing and generating tool
payloads. The bytes still need to reach the wallet, but they travel through
ordinary retrieval rather than model output.

Restricting each plan to one sender and one chain allows one wallet account to
validate and execute the complete sequence. Cross-chain and multi-account
workflows can coordinate several plans outside this format.

### Quantity encoding

Decimal strings avoid the precision limits of JSON numbers in implementations
using IEEE-754 doubles. They also allow quantities to be read without converting
from hexadecimal. Addresses and calldata remain hexadecimal byte strings.
The redundant `caip2_chain_id` supports configuration that uses namespaced chain
identifiers; requiring agreement with `chain_id` prevents conflicting values.

### Digest scope

Gas is excluded because consumers estimate it against current state. This allows
gas estimation to change without changing the identity of the calls. The wallet
remains responsible for gas limits and fee policy.

The plan digest and reference integrity digest serve different purposes. The
former identifies the ordered calls and labels; the latter detects any change
to the serialized document, including fields excluded from the plan digest.

### Required capabilities

Ignoring an unsupported capability could change execution semantics. For
example, executing an approval, swap, and cleanup as separate transactions can
leave an allowance active if a later step fails. Requiring consumers to reject
unsupported capabilities prevents this implicit fallback.

### Reference envelope

A separate envelope provides a URL, document type, byte length, and integrity
digest in a transport-independent format. Keccak256 is used consistently for
plan and reference digests. Subresource Integrity alone does not define the
document types or retrieval rules needed here.

Unknown envelope members are ignored to allow additional metadata. Unknown
integrity members are rejected because they may express verification
requirements the consumer does not implement.

Redirects are prohibited to keep retrieval and provenance checks limited to the
URL supplied in the reference.

## Backwards Compatibility

This proposal introduces a new document format and no changes to the chain, to
existing interfaces, or to deployed contracts. Producers and consumers that do
not implement it are unaffected.

Consumers may support execution plans without supporting typed-data signature
requests. Existing transaction signing permissions MUST NOT implicitly
authorize typed-data signatures.

EIP-5792 conversion is described in the Specification. EIP-7702 is one way to
provide atomic execution and is not required by this format.

## Test Cases

[Execution-plan test vectors](../assets/eip-8410/digest-test-vectors.json)
cover canonical serialization and digest computation. Each case
gives a plan, the exact canonical byte string its digest is taken over, and the
resulting digest.

| Case | Digest |
| --- | --- |
| A one-step plan with empty calldata | `0x93aeec006e55dfe0f54041d53c94387e08c504d4f3b3826cd3426dbc7da38ea5` |
| A two-step plan, whose step 2 carries a gas hint the canonical form omits | `0x13c4b058741ee7bdfe5a51825c71bd205859b89436505a15adf60bd3f2281deb` |
| The same two-step plan with different `gas`, `extensions`, `required_capabilities`, and a different failure instruction | `0x13c4b058741ee7bdfe5a51825c71bd205859b89436505a15adf60bd3f2281deb` |
| The same two-step plan with the final byte of step 2 calldata changed | `0x5bf27f490fa44998b6386dfe1b0fae6df0c1b2a9604b69d83bbbbd7a6d285549` |

The third case changes only digest-excluded fields. The fourth changes calldata
and therefore has a different digest.

[Typed-data signature request vectors](../assets/eip-8410/typed-data-test-vectors.json)
cover the request digest, EIP-712 domain binding, delivery constraints, and
structural validation. The [fixture runner](../assets/eip-8410/typed-data-tests/README.md)
checks both document types and compares EIP-712 hashes from ethers and viem.

## Reference Implementation

Ekubo Wallet (`github.com/EkuboProtocol/wallet`) implements plan validation and
digest computation in `crates/ekubo-wallet-core/src/core/execution_plan.rs` and
reference retrieval in `crates/ekubo-wallet-core/src/plan_fetch.rs`. It records
the plan digest with approvals and transaction hashes and uses EIP-7702 batches
for atomic multi-step execution.

The Ekubo protocol's producer at `mcp.ekubo.org` returns execution plans and
artifact references for transfers, swaps, liquidity, and yield operations.

## Security Considerations

### Authorization

Plans and references are untrusted input. Consumers MUST validate the document
and evaluate their own authorization rules against its calls. They MUST NOT use
`kind`, `instruction`, `extensions`, or `revert_decode` output as authorization
inputs. Producer descriptions can misrepresent calldata, and a relay may modify
or replace a request.

Neither digest authenticates the producer or proves approval. Reference
verification detects changes relative to the supplied envelope; a relay able to
replace both the document and its reference can supply a different valid pair.

The plan digest excludes `required_capabilities`. Equal digests therefore do not
imply equal execution requirements or interchangeable approvals. Consumers must
evaluate those requirements independently of the digest. Gas hints are also
excluded and MUST be independently estimated before use.

### Simulation and execution

State changes between simulation and inclusion may cause a call to revert or
execute differently. Consumers SHOULD re-simulate immediately before signing
and MUST NOT treat a stored simulation result as authorization to broadcast.

An EIP-7702 delegation persists beyond the transaction that installs it. A
consumer using delegation for batching needs to account for the delegated
implementation's continuing authority.

### Resource limits

Unbounded documents, calldata, and ABI fragments can exhaust memory or stall
parsing. Consumers MUST apply limits before parsing where the transport allows
and MUST reject oversized input rather than truncate it.

Retrieval limits apply to the retrieval implementation, including concurrent
requests and timeouts. A consumer decoding a content encoding MUST bound the
decoded stream as it decodes.

### Revert decoding

Both the ABI fragment and the revert data may be attacker-controlled. Consumers
MUST bound the fragment, MUST isolate decoder failures, and MUST escape decoded
output before displaying it as untrusted text.

### Local file access

A `file` reference causes the consumer to read with its own filesystem
privileges. Directory restrictions limit which files a relay can request.
Missing, oversized, mismatched, and unparsable input must produce
indistinguishable failures to avoid exposing file contents through errors.
Even with uniform failures, a guessed path and digest can confirm file contents
if the consumer accepts the document.

Special files can block or have side effects when read. Consumers SHOULD apply
read timeouts and treat stalled reads as failures. Consumers that do not support
local file access MUST reject `file` references.

### Server-side request forgery

Fetching caller-supplied URLs can expose services on the consumer's network.
Consumers MUST check resolved addresses rather than hostnames alone and SHOULD
connect to an address they checked. Otherwise, DNS rebinding can change a public
address to a private address between validation and connection. The transport
rules prohibit redirects, credentials, and non-default ports, and prevent
response bodies from being disclosed in errors.

### Reference integrity

Consumers MUST reject a digest mismatch and MUST NOT fall back to using the
retrieved bytes. Producers SHOULD serve immutable bodies at each URL.
Integrity verification and provenance are separate: a `data:` URI has no
producer provenance, and a `file` URI establishes only a local source.

## Copyright

Copyright and related rights waived via [CC0](../LICENSE.md).
