Skip to main content

agent-action-capsule (reference library)

The reference implementation of the Agent Action Capsule profile: parse and seal a Capsule, and run the Class 1 verifier defined in the Internet-Draft (../spec/). Published to PyPI as agent-action-capsule. See RELEASING.md for the release workflow and PyPI Trusted Publisher setup.

The spec (../spec/) is the source of truth. Where the draft says MUST / MUST NOT, the code and tests enforce it, section by section.

Quickstart (30 seconds)

Install the payload-only core (stdlib only — zero required dependencies):

pip install agent-action-capsule        # from a checkout: cd python && pip install .

Verify a conformant capsule — point at a shipped positive vector:

$ agent-action-capsule verify ../vectors/capsule/pos-executed-confirmed/input.json
Agent Action Capsule — Class-1 payload verification: ../vectors/capsule/pos-executed-confirmed/input.json
  ok: True
  capsule_id (recomputed): 5b7c4ff1bcf4364e0dd8c8b65eddddbb1d6102f27bed3ad1a3595cba51d3502a
  derived: effect_mode=confirmed attestation_mode=self_attested ledger_mode=standalone
  findings: none
# exit code 0

Verify a failing capsule — the finding is self-explaining, with its §6 check number:

$ agent-action-capsule verify ../vectors/capsule/neg-confirmed-without-response/input.json
  ok: False
  ...
  findings:
    - [error] (check 3) confirmed_without_response: effect.status 'confirmed' requires a 64-hex response_digest (§5.2)
# exit code 1  (0 = ok, 1 = not ok, 2 = could not run / malformed input)

Add --json for the raw VerificationResult, or --store <dir-or-ledger-file> for store-level chain checks (supersedes / concurrent-supersedes / open-items).

The two layers

Verification is two layers, and this package is honest about which it does:

Layer Who verifies it This package
payload — the Agent Action Capsule (Class-1, §6): capsule_id, confirmed-effect binding, effect_attestation matrix, disposition, assurance this profile ✅ implemented
producer authentication — exact three-header Producer Envelope over the raw Capsule ID this profile ✅ implemented by producer_envelope.py
transparency substrate — distinct RFC 9943 registration statement plus Receipt / inclusion proof SCITT/COSE, by reference calls scitt-cose — never reimplemented

The current AAC Producer Envelope is separate from an RFC 9943 Signed Statement: it signs the raw 32-byte Capsule ID with exactly three protected headers and does not carry RFC 9943's required CWT claims. Install agent-action-capsule[envelope] and call verify_producer_envelope(capsule_id, envelope_bytes) to verify that profile. The function returns the authenticated public key; it does not decide whether the key is authorized for an operator or developer.

from agent_action_capsule.producer_envelope import verify_producer_envelope

To verify an Evidence Bundle's interval coverage and per-record membership (agent_action_capsule.bundle.verify_bundle), install the bundle extra. It adds the public CLL reference that checks the MMR proofs:

pip install 'agent-action-capsule[bundle]'

Without it those two claims fail closed as completeness_verifier_unavailable. That finding means this installation could not check the proofs. It does not mean the certificate is invalid.

To verify both layers of a SCITT Signed Statement, install the optional extra and pass --transparent:

pip install 'agent-action-capsule[transparent]'     # adds the public scitt-cose verifier

agent-action-capsule verify --transparent statement.cose --issuer-key issuer_pub.pem [--log-key log_pub.pem --leaf-entry-hex <hex>]

For an RFC 9943 Capsule-ID registration statement, this command authenticates and prints the raw Capsule ID. Class-1 verification of the Capsule JSON is a separate operation. For a JSON-bearing Signed Statement, it also runs Class-1 verification over the authenticated payload.

  substrate (SCITT/COSE, via scitt-cose):
    signature_verified : True
    receipt_verified   : True
    attestation_tier   : anchored        # 'anchored' ONLY when a receipt actually verified (§3.2)
  payload (Agent Action Capsule, Class-1):
  ok: True

Without a verified receipt the substrate tier is self_attested, never anchored. The optional extra is public (scitt-cose, deps cbor2 + cryptography); the default install pulls nothing extra.

