Internet-Draft rer-artifact June 2026
Cardillo Expires 14 December 2026 [Page]
Workgroup:
Independent Submission
Internet-Draft:
draft-car-rer-artifact-00
Published:
Intended Status:
Informational
Expires:
Author:
K. Cardillo
Tech Enrichment

RER Run Artifact Format: Hash-Chained, Signed Records of AI Inference Execution

Abstract

This document specifies the Run Envelope Runtime (RER) artifact format: a hash-chained, Ed25519-signed JSON record of a single AI inference run. An RER artifact carries (1) a signed envelope declaring the permissions and limits under which the run was authorized, (2) a hash-chained event log covering every model call, tool call, and policy decision made during execution, and (3) a runtime signature binding the envelope hash and event log head to the runtime implementation that produced the record.

The artifact is designed for offline verification by parties who did not participate in the run. A separate verifier package, with a minimal dependency surface (canonicalization, SHA-256, Ed25519, JSON schema validation), can establish the artifact's structural and cryptographic integrity without any of the code that produced it. After sealing, the integrity of the record does not depend on trust in the operator.

This document complements the declaration-side agents.txt and ai.txt well-known URI proposals (both filed, under review at the IANA Well-Known URIs registry). Declaration says what an agent was permitted to do. An RER artifact records what the runtime attested actually happened. The two surfaces together cover the before-and-after of an agent interaction.

Two on-the-wire schema versions are defined here: rer-artifact/0.1 and rer-artifact/0.2. Version 0.2 adds a bundle format, manifest binding, and approval-protocol metadata while preserving the verification semantics of 0.1.

Status of This Memo

This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.

Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.

Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."

This Internet-Draft will expire on 14 December 2026.

Table of Contents

1. Introduction

The deployment of AI agents that take action -- calling tools, invoking external services, completing transactions -- has outrun the audit mechanisms available to verify what those agents actually did. Logs produced by operators are necessarily trusted by consumers of those logs: the operator owns the log, the storage, the redaction policy, and the key. There is no structural reason for a third party to believe an operator's account of an agent run.

Several adjacent surfaces address parts of this problem. [AGENTSTXT] specifies what an agent is permitted to do on a site, in advance. [AITXT] specifies training-policy and licensing declarations. Both are declarations at the boundary; neither describes what happened inside an agent run.

This document specifies the artifact half of the picture: a hash-chained, cryptographically signed record of one execution. The artifact is structured so that a verifier with no relationship to the operator can establish, offline, that:

  1. The events in the log have not been reordered, removed, or modified.

  2. The envelope declaring the run's permissions has not been substituted.

  3. The runtime that produced the artifact attested to the (envelope, log-head) pair with an Ed25519 signature.

  4. Where payloads are present, their content has not changed since the event was sealed.

1.1. Trust boundary: verify never imports runtime

A design constraint enforced throughout the reference implementation is that the verifier package has no compile-time or run-time dependency on the runtime that produces artifacts. The verifier depends only on:

  • An RFC 8785 [RFC8785] canonicalization implementation.

  • A SHA-256 hash implementation conformant with [FIPS180-4].

  • An Ed25519 signature verification implementation conformant with [RFC8032].

  • A JSON Schema validator.

A third party who wants to check a proof installs only the verifier. They do not install, configure, or run the operator's runtime. This constraint is what allows the verification result to be meaningful independently of the operator's posture.

1.2. Relationship to other standards

W3C Verifiable Credentials [W3C-VC] is the closest existing analog. VC defines a signed claim about an entity; an RER artifact is a signed record about an execution. The two surfaces are compatible -- a VC could be issued attesting the contents of an RER artifact -- but RER carries information VC does not address natively, including a hash-chained event log and a separation between envelope (what was allowed) and events (what occurred).

1.3. Requirements language

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.

2. Terminology

3. Artifact format

An RER artifact is a JSON document. Two on-the-wire versions are defined: rer-artifact/0.1 (frozen) and rer-artifact/0.2 (frozen).

This section specifies the wire format. Section 4 specifies the event log carried inside the artifact. Section 5 specifies the envelope. Section 6 specifies the cryptographic primitives. Section 7 specifies the seven-check verification algorithm. Section 8 specifies the additions made in version 0.2. Section 9 specifies the bundle format (v0.2 only).

3.1. Schema identifiers

Three families of schema identifier appear:

  • envelope_version: rer-envelope/0.1 or rer-envelope/0.2.

  • event_version: rer-event/0.1 or rer-event/0.2.

  • artifact_version: rer-artifact/0.1 or rer-artifact/0.2.

An rer-artifact/0.1 artifact MUST contain only rer-envelope/0.1 and rer-event/0.1 sub-records. An rer-artifact/0.2 artifact MUST contain only rer-envelope/0.2 and rer-event/0.2 sub-records. Mixed-version artifacts are not defined and MUST be rejected.

3.2. Top-level structure

The artifact is a JSON object with the following fields:

