Attesting an AI agent's decisions is free. Verifying one costs $0.10.
Every audit-trail product prices the write, so teams attest selectively - and a trail with gaps proves nothing. This inverts it: attestation writes cost nothing, forever, and what costs money is doubt. Anyone can verify any record for $0.10, machine-to-machine over x402, no account.
What a record is: hash commitments only (SHA3-256 over JCS) - the decision payload never leaves your client by default. Records chain per agent, carry Merkle proofs, are signed by a threshold of ML-DSA-65 (post-quantum) keys across regions, and are anchored to Hedera mainnet minutes after the decision. Once anchored, nobody can backdate or revise a record. Including us.
Live verify endpoint (returns its price and shape to a plain GET):
curl https://rubric-protocol.com/v1/x402/decision-verify
Honest limits, current state: the signing federation is three regions today, all
operated by Rubric - independent operators are the roadmap, so today the
threshold protects against key compromise, not against the operator as an
institution. The verify response covers internal consistency plus the on-ledger
anchor ref; linking the anchor to a specific record is a documented hash-bridge
walk. Details in spec/dar-0.1.md.
Monorepo for the Rubric Decision Attestation system.
| Path | What |
|---|---|
spec/dar-0.1.md | DAR/0.1 record format: field table, JCS + SHA3-256 rules, versioning rules. Frozen. |
packages/attest-decision | Core SDK — DAR builder, JCS canonicalization, SHA3-256, fire-and-forget attest(), batcher, durable spool (P1). |
packages/attest-index | Attestation index — SQLite day-shard writer (WAL), backfill, and query lib (byDecisionId, byAgentRange, byAgentSchema, chainHead) (P2). |
packages/jev | Adapter — Jev decision shape → DAR inputs; all Jev knowledge confined here (P3). |
packages/schema | Adapter — JSON Schema / Zod → DAR inputs (P3). |
packages/verify | x402-gated /v1/x402/decision-verify route — Merkle proof, HCS anchor ref, signature, drift flag; chain-check continuity (P4). |
packages/evidence | rubric-evidence export CLI — offline evidence bundles (DARs, proofs, anchors, continuity, schema-change log) from index shards + store (P5). |
.github/workflows/ci.yml | Install + lint + typecheck + build + test, on every PR. |
CHANGELOG.md | Release history (Keep a Changelog). |
spec/dar-0.1.md. Field set frozen; additive
changes bump the minor.RUBRIC_API_KEY.attest() never blocks the caller and never throws into app code; failures
spool.@rubric-protocol/attest-decision)import { Attestor, HttpTransport } from "@rubric-protocol/attest-decision";
const attestor = new Attestor({
transport: new HttpTransport({ baseUrl: "https://attest.example" }), // reads RUBRIC_API_KEY
spoolPath: "/var/lib/rubric/attest.spool",
// mode: "payload", // optional — also ship raw content in the transport envelope; default is "hash-only"
});
// Fire-and-forget: returns immediately (<1 ms), never throws into app code.
// The DAR core is hashes-only: schema/input/output are hashed, never carried raw.
attestor.attest({
agentId: "agent://jev/pricing-v3",
schema: pricingSchema, // hashed to schemaHash
input: { requestId: "req-1" }, // hashed to inputHash
output: { action: "approve", limitUsd: "2500.00" }, // hashed to outputHash
});
Records are appended to a durable spool (its parent directory is created if
missing) and flushed in batches (64 records or 5000 ms) with one POST per flush
to /v1/tiered-attest. A kill -9 at any point loses zero spooled records — the
next process drains the spool on startup.
schemaHash/inputHash/outputHash and a decisionHash over the three.
Raw content is transmitted only in mode: "payload", in the transport
envelope, never in a core; the default is hash-only.attest() latency is sub-millisecond for typical payloads; decisions
larger than maxDecisionBytes (default 256 KiB) are rejected rather than
block the caller.writeSync (no fsync) makes a record survive process death
(kill -9); a background fsync before each flush, plus a directory fsync
after spool compaction, defend against power loss. A write failure (disk full)
surfaces via onError — the record is not spooled.decisionId.onError) and the in-memory queue caps at
maxQueue; the HTTP transport times out (timeoutMs, default 30 s) and
refuses a non-HTTPS baseUrl unless allowInsecure is set.prev chaining is per producer process. Multiple producers writing the
same agentId form independent branches; there is no global sequencing.chainHead (index) returns the latest decisionId — an approximation of
the chain tip; use the verify package's chainCheck for true continuity.Requires Node.js >= 20.
npm install # install workspace deps
npm run lint # eslint
npm run typecheck # tsc --noEmit across all packages
npm run build # emit dist JS + .d.ts for each package (consumable output)
npm run test # vitest run (resolves @rubric-protocol/* to source via the dev condition)
npm run check # lint + typecheck + test
This is an npm-workspaces monorepo; npm install at the root wires the packages
together. Each package publishes its built dist/ (exports map with a
development condition that points tools at src/ in this workspace); run
npm run build before publishing.
49 commits
TypeScript
99.6%
Attesting an AI agent's decisions is free. Verifying one costs $0.10.
Every audit-trail product prices the write, so teams attest selectively - and a trail with gaps proves nothing. This inverts it: attestation writes cost nothing, forever, and what costs money is doubt. Anyone can verify any record for $0.10, machine-to-machine over x402, no account.
What a record is: hash commitments only (SHA3-256 over JCS) - the decision payload never leaves your client by default. Records chain per agent, carry Merkle proofs, are signed by a threshold of ML-DSA-65 (post-quantum) keys across regions, and are anchored to Hedera mainnet minutes after the decision. Once anchored, nobody can backdate or revise a record. Including us.
Live verify endpoint (returns its price and shape to a plain GET):
curl https://rubric-protocol.com/v1/x402/decision-verify
Honest limits, current state: the signing federation is three regions today, all
operated by Rubric - independent operators are the roadmap, so today the
threshold protects against key compromise, not against the operator as an
institution. The verify response covers internal consistency plus the on-ledger
anchor ref; linking the anchor to a specific record is a documented hash-bridge
walk. Details in spec/dar-0.1.md.
Monorepo for the Rubric Decision Attestation system.
| Path | What |
|---|---|
spec/dar-0.1.md | DAR/0.1 record format: field table, JCS + SHA3-256 rules, versioning rules. Frozen. |
packages/attest-decision | Core SDK — DAR builder, JCS canonicalization, SHA3-256, fire-and-forget attest(), batcher, durable spool (P1). |
packages/attest-index | Attestation index — SQLite day-shard writer (WAL), backfill, and query lib (byDecisionId, byAgentRange, byAgentSchema, chainHead) (P2). |
packages/jev | Adapter — Jev decision shape → DAR inputs; all Jev knowledge confined here (P3). |
packages/schema | Adapter — JSON Schema / Zod → DAR inputs (P3). |
packages/verify | x402-gated /v1/x402/decision-verify route — Merkle proof, HCS anchor ref, signature, drift flag; chain-check continuity (P4). |
packages/evidence | rubric-evidence export CLI — offline evidence bundles (DARs, proofs, anchors, continuity, schema-change log) from index shards + store (P5). |
.github/workflows/ci.yml | Install + lint + typecheck + build + test, on every PR. |
CHANGELOG.md | Release history (Keep a Changelog). |
spec/dar-0.1.md. Field set frozen; additive
changes bump the minor.RUBRIC_API_KEY.attest() never blocks the caller and never throws into app code; failures
spool.@rubric-protocol/attest-decision)import { Attestor, HttpTransport } from "@rubric-protocol/attest-decision";
const attestor = new Attestor({
transport: new HttpTransport({ baseUrl: "https://attest.example" }), // reads RUBRIC_API_KEY
spoolPath: "/var/lib/rubric/attest.spool",
// mode: "payload", // optional — also ship raw content in the transport envelope; default is "hash-only"
});
// Fire-and-forget: returns immediately (<1 ms), never throws into app code.
// The DAR core is hashes-only: schema/input/output are hashed, never carried raw.
attestor.attest({
agentId: "agent://jev/pricing-v3",
schema: pricingSchema, // hashed to schemaHash
input: { requestId: "req-1" }, // hashed to inputHash
output: { action: "approve", limitUsd: "2500.00" }, // hashed to outputHash
});
Records are appended to a durable spool (its parent directory is created if
missing) and flushed in batches (64 records or 5000 ms) with one POST per flush
to /v1/tiered-attest. A kill -9 at any point loses zero spooled records — the
next process drains the spool on startup.
schemaHash/inputHash/outputHash and a decisionHash over the three.
Raw content is transmitted only in mode: "payload", in the transport
envelope, never in a core; the default is hash-only.attest() latency is sub-millisecond for typical payloads; decisions
larger than maxDecisionBytes (default 256 KiB) are rejected rather than
block the caller.writeSync (no fsync) makes a record survive process death
(kill -9); a background fsync before each flush, plus a directory fsync
after spool compaction, defend against power loss. A write failure (disk full)
surfaces via onError — the record is not spooled.decisionId.onError) and the in-memory queue caps at
maxQueue; the HTTP transport times out (timeoutMs, default 30 s) and
refuses a non-HTTPS baseUrl unless allowInsecure is set.prev chaining is per producer process. Multiple producers writing the
same agentId form independent branches; there is no global sequencing.chainHead (index) returns the latest decisionId — an approximation of
the chain tip; use the verify package's chainCheck for true continuity.Requires Node.js >= 20.
npm install # install workspace deps
npm run lint # eslint
npm run typecheck # tsc --noEmit across all packages
npm run build # emit dist JS + .d.ts for each package (consumable output)
npm run test # vitest run (resolves @rubric-protocol/* to source via the dev condition)
npm run check # lint + typecheck + test
This is an npm-workspaces monorepo; npm install at the root wires the packages
together. Each package publishes its built dist/ (exports map with a
development condition that points tools at src/ in this workspace); run
npm run build before publishing.
49 commits
TypeScript
99.6%