Normative documents
Full text of the canonical normative documents for ACTIS v1.
ACTIS v1 — Autonomous Coordination & Transaction Integrity Standard
Version: 1.0
Status: Normative
Protocol identifier: actis/1.0
This document is the single normative entrypoint for the ACTIS standard. It defines integrity verification and replay semantics for signed, hash-linked transaction evidence. It does not define blame, reputation, risk scoring, or settlement rails.
Normative: The body of this document is normative. Examples and developer notes are labeled informative.
Normative Language
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.
Goals
ACTIS provides a deterministic and vendor-neutral procedure for verifying the integrity of autonomous transaction evidence.
Non-Goals
ACTIS does not define:
- transaction semantics
- settlement protocols
- dispute resolution
- identity systems
- reputation systems
ACTIS is an integrity layer, not an application protocol. The following are not part of ACTIS and MUST NOT appear as normative requirements or as keys in the canonical ACTIS verification report:
- Blame or fault determination (who is at fault).
- Reputation, confidence scoring, or risk tiers.
- Risk or actuarial scoring (e.g. outcome probability estimates).
- Settlement rails, payment, or money movement.
- Claim qualification, disbursement, or underwriting decisions.
- Identity or credential verification beyond what is required to verify signatures on the transcript.
Implementations that produce or consume such information MUST treat it as optional, non-ACTIS data (e.g. in separate attachments or clearly labeled non-ACTIS sections).
1. Purpose
ACTIS specifies:
- Integrity verification — Whether evidence is cryptographically intact: signature validity, hash-chain validity, and checksum consistency.
- Replay — Whether a transcript can be deterministically replayed and produces the same integrity outcomes (e.g. same round hashes, same final hash).
Conformance to ACTIS means a verifier accepts only the inputs and outputs defined in this standard and reports only the fields defined in the verification report schema (§5). Any additional inputs, outputs, or semantics are out of scope.
2. Canonical Identifiers (v1.0.0)
The following identifiers are the normative reference for implementations. They MUST be used when referencing the standard, schema, report format, or bundle format in manifests, tooling, or documentation.
| Identifier | Value |
|---|---|
| ACTIS_STANDARD_ID | ACTIS/1.0.0 |
| TRANSCRIPT_SCHEMA_ID | actis.transcript.v1 |
| REPORT_SCHEMA_ID | actis.report.v1 |
| BUNDLE_FORMAT_ID | actis.bundle.v1 |
Full definitions and stability rules: ACTIS_CANONICAL_IDS.md (repository path: actis/ACTIS_CANONICAL_IDS.md).
3. Formats
3.1 Transcript
- A transcript is a JSON document that represents a single negotiation or transaction session.
- It MUST be hash-linked: each round (or equivalent unit) MUST include or reference a content hash; the chain MUST link to a final hash.
- It MUST include signature material so that each required signer's contribution can be verified.
- Schema and version requirements are defined by the transcript schema in use (e.g. a versioned transcript format). ACTIS verifiers MUST validate schema and version before reporting integrity. Strings MUST be interpreted exactly as encoded in the JSON document without Unicode normalization.
3.2 Manifest
- A manifest is a JSON document that describes an evidence bundle.
- It MUST list the bundle's contents (e.g. file paths or entry identifiers) and MAY list content hashes or other integrity metadata.
- For ACTIS-aligned bundles, the manifest MAY include:
standard:{ "name": "ACTIS", "version": "1.0" }core_files: array of paths required for ACTIS verification (e.g.["checksums.sha256", "manifest.json", "input/transcript.json"]).optional_files: array of paths that are not required for ACTIS validity.
When standard.name is "ACTIS", verification MUST require only the files listed in core_files (or the default core set). All other files are optional for ACTIS pass/fail.
3.3 Bundle
- A bundle is a container (e.g. a ZIP archive) that includes at least:
- A manifest.
- A transcript (e.g. at a path such as
input/transcript.json). - Integrity data (e.g. a checksum file) covering at least the core files.
- Additional files MAY be present. If the manifest declares them as optional, they MUST NOT affect ACTIS verification outcome (pass/fail). Verifiers MAY report their presence as a neutral notice (e.g.
optional_attachments_present).
4. Verification semantics
Verification outcomes MUST be computed using the algorithms defined in ACTIS_COMPATIBILITY.md.
- Checksums: All paths in the manifest's core set (or default core set) MUST be present in the bundle and MUST match the checksums in the bundle's checksum file.
- Hash chain: The transcript's hash chain MUST be valid (each round's hash matches recomputation; final hash consistent).
- Signatures: All required signatures on the transcript MUST verify. Signatures are over the round’s envelope_hash; the construction of envelope_hash is defined in ACTIS_COMPATIBILITY.md.
- Replay: For v1.0, replay is hash-chain recomputation only: recompute round hashes and final_hash (if present) and confirm equality with claimed values. For v1.0,
replay_okSHALL equalhash_chain_ok. Signature validity is reported viasignatures_okonly. See ACTIS_COMPATIBILITY.md for normative algorithms (envelope hash, round/transcript hashing, signature verification, and actis_status decision tree).
A bundle passes ACTIS verification if and only if all of the above hold. No other criteria (e.g. presence or content of optional files) may affect the ACTIS pass/fail result.
5. Verification report schema
The canonical ACTIS verification report is a single JSON object. No key in this object may encode blame, reputation, risk scoring, or settlement. The following keys are normative.
Canonical status field: actis_status is the canonical status field and MUST be one of:
ACTIS_COMPATIBLEACTIS_PARTIALACTIS_NONCOMPLIANT
integrity_status is a deprecated compatibility alias retained for legacy implementations. Implementations MAY emit additional diagnostic values in integrity_status (e.g. "TAMPERED", "VALID", "INDETERMINATE"), but these MUST map to one of the canonical actis_status values. Consumers MUST use only actis_status for conformance determination.
Required:
| Field | Type | Description |
|---|---|---|
actis_version | string | ACTIS version, e.g. "1.0". |
actis_status | string | Canonical conformance level. One of exactly: ACTIS_COMPATIBLE, ACTIS_PARTIAL, ACTIS_NONCOMPLIANT. Case-sensitive. |
signatures_ok | boolean | All required signatures verified. |
hash_chain_ok | boolean | Hash chain intact. |
schema_ok | boolean | Transcript schema valid. |
replay_ok | boolean | Deterministic replay succeeded. |
warnings | string[] | Neutral warnings (no blame or risk). |
Optional:
| Field | Type | Description |
|---|---|---|
integrity_status | string | Deprecated alias. One of: "VALID", "TAMPERED", "INDETERMINATE". When present, MUST match the semantics of actis_status (VALID↔ACTIS_COMPATIBLE, INDETERMINATE↔ACTIS_PARTIAL, TAMPERED↔ACTIS_NONCOMPLIANT or ACTIS_PARTIAL per ACTIS_COMPATIBILITY.md). New implementations SHOULD emit actis_status and MAY omit integrity_status. |
checksums_ok | boolean | Bundle checksums valid for core files. |
recompute_ok | boolean | Recomputed artifacts match bundle (when applicable). |
evidence_refs_checked_count | number | Number of evidence refs checked. |
files_hashed_count | number | Number of files hashed in replay. |
optional_attachments_present | string[] | Namespaces of optional attachments present (e.g. ["vendor/*"]). Informational only. |
Informative example:
{
"actis_version": "1.0",
"actis_status": "ACTIS_COMPATIBLE",
"signatures_ok": true,
"hash_chain_ok": true,
"schema_ok": true,
"replay_ok": true,
"checksums_ok": true,
"warnings": []
}
6. Compatibility requirements
- Implementations that claim ACTIS conformance MUST consume transcripts and bundles that satisfy the format requirements in §3 and MUST produce reports that conform to §5.
- Consumers of ACTIS reports MUST treat only the fields defined in §5 as normative for ACTIS; any extra fields are non-ACTIS and must not be used to infer ACTIS pass/fail.
- Backward compatibility: Future ACTIS versions that add optional report fields or optional manifest fields MUST remain backward compatible for the core semantics (integrity, replay, report schema). Breaking changes require a new major version of the standard.
7. Security Considerations
7.1 Security Properties
ACTIS v1.0 provides the following guarantees for a presented transcript bundle:
- Transcript integrity — Any modification to a round's fields breaks the hash chain and is detectable.
- Signature authenticity — Each round's envelope_hash is signed with Ed25519. A verifier with the signer's public key can confirm the round was produced by the claimed key holder.
- Truncation resistance — Each round binds its predecessor via
previous_round_hash. Removing any round from the middle or end of the chain breaks the chain and failshash_chain_ok. - Round substitution resistance — A round from a different position or transcript cannot be silently inserted; the hash chain binding will break.
- Cross-protocol signature resistance — The signing message is
utf8("ACTIS/v1") || hex_decode(envelope_hash). Signatures produced for ACTIS v1.0 are not valid in other protocol contexts that use a different domain string. - Replay determinism — Hash chain recomputation is deterministic. Two conformant verifiers presented with the same bundle MUST reach the same
hash_chain_okresult.
7.2 Known Limitations
Equivocation (split-view attack)
ACTIS verifies that a presented transcript bundle is internally intact and replay-consistent. ACTIS does not by itself prove that no alternate valid transcript exists for the same underlying transaction.
An actor in possession of a valid signing key may produce two or more transcripts that each verify independently under ACTIS. Both will be ACTIS_COMPATIBLE. They may describe contradictory realities.
This is the equivocation or split-view attack — the same class of problem as Certificate Transparency log forks and distributed ledger equivocation.
ACTIS v1.0 explicitly does not close this gap. Preventing equivocation requires coordination mechanisms outside the scope of this standard, such as:
- Bilateral counter-signing, where each participant signs the other's rounds, creating mutual acknowledgment of a single shared history.
- Publication to an append-only transparency log, enabling detection of duplicate
transcript_idcommitments. - External monotonic transaction identifiers or registry anchoring that binds a transaction to exactly one transcript.
Implementers and integrators operating in adversarial environments SHOULD layer one of the above mechanisms on top of ACTIS verification. The absence of such a layer means that ACTIS verification alone cannot rule out the existence of a competing valid transcript.
Key compromise
ACTIS verification confirms that a signature is valid for a given public key. It does not verify that the signing key was not compromised at the time of signing. Key lifecycle management, revocation, and hardware security are outside the scope of this standard.
Off-chain collusion
ACTIS does not defend against colluding participants who agree off-chain to produce a false but internally consistent transcript. A transcript that is ACTIS_COMPATIBLE reflects only that its internal structure is intact, not that the events it describes occurred.
7.3 Threat Model Summary
| Attack | ACTIS Defense | Gap |
|---|---|---|
| Transcript field tampering | Hash chain breaks | None |
| Round truncation | Chain completeness check | None |
| Round substitution | prev_hash + round_index binding | None |
| Signature forgery | Ed25519 verification | None |
| Cross-protocol signature reuse | Domain string "ACTIS/v1" | None |
| Equivocation / split-view | — | Not closed in v1.0; requires external coordination |
| Compromised signing key | — | Out of scope; key management is implementer responsibility |
| Off-chain collusion | — | Out of scope by design |
Developer note (Informative): separation of concerns
- ACTIS defines only neutral verification (integrity, replay, report schema). Conformant implementations MUST NOT require or normatively reference blame, reputation, risk scoring, or settlement.
- Vendor-specific extensions (e.g. fault determination, actuarial snapshots, reputation snapshots, underwriting summaries) are optional, non-standard attachments. They MUST be clearly labeled as such and MUST NOT affect ACTIS pass/fail. Bundles and UIs should keep them in separate namespaces or panels from the ACTIS verification result.
Non-normative examples: multiple implementations may exist. ACTIS conformance is determined by the normative documents and the test vector corpus; no specific implementation is required.
ACTIS is an open standard. Contributions and implementations from any party are welcome.
ACTIS v1 — Compatibility and Verification Algorithms
Version: 1.0
Status: Normative
Entrypoint: ACTIS_STANDARD_v1.md
This document defines the normative algorithms for ACTIS v1.0 verification: envelope hash construction, round and transcript hashing, signature verification, and the actis_status decision tree. Implementations MUST follow these algorithms to be conformant.
Normative: The algorithms and decision rules in this document are normative. Formulas and worked examples are labeled informative.
1. Normative Language
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" are to be interpreted as in RFC 2119 and RFC 8174.
2. Envelope Hash Construction
envelope_hash is a round-local digest. It is the hash of a canonical JSON serialization of the round’s envelope object. The signature in the round is computed over this digest (the 64-character hex string), not over the raw envelope. The envelope object MUST NOT include the signature or the envelope_hash field itself.
2.1 Envelope Object (round-local)
The envelope object is a JSON object containing only the following round fields, with all other round fields excluded:
Included (if present in the round):
round_numberround_typemessage_hashtimestamp_msprevious_round_hashround_hash(optional in round; include in envelope object only if present in the round)agent_id(optional; include only if present)public_key_b58(optional; include only if present)content_summary(optional; include only if present)
Excluded:
envelope_hash— excluded so the digest is not self-referential.signature— excluded; the signature is over the digest of the envelope, not part of the envelope.
No transcript-level fields (e.g. transcript_id, intent_id) are part of the envelope. The envelope is round-local only.
2.2 Serialization and Digest
- Build the envelope object from the round by taking only the included keys listed in §2.1 (with the same values as in the round). Omit any key not present in the round.
- Serialize the envelope object to canonical JSON per RFC 8785 (JSON Canonicalization Scheme, JCS): deterministic key ordering, no unnecessary whitespace, deterministic encoding of numbers and strings. Implementations MUST use an RFC 8785–compliant canonicalizer (or equivalent that produces the same output). Strings MUST be interpreted exactly as encoded in the JSON document without Unicode normalization.
- Compute SHA-256 over the UTF-8 encoding of the canonical JSON string.
- Encode the digest as exactly 64 lowercase hexadecimal characters (no prefix, no spaces).
Formula (informative):
envelope_object = { round fields except "envelope_hash" and "signature" }
envelope_hash = to_lower_hex( SHA-256( utf8( canonical_json( envelope_object ) ) ) )
2.2.1 Encoding Rationale (Informative)
ACTIS v1.0 uses RFC 8785 (JCS) as the canonical serialization for all hash inputs. JCS was chosen for the following reasons:
- Human-readable without a decoder. JSON transcripts can be inspected by auditors, legal reviewers, and compliance teams using any text editor, without tooling dependencies.
- Low integration barrier. JSON parsers are available in every major language and runtime. Enterprise integrators can adopt ACTIS without introducing binary encoding dependencies.
- Deterministic under RFC 8785. JCS defines unambiguous key ordering, number encoding, and string handling. Implementations that follow RFC 8785 produce byte-identical output for the same logical input.
CBOR as a tracked v2 path. Deterministic CBOR (RFC 8949 + RFC 8943 or CBOR Deterministic Encoding) offers stricter encoding guarantees, smaller wire size, and no dependence on a canonicalization layer over an inherently order-agnostic format. These properties make it a strong candidate for a future ACTIS v2 encoding profile. The decision to use JCS in v1 is pragmatic and intentional; implementers who raise CBOR as a concern will find this documented answer rather than silence.
Implementations MUST NOT substitute CBOR or any other encoding for JCS in ACTIS v1.0. A future version that adopts CBOR will define a new canonicalization algorithm identifier and maintain a separate conformance corpus.
2.3 Signature Binding
The signing message is the 40-byte concatenation of the UTF-8 encoding of the domain string "ACTIS/v1" (8 bytes) followed by the 32-byte binary value obtained by hex-decoding the round's envelope_hash field (64 lowercase hex characters):
signing_message = utf8("ACTIS/v1") || hex_decode(envelope_hash) // 8 + 32 = 40 bytes
Implementations MUST construct the signing message exactly as above. The domain string prevents cross-protocol signature reuse. The raw hex-decoded envelope_hash alone is NOT the signing message, and the UTF-8 encoding of the hex string is NOT the signing message.
Verifiers MAY recompute the envelope object from the round (excluding envelope_hash and signature), compute the digest, and compare it to the round's envelope_hash to detect tampering of envelope fields.
2.4 Informative example (pseudocode)
Round (excerpt):
round_number: 0
round_type: "INTENT"
message_hash: "a1b2..."
envelope_hash: "<to be computed>"
signature: { ... } // not part of envelope
Envelope object (input to hash):
{ "message_hash": "a1b2...", "previous_round_hash": "c3d4...", "round_hash": "e5f6...", "round_number": 0, "round_type": "INTENT", "timestamp_ms": 1709500000000 }
Canonical JSON string → SHA-256 → 64 lowercase hex → envelope_hash.
Then: signing_message = utf8("ACTIS/v1") || hex_decode(envelope_hash). // 8 + 32 = 40 bytes
signature = Ed25519_sign(signing_message).
3. Round and Transcript Hashing
3.1 Round 0 — previous_round_hash (genesis)
For round 0 there is no previous round. The genesis value is:
previous_round_hash = to_lower_hex( SHA-256( utf8( intent_id + ":" + created_at_ms ) ) )
intent_id and created_at_ms are the transcript-level values. The string is the concatenation of intent_id, the literal character :, and the decimal representation of created_at_ms (no leading zeros, no JSON wrapping). No Unicode normalization or trimming is applied to intent_id.
3.2 Round hash (per round)
For each round, the round_hash is the SHA-256 digest of the canonical JSON serialization of the round object excluding the round_hash and signature fields. RFC 8785 canonicalization MUST be used. The result is 64 lowercase hex characters.
round_object_for_hashing = round with keys "round_hash" AND "signature" removed
round_hash = to_lower_hex( SHA-256( utf8( canonical_json( round_object_for_hashing ) ) ) )
Invariant: hash_chain_ok MUST NOT depend on signature verification. Signatures are attestations, not structural state; they are excluded from structural hashing.
3.3 Chain linkage
For round index i > 0, round[i].previous_round_hash MUST equal the round_hash of round i-1 (or the digest computed from round i-1 as in §3.2 if round_hash is omitted).
3.4 Final hash (transcript)
The final_hash of the transcript is the SHA-256 digest of the canonical JSON serialization of the entire transcript object excluding the final_hash field. The model_context field (if present) is excluded from the object before serialization so that MRM metadata does not affect determinism. RFC 8785 MUST be used. The result is 64 lowercase hex.
transcript_for_hash = transcript with "final_hash" and "model_context" removed
final_hash = to_lower_hex( SHA-256( utf8( canonical_json( transcript_for_hash ) ) ) )
3.5 Hash Chain Framing (Informative)
A common attack against hash-chain protocols is framing ambiguity: if a chain
is constructed by raw concatenation of variable-length fields, then
hash("ab" + "c") equals hash("a" + "bc"), allowing an attacker to produce
two different logical inputs that yield the same hash.
ACTIS v1.0 is not vulnerable to this attack. The round_hash and final_hash are computed over the RFC 8785 canonical JSON serialization of a structured object, not over raw field concatenation. RFC 8785 produces an unambiguous, length-delimited encoding for every field. Two distinct JSON objects with different field values or structure cannot produce the same canonical serialization, and therefore cannot produce the same hash.
Implementations MUST use an RFC 8785-compliant canonicalizer. The use of raw string concatenation or non-canonical serialization in place of JCS is a conformance defect and voids the framing ambiguity protection.
3.6 Replay Determinism (Normative)
Replay MUST be deterministic. Two conformant verifiers presented with the same
bundle MUST produce identical hash_chain_ok and replay_ok results.
The following are forbidden sources of nondeterminism in replay:
- Timestamps and wall-clock time — Verifiers MUST NOT read the system clock during replay. All timestamps used in hash computation MUST come from the transcript fields.
- Random number generation — Verifiers MUST NOT use RNG during replay.
- Locale and collation — Verifiers MUST NOT apply locale-specific string ordering or comparison during canonicalization. RFC 8785 key ordering is lexicographic by Unicode code point, not locale-dependent.
- Floating-point serialization — Verifiers MUST NOT use platform-specific float-to-string conversion. RFC 8785 defines deterministic number encoding; implementations MUST follow it.
- Unicode normalization — Verifiers MUST NOT apply NFC, NFD, NFKC, or NFKD normalization to string values during canonicalization. Strings MUST be interpreted exactly as encoded in the JSON document.
Any input that affects replay outcome MUST be recorded in the transcript itself. External state that is not recorded in the transcript MUST NOT affect replay.
4. Signature Verification
- Scheme: ACTIS v1.0 supports Ed25519 only. The signature is a detached Ed25519 signature.
- Message signed: The signing message is the 40-byte concatenation of utf8("ACTIS/v1") (8 bytes) and the 32-byte binary value obtained by hex-decoding the round's envelope_hash field (64 lowercase hex characters → 32 raw bytes): signing_message = utf8("ACTIS/v1") || hex_decode(envelope_hash). Implementations MUST use this exact construction. The raw hex-decoded envelope_hash alone is NOT the signing message.
- Verification: The verifier MUST decode signature.signer_public_key_b58 and signature.signature_b58 (Base58), construct the 40-byte signing message as utf8("ACTIS/v1") || hex_decode(envelope_hash), and verify that the signature is valid for the public key over that message. If the round’s public_key_b58 is present, it MUST match signature.signer_public_key_b58 for the round to be considered valid.
5. Manifest and Bundle Rules
Manifest: The bundle MUST contain a manifest.json at the root (or path declared in the bundle format). The manifest MUST include a core_files array: relative paths, unique, forward slashes, no ../, no absolute or drive paths. The minimal ACTIS core set is ["checksums.sha256", "manifest.json", "input/transcript.json"]. See ACTIS_AUDITOR_PACK.md §2 and schema actis/schemas/actis_manifest_v1.json. The checksum file (e.g. checksums.sha256) MUST NOT list itself; it lists the other core files only.
Bundle security:
- Checksums apply only to paths listed in
core_files. Only those paths are in the ACTIS verification surface. Unlisted files MUST NOT affect actis_status; verifiers SHOULD warn when unlisted files are present. - Core file paths in the manifest MUST be unique. Duplicate archive entries for the same core path MUST result in ACTIS_NONCOMPLIANT. Symlinks for core paths MUST be rejected. Path traversal (e.g.
../, absolute paths, drive prefixes) in core paths MUST be rejected. - ACTIS_COMPATIBLE means the verified core surface (transcript, hash chain, signatures, listed checksums) is intact. It does not imply that the entire archive is safe to execute or trust beyond that surface. Verifiers MAY add a warning (e.g. in
warnings) when unlisted or suspicious entries are present.
6. actis_status Decision Tree
Verification MUST derive actis_status from the following (and only the following) checks:
- Schema/layout: Transcript conforms to schema; required fields present; bundle layout valid; manifest core files present and unique; no duplicate core path entries; no symlinks or path traversal for core paths.
- Hash chain: All round hashes and previous_round_hash linkage valid; final_hash (if present) matches recomputation.
- Checksums: All core files listed in manifest match checksums (if checksum file present).
- Evidence refs: All artifacts referenced in evidence_refs arrays are present in the bundle (see §8).
- Signatures: All rounds have valid Ed25519 signatures over the 40-byte domain-separated message (see §4).
ACTIS_COMPATIBLE: All of (1)–(5) pass.
ACTIS_PARTIAL: (1)–(4) pass; one or more signatures missing or invalid. No other failure.
ACTIS_NONCOMPLIANT: Any of (1)–(4) fail, or any failure other than signature-only.
Implementations MUST NOT use blame, reputation, risk, or settlement to determine actis_status. Only the checks above are normative.
7. References
- RFC 2119: Key words for use in RFCs
- RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words
- RFC 8785: JSON Canonicalization Scheme (JCS)
- RFC 8949: Concise Binary Object Representation (CBOR) (tracked v2 encoding path)
- NIST FIPS 180-4: Secure Hash Standard (SHA-256)
8. Evidence Omission Enforcement (Normative)
If a transcript round or failure_event contains an evidence_refs array, all
referenced artifacts MUST be present in the bundle for the bundle to be
considered complete.
Verifiers MUST check that every entry in every evidence_refs array resolves
to an artifact present in the bundle. If any referenced artifact is absent,
verification MUST fail with actis_status: ACTIS_NONCOMPLIANT.
This rule closes the evidence omission attack: an attacker cannot exclude
unfavorable evidence from a bundle while keeping the remaining structure intact.
A bundle that is missing referenced evidence is not a complete ACTIS bundle and
MUST NOT receive ACTIS_COMPATIBLE or ACTIS_PARTIAL status.
Verification order (normative):
Implementations MUST check bundle integrity in the following order and MUST fail fast at the first failure:
- Manifest present and valid (core_files listed, no path traversal, no duplicates)
- All core files present and checksums match
- Transcript schema valid
- Hash chain valid (round hashes, chain linkage, final_hash)
- Evidence refs complete (all evidence_refs resolve to bundle artifacts)
- Signatures valid (Ed25519 over 40-byte domain-separated message)
Reporting actis_status MUST reflect the earliest failure in this order.
ACTIS_PARTIAL is reserved for signature-only failures (step 6 only).
Any failure at steps 1–5 MUST yield ACTIS_NONCOMPLIANT.
Emitting ACTIS Bundles: Integration Guide
Status: Informational
Version: 1.0
Scope: Step-by-step guide for producers and verifiers (example roles) to produce and verify ACTIS-compatible evidence bundles independently.
1. What Is an ACTIS Bundle?
An ACTIS bundle is a ZIP archive containing a cryptographically verifiable record of an autonomous agent transaction. It provides a deterministic verification procedure by which any independent verifier may confirm that a specific sequence of events occurred, in a specific order, signed by specific agents, without tampering. The bundle is self-contained: everything needed to verify integrity is inside it.
2. Minimum Required Files
An ACTIS bundle MUST contain exactly three core files:
bundle.zip
├── manifest.json # Bundle metadata and file listing
├── checksums.sha256 # SHA-256 checksums of core files
└── input/
└── transcript.json # Hash-linked, signed transcript
2.1 manifest.json
The manifest is normative. Schema: actis/schemas/actis_manifest_v1.json. Required: core_files (array of relative paths). Paths MUST be unique, relative, use forward slashes, and MUST NOT contain ../ or absolute/drive segments. Minimal ACTIS core: checksums.sha256, manifest.json, input/transcript.json. The checksum file lists hashes for the other core files only (not itself).
{
"standard": { "name": "ACTIS", "version": "1.0" },
"core_files": ["checksums.sha256", "manifest.json", "input/transcript.json"],
"optional_files": [],
"created_at": "2026-03-03T22:00:00Z",
"producer": "noble.xyz"
}
2.2 checksums.sha256
A text file with one line per file, in the format <sha256-hex> <filepath>:
a1b2c3d4e5f6...64chars... manifest.json
f6e5d4c3b2a1...64chars... input/transcript.json
Critical: The checksum file MUST NOT include a checksum of itself. Only hash manifest.json and input/transcript.json.
2.3 Bundle security (verifiers)
Verifiers MUST reject bundles that have duplicate entries for any core path, symlinks for core paths, or path traversal (../, absolute, drive) in core paths. Unlisted files in the archive MUST NOT affect actis_status; verifiers SHOULD warn when present. ACTIS_COMPATIBLE means only the declared core surface is verified—not that the entire ZIP is safe to execute. See ACTIS_COMPATIBILITY.md §5 and ACTIS_AUDITOR_PACK.md §5.
2.4 input/transcript.json
The transcript is the core evidence artifact. See §3 for the complete field specification.
3. Transcript Schema (actis-transcript/1.0)
ACTIS v1.0 uses the transcript format identified by actis-transcript/1.0.
3.1 Required Top-Level Fields
| Field | Type | Description | Example |
|---|---|---|---|
transcript_version | string | MUST be "actis-transcript/1.0" | "actis-transcript/1.0" |
transcript_id | string | "transcript-" + SHA-256 hex of intent_id + created_at_ms | "transcript-4500579f..." |
intent_id | string | Unique intent identifier | "intent-noble-weather-001" |
intent_type | string | Service category | "weather.data" |
created_at_ms | integer | Unix milliseconds | 1709500000000 |
policy_hash | string | SHA-256 hex of canonical buyer policy JSON | "a1b2c3d4..." |
strategy_hash | string | SHA-256 hex of canonical strategy JSON | "e5f6a7b8..." |
identity_snapshot_hash | string | SHA-256 hex of canonical identity snapshot JSON | "c9d0e1f2..." |
rounds | array | Array of TranscriptRound objects (min 1) | See §3.2 |
3.2 TranscriptRound Fields
| Field | Type | Required | Description |
|---|---|---|---|
round_number | integer | Yes | Zero-indexed, sequential, no gaps |
round_type | string | Yes | One of: "INTENT", "ASK", "BID", "COUNTER", "ACCEPT", "REJECT", "ABORT" |
message_hash | string | Yes | SHA-256 hex of canonical message content |
envelope_hash | string | Yes | SHA-256 hex of round envelope (excl. envelope_hash and signature); see ACTIS_COMPATIBILITY.md §2 |
signature | object | Yes | { signer_public_key_b58, signature_b58, signed_at_ms, scheme: "ed25519" } |
timestamp_ms | integer | Yes | Unix milliseconds, non-decreasing |
previous_round_hash | string | Yes | SHA-256 hex — see §4.1 for round-0 computation |
round_hash | string | No | SHA-256 hex of this round (canonical, excluding round_hash itself) |
agent_id | string | No | Agent identifier (e.g., "buyer", "seller") |
public_key_b58 | string | No | Base58-encoded Ed25519 public key |
content_summary | object | No | Human-readable content (does not affect hashing) |
3.3 Optional Top-Level Fields
| Field | Type | Description |
|---|---|---|
failure_event | object | Terminal failure event (if negotiation failed) |
final_hash | string | SHA-256 hex of canonical transcript (excluding final_hash itself) |
metadata | object | Non-deterministic metadata |
model_context | object | MRM traceability (model_id required within) |
4. Critical Implementation Details
4.1 Round-0 previous_round_hash
Round 0 (the INTENT round) has no previous round. Its previous_round_hash is computed as:
genesis_input = UTF-8 encode of (intent_id + ":" + decimal(created_at_ms))
previous_round_hash = SHA-256(genesis_input) // 64 lowercase hex
Implementations MUST treat leading/trailing whitespace in intent_id as significant and MUST NOT apply Unicode normalization. The decimal representation of created_at_ms uses no leading zeros and no JSON wrapping. See ACTIS_COMPATIBILITY.md §3.1.
Example:
import { createHash } from "node:crypto";
const intentId = "intent-noble-weather-001";
const previousRoundHash = createHash("sha256")
.update(intentId, "utf8")
.digest("hex");
4.2 Ed25519 Signing
Each round MUST be signed with Ed25519. The signature covers the envelope_hash:
import nacl from "tweetnacl";
import bs58 from "bs58";
// Generate a keypair (or load from your agent's identity store)
const keypair = nacl.sign.keyPair();
// The signing message is 40 bytes: utf8("ACTIS/v1") (8 bytes) || hex_decode(envelope_hash) (32 bytes).
// Do NOT sign the UTF-8 envelope_hash string. See ACTIS_COMPATIBILITY.md §2.3 and §4.
const envelopeHash = "144090ff43d039c1ea7cae50824a1bc1376ca97f7fc326dfa8021315af47e077";
const message = new TextEncoder().encode(envelopeHash);
// Sign
const signatureBytes = nacl.sign.detached(message, keypair.secretKey);
// Encode for transcript
const signature = {
signer_public_key_b58: bs58.encode(keypair.publicKey),
signature_b58: bs58.encode(signatureBytes),
signed_at_ms: Date.now(),
scheme: "ed25519"
};
4.3 Hash Chain Verification
For each round n > 0:
round[n].previous_round_hash === SHA-256( canonical_json( round[n-1] ) )
Where canonical_json means JSON.stringify with sorted keys and no whitespace (RFC 8785 JCS or equivalent deterministic serialization). The round_hash field itself MUST be excluded from the canonical form before hashing.
4.4 Computing final_hash
After the last round is appended:
final_hash = SHA-256( canonical_json( transcript_without_final_hash ) )
The final_hash field is excluded from the canonical form.
4.5 Generating checksums.sha256
sha256sum manifest.json input/transcript.json > checksums.sha256
Or programmatically:
import { createHash, readFileSync } from "node:fs";
function sha256File(path) {
const content = readFileSync(path);
return createHash("sha256").update(content).digest("hex");
}
const lines = [
`${sha256File("manifest.json")} manifest.json`,
`${sha256File("input/transcript.json")} input/transcript.json`,
].join("\n") + "\n";
writeFileSync("checksums.sha256", lines);
Do not include checksums.sha256 in the checksum list.
5. Complete Example: 2-Round Transcript (INTENT + ACCEPT)
{
"transcript_version": "actis-transcript/1.0",
"transcript_id": "transcript-b7e23f8a91c04d5e6f78901234abcdef0123456789abcdef0123456789abcdef",
"intent_id": "intent-noble-weather-20260303-001",
"intent_type": "weather.data",
"created_at_ms": 1709500000000,
"policy_hash": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2",
"strategy_hash": "b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3",
"identity_snapshot_hash": "c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4",
"rounds": [
{
"round_number": 0,
"round_type": "INTENT",
"message_hash": "d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5",
"envelope_hash": "d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5",
"signature": {
"signer_public_key_b58": "21wxunPRWgrzXqK48yeE1aEZtfpFU2AwY8odDiGgBT4J",
"signature_b58": "128SdR5tqs76YAyF2cxD5f2pRbmYZaTNrcA8LJugU6kTwJrJNaA3pALAS3mPwVMsL1yTbxBKtk2B4ZVSxaUmz6ca",
"signed_at_ms": 1709500000000,
"scheme": "ed25519"
},
"timestamp_ms": 1709500000000,
"previous_round_hash": "SHA256('intent-noble-weather-20260303-001:' + decimal(created_at_ms))",
"agent_id": "buyer",
"public_key_b58": "21wxunPRWgrzXqK48yeE1aEZtfpFU2AwY8odDiGgBT4J",
"content_summary": {
"intent_type": "weather.data",
"description": "Request current weather data for NYC"
},
"round_hash": "e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6"
},
{
"round_number": 1,
"round_type": "ACCEPT",
"message_hash": "f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7",
"envelope_hash": "f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7",
"signature": {
"signer_public_key_b58": "HBUkwmmQVFX3mGF6ris1mWDATY27nAupX6wQNXgJD9j9",
"signature_b58": "3LwhuzhEt4FdM117RKYfQ5kJbPerSrH86SoiM226PDoQHyXhEN8MbNDJPJkiQAVGoEqUgYD3dDANNbmcZ4T6Fitx",
"signed_at_ms": 1709500001000,
"scheme": "ed25519"
},
"timestamp_ms": 1709500001000,
"previous_round_hash": "e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6",
"agent_id": "seller",
"public_key_b58": "HBUkwmmQVFX3mGF6ris1mWDATY27nAupX6wQNXgJD9j9",
"content_summary": {
"price": 0.00005,
"accepted": true
},
"round_hash": "a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8"
}
],
"final_hash": "b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9"
}
Note: The hashes and signatures in this example are illustrative placeholders showing the correct format and structure. In a real implementation, all hashes MUST be computed from actual content, and all signatures MUST be cryptographically valid Ed25519 signatures.
6. Submitting to a verifier (example)
ACTIS defines no network API; the following is an example only. A verifier service might accept a bundle and return a verification report.
curl -X POST https://verifier.example/v1/verify \
-H "Content-Type: application/zip" \
-H "Authorization: Bearer <your-api-key>" \
--data-binary @bundle.zip
Example successful response (HTTP 200):
{
"bundle_id": "bundle-abc123def456...",
"actis_status": "ACTIS_COMPATIBLE",
"actis_version": "1.0",
"verified_at": "2026-03-03T22:00:00Z",
"verifier_version": "0.9.0",
"report_content_hash": "sha256-of-full-report..."
}
Status values:
| Status | Meaning |
|---|---|
ACTIS_COMPATIBLE | All integrity checks pass |
ACTIS_PARTIAL | Some checks pass, some fail (e.g., valid structure but invalid signature) |
ACTIS_NONCOMPLIANT | Critical checks fail (e.g., broken hash chain, invalid schema) |
7. Troubleshooting: Five Most Common Failures
7.1 Wrong transcript_version Constant
Symptom: schema_ok: false, ACTIS_NONCOMPLIANT.
Cause: transcript_version is not the required value. ACTIS bundles MUST use "actis-transcript/1.0". Common mistakes: using "4.0", "actis/1.0", or any other variant.
Fix: Set transcript_version to the exact string "actis-transcript/1.0".
7.2 Round-0 previous_round_hash Format
Symptom: hash_chain_ok: false, hash chain broken at round 0.
Cause: previous_round_hash for round 0 was computed incorrectly. Common mistakes:
- Hashing
intent_idas an object instead of a plain string - Using a different encoding (e.g., Latin-1 instead of UTF-8)
- Including surrounding quotes in the hash input
Fix: Compute SHA-256( intent_id ) where intent_id is the raw string value encoded as UTF-8 bytes. No JSON wrapping, no quotes.
7.3 Checksum File Self-Reference
Symptom: checksums_ok: false, checksum mismatch.
Cause: checksums.sha256 includes a hash of itself, creating a circular reference that always fails.
Fix: Hash only manifest.json and input/transcript.json. Do not include checksums.sha256 in its own content.
7.4 Missing Required Fields
Symptom: schema_ok: false or parse error.
Cause: A required field is missing from the transcript or round objects. The most commonly missed fields are:
identity_snapshot_hash(top-level)previous_round_hash(in rounds)envelope_hash(in rounds — sometimes confused withmessage_hash)
Fix: Ensure all fields marked "Required" in §3.1 and §3.2 are present and non-null.
7.5 Signature Scheme Mismatch
Symptom: signatures_ok: false, signature verification fails.
Cause: The signature was created using a different scheme (e.g., secp256k1, RSA) but scheme is set to "ed25519", or the signature was created over different data than envelope_hash.
Fix: ACTIS v1.0 supports Ed25519 only. The signature MUST be an Ed25519 detached signature over the 40-byte message: utf8("ACTIS/v1") || hex_decode(envelope_hash). Do NOT sign the UTF-8 hex string directly. Verify that signer_public_key_b58 matches the key that created the signature.
8. Quick Start Checklist
- Generate an Ed25519 keypair for your agent
- Create a transcript with
transcript_version: "actis-transcript/1.0" - Compute round-0
previous_round_hashas SHA-256(utf8(intent_id + ":" + decimal(created_at_ms))) - Sign each round with Ed25519 over the 40-byte message: utf8("ACTIS/v1") || hex_decode(envelope_hash)
- Compute each round's
round_hash(canonical JSON, sorted keys, excludinground_hash) - Set each subsequent round's
previous_round_hashto the prior round'sround_hash - Compute
final_hash(canonical JSON of entire transcript, excludingfinal_hash) - Create
manifest.jsonwithstandard.name: "ACTIS"andstandard.version: "1.0" - Generate
checksums.sha256(exclude the checksum file itself) - Package as ZIP and submit to your verifier (e.g.
POST /v1/verify)- Verify response:
actis_status: "ACTIS_COMPATIBLE"
- Verify response:
9. Next steps for implementers
- Produce real transaction transcripts (e.g. successful procurement, policy-driven rejection, terminal failure) with the structure above.
- Anonymize sensitive data while preserving hash chain and signature validity.
- Run the ACTIS conformance harness to validate your verifier. The harness exists in the ACTIS repository at
actis/test-vectors/run_conformance.sh. If you are reading a vendored copy of ACTIS, ensure you have included theactis/test-vectors/directory (includingexpected_results.jsonand thegenerated/zip files). If you do not have the harness, you can still validate by running your verifier on each of tv-001..tv-008 and comparing the JSON output to the expected values inexpected_results.json.
10. Transparency Log Anchoring (Informative)
ACTIS bundles are self-evidencing and offline-verifiable by design. A bundle that passes ACTIS verification is cryptographically intact without any external dependency. Transparency log anchoring is an optional layer that can be added on top of ACTIS verification to provide additional guarantees.
10.1 Why anchor?
Anchoring a bundle's final_hash to an append-only transparency log provides:
- Timestamping — Proof that a bundle existed at or before a given time, independent of the bundle producer.
- Public tamper resistance — Any attempt to retroactively alter a committed bundle hash is detectable by any log observer.
- Equivocation detection — A log that enforces unique
transcript_idcommitments can detect whether two different bundles have been produced for the same transaction. See ACTIS_STANDARD_v1.md §7.2 for the equivocation threat model.
10.2 How to anchor
ACTIS does not mandate any specific transparency log. Producers MAY anchor
bundles by committing the final_hash (and optionally the transcript_id) to
any append-only log that provides inclusion proofs. Examples include:
- RFC 6962 / RFC 9162 Certificate Transparency logs
- Rekor (Sigstore)
- Any organizational or consortium append-only ledger
The anchor commitment and inclusion proof SHOULD be stored as optional files
in the bundle (e.g. optional/tlog_inclusion_proof.json) and listed in
optional_files in the manifest. They MUST NOT be listed in core_files and
MUST NOT affect actis_status.
10.3 The external_anchors manifest field
The manifest schema reserves the external_anchors field for future use.
When present, it is an array of objects describing transparency log commitments
for this bundle. This field is optional and carries no normative weight in
ACTIS v1.0; verifiers MUST ignore it for the purposes of determining
actis_status.
"external_anchors": [
{
"log": "rekor.sigstore.dev",
"entry_id": "<log entry UUID or hash>",
"committed_at": "<ISO 8601 timestamp>",
"inclusion_proof_path": "optional/tlog_inclusion_proof.json"
}
]
Questions? File an issue in the ACTIS repository.