{
  "artifact_version": "rer-artifact/0.2",
  "run_id":           "<implementation-defined run identifier>",
  "envelope_hash":    "<hex SHA-256 over canonical envelope>",
  "log_head_hash":    "<hex SHA-256 -- last event's event_hash>",
  "manifest_hash":    null,
  "runtime": {
    "implementation": "<runtime name>",
    "version":        "<runtime version string>",
    "key_id":         "<base64url SHA-256 of public key>",
    "algorithm":      "Ed25519"
  },
  "runtime_signature": "<hex Ed25519 signature over header>",
  "envelope":          <SignedEnvelope>,
  "events":            [<Event>, <Event>, ...]
}

3.2.1. Field semantics

artifact_version (REQUIRED):

The schema identifier. MUST be rer-artifact/0.1 or rer-artifact/0.2.

run_id (REQUIRED):

A run-unique identifier. Format is implementation-defined; the identifier is opaque to the verifier and is not parsed.

envelope_hash (REQUIRED):

Lower-case hexadecimal SHA-256 over the RFC 8785 canonical JSON form of the envelope's signable bytes (see Section 5 for the signable byte rule).

log_head_hash (REQUIRED):

Lower-case hexadecimal value equal to the event_hash of the final event in the events array. When events is empty the artifact MUST fail verification (see Section 7).

manifest_hash (v0.2 only):

Present in rer-artifact/0.2 artifacts. When the artifact is distributed standalone, the value MUST be JSON null. When the artifact is distributed inside a bundle, the value MUST equal the bundle's bundle_hash (Section 9). In v0.1 the field MUST be absent entirely (not null, not present-with-null) -- null and absent have different canonicalization outcomes and therefore different signature domains. This is intentional.

runtime (REQUIRED):

An object describing the producing runtime. The implementation field is a free-form identifier (for example, "@rer/runtime") used to distinguish runtimes. The version field SHOULD be a semantic version string. The key_id field MUST be the base64url-encoded [RFC4648] SHA-256 of the raw 32-byte Ed25519 public key. The algorithm field MUST be the literal string "Ed25519".

runtime_signature (REQUIRED):

Lower-case hexadecimal Ed25519 signature, 128 hex characters, computed over the canonical form of the artifact header (Section 6).

envelope (REQUIRED):

The signed envelope under which the run executed. See Section 5.

events (REQUIRED):

An ordered array of zero or more events. See Section 4. An empty events array MUST fail verification.

4. Event format

Each event in the events array is a JSON object with the following fields:

{
  "event_version":      "rer-event/0.2",
  "step_index":         0,
  "event_type":         "rer.run.started",
  "parent_event_hash":  null,
  "timestamp":          "2026-05-13T12:34:56.789Z",
  "payload":            { ... } | absent,
  "payload_redacted":   false,
  "payload_hash":       "<hex SHA-256>",
  "event_hash":         "<hex SHA-256>"
}

4.1. Field semantics

event_version (REQUIRED):

rer-event/0.1 or rer-event/0.2. Within a single artifact all events MUST share the version implied by the artifact_version (Section 3.1).

step_index (REQUIRED):

A non-negative integer that increases monotonically across the events array. step_index is not required to be a dense sequence (gaps are permitted) but MUST be strictly increasing between consecutive events.

event_type (REQUIRED):

A dotted lower-case string identifying the event family. The base set of event types defined by this specification is:

  • rer.run.started -- first event in every run

  • rer.policy.evaluated -- a policy decision applied to a model or tool action

  • rer.policy.step_blocked -- a model or tool action denied by policy

  • rer.model.called -- a request was issued to a model

  • rer.model.returned -- a model returned a response

  • rer.tool.called -- a tool was invoked

  • rer.tool.returned -- a tool returned a result

  • rer.artifact.written -- an out-of-band artifact (blob) was produced during the run

  • rer.run.ended -- final event in every run

Implementations MAY define additional event types under the rer.* namespace. Verifier behavior MUST NOT depend on the specific event_type other than for human-readable inspection.

parent_event_hash (REQUIRED):

The event_hash of the immediately prior event in the events array. For the first event (step_index = 0) the value MUST be JSON null. This field is what makes the log a hash chain.

timestamp (REQUIRED):

An RFC 3339 timestamp with fractional seconds and "Z" timezone. The timestamp is informational; verification does not depend on timestamp ordering.

payload (OPTIONAL):

The event-specific data structure. The shape of payload depends on event_type and is documented per event family. When the payload is redacted (payload_redacted is true), the payload field MUST be absent. When the payload is present (payload_redacted is false), the payload field MUST contain the data over which payload_hash was computed.

payload_redacted (REQUIRED):

A boolean. true means the producer chose not to embed payload bytes in this event record. Verifiers MUST still recompute payload_hash for non-redacted events (Section 7, check 7).

payload_hash (REQUIRED):

Lower-case hexadecimal SHA-256 over the RFC 8785 canonical form of the payload. When payload is absent because the event has no payload data at all (rather than because it is redacted), the canonical form is canonicalize(null) and the hash is SHA-256 over that canonical form. The payload_redacted flag distinguishes "redacted (data existed)" from "no data."

event_hash (REQUIRED):

Lower-case hexadecimal SHA-256 over the canonical JSON form of the event-header object, where the event-header object consists of exactly these fields:

  • event_version

  • step_index

  • event_type

  • parent_event_hash

  • timestamp

  • payload_hash

Note: event_hash itself is NOT a field of the event-header object. This breaks the obvious circular dependency in computing event_hash.

4.2. Hash chain rule

For every event at position i > 0 in events:

events[i].parent_event_hash == events[i-1].event_hash

For event at position 0:

events[0].parent_event_hash == null

Any deviation MUST fail verification.

5. Envelope format

An envelope is a JSON object declaring the permissions and limits of a run. The envelope is signed by the same key that signs the artifact header. The envelope is sealed at run start; events are appended after.

5.1. Top-level structure

{
  "envelope_version": "rer-envelope/0.2",
  "permissions": {
    "allowed_models":  ["<model id>", ...],
    "allowed_tools":   ["<tool id>", ...]
  },
  "limits": {
    "max_steps":       <integer> | absent,
    "max_spend_usd":   <number>  | absent,
    "rate_limit_rpm":  <integer> | absent
  },
  "expiry":   "<RFC 3339 timestamp>" | absent,
  "metadata": { ... } | absent,
  "required_approvals":     [<ApprovalRule>, ...] | absent,   (v0.2)
  "required_signer_types":  ["<signer-type>", ...] | absent,  (v0.2)
  "signature":              "<hex Ed25519 signature>"
}

5.1.1. Field semantics

envelope_version (REQUIRED):

rer-envelope/0.1 or rer-envelope/0.2.

permissions.allowed_models (REQUIRED):

An ordered array of model identifier strings the run is permitted to invoke. Order is significant for canonicalization but not for policy semantics.

permissions.allowed_tools (REQUIRED):

An ordered array of tool identifier strings the run is permitted to invoke.

limits (REQUIRED, fields OPTIONAL):

Implementation-enforced caps. Fields that are absent denote "no limit on this dimension." The max_spend_usd field MAY be 0, denoting "spending is forbidden." The max_steps and rate_limit_rpm fields MUST be at least 1 when present.

expiry (OPTIONAL):

A wall-clock deadline after which the envelope MUST NOT authorize further actions. Runtimes SHOULD reject envelopes whose expiry has passed at the moment of run start; verifiers MAY surface an expired envelope as informational and SHOULD NOT use expiry to make the artifact fail verification, since verification can occur arbitrarily later than the run.

metadata (OPTIONAL):

An open object. Producers MAY place application-specific data here. Verifiers MUST treat metadata as opaque.

required_approvals (v0.2 only, OPTIONAL):

An ordered array of ApprovalRule objects describing actions that require an explicit approval token before execution. Each ApprovalRule MUST contain an action field naming the action requiring approval and MAY contain tool_pattern, model_pattern, and signer_types fields scoping the rule. signer_types values MUST be drawn from the identifier set defined for required_signer_types below.

required_signer_types (v0.2 only, OPTIONAL):

An ordered array of signer-type identifiers that approvers MUST satisfy across the run as a whole. Each identifier MUST be one of "human", "delegate", or "automated"; schema validation (check 1) rejects other values. The semantics of "satisfy" (counting rules, role hierarchy, threshold) are out of scope for this document.

signature (REQUIRED):

Lower-case hexadecimal Ed25519 signature, 128 hex characters, computed over the envelope's signable bytes (Section 5.2).

5.2. Signable bytes

The envelope is signed over its canonical form with the signature field removed:

signable = canonicalize(envelope \ { signature })

Equivalently: copy the envelope, delete the signature field, then run RFC 8785 canonicalization over the result. The signature is computed over the UTF-8 bytes of the canonical string.

The envelope_hash referenced from the artifact header is the SHA-256 over those same signable bytes. The envelope_hash is therefore independent of the signature value -- substituting a different valid signature does not change envelope_hash, but the envelope's signature check (Section 7, check 3) will still fail because the public key the verifier uses is the one bound to runtime.key_id, not to the swapped signature.

6. Cryptographic primitives

This section binds the document to specific algorithms. Implementations MUST use exactly these primitives. Algorithm agility is intentionally not provided: the artifact format is small, the primitives are widely supported, and a future version (rer-artifact/0.3 or later) would declare alternative primitives in a new versioned envelope rather than through field-level negotiation.

6.1. Signing: Ed25519

All signatures (envelope.signature and runtime_signature) MUST use the Ed25519 signature scheme as specified in [RFC8032]. The signature value MUST be exactly 64 bytes, encoded as 128 lower-case hexadecimal characters in the JSON representation.

The signed bytes are always the UTF-8 representation of an RFC 8785 canonical JSON string, computed over the field selection rules specified in the appropriate section above (Section 5.2 for envelopes, Section 6.6 for the artifact header).

6.2. Hashing: SHA-256

All hashes MUST be computed using SHA-256 as specified in [FIPS180-4]. Hashes appear in JSON as 64 lower-case hexadecimal characters representing the 32-byte digest.