Build → verify (producer side)

../examples/build_and_verify.py is the smallest honest round trip: construct a capsule with the typed builders, seal() it (compute capsule_id), then verify() it — an executed action and a blocked one, in ~40 lines.

Conformance vectors

../vectors/capsule/ is the frozen conformance suite a second implementer runs against their own verifier: each input.json plus its spec-anchored expected.json (ok, the §6 check numbers + severities, the derived modes, the recomputed capsule_id). See ../spec/section-map.md for the spec section map.

What it implements

Module Spec Implements
canonical.py §2, §5.1 Current JSON-DIGEST uses plain RFC 8785 JCS. Format-4 capsule_id excludes itself and the local-only producer-envelope fields (signature, key_id) — those are attached to the ledger line after the id is computed, so they can never be part of its preimage — while committing the declaration and chain. This reference is format-4-only: any other format_version is rejected with unsupported_format_version, and the legacy absent-field construction has been removed. Vintage format-2 records are verified with the frozen legacy-verify/v0.1.0 release, not this reference.
producer_envelope.py §3, §6 Optional exact-profile COSE_Sign1 verification over the raw 32-byte Capsule ID. Returns the authenticated Ed25519 key; caller authorization remains separate.
registries.py §12 + Disclosure Envelope §4 Loads the seven registries (incl. citation_purpose, draft-04) from ../spec/REGISTRY.md and exports the companion disclosure-eligibility table.
disclosure_envelope.py Disclosure Envelope DE-1–DE-3 Runs Class 1 over the embedded Capsule independently, then validates eligibility, committed-digest presence, and disclosure JSON-DIGEST equality.
contracts.py §5.2–§5.4, §5.5.5 Typed producer carriers whose constructors enforce the invariants a producer MUST NOT violate: the disposition honesty invariant and the closed approver enum (§5.4), the confirmed-effect binding and the status/digest table (§5.2), and references[] entry structure / AAC self-identity digest format (§5.5.5). A non-conforming Capsule cannot be built. Also the effect_mode derivation (§5.2) and the never-dispatch set (§5.4.2).
verify.py §6, §5.5.5 The Class 1 verifier: the eight checks in fixed order plus references[] findings (§5.5.5, spliced into checks 1/6/8), a structured result that never throws, a single ok boolean, store-level chain checks (verify_store), and the SHOULD-level defensive disposition-honesty assert over arbitrary bytes. Unknown registry values are informational, never a rejection.
parse.py §5, §5.5.5 Capsule builder + seal() (computes capsule_id); strict parse_capsule (raises on a non-conforming Capsule). references[] is tri-state: absent, present-and-empty, and present-and-populated are three distinct wire forms (and therefore three distinct capsule_id digests).
from agent_action_capsule import verify, Capsule, EffectRecord, Disposition, AssuranceBlock

capsule = Capsule(
    spec_version="draft-mih-scitt-agent-action-capsule-05", format_version="4",
    canonicalization_id="jcs",
    action_id="po-12345", action_type="decide", operator="ACME-CO", developer="agent@v1",
    timestamp="2026-06-13T00:00:00Z",
    effect=EffectRecord(status="confirmed", type="write_order",
                        response_digest="a"*64, effect_attestation="gate_executed"),
    assurance=AssuranceBlock(attestation_mode="self_attested", effect_mode="confirmed",
                             ledger_mode="standalone"),
    disposition=Disposition(decision="accept", approver="human", human_disposed=True,
                            verdict_class="executed"),
).seal()

result = verify(capsule)        # never throws
assert result.ok               # a single `ok` gates trust in every other field

Cross-record references (references[], §5.5.5)

chain is exclusively a Capsule's own same-stream parent. references cites everything else — a different producer's Capsule, or any other artifact this action targeted or responded to. Absent and an explicit empty array are semantically the same ("no such citation") but are distinct bytes, so they produce different capsule_id digests — pass references=() only when that distinction matters to a caller:

from agent_action_capsule import Capsule, ReferenceEntry, LogCoordinates

