<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE rfc SYSTEM "rfc2629.dtd">
<?rfc toc="yes"?>
<?rfc sortrefs="yes"?>
<?rfc symrefs="yes"?>
<?rfc compact="yes"?>
<?rfc subcompact="no"?>
<rfc ipr="trust200902" docName="draft-stone-aivs-00" category="info"
     xmlns:xi="http://www.w3.org/2001/XInclude">
  <front>
    <title abbrev="AIVS">AIVS: Agentic Integrity Verification Standard</title>
    <author fullname="Ben Stone" initials="B." surname="Stone">
      <organization>SwarmSync.AI</organization>
      <address>
        <email>benstone@swarmsync.ai</email>
      </address>
    </author>
    <date year="2026" month="March"/>
    <area>Applications</area>
    <workgroup>Individual Submission</workgroup>
    <keyword>AI agents</keyword>
    <keyword>audit log</keyword>
    <keyword>integrity verification</keyword>
    <keyword>hash chain</keyword>
    <keyword>Ed25519</keyword>
    <keyword>proof bundle</keyword>
    <abstract>
      <t>This document specifies the Agentic Integrity Verification Standard
      (AIVS), a portable, self-verifiable archive format for cryptographic
      proof of AI agent sessions. An AIVS proof bundle is a gzip-compressed
      tar archive containing a SHA-256 hash-chained audit log, an Ed25519
      digital signature over the chain, a machine-readable manifest, and an
      embedded verification script that requires only the Python 3 standard
      library to execute. AIVS also defines AIVS-Micro: a minimal six-field
      attestation (~200 bytes) for continuous monitoring and API contexts
      where a full session bundle is not required. AIVS enables any party to
      independently verify that every action in an agent session is
      accounted for and unmodified, that no actions have been inserted,
      deleted, or reordered, and that the session was produced by a specific
      cryptographic identity — entirely offline, without installing any
      software beyond Python 3.</t>
    </abstract>
  </front>

  <middle>

  <section title="1. Introduction">
    <section title="1.1 Problem Statement">
      <t>AI agents increasingly perform consequential actions on behalf of
      humans: navigating websites, filling forms, executing JavaScript,
      extracting data, and making purchases. Existing observability platforms
      (OpenTelemetry, LangSmith, Langfuse) log these actions but provide no
      cryptographic guarantees that the logs are complete, unmodified, or
      authentic.</t>

      <t>Regulatory frameworks mandate audit trails but do not prescribe
      formats:</t>

      <t>EU AI Act Article 19 requires automatically generated logs for
      high-risk AI systems but specifies no format.</t>

      <t>ISO/IEC 42001:2023 Annex A.6.2.8 requires event logging but defines
      no data structure.</t>

      <t>NIST AI RMF requires documentation and audit trails but deliberately
      avoids prescribing formats.</t>

      <t>This creates a gap: organizations that must prove what their AI
      agents did have no standard way to produce, exchange, or verify that
      proof.</t>
    </section>

    <section title="1.2 Design Goals">
      <figure>
        <artwork type="table"><![CDATA[
| Goal              | Rationale                                              |
|-------------------|--------------------------------------------------------|
| Self-verifiable   | Bundles verifiable without contacting any server,      |
|                   | blockchain, or authority.                              |
| Portable          | A single file that can be emailed, stored, or          |
|                   | submitted to any system.                               |
| Tamper-evident    | Modifying any action in the log must be detectable.    |
| Zero dependencies | Verification requires only Python 3 standard library.  |
| Session-level     | Covers an entire session (sequence of actions).        |
| Lightweight       | AIVS-Micro provides ~200-byte attestation for          |
| profile           | high-frequency monitoring.                             |
| Domain-agnostic   | Applicable to any AI agent performing any action.      |
]]></artwork>
      </figure>
    </section>

    <section title="1.3 Relationship to Existing Standards">
      <figure>
        <artwork type="table"><![CDATA[
| Standard              | Scope                        | AIVS Relationship              |
|-----------------------|------------------------------|--------------------------------|
| W3C VC 2.0            | Identity claims              | AIVS bundles may be wrapped    |
|                       |                              | as VC credentialSubject        |
| IETF SCITT            | Supply chain transparency    | AIVS bundles may be registered |
|                       |                              | as SCITT signed statements     |
| C2PA v2.2             | Media asset provenance       | AIVS applies the same          |
|                       |                              | manifest-chain concept to      |
|                       |                              | agent actions                  |
| Certificate           | Append-only Merkle logs      | AIVS hash chain is a           |
| Transparency RFC 6962 |                              | simplified linear variant      |
| EU AI Act Art. 19     | Audit log requirements       | AIVS is a concrete format      |
|                       |                              | satisfying Article 19          |
]]></artwork>
      </figure>
    </section>

    <section title="1.4 Terminology">
      <figure>
        <artwork type="table"><![CDATA[
| Term               | Definition                                              |
|--------------------|--------------------------------------------------------|
| Session            | A bounded sequence of actions performed by a single    |
|                    | AI agent instance, identified by a session_id.         |
| Action             | A single operation performed by the agent (e.g.,       |
|                    | navigate to URL, click element, execute JavaScript).   |
| Audit Row          | A JSON object recording one action with its inputs,    |
|                    | outputs, timestamp, cost, and hash chain fields.       |
| Hash Chain         | A sequence of audit rows where each row's hash         |
|                    | depends on the previous row's hash.                    |
| Chain Hash         | A single SHA-256 hash computed over all row hashes,    |
|                    | serving as a fingerprint of the entire session.        |
| Proof Bundle       | A .tar.gz archive containing the audit log, signature, |
|                    | manifest, public key, and verifier script.             |
| AIVS-Micro         | A minimal 6-field JSON proof (~200 bytes) for a        |
|                    | single-URL scan attestation.                           |
| Identity Key       | An Ed25519 keypair used to sign the chain hash.        |
]]></artwork>
      </figure>
    </section>
  </section>

  <section title="2. Hash Chain Specification">
    <section title="2.1 Row Hash Computation">
      <t>Each audit row is identified by a deterministic SHA-256 hash. The
      hash input is a colon-separated string of exactly seven fields in this
      order:</t>

      <figure>
        <artwork type="sourcecode"><![CDATA[
row_hash = SHA-256(
    "{row_id}:{session_id}:{action_type}:{tool_name}:{cost_cents}:{timestamp}:{prev_hash}"
)
]]></artwork>
      </figure>

      <t>The hash is represented as a lowercase hexadecimal string (64
      characters).</t>
    </section>

    <section title="2.2 Field Definitions">
      <figure>
        <artwork type="table"><![CDATA[
| Field        | Type    | Description                                     |
|--------------|---------|-------------------------------------------------|
| row_id       | Integer | Monotonically increasing row identifier          |
|              |         | (1-indexed).                                    |
| session_id   | String  | Unique identifier for the session.              |
| action_type  | String  | Classification of the action.                   |
|              |         | Default: "tool_call".                           |
| tool_name    | String  | Namespaced tool identifier                      |
|              |         | (e.g., "browser.navigate").                     |
| cost_cents   | Integer | Cost of the action in cents. 0 for free         |
|              |         | actions.                                        |
| timestamp    | Float   | Unix timestamp with fractional seconds.         |
| prev_hash    | String  | row_hash of the immediately preceding row.      |
|              |         | Empty string for the first row.                 |
]]></artwork>
      </figure>
    </section>

    <section title="2.3 Chain Integrity Property">
      <t>For a chain of N rows, modifying any field of row K invalidates
      row_hash[K], which invalidates prev_hash[K+1], and so on through
      row_hash[N]. This means:</t>

      <t>Insertion of a row is detectable (changes all subsequent row_id
      values and hashes).</t>

      <t>Deletion of a row is detectable (breaks the prev_hash link).</t>

      <t>Reordering of rows is detectable (changes prev_hash linkage).</t>

      <t>Modification of any field is detectable (changes the affected row's
      hash and all subsequent hashes).</t>
    </section>

    <section title="2.4 Chain Hash Computation">
      <figure>
        <artwork type="sourcecode"><![CDATA[
If rows is empty:
    chain_hash = SHA-256(b"empty")
Else:
    combined = concatenate(row_hash[1], row_hash[2], ..., row_hash[N])
    chain_hash = SHA-256(combined.encode("utf-8"))
]]></artwork>
      </figure>
      <t>The chain hash is represented as a lowercase hexadecimal string
      (64 characters).</t>
    </section>
  </section>

  <section title="3. Audit Row Schema">
    <section title="3.1 Row Format">
      <figure>
        <artwork type="sourcecode"><![CDATA[
{
  "id":           1,
  "session_id":   "sess-abc123",
  "action_type":  "tool_call",
  "tool_name":    "browser.navigate",
  "inputs_json":  "{\"url\": \"https://example.com\"}",
  "outputs_json": "{\"title\": \"Example Domain\"}",
  "cost_cents":   0,
  "error":        "",
  "timestamp":    1710252645.123456,
  "prev_hash":    "",
  "row_hash":     "a1b2c3d4e5f6..."
}
]]></artwork>
      </figure>
    </section>

    <section title="3.2 Field Specifications">
      <figure>
        <artwork type="table"><![CDATA[
| Field        | Type    | Required | Description                        |
|--------------|---------|----------|------------------------------------|
| id           | Integer | Yes      | Row identifier, 1-indexed,         |
|              |         |          | monotonically increasing.          |
| session_id   | String  | Yes      | Session identifier.                |
| action_type  | String  | Yes      | Action classification.             |
| tool_name    | String  | Yes      | Namespaced tool identifier.        |
| inputs_json  | String  | Yes      | JSON-encoded action inputs.        |
|              |         |          | Sensitive keys MUST be redacted.   |
| outputs_json | String  | Yes      | JSON-encoded action outputs.       |
|              |         |          | MAY be truncated.                  |
| cost_cents   | Integer | Yes      | Action cost in cents.              |
| error        | String  | Yes      | Error message; empty if success.   |
| timestamp    | Float   | Yes      | Unix timestamp, fractional secs.   |
| prev_hash    | String  | Yes      | Previous row's row_hash.           |
| row_hash     | String  | Yes      | This row's computed SHA-256 hash.  |
]]></artwork>
      </figure>
    </section>

    <section title="3.3 Sensitive Input Redaction">
      <t>Before computing the row hash, implementations MUST redact values
      for input keys matching any of the following case-insensitive
      substrings:</t>

      <figure>
        <artwork type="sourcecode"><![CDATA[
password, token, api_key, secret, key, authorization,
bearer, credential, passwd, passphrase
]]></artwork>
      </figure>

      <t>Redacted values MUST be replaced with the string "[REDACTED]".</t>
    </section>

    <section title="3.4 Output Truncation">
      <t>Implementations MAY truncate outputs_json to a maximum length. The
      reference implementation truncates to 2000 characters. Truncation does
      not affect the hash chain because outputs_json is not included in the
      row hash computation.</t>
    </section>
  </section>

  <section title="4. Ed25519 Signature">
    <section title="4.1 Signing">
      <figure>
        <artwork type="sourcecode"><![CDATA[
signature_bytes = Ed25519_Sign(private_key, chain_hash.encode("utf-8"))
signature_b64   = Base64_Encode(signature_bytes)
]]></artwork>
      </figure>
    </section>

    <section title="4.2 Identity Key Properties">
      <figure>
        <artwork type="table"><![CDATA[
| Property            | Value                                     |
|---------------------|-------------------------------------------|
| Algorithm           | Ed25519 (RFC 8032)                        |
| Private key size    | 32 bytes                                  |
| Public key size     | 32 bytes                                  |
| Storage format      | Raw bytes (not PEM)                       |
| Public key repr.    | 64-character lowercase hexadecimal string |
| File permissions    | 0600 (owner read/write only)              |
]]></artwork>
      </figure>
    </section>

    <section title="4.3 Signature File Format (session_sig.txt)">
      <figure>
        <artwork type="sourcecode"><![CDATA[
chain_hash:{64-char-hex-chain-hash}
signature:{base64-encoded-signature}
]]></artwork>
      </figure>
    </section>

    <section title="4.4 Signature is Optional">
      <t>Implementations MAY produce bundles without Ed25519 signatures.
      The hash chain provides tamper-evidence independent of the signature.
      The signature adds identity binding (proof of who produced the
      bundle).</t>
    </section>
  </section>

  <section title="5. AIVS Full Bundle Format">
    <section title="5.1 Archive Structure">
      <figure>
        <artwork type="sourcecode"><![CDATA[
aivs_proof_{session_prefix}_{unix_timestamp}.tar.gz
└── session_proof/
    ├── audit_log.jsonl       # Hash-chained action log
    ├── manifest.json         # Bundle metadata
    ├── session_sig.txt       # Ed25519 signature
    ├── public_key.pem        # Signer's public key
    └── verify.py             # Self-contained verifier (stdlib only)
]]></artwork>
      </figure>
    </section>

    <section title="5.2 manifest.json">
      <figure>
        <artwork type="sourcecode"><![CDATA[
{
  "session_id":    "sess-abc123",
  "exported_at":   "2026-03-14T15:30:45Z",
  "action_count":  42,
  "chain_hash":    "a1b2c3d4...",
  "aivs_version":  "1.0",
  "generator":     "ExampleAgent",
  "generator_url": "https://github.com/example/agent"
}
]]></artwork>
      </figure>
    </section>

    <section title="5.3 verify.py Requirements">
      <t>The embedded verifier script MUST:</t>

      <t>Verify the hash chain using only Python 3 standard library
      (hashlib, json, sys, pathlib).</t>

      <t>Exit with code 0 on successful verification.</t>

      <t>Exit with code 1 if the hash chain is broken or the signature
      is invalid.</t>

      <t>Print human-readable verification results to stdout.</t>

      <t>The embedded verifier MAY verify the Ed25519 signature if the
      cryptography library is available.</t>
    </section>

    <section title="5.4 Bundle Chaining (Optional)">
      <t>When a session produces multiple sequential bundles, each bundle
      MAY reference its predecessor via previous_bundle_hash.txt, containing
      the SHA-256 hex digest of the prior .tar.gz file. This forms a
      scan chain: a tamper-evident sequence of bundles where each bundle
      cryptographically references its predecessor.</t>
    </section>
  </section>

  <section title="6. AIVS-Micro">
    <section title="6.1 Purpose">
      <t>AIVS-Micro is a minimal single-URL scan attestation (~200 bytes)
      designed for use cases where a full session bundle is impractical:
      continuous monitoring, embedded score widgets, API responses, and
      DNS TXT record verification.</t>
    </section>

    <section title="6.2 Micro Proof Format">
      <figure>
        <artwork type="sourcecode"><![CDATA[
{
  "url":                  "https://example.com",
  "dom_hash":             "sha256:a1b2c3d4e5f6...",
  "timestamp":            "2026-03-14T10:22:01.000000000Z",
  "signature":            "ed25519:BASE64_ENCODED_SIGNATURE",
  "scanner_version_hash": "sha256:def456...",
  "scan_origin":          "local"
}
]]></artwork>
      </figure>
    </section>

    <section title="6.3 Canonical Payload for Signing">
      <figure>
        <artwork type="sourcecode"><![CDATA[
{url}|{dom_hash}|{timestamp}|{scanner_version_hash}|{scan_origin}
]]></artwork>
      </figure>
    </section>

    <section title="6.4 Micro Proof Verification">
      <figure>
        <artwork type="sourcecode"><![CDATA[
1. Reconstruct canonical payload:
   payload = "{url}|{dom_hash}|{timestamp}|{scanner_version_hash}|{scan_origin}"

2. Decode signature:
   sig_b64 = micro_proof["signature"].removeprefix("ed25519:")
   sig_bytes = Base64_Decode(sig_b64)

3. Verify:
   Ed25519_Verify(public_key, sig_bytes, payload.encode("utf-8"))

4. If verification succeeds: PASS
5. If verification fails:   FAIL
6. If signature == "unsigned": SKIP
]]></artwork>
      </figure>
    </section>
  </section>

  <section title="7. Verification Algorithm">
    <section title="7.1 Hash Chain Verification (REQUIRED)">
      <figure>
        <artwork type="sourcecode"><![CDATA[
Input:  audit_log.jsonl
Output: PASS or FAIL with row number

prev_hash = ""
for each row in audit_log.jsonl (ordered by id):
    expected = SHA-256(
        "{row.id}:{row.session_id}:{row.action_type}:"
        "{row.tool_name}:{row.cost_cents}:{row.timestamp}:{prev_hash}"
    )
    if row.row_hash != expected:
        FAIL at row.id
    prev_hash = row.row_hash

PASS: all N rows verified
]]></artwork>
      </figure>
    </section>

    <section title="7.2 Ed25519 Signature Verification (OPTIONAL)">
      <figure>
        <artwork type="sourcecode"><![CDATA[
Input:  session_sig.txt, public_key.pem
Output: PASS, FAIL, or SKIP

1. Parse chain_hash and signature from session_sig.txt
2. Parse public key hex from public_key.pem
3. If no signing key configured: SKIP
4. Reconstruct Ed25519PublicKey from raw bytes
5. Verify: Ed25519_Verify(public_key, signature,
           chain_hash.encode("utf-8"))
6. If verification succeeds: PASS
7. If verification fails:    FAIL (exit 1)
8. If cryptography library unavailable: SKIP with notice
]]></artwork>
      </figure>
    </section>

    <section title="7.3 Exit Codes">
      <figure>
        <artwork type="table"><![CDATA[
| Code | Meaning                                                       |
|------|---------------------------------------------------------------|
| 0    | Hash chain verified. Signature verified (if available).       |
| 1    | Hash chain broken OR signature invalid.                       |
]]></artwork>
      </figure>
    </section>
  </section>

  <section title="8. Security Considerations">
    <section title="8.1 What AIVS Proves">
      <t>Integrity: The sequence of actions has not been modified since the
      bundle was created.</t>

      <t>Completeness: No actions have been inserted or deleted from the
      chain.</t>

      <t>Ordering: Actions occurred in the recorded sequence.</t>

      <t>Identity (with signature): The bundle was produced by the holder
      of a specific Ed25519 private key.</t>
    </section>

    <section title="8.2 What AIVS Does NOT Prove">
      <t>Truthfulness: AIVS does not prove that the recorded inputs/outputs
      actually occurred. A malicious agent could fabricate actions and
      produce a valid chain.</t>

      <t>Timeliness: Timestamps are self-reported. Implementations requiring
      trusted timestamps SHOULD layer RFC 3161 on top.</t>

      <t>Key authenticity: AIVS does not include a PKI or certificate chain.
      The public key in the bundle is self-asserted.</t>
    </section>

    <section title="8.3 Threat Model">
      <figure>
        <artwork type="table"><![CDATA[
| Threat                            | Mitigated By                          |
|-----------------------------------|---------------------------------------|
| Post-hoc modification of log      | Hash chain (Section 2)                |
| Deletion of actions               | Sequential prev_hash chaining         |
| Insertion of actions              | Sequential row_id + prev_hash         |
| Reordering of actions             | prev_hash depends on previous hash    |
| Impersonation of agent identity   | Ed25519 signature (Section 4)         |
| Exposure of sensitive inputs      | Mandatory redaction (Section 3.3)     |
| Replay of old proof bundle        | session_id + timestamp uniqueness     |
]]></artwork>
      </figure>
    </section>
  </section>

  <section title="9. IANA Considerations">
    <t>This document defines no new IANA registries. The following existing
    standards are referenced: SHA-256 (FIPS 180-4), Ed25519 (RFC 8032),
    JSON (RFC 8259), gzip (RFC 1952).</t>
  </section>

  </middle>

  <back>
    <references title="Normative References">
      <reference anchor="RFC8032">
        <front>
          <title>Edwards-Curve Digital Signature Algorithm (EdDSA)</title>
          <author initials="S." surname="Josefsson"/>
          <author initials="I." surname="Liusvaara"/>
          <date year="2017" month="January"/>
        </front>
        <seriesInfo name="RFC" value="8032"/>
      </reference>
      <reference anchor="RFC8259">
        <front>
          <title>The JavaScript Object Notation (JSON) Data Interchange Format</title>
          <author initials="T." surname="Bray"/>
          <date year="2017" month="December"/>
        </front>
        <seriesInfo name="RFC" value="8259"/>
      </reference>
      <reference anchor="RFC1952">
        <front>
          <title>GZIP file format specification version 4.3</title>
          <author initials="P." surname="Deutsch"/>
          <date year="1996" month="May"/>
        </front>
        <seriesInfo name="RFC" value="1952"/>
      </reference>
    </references>

    <references title="Informative References">
      <reference anchor="RFC6962">
        <front>
          <title>Certificate Transparency</title>
          <author initials="B." surname="Laurie"/>
          <author initials="A." surname="Langley"/>
          <author initials="E." surname="Kasper"/>
          <date year="2013" month="June"/>
        </front>
        <seriesInfo name="RFC" value="6962"/>
      </reference>
      <reference anchor="RFC3161">
        <front>
          <title>Internet X.509 Public Key Infrastructure Time-Stamp Protocol (TSP)</title>
          <author initials="C." surname="Adams"/>
          <date year="2001" month="August"/>
        </front>
        <seriesInfo name="RFC" value="3161"/>
      </reference>
      <reference anchor="I-D.rosenberg-oauth-aauth">
        <front>
          <title>OAuth for Agentic AI Authorization</title>
          <author initials="J." surname="Rosenberg"/>
          <date year="2026"/>
        </front>
        <seriesInfo name="Internet-Draft" value="draft-rosenberg-oauth-aauth-01"/>
      </reference>
      <reference anchor="I-D.stone-vcap">
        <front>
          <title>VCAP: Verified Commerce for Agent Protocols</title>
          <author initials="B." surname="Stone"/>
          <date year="2026"/>
        </front>
        <seriesInfo name="Internet-Draft" value="draft-stone-vcap-00"/>
      </reference>
    </references>
  </back>
</rfc>