6.3. Canonicalization: RFC 8785 (JCS)

All canonical JSON forms MUST be computed using JSON Canonicalization Scheme [RFC8785]. The key properties this provides:

  • Object members are emitted in lexicographic order of UTF-16 code units of the member name.

  • Whitespace is eliminated outside string values.

  • Number serialization follows ECMAScript ToString semantics with specific normalization rules.

  • String escaping is normalized.

This makes the byte representation of a JSON value a function only of the value's structural content, not of the encoding choices of the producer.

6.4. Key format

For key import and export, this specification uses JSON Web Key (JWK) [RFC7517] format with the OKP key type and the Ed25519 curve.

A public key JWK has the form:

{ "kty": "OKP", "crv": "Ed25519", "x": "<base64url 32-byte public>" }

A private key JWK additionally includes a "d" member encoding the 32-byte private key seed:

{ "kty": "OKP", "crv": "Ed25519",
  "x": "<base64url public>", "d": "<base64url private seed>" }

Internally to a runtime or verifier implementation, keys MAY be held as raw octet sequences. JWK is the on-the-wire form when keys are exchanged.

6.5. Key identifier (key_id)

The key_id field carried in the artifact's runtime object is computed as:

key_id = base64url(SHA-256(raw_public_key_bytes))

where raw_public_key_bytes is the 32-byte Ed25519 public key in the form defined by [RFC8032] Section 5.1.5. The base64url encoding follows [RFC4648] Section 5 (URL-safe, padding stripped).

The key_id is a stable, content-addressed identifier for the key. Two artifacts produced under the same key will carry the same key_id. A verifier obtains the public key out-of-band (from a key directory, a TOFU pin, a CA-issued certificate, or any other channel appropriate to the deployment) and confirms that the public key's key_id matches the value embedded in the artifact.

This document does not specify how public keys are distributed. The key_id is the index into whatever distribution mechanism is in use.

6.6. Artifact header canonicalization

The artifact header object -- what runtime_signature signs over -- is a derived object containing the following members of the artifact:

header = canonicalize({ artifact_version, run_id, envelope_hash,
                        log_head_hash, runtime })

In rer-artifact/0.2 the header additionally includes the manifest_hash member, carrying its value at signing time (null when the artifact is not bound to a bundle, or the bundle_hash when it is). This is the only signature-domain difference between the two versions (Section 8.1). In rer-artifact/0.1 the member is absent from the header, matching its absence from the artifact.

The envelope object and the events array are not signed directly by the runtime_signature; they are covered indirectly through envelope_hash and log_head_hash, which a verifier independently recomputes from the envelope and event bytes before checking the signature (the "recompute, don't trust" rule in Section 7.1).

The fields covered by the runtime_signature are therefore:

  • artifact_version

  • run_id

  • envelope_hash (binding the entire signed envelope)

  • log_head_hash (binding the entire ordered event array)

  • runtime (entire object)

  • manifest_hash (rer-artifact/0.2 only; value or null)

7. Verification algorithm

A verifier MUST perform seven checks. Failure of any check MUST cause the verification to return failure. A conforming verifier MUST NOT short-circuit: all seven checks MUST be evaluated regardless of earlier failures, and the verifier's output MUST include the per-check result so consumers can distinguish, for example, a signature failure from a chain failure.

The no-short-circuit rule exists because verifier output is consumed by downstream audit pipelines that benefit from a complete check matrix, not a single pass/fail. The performance cost of running all checks on an already-failing artifact is negligible compared to the diagnostic value.

7.1. The seven checks

Check 1 -- schema validation. The artifact, viewed as a JSON object, MUST conform to the rer-artifact/0.1 or rer-artifact/0.2 JSON Schema. The envelope and every event MUST conform to their corresponding versioned schemas. Type errors, missing required fields, and unexpected field types cause this check to fail.

Check 2 -- envelope hash. Recompute envelope_hash by:

  1. Taking the artifact's envelope object,

  2. Removing the signature field,

  3. RFC 8785 canonicalizing the remainder,

  4. SHA-256 over the UTF-8 bytes of the canonical string.

The recomputed hash MUST equal the artifact's envelope_hash field, compared using a constant-time hex-string equality.

Check 3 -- envelope signature. Verify the Ed25519 signature artifact.envelope.signature against the public key (identified by runtime.key_id and supplied to the verifier out-of-band) over the same signable bytes computed in check 2.

Check 4 -- event chain hash. For each event at index i:

  • Recompute event_hash as SHA-256 over the canonical form of the event-header subset specified in Section 4.1, which includes the event's payload_hash field as carried. Verify it equals the event's event_hash field.

  • For i > 0, verify events[i].parent_event_hash equals events[i-1].event_hash.

  • For i == 0, verify events[0].parent_event_hash is null.

Payload bytes themselves are not examined in this check; payload integrity is established by check 7. For redacted events the producer-stated payload_hash is bound through the event_hash recomputation here and is never recomputed (the payload bytes are no longer present).

Any deviation fails this check.

Check 5 -- artifact header hash (log_head_hash). Verify that the final event's event_hash equals the artifact's log_head_hash. An empty events array fails this check.

Check 6 -- artifact header signature. Verify the Ed25519 runtime_signature against the public key, over the canonical form of the artifact header as specified in Section 6.6.

Important: in check 6 the verifier MUST use the freshly recomputed envelope_hash from check 2 and the freshly recomputed log_head_hash from check 5 -- not the values as carried in the artifact header. If the artifact carries inconsistent values (a tampering attack that changes envelope_hash but not envelope), the verifier independently arrives at the correct hashes from the underlying envelope and event bytes, then verifies the signature over those values. This is the "recompute, don't trust" rule.

Check 7 -- payload hashes. For each non-redacted event, recompute payload_hash as SHA-256 over canonicalize(payload) and confirm it matches the event's payload_hash. For redacted events this check trivially passes (the payload bytes are not present, so there is nothing for the verifier to recompute -- the bound exists through the event_hash check in step 4, which covered the producer-stated payload_hash).

7.2. Result structure

A verifier conformant with this document MUST return a result structure containing:

  • A boolean overall pass.

  • A per-check boolean matrix containing exactly seven booleans, one per check above.

  • A list of human-readable reason strings describing every check that failed.

The pass value MUST be true if and only if every check passed.

7.3. Safe parsing

Implementations MAY expose a "safe" verification entry point that accepts arbitrary input (raw JSON string, untyped object, or any other value) and never throws. Such an entry point applies, in order:

  1. JSON parsing (failures are surfaced as schema_valid = false).

  2. Structural type guarding (non-object inputs fail schema_valid).

  3. JSON Schema validation (failures fail schema_valid).

  4. The full seven-check verification.

This is the recommended interface for verifiers that receive artifacts from untrusted sources. A separate "strict" entry point MAY throw on schema failure, suited for use by code that has already validated the input.

8. Version 0.2 additions

Version 0.2 extends 0.1 in four areas while preserving the verification semantics of 0.1.

8.1. manifest_hash

The artifact header gains a manifest_hash field. Its value is JSON null when the artifact is distributed standalone (without a bundle), and equals the bundle's bundle_hash (Section 9) when distributed in a bundle. The field MUST NOT appear in rer-artifact/0.1 artifacts.

The field is excluded from runtime_signature coverage (Section 6.6). This is intentional and is the only signature-domain difference between 0.1 and 0.2.

8.2. required_approvals

The envelope gains an optional required_approvals array. Each ApprovalRule is a JSON object:

{
  "action":         "<action identifier>",
  "tool_pattern":   "<glob>" | absent,
  "model_pattern":  "<glob>" | absent,
  "signer_types":   ["<signer-type>", ...] | absent
}

When a run executes an action that matches an ApprovalRule, the runtime MUST have received a valid approval token (mechanism out of scope for this document; signing of approvals is reserved for a future v0.3) before the action's policy event records an "allow" decision.

8.3. required_signer_types

The envelope gains an optional required_signer_types array. Each identifier MUST be one of "human", "delegate", or "automated" (enforced by schema validation in check 1). The operational semantics are deployment-specific -- the field is a contract between the runtime and the operator's approval policy. Beyond schema validation, verifiers do not interpret the field; it is part of the signed envelope so its presence and content are tamper-evident.

8.4. Step identity

Events gain optional step_id, tool_call_id, and approval_id fields inside their payloads. These are correlation identifiers for tracing related events (a model.called and its paired model.returned; an approval and the action it authorized). The fields are part of the event payload, not the event header -- they affect payload_hash but not event_hash directly. They are opaque to the verifier.

9. Bundle format (v0.2 only)

A bundle is a portable distribution form for an artifact, a manifest, the public key, and any blobs referenced by rer.artifact.written events. Bundles are the recommended distribution form when artifacts reference out-of-band payloads.

9.1. Manifest structure

A manifest is a JSON object:

{
  "artifact_hash":        "<hex SHA-256 over artifact content>",
  "runtime_key_hash":     "<hex SHA-256 over raw public key>",
  "total_event_count":    <integer>,
  "redacted_event_count": <integer>,
  "blobs": [
    {
      "name":       "<filename or label>",
      "hash":       "<hex SHA-256 over blob bytes>",
      "size_bytes": <integer>
    },
    ...
  ],
  "bundle_hash":          "<hex SHA-256 over manifest minus bundle_hash>"
}

9.1.1. artifact_hash (manifest field)

The manifest's artifact_hash is SHA-256 over the canonical form of the artifact with manifest_hash and runtime_signature removed:

content_fields = artifact \ { manifest_hash, runtime_signature }
artifact_hash  = SHA-256(canonicalize(content_fields))