capsule = Capsule(
    spec_version="draft-mih-scitt-agent-action-capsule-05", format_version="4",
    canonicalization_id="jcs",
    action_id="deny-42", action_type="decide", operator="ACME-CO", developer="agent@v1",
    timestamp="2026-09-08T00:00:00Z",
    references=(
        ReferenceEntry(
            type="agent-action-capsule", digest_alg="SHA-256",
            digest="b" * 64,                    # the cited Capsule's capsule_id
            citation_purpose="responds_to",      # or "acted_on"; §12 citation_purpose registry
        ),
        ReferenceEntry(
            type="foreign-artifact", digest_alg="future-hash", digest="opaque-digest",
            log_coordinates=LogCoordinates(log_id="example-log", leaf_index=3, inclusion_proof={...}),
        ),
    ),
).seal()

capsule["references"]  # [{"type": ..., "digest_alg": ..., "digest": ..., "citation_purpose": ...}, {...}]

A references entry MUST NOT duplicate chain.parent_capsule_id — citing the producer's own same-stream parent is stated once, in chain, never redundantly in references (Capsule(...) raises InvariantError if it does). Only the agent-action-capsule/SHA-256 self-identity context constrains digest to 64-lowercase-hex; every other type/digest_alg combination is CPB's to define and stays open. log_coordinates, when present, is an upgrade (not a second identity) and its inclusion_proof is never independently verified by Class 1.

Scope boundary (deliberate)

Does — the Class 1 agent-profile surface (§6), performable from the Capsule's own bytes plus the registry contents (and, for chain checks, the producer's store of Capsules).

Does NOT —

  • Substrate verification. The COSE_Sign1 signature, registration, and the Receipt's inclusion proof are the SCITT/COSE substrate's, by reference (scitt-cose). This package never derives anchored; a claimed anchored mode is reported as an unverifiable overclaim (§5.3).
  • Class 2 / manifest-aware verification (§8.2). Constraint Records are represented as data (§8.1) but no manifest is fetched and no evidence-schema check is performed.

Develop / test

cd python
pip install -e ".[dev]"
python -m pytest -q        # positive + negative (MUST-reject) suite
python -m ruff check .

The test suite is the conformance contract: every MUST / MUST NOT in the implemented sections has a positive and a negative case, and the frozen byte-level vectors under ../vectors/capsule/ are replayed through verify() / verify_store(). The two-layer (--transparent) tests run only when the optional [transparent] extra is installed, and skip cleanly otherwise.

Metadata

Release files for agent-action-capsule 0.7.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for agent-action-capsule 0.7.1
File Size Uploaded
agent_action_capsule-0.7.1.tar.gz 173.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for agent-action-capsule 0.7.1
File Interpreter ABI Platform
agent_action_capsule-0.7.1-py3-none-any.whl Python 3 none any Details

Total release size: 265.4 kB

Release files / agent_action_capsule-0.7.1.tar.gz

Download URL agent_action_capsule-0.7.1.tar.gz
Size 173.7 kB
Tags Source
SHA-256 checksum
How to use checksums
65dd3aeabf92df1836f35d4d414d2edb9cea78e4ef8771f9ccf9af7aaf783cc2
BLAKE2b-256 checksum
How to use checksums
c3b2c57a8ac726b976b99b329c491d8277410ae28cb36462f852cdef036e3995
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 14, 2026.

Transparency log

Release files / agent_action_capsule-0.7.1-py3-none-any.whl

Download URL agent_action_capsule-0.7.1-py3-none-any.whl
Size 91.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
357ac7eb88eb12033168e7585e42eaa20b3e6025f1ad1c740f6f093467598ad2
BLAKE2b-256 checksum
How to use checksums
d8fab4ef6fd90bddaea6f1d37d09984075ea719455f3b7d1844ad69d21b43728
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 14, 2026.

Transparency log

Release history Release notifications | RSS feed

0.9.1

2 release files

0.9.0

2 release files

0.8.1

2 release files

0.8.0

2 release files

This release

0.7.1 This release

2 release files

0.7.0

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.0

2 release files

0.0.3

2 release files

0.0.2

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page