These two fields are excluded because manifest_hash is computed downstream of the manifest (would-be circular) and runtime_signature is downstream of the artifact content hash. Excluding them breaks the circular dependency while preserving binding (the bundle_hash, which references artifact_hash, is itself referenced by the artifact's manifest_hash -- closing the binding from the other direction).

9.1.2. bundle_hash (manifest field)

The manifest's bundle_hash is SHA-256 over the manifest with bundle_hash itself removed:

self_minus_hash = manifest \ { bundle_hash }
bundle_hash     = SHA-256(canonicalize(self_minus_hash))

The bundle_hash is then referenced by the artifact's manifest_hash field. Together this creates a closed binding: artifact carries bundle_hash, bundle_hash is over manifest, manifest carries artifact_hash which is over the rest of the artifact.

9.2. Ten-check bundle verification

When verifying a bundle, a conformant verifier MUST perform ten checks (the seven artifact checks plus three bundle-specific checks). As with single-artifact verification, no short-circuiting is permitted: all ten checks MUST be evaluated.

The ten checks are:

Bundle check 1 -- artifact verification. Run the full seven-check artifact verification (Section 7) on the artifact carried in the bundle. This check passes only if all seven artifact checks passed.

Bundle check 2 -- manifest self-integrity. Recompute bundle_hash over the manifest with bundle_hash removed and confirm it matches the manifest's bundle_hash field.

Bundle check 3 -- artifact content hash. Recompute the manifest's artifact_hash by canonicalizing the artifact with manifest_hash and runtime_signature removed, and SHA-256 over the canonical bytes. Confirm it matches the manifest's artifact_hash.

Bundle check 4 -- manifest binding. Confirm artifact.manifest_hash equals manifest.bundle_hash. This closes the binding from artifact to manifest.

Bundle check 5 -- key integrity. Confirm SHA-256 over the supplied raw public key bytes equals manifest.runtime_key_hash.

Bundle check 6 -- blob integrity. For each entry in manifest.blobs, confirm that the supplied blob with that hash exists and that its SHA-256 matches.

Bundle check 7 -- blob completeness. For every event in the artifact with event_type rer.artifact.written, confirm the payload's artifact_hash references a blob present in manifest.blobs.

Bundle check 8 -- event count. Confirm manifest.total_event_count equals events.length.

Bundle check 9 -- redacted count. Confirm manifest.redacted_event_count equals the number of events with payload_redacted = true.

Bundle check 10 -- blob metadata. For each manifest blob, confirm its size_bytes equals the supplied blob's byte length.

A conforming verifier MUST return a ten-boolean per-check matrix and a list of human-readable reasons for each failed check.

9.3. Export form

A bundle is distributed as a directory or a tar/zip-equivalent package containing:

  • artifact.json -- the RER artifact.

  • manifest.json -- the manifest above.

  • key.bin -- the 32-byte raw Ed25519 public key (or key.jwk -- a JWK public key file).

  • blobs/<hash>.bin -- one file per manifest.blobs entry, named by its hex hash.

The exact filesystem layout is informational; verifier interfaces typically accept the four components as parameters rather than prescribing a directory shape.

10. Operational considerations

10.1. Empty and minimal artifacts

The smallest valid artifact contains exactly two events (rer.run.started and rer.run.ended). An events array of length zero MUST fail verification (check 5 cannot pass) and is not a permitted shape. Implementations producing very short runs SHOULD still emit both lifecycle events.

10.2. Time

Timestamps are RFC 3339 with fractional-second precision and "Z" timezone. Producers SHOULD use a monotonically non-decreasing timestamp source across consecutive events. Verifiers MUST NOT use timestamp values to gate verification; the hash chain is the authoritative ordering primitive.

10.3. Redaction

Setting payload_redacted = true and omitting the payload removes the payload bytes from the artifact while preserving the hash binding. This is the supported redaction primitive. Operators MUST NOT remove events from the chain to redact -- that breaks check 4. The supported operation is "this event happened, here is its hash chain, the payload bytes have been withheld."

A consequence is that redacted events are partially opaque: the verifier confirms structural integrity but cannot recompute the payload contents. Whether to trust the producer-stated payload_hash in a redacted event is a deployment policy question -- for high-trust deployments where the producer is the operator, this is acceptable; for low-trust deployments, non-redacted events are required.

10.4. Key rotation

Key rotation is supported by reference: each artifact embeds its runtime.key_id, and a verifier obtains the public key by looking up that key_id. When a runtime rotates keys, the new key gets a new key_id (because key_id is content-addressed), and the operator publishes the new key in whatever directory the verifier consults. Old artifacts continue to verify against their original key_id; their public key remains discoverable so long as the directory retains it.

This specification does not define a key directory format. Several plausible options exist (a JWKS endpoint, a CT-like log, a TOFU pinning model). The choice is deployment-specific.

11. Security considerations

11.1. What the format establishes

A successful verification establishes, with cryptographic strength:

  • The events have not been reordered, inserted, or removed since the artifact was sealed.

  • The envelope has not been substituted for a different envelope since the artifact was sealed.

  • The runtime that signed the artifact had access to the private key corresponding to runtime.key_id at signing time.

  • Non-redacted payloads have not been modified since the event was sealed.

11.2. What the format does not establish

A successful verification does NOT establish:

  • That the recorded events correspond to what actually occurred at creation time. The runtime holds the signing key; a runtime (or an operator with access to the key) can produce a fully valid artifact describing events that never happened, or seal a log from which events were omitted before signing. The hash chain makes the record tamper-evident after sealing; it does not make the record's creation honest. Assurance about the producing runtime's integrity must come from outside the artifact -- for example, attestation of the runtime build, operational controls, or cross-checks against counterparty records.

  • That the policy decisions recorded in events were correct. The verifier sees that "policy decided allow"; it does not verify that allow was the right answer given the envelope. Policy correctness requires re-running the policy engine against the same envelope and inputs, which is outside the scope of verification.

  • That the model or tool responses recorded in events are themselves truthful. The model's output is part of the signed record, but the signature attests that the runtime saw that output, not that the output is accurate.

  • That the operator is honest about which key signed which run. The key_id is content-addressed; if the operator publishes a key under a misleading label, the artifact still verifies against the actual key bytes. Operator identity binding is the job of the key directory.

11.3. Trust boundary in the implementation

The reference implementation enforces, as a build-time rule, that the verifier package does not import or depend on the runtime package. The verifier's dependencies are limited to:

  • RFC 8785 canonicalization.

  • Node.js SubtleCrypto (or equivalent) for SHA-256 and Ed25519 verification.

  • A JSON Schema validator (e.g., Ajv).

This constraint is the technical realization of the trust boundary. A third party can install the verifier and check any artifact without the operator's runtime code being present in the verification path.

11.4. Redaction and verifiability

Redaction trades transparency for verifiability of the chain. The chain still proves the event happened; it no longer proves what the event contained. Operators redacting sensitive payloads SHOULD clearly publish their redaction policy so verifiers can evaluate whether they accept redacted-payload artifacts as sufficient for their use case.

11.5. Replay and uniqueness

The artifact format does not include a nonce or anti-replay mechanism. run_id is implementation-defined and the verifier treats it as opaque. Deployments needing replay protection (e.g., billing, non-repudiation against duplicate-claim attacks) SHOULD layer a deduplication index over run_id at the audit pipeline level.

11.6. Algorithm agility

Algorithm agility is not provided at the field level. Ed25519 and SHA-256 are the only primitives. A future revision of this document (rer-artifact/0.3 or later) MAY define alternative primitives in a new envelope version. Verifiers MUST reject artifacts whose version identifiers they do not recognize.

This decision trades flexibility for security predictability. The artifact format is small; supporting algorithm negotiation in a small format adds substantial complexity for limited benefit. Versioning the entire envelope is the upgrade path.

11.7. Side channels

Constant-time comparisons MUST be used for all signature and hash equality checks in the verifier. The reference implementation uses the host platform's constant-time comparison primitive (Node.js crypto.timingSafeEqual) for hex-encoded hash and signature comparisons. Implementations on other platforms MUST use the equivalent primitive.

12. IANA considerations

This document does not request a new IANA registration. The related well-known URI registrations on which this work depends have been filed and are under review at the IANA Well-Known URIs registry [IANA-WK]:

These declarations cover the declaration-side surface (what an agent is permitted to do, what training policies apply, how to connect, what physical claims are verifiable). RER artifacts cover the proof-side surface (what an agent actually did).

A future revision of this document MAY register a media type for RER artifacts (provisional candidate: "application/vnd.rer.artifact+json"). A media type registration is not requested at this time because operational deployments have not yet generated the practice from which a stable registration request should be derived.

13. References

13.1. Normative References

[FIPS180-4]
National Institute of Standards and Technology, "Secure Hash Standard (SHS)", , <https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf>.
[RFC2119]
Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, DOI 10.17487/RFC2119, , <https://www.rfc-editor.org/rfc/rfc2119>.
[RFC4648]
Josefsson, S., "The Base16, Base32, and Base64 Data Encodings", RFC 4648, DOI 10.17487/RFC4648, , <https://www.rfc-editor.org/rfc/rfc4648>.
[RFC7517]
Jones, M., "JSON Web Key (JWK)", RFC 7517, DOI 10.17487/RFC7517, , <https://www.rfc-editor.org/rfc/rfc7517>.
[RFC8032]
Josefsson, S. and I. Liusvaara, "Edwards-Curve Digital Signature Algorithm (EdDSA)", RFC 8032, DOI 10.17487/RFC8032, , <https://www.rfc-editor.org/rfc/rfc8032>.
[RFC8174]
Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, , <https://www.rfc-editor.org/rfc/rfc8174>.
[RFC8785]
Rundgren, A., Jordan, B., and S. Erdtman, "JSON Canonicalization Scheme (JCS)", RFC 8785, DOI 10.17487/RFC8785, , <https://www.rfc-editor.org/rfc/rfc8785>.

13.2. Informative References

[AGENTSTXT]
"AGENTS.TXT: Capability Declarations for Web Agents", Work in Progress, Internet-Draft, draft-car-agents-txt-wellknown-00, , <https://datatracker.ietf.org/doc/draft-car-agents-txt-wellknown/>.
[AITXT]
"AI.TXT: Training Policy, Licensing, and Attribution Declarations for AI Systems", Work in Progress, Internet-Draft, draft-car-ai-txt-wellknown-00, , <https://datatracker.ietf.org/doc/draft-car-ai-txt-wellknown/>.
[CONNECTTXT]
"CONNECT.TXT: Agent Connection Declarations", , <https://github.com/kaylacar/connect-txt>.
[IANA-WK]
"Well-Known URIs", n.d., <https://www.iana.org/assignments/well-known-uris/well-known-uris.xhtml>.
[VERIFYTXT]
"VERIFY.TXT: Physical Verification Declaration Standard", , <https://github.com/kaylacar/verify-txt>.
[W3C-VC]
"Verifiable Credentials Data Model", , <https://www.w3.org/TR/vc-data-model/>.

Appendix A. Acknowledgments

The RER artifact format was designed and developed by the author at Tech Enrichment as part of a broader stack covering both declaration and proof for AI agent interactions. The companion declaration specifications appear in [AGENTSTXT], [AITXT], [CONNECTTXT], and [VERIFYTXT].

The trust-boundary discipline -- verify never imports runtime -- was informed by long-standing practice in cryptographic software engineering, where verification surfaces are isolated from key material and execution surfaces. The seven-check structure with no short-circuit, and the ten-check structure for bundles, reflects the discipline that a verifier's value comes from telling the consumer exactly which property failed, not from reaching a single pass/fail decision quickly.

Appendix B. Worked example: a minimal run

The following non-normative example shows the smallest verifiable artifact: a run with two lifecycle events (rer.run.started, rer.run.ended), no model or tool calls, no redacted events. All hash and signature values are placeholders represented as "<hex...>".

{
  "artifact_version": "rer-artifact/0.2",
  "run_id": "01HX9C3MPN5K8VYE0G2DZ1Q7HA",
  "envelope_hash": "<hex64>",
  "log_head_hash": "<hex64>",
  "manifest_hash": null,
  "runtime": {
    "implementation": "@rer/runtime",
    "version": "0.2.0",
    "key_id": "<base64url 43 chars>",
    "algorithm": "Ed25519"
  },
  "runtime_signature": "<hex128>",
  "envelope": {
    "envelope_version": "rer-envelope/0.2",
    "permissions": {
      "allowed_models": ["claude-sonnet-4-5"],
      "allowed_tools": []
    },
    "limits": { "max_steps": 4 },
    "expiry": "2026-05-13T14:00:00.000Z",
    "signature": "<hex128>"
  },
  "events": [
    {
      "event_version": "rer-event/0.2",
      "step_index": 0,
      "event_type": "rer.run.started",
      "parent_event_hash": null,
      "timestamp": "2026-05-13T12:34:56.789Z",
      "payload": { "envelope_hash": "<hex64>",
                   "runtime_version": "0.2.0" },
      "payload_redacted": false,
      "payload_hash": "<hex64>",
      "event_hash": "<hex64>"
    },
    {
      "event_version": "rer-event/0.2",
      "step_index": 1,
      "event_type": "rer.run.ended",
      "parent_event_hash": "<hex64 -- events[0].event_hash>",
      "timestamp": "2026-05-13T12:34:57.123Z",
      "payload": { "status": "completed",
                   "reason": "no work requested",
                   "total_model_calls": 0,
                   "total_tool_calls": 0,
                   "total_spend_usd": 0 },
      "payload_redacted": false,
      "payload_hash": "<hex64>",
      "event_hash": "<hex64>"
    }
  ]
}

A verifier running the seven checks against this artifact, given the correct public key, will recompute envelope_hash from the canonical envelope-minus-signature bytes, verify the envelope signature, walk the chain (parent_event_hash, event_hash, payload_hash for each event), confirm the second event's event_hash equals log_head_hash, verify runtime_signature over the artifact header, and confirm payload hashes for both events. Every check resolves to a boolean; the overall pass requires all seven booleans to be true.

Appendix C. Worked example: tampering

The following non-normative example shows two equivalence-class attacks the seven checks catch.

Attack A -- event removal. Remove events[1] from the example above. Effects:

The verifier returns pass = false with two failed checks identified.

Attack B -- payload swap on a non-redacted event. Modify events[0] payload from {"envelope_hash": "X", ...} to {"envelope_hash": "Y", ...} without changing payload_hash. Effects:

The verifier returns pass = false with one failed check identified.

A check matrix that distinguishes (envelope failure | chain failure | log-head failure | signature failure | payload failure) is more informative for audit pipelines than a single pass/fail bit. That is the operational rationale for the no-short-circuit rule.

Appendix D. Implementation notes

The reference implementation is available at github.com/kaylacar/rer. It is structured as an npm monorepo with the following packages relevant to this document:

A standalone verifier (kaylacar/rer-verify) packages the verifier with zero JavaScript runtime dependencies beyond Node.js's built-in crypto and a JCS implementation. It is the operational realization of the "anyone can check a proof without trusting the producer" property.

Author's Address

Kayla Cardillo
Tech Enrichment