{"_id":"@attesto/sdk","_rev":"8-7cf71472ccc574c1b29523fdbfd0837a","name":"@attesto/sdk","dist-tags":{"latest":"0.5.0"},"versions":{"0.1.1":{"name":"@attesto/sdk","version":"0.1.1","keywords":["attesto","ai","compliance","eu-ai-act","audit","merkle","polygon"],"author":{"name":"Attesto","email":"info@attesto.eu"},"license":"Apache-2.0","_id":"@attesto/sdk@0.1.1","maintainers":[{"name":"attestoeu","email":"development@attesto.eu"}],"homepage":"https://attesto.eu","bugs":{"url":"https://attesto.eu/security"},"dist":{"shasum":"3265aea53df544b1efdc7088676f373376bff594","tarball":"https://registry.npmjs.org/@attesto/sdk/-/sdk-0.1.1.tgz","fileCount":12,"integrity":"sha512-VzyXhTuELtHJc8awwh+KE2GKAtbHvGoB3gj3xwIRzOP8MwOXTz4KBSGluPwfLpKSMVs0y7PRWlDjTOPXPafVmg==","signatures":[{"sig":"MEYCIQD4hO+mErRvz6qkviOv1Qdsd4fG66pL+fe9jurbIYY/HgIhAIMdhG+UojhHeq9McQmmMme8iW37LKbN5dYVEt6N8QQM","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":41509},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"1823e6a004376807a1bb1660937dfd9ab1563565","scripts":{"test":"npm run build && node --test test/*.test.mjs","build":"tsc -p tsconfig.json","clean":"rm -rf dist","prepack":"npm run clean && npm run build && find dist -name '*.map' -o -name '*.tsbuildinfo' | grep -q . && exit 1 || true","typecheck":"tsc -p tsconfig.json --noEmit","prepublishOnly":"npm test","test:proofstream":"npm run build && node --test test/proofstream-golden-vectors.test.mjs"},"_npmUser":{"name":"attestoeu","email":"development@attesto.eu"},"repository":{"url":"https://git.rotz.ai/rotzmediagroup/attesto-v1.git","type":"git","directory":"sdk/typescript"},"_npmVersion":"10.9.4","description":"Attesto TypeScript SDK - log verifiable AI events to Attesto with production-safe defaults.","directories":{},"_nodeVersion":"22.22.0","publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.8.3"},"_npmOperationalInternal":{"tmp":"tmp/sdk_0.1.1_1780860101716_0.16155236179494903","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@attesto/sdk","version":"0.1.2","keywords":["attesto","ai","compliance","eu-ai-act","audit","merkle","polygon"],"author":{"name":"Attesto","email":"info@attesto.eu"},"license":"Apache-2.0","_id":"@attesto/sdk@0.1.2","maintainers":[{"name":"attestoeu","email":"development@attesto.eu"}],"homepage":"https://attesto.eu","bugs":{"url":"https://attesto.eu/security"},"dist":{"shasum":"ca5becc18fe53972d38081514c3ec9d24ff66803","tarball":"https://registry.npmjs.org/@attesto/sdk/-/sdk-0.1.2.tgz","fileCount":12,"integrity":"sha512-pVtsKoyXlwkQaAzFLjiVeWtCi26yAe/3f2Iyoow7NjAuXZkvZ/YBJB1gkx5cdzsX/EBYAgTMpd00v2qtf+ZLQQ==","signatures":[{"sig":"MEQCIB0t7/SSrg1zahNNa1FoXmGGUDu43NpeiX2r2oBFeFkMAiA9RBNSisphNL63iFJUEuPUxxOilRHDWEG4Z8UsIhieiA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":54193},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"d1fa15ea7bcb8a9cfe44b31c141d091025ed9cf2","scripts":{"test":"npm run build && node --test test/*.test.mjs","build":"tsc -p tsconfig.json","clean":"rm -rf dist","prepack":"npm run clean && npm run build && find dist -name '*.map' -o -name '*.tsbuildinfo' | grep -q . && exit 1 || true","typecheck":"tsc -p tsconfig.json --noEmit","prepublishOnly":"npm test","test:proofstream":"npm run build && node --test test/proofstream-golden-vectors.test.mjs"},"_npmUser":{"name":"attestoeu","email":"development@attesto.eu"},"repository":{"url":"https://git.rotz.ai/rotzmediagroup/attesto-v1.git","type":"git","directory":"sdk/typescript"},"_npmVersion":"10.9.4","description":"Attesto TypeScript SDK - log verifiable AI events to Attesto with production-safe defaults.","directories":{},"_nodeVersion":"22.22.0","publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.8.3"},"_npmOperationalInternal":{"tmp":"tmp/sdk_0.1.2_1780863083036_0.6116861542782654","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@attesto/sdk","version":"0.2.0","keywords":["attesto","ai","compliance","eu-ai-act","audit","merkle","polygon"],"author":{"name":"Attesto","email":"info@attesto.eu"},"license":"Apache-2.0","_id":"@attesto/sdk@0.2.0","maintainers":[{"name":"attestoeu","email":"development@attesto.eu"}],"homepage":"https://attesto.eu","bugs":{"url":"https://attesto.eu/security"},"dist":{"shasum":"d3005d1506d0bad2c17923d51ea676b69fccca2e","tarball":"https://registry.npmjs.org/@attesto/sdk/-/sdk-0.2.0.tgz","fileCount":12,"integrity":"sha512-n+A0SaA0fxINHBfIaHZLiPfYZc+jloYdej09Ac5f5F2eMe/if0TvGNFZTa3/uqko622Kx4AEGUty8TDAAXD26Q==","signatures":[{"sig":"MEUCIHJ2bYjR0w9nFHzFlmyW126nP7Sb/QUnYZWFEMz2rQSlAiEAoX4sv4dOgFF2SjKeUuOSDCqd0NP1qvsqgus4ysLNxUU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":54193},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"d1fa15ea7bcb8a9cfe44b31c141d091025ed9cf2","scripts":{"test":"npm run build && node --test test/*.test.mjs","build":"tsc -p tsconfig.json","clean":"rm -rf dist","prepack":"npm run clean && npm run build && find dist -name '*.map' -o -name '*.tsbuildinfo' | grep -q . && exit 1 || true","typecheck":"tsc -p tsconfig.json --noEmit","prepublishOnly":"npm test","test:proofstream":"npm run build && node --test test/proofstream-golden-vectors.test.mjs"},"_npmUser":{"name":"attestoeu","email":"development@attesto.eu"},"repository":{"url":"https://git.rotz.ai/rotzmediagroup/attesto-v1.git","type":"git","directory":"sdk/typescript"},"_npmVersion":"10.9.4","description":"Attesto TypeScript SDK - log verifiable AI events to Attesto with production-safe defaults.","directories":{},"_nodeVersion":"22.22.0","publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.8.3"},"_npmOperationalInternal":{"tmp":"tmp/sdk_0.2.0_1780863746768_0.48591549979949855","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@attesto/sdk","version":"0.3.0","keywords":["attesto","ai","compliance","eu-ai-act","audit","merkle","polygon"],"author":{"name":"Attesto","email":"info@attesto.eu"},"license":"Apache-2.0","_id":"@attesto/sdk@0.3.0","maintainers":[{"name":"attestoeu","email":"development@attesto.eu"}],"homepage":"https://attesto.eu","bugs":{"url":"https://attesto.eu/security"},"dist":{"shasum":"0d3c25dcbd1721eb09f2fd509a0d3ec4abe0fab4","tarball":"https://registry.npmjs.org/@attesto/sdk/-/sdk-0.3.0.tgz","fileCount":23,"integrity":"sha512-fwm0b2sV6wZT9Vip3YoW7yZF4LPWLRtuoqHdbmp/uXG9Z1Poyw6XsaQrERwf+0ZDUrVuS+nlN1U4c2qWpfGekw==","signatures":[{"sig":"MEUCIH2DJ/aKJ2Oh5eJZ2BSqfCYA4rSooLtJAt1h5BauPLABAiEApwS08ajaL38J1cbnULqAue+NH/oGDpCNdfJXdEs7M0I=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":118857},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"a0f4d65dbeaf752a46aaba28b41ae6a4bf00e3b5","scripts":{"test":"npm run build && node --test test/*.test.mjs","build":"tsc -p tsconfig.json","clean":"rm -rf dist","prepack":"npm run clean && npm run build && find dist -name '*.map' -o -name '*.tsbuildinfo' | grep -q . && exit 1 || true","typecheck":"tsc -p tsconfig.json --noEmit","prepublishOnly":"npm test","test:proofstream":"npm run build && node --test test/proofstream-golden-vectors.test.mjs"},"_npmUser":{"name":"attestoeu","email":"development@attesto.eu"},"repository":{"url":"https://git.rotz.ai/rotzmediagroup/attesto-v1.git","type":"git","directory":"sdk/typescript"},"_npmVersion":"10.9.4","description":"Attesto TypeScript SDK - log verifiable AI events to Attesto with production-safe defaults.","directories":{},"_nodeVersion":"22.22.0","publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.8.3"},"_npmOperationalInternal":{"tmp":"tmp/sdk_0.3.0_1781207226104_0.08287710775430379","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@attesto/sdk","version":"0.4.0","keywords":["attesto","ai","compliance","eu-ai-act","audit","merkle","polygon"],"author":{"name":"Attesto","email":"info@attesto.eu"},"license":"Apache-2.0","_id":"@attesto/sdk@0.4.0","maintainers":[{"name":"attestoeu","email":"development@attesto.eu"}],"homepage":"https://attesto.eu","bugs":{"url":"https://attesto.eu/security"},"dist":{"shasum":"4d9b123e5082ad7b604936d7cf35d1bbb6758972","tarball":"https://registry.npmjs.org/@attesto/sdk/-/sdk-0.4.0.tgz","fileCount":33,"integrity":"sha512-FlpCYA4sCOLVumwSJVeVQiEZLDIZOem7A6XLEH0FKZ9E6/Nqwtw2eLh/H5EQtV47RWasIxqLk7Ybzy0dv2Ug7g==","signatures":[{"sig":"MEQCIQDh7XCz4IBEG3+nwRgpkGVUe/Vmmlo33sCj2/lyf/i6kwIfNq1ua7mpnB2phlh996K/YMQi4ubaBf3xNchwLsyHJQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":156090},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"def190e56699d3ab16f2f2d822322f92dc4c23f4","scripts":{"test":"npm run build && node --test test/*.test.mjs","build":"tsc -p tsconfig.json","clean":"rm -rf dist","prepack":"npm run clean && npm run build && find dist -name '*.map' -o -name '*.tsbuildinfo' | grep -q . && exit 1 || true","typecheck":"tsc -p tsconfig.json --noEmit","prepublishOnly":"npm test","test:proofstream":"npm run build && node --test test/proofstream-golden-vectors.test.mjs"},"_npmUser":{"name":"attestoeu","email":"development@attesto.eu"},"repository":{"url":"https://git.rotz.ai/rotzmediagroup/attesto-v1.git","type":"git","directory":"sdk/typescript"},"_npmVersion":"10.9.4","description":"Attesto TypeScript SDK - log verifiable AI events to Attesto with production-safe defaults.","directories":{},"_nodeVersion":"22.22.0","publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.8.3"},"_npmOperationalInternal":{"tmp":"tmp/sdk_0.4.0_1781285046086_0.5359159975363363","host":"s3://npm-registry-packages-npm-production"}},"0.5.0":{"name":"@attesto/sdk","version":"0.5.0","description":"Attesto TypeScript SDK - log verifiable AI events to Attesto with production-safe defaults.","type":"module","main":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"scripts":{"build":"tsc -p tsconfig.json","clean":"rm -rf dist","prepack":"npm run clean && npm run build && find dist -name '*.map' -o -name '*.tsbuildinfo' | grep -q . && exit 1 || true","prepublishOnly":"npm test","test":"npm run build && node --test test/*.test.mjs","test:proofstream":"npm run build && node --test test/proofstream-golden-vectors.test.mjs","test:provenance":"npm run build && node --test test/provenance-golden-vectors.test.mjs","test:zkrange":"npm run build && node --test test/zk-range-result-vectors.test.mjs","test:pedersen":"npm run build && node --test test/pedersen-opening-vectors.test.mjs","test:disclosure":"npm run build && node --test test/disclosure-verification.test.mjs","typecheck":"tsc -p tsconfig.json --noEmit"},"keywords":["attesto","ai","compliance","eu-ai-act","audit","merkle","polygon"],"author":{"name":"Attesto","email":"info@attesto.eu"},"license":"Apache-2.0","homepage":"https://attesto.eu","repository":{"type":"git","url":"https://git.rotz.ai/attesto/attesto-2-npm-sdk.git","directory":"sdk/typescript"},"bugs":{"url":"https://attesto.eu/security"},"engines":{"node":">=18"},"devDependencies":{"@noble/curves":"2.3.0","typescript":"^5.8.3"},"peerDependencies":{"@noble/curves":">=2"},"peerDependenciesMeta":{"@noble/curves":{"optional":true}},"_id":"@attesto/sdk@0.5.0","gitHead":"7365a5eec52c5afeabc9f9ff085f421f3fff53c0","_nodeVersion":"22.22.0","_npmVersion":"10.9.4","dist":{"integrity":"sha512-7LpA80KguIL51Ym63zHFRIJvpn4yJh++rvMfJAIG918UZ3b+7Izao6TVPR+1bCtoZ0yydcxBd7kdR22Sa/xRKQ==","shasum":"410634e93b9c8d025070c8dbc24d2513a57afa57","tarball":"https://registry.npmjs.org/@attesto/sdk/-/sdk-0.5.0.tgz","fileCount":37,"unpackedSize":243801,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDihYqXw8mjWrgXNLjJARN6f1e3SlfOsn5kIp8ZVvC98QIhAIIBzDLOSRfCPsjPNpmJ7I03zNiExa04+trdwdlhD+yH"}]},"_npmUser":{"name":"attestoeu","email":"development@attesto.eu"},"directories":{},"maintainers":[{"name":"attestoeu","email":"development@attesto.eu"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/sdk_0.5.0_1787518608879_0.6068757572777161"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-07T19:21:41.595Z","modified":"2026-08-23T20:56:49.179Z","0.1.0":"2026-06-07T19:04:15.550Z","0.1.1":"2026-06-07T19:21:41.860Z","0.1.2":"2026-06-07T20:11:23.173Z","0.2.0":"2026-06-07T20:22:26.936Z","0.3.0":"2026-06-11T19:47:06.257Z","0.4.0":"2026-06-12T17:24:06.272Z","0.5.0":"2026-08-23T20:56:49.019Z"},"bugs":{"url":"https://attesto.eu/security"},"author":{"name":"Attesto","email":"info@attesto.eu"},"license":"Apache-2.0","homepage":"https://attesto.eu","keywords":["attesto","ai","compliance","eu-ai-act","audit","merkle","polygon"],"repository":{"type":"git","url":"https://git.rotz.ai/attesto/attesto-2-npm-sdk.git","directory":"sdk/typescript"},"description":"Attesto TypeScript SDK - log verifiable AI events to Attesto with production-safe defaults.","maintainers":[{"name":"attestoeu","email":"development@attesto.eu"}],"readme":"# Attesto TypeScript SDK\n\nServer-side TypeScript SDK for logging AI events to Attesto's verifiable\nevidence API.\n\n```bash\nnpm install @attesto/sdk\n```\n\n## Quick Start\n\n```ts\nimport { AttestoClient } from \"@attesto/sdk\";\n\nconst attesto = new AttestoClient({\n  apiKey: process.env.ATTESTO_API_KEY!,\n});\n\nconst ack = await attesto.logEvent({\n  type: \"inference\",\n  status: \"verified\",\n  ts: new Date(),\n  latencyMs: 42,\n  inputHash: \"sha256:deadbeef...\",\n  outputHash: \"sha256:cafebabe...\",\n  payload: { score: 0.87, model: \"gpt-4o\" },\n});\n\nconsole.log(ack.id, ack.systemId, ack.ts);\n```\n\n## Runnable examples\n\n[`examples/`](examples/) — first attested event, offline receipt\nverification, completeness check. Each runs against real Attesto with\n`ATTESTO_API_KEY` set, or fully offline against the bundled emulator\nwithout it (CI runs them that way so they can't rot).\n\n\nCanonicalization is specified normatively in [ATTESTO-CANONICAL-JSON-001](../../docs/protocol/ATTESTO-CANONICAL-JSON-001.md); the parity corpus `golden-vectors/sdk-parity/` is its conformance set.\n\n## Committed payload number rule\n\nWhen events are committed to a Proofstream, payload and metadata numbers must\nserialize identically across Python, Go, and JavaScript. Non-integer numbers\nand integers beyond ±(2^53−1) are rejected at ingestion (HTTP 422); encode\ndecimals and large integers as strings (e.g. `{ score: \"0.87\" }`). This keeps\ncross-language commitment recomputation byte-exact.\n\nThe SDK enforces the same rule **locally** before sending, so you see it at dev\ntime rather than as a production 422. `logEvent` / `logEvents` throw\n`AttestoUnsafeNumberError` (with `.path`, the JSON path to the offending value).\nPass `{ preflight: false }` in the request options to defer to the server.\n\n```ts\nimport { payloadCommitment, verifyPayloadCommitment } from \"@attesto/sdk\";\n\n// Commitment a Proofstream stores for a payload, byte-identical to the server\n// (and to the Python / Go SDKs). These helpers are async (WebCrypto):\nawait payloadCommitment({ decision: \"approve\", scoreBp: 8700 });\n// -> { hash_alg: \"sha256\", canonical_payload_hash: \"...\" }\n\nawait verifyPayloadCommitment(myPayload, event); // -> true / false\n```\n\n## Verify receipts offline\n\n`verifyReceipt` checks a receipt **entirely in your runtime** — it recomputes the\ndomain-separated hash and verifies the Ed25519 signature via WebCrypto, with no\ncall back to Attesto. This is the point of the SDK: you do not ask the party you\nare distrusting whether to trust it.\n\n```ts\nimport { verifyReceipt } from \"@attesto/sdk\";\n\nconst report = await verifyReceipt(receipt, signerPublicKeyHex);\nreport.ok;       // true only when no problems were found\nreport.problems; // [] or e.g. [\"receipt signature mismatch\"]\n```\n\nRequires a runtime with WebCrypto Ed25519 (Node ≥ 20, modern browsers); on older\nruntimes it throws a clear `AttestoError` rather than falling back to the server.\n`AttestoV2Client.verifyReceipt(...)` is the **server-assisted** (remote) check,\nkept for compatibility; prefer the offline function when you have the signer's key.\n\n## Verify inclusion, checkpoints, and completeness\n\nThe offline trust model extends across the whole proof chain — all client-side:\n\n```ts\nimport {\n  verifyInclusionProof,      // async: an event is in a window root\n  verifyCheckpointRoot,      // async: window hashes fold to the checkpoint root\n  verifyCheckpointExtension, // one checkpoint continues the previous\n  verifyCompleteness,        // no events were omitted in a range\n} from \"@attesto/sdk\";\n\nawait verifyInclusionProof(leafHash, proof, windowRoot);  // boolean\nawait verifyCheckpointRoot(windowHashes, checkpointRoot); // boolean\nverifyCheckpointExtension(previousCheckpoint, currentCheckpoint).ok;\nverifyCompleteness(events, 5, 8).ok;\n```\n\n`verifyCompleteness` proves **no events were omitted** in a range: the sequence\nnumbers must be gap-free and each event's `prev_event_hash` must chain to the\nprevious event's `event_hash`.\n\n## Provenance verification (Attesto 3)\n\nThe provenance exports are the verification client for the Attesto 3\nprovenance lane: commitment-only provenance streams fed by a customer-controlled\nLocal Vault. The vault's pinned Rust edge core builds capsules, commitments and\nsignatures; this module re-derives and checks them, and deliberately cannot\nconstruct a capsule or a randomizer. Every function below runs **offline** — no\nnetwork, no call to Attesto — on WebCrypto (Node ≥ 20 or a modern browser,\nwhich is how the tenant verification screen runs it). Protocols:\n`ATTESTO-PROVENANCE-001`, `ATTESTO-DISCLOSURE-001`, `ATTESTO-ZK-RANGE-001`,\nand the bundle provenance binding of `ATTESTO-PROOFSTREAM-001` (ADR-0014).\nConformance is pinned by `golden-vectors/provenance-v0.1-dev/` and\n`golden-vectors/zk-range-v0.1-dev/`.\n\nEvery report carries a `not_claimed` list. Render it next to the result.\n\n### Verify a disclosure presentation offline\n\nA holder's Local Vault issues a selective-disclosure presentation: the revealed\nleaves with their randomizers, two-hop inclusion proofs to the capsule root, and\nan Ed25519 signature by the issuing installation. `verifyDisclosure` opens each\nrevealed commitment and replays each proof to the presented `capsule_root`.\n\n```ts\nimport { verifyDisclosure } from \"@attesto/sdk\";\n\nconst report = await verifyDisclosure(presentation, {\n  expectedNonce: challenge,            // the nonce you issued; omit for bounded-lifetime mode\n  subjectCommitment: assetCommitment,  // the asset you hold; omit if you hold none\n});\nreport.ok;               // true only when every leaf opened and every proof folded to capsule_root\nreport.capsule_root;     // string | null\nreport.verified_leaves;  // [{ subtree: \"claims\", leaf_role: \"c2pa_manifest_valid\", value: ... }]\nreport.freshness;        // \"challenge\" when expectedNonce was given, else \"bounded_lifetime\"\nreport.subject_checked;  // true only when subjectCommitment matched the presentation's binding\nreport.problems;         // [] or every problem found, e.g. [\"disclosure has expired\"]\nreport.not_claimed;      // [{ id: \"undisclosed_facts_absent\", statement: ... }]\n```\n\nSignature: `verifyDisclosure(presentation, { expectedNonce?, subjectCommitment?,\nnow? }): Promise<DisclosureReport>`. Problems are collected, not thrown, so a\ncaller sees everything wrong with a presentation. Without `expectedNonce` the\npresentation is bounded only by its `expires_at`, and the report says\n`bounded_lifetime` rather than implying a freshness it did not check. Non-claim:\nthe disclosure proves the revealed leaves are in the capsule; it is not a\nstatement that the capsule holds nothing else.\n\n### Verify bundle provenance inclusion and key revocation offline\n\nA verifier bundle over a provenance stream carries `provenance_root`,\n`provenance_event_count` and `vault_key_lifecycle` inside its hashed payload\n(ADR-0014). An inclusion object — from\n`GET /v2/streams/{stream_id}/provenance-events/{source_ref}/bundle-inclusion?from_checkpoint_id=...&to_checkpoint_id=...`,\nor carried as `provenance_inclusions` next to the bundle — proves one capsule\nroot under that root. `verifyBundleProvenance` recomputes the bundle hash,\nre-hashes the leaf from its fields, replays the proof, takes the receipt time\nfor the leaf's `seq_no` from the bundle's own receipts, and applies the frozen\nrevocation rule to the installation the leaf names.\n\n```ts\nimport { verifyBundleProvenance } from \"@attesto/sdk\";\n\nconst report = await verifyBundleProvenance(bundle, inclusion); // inclusion = the \"inclusion\" field of the bundle-inclusion response\nreport.ok;               // bundle hash holds, inclusion VALID, and the key was live at receipt\nreport.inclusion;        // \"VALID\" | \"INVALID\"\nreport.key_status;       // \"valid\" | \"revoked_at_receipt\" | \"unknown_installation\" | \"not_evaluated\"\nreport.flags;            // e.g. [\"revoked_at_receipt\", \"suspect_backdated\"]\nreport.seq_no; report.installation_id; report.capsule_root;\nreport.receipt_time;     // platform issued_at of that seq_no, from the bundle's receipts\nreport.revoked_at;       // from vault_key_lifecycle, or null\nreport.problems;         // [] or every problem found\nreport.not_claimed;      // three statements, see below\n```\n\n`inclusion` and `key_status` are separate on purpose: a capsule root can be\nprovably under the bundle while its key was revoked before receipt.\n`not_evaluated` means the bundle did not give enough to say; it is never a pass.\n\nThe revocation rule is available on its own and mirrors the platform's ingest\npath exactly:\n\n```ts\nimport { evaluateKeyRevocation } from \"@attesto/sdk\";\n\nconst verdict = evaluateKeyRevocation(\n  keyStatus.revokedAt,            // from GET .../key-status or vault_key_lifecycle; null = never revoked\n  receipt.payload.issued_at,      // platform receipt time, never the vault's occurred_at\n  envelope.occurred_at,\n  keyStatus.revocationReason,\n);\nverdict.status;               // \"valid\" | \"revoked_at_receipt\"\nverdict.flags;                // [\"revoked_at_receipt\", \"suspect_backdated\"] when the claim predates the revocation\nverdict.accepted;             // status === \"valid\"\nverdict.instantReconstructed; // true when reason === \"unrecorded\": the instant was reconstructed by migration\n```\n\nSignature: `evaluateKeyRevocation(revokedAt, receiptTime, claimedOccurredAt =\nnull, reason = null): KeyRevocationVerdict`. The boundary is inclusive — an\nevent receipted exactly at the revocation instant is revoked. Non-claims carried\nby `BundleProvenanceReport.not_claimed`: the root proves the capsule root existed\nunder the bundle and nothing about the capsule's contents (the platform never\nopens one); revocation is evaluated against platform receipt time, never the\nvault-claimed `occurred_at`; the lifecycle is as of bundle build, so a\nrevocation recorded later is not in the bundle.\n\n### Derive effective assurance (L3 is derived, never signed)\n\nA vault signs `L0`, `L1` or `L2` in its envelope. `L3` has no on-wire\nrepresentation: it is derived by the verifier from `L2` plus a met witness\nquorum on the containing checkpoint, and an envelope claiming `L3` is refused.\n\n```ts\nimport { effectiveAssurance } from \"@attesto/sdk\";\n\nconst report = effectiveAssurance(\"L2\", { witnessQuorumMet: true, anchorConfirmed: true });\nreport.effective;         // \"L3\"\nreport.derived;           // true: derived here, signed by nobody\nreport.vaultAssurance;    // \"L2\"\nreport.witnessQuorumMet;  // true | false | null (null = not evaluated)\nreport.anchorConfirmed;   // true | false | null\nreport.reasons;           // [\"anchor confirmed; anchoring does not promote assurance\", \"L3 derived from L2 plus a met witness quorum\"]\n\neffectiveAssurance(\"L1\", { witnessQuorumMet: true }).effective; // \"L1\": only L2 can become L3\neffectiveAssurance(\"L2\").effective;                             // \"L2\": quorum not evaluated withholds L3\neffectiveAssurance(\"L3\");                                       // throws AttestoProvenanceError\n```\n\nLevels: `L0` software-held key; `L1` hardware-held key (PKCS#11 token,\nnon-extractable); `L2` hardware-held key plus a TPM 2.0 quote over the vault's\nmeasurement; `L3` = `L2` and the containing checkpoint met the witness quorum.\nAnchoring is reported alongside and never promotes a level. The platform\nrefuses an `L1`/`L2` envelope it cannot substantiate from a registered\nattestation (`provenance_assurance_not_substantiated`). The four facts stay\nseparate in the report because ADR-0010 forbids collapsing them into one badge.\n\n### Verify an exact private-numeric opening (optional curve extra)\n\nA private numeric claim commits its encoded value as a Pedersen commitment\n`C = v·B + r·H` over ristretto255. When a holder reveals `v` and the blinding\n`r`, `verifyPedersenOpening` recomputes `C` and compares it byte for byte. The\ncurve library is an optional peer dependency — `@attesto/sdk` itself has no\nruntime dependencies:\n\n```bash\nnpm install @noble/curves\n```\n\n```ts\nimport { pedersenAvailable, verifyPedersenOpening } from \"@attesto/sdk\";\n\nconst opened = (await pedersenAvailable())\n  ? await verifyPedersenOpening(descriptor, encodedValue, blindingScalar) // boolean\n  : null; // report \"not_checked\": this installation did not check, not \"nobody can\"\n```\n\n`descriptor` is the claim's private-numeric descriptor (its `pedersen.commitment`\nand `encoding` bounds); `encodedValue` is the canonical encoded integer (a\ndetector score of exactly 0.0 encodes as 0, which is a legal opening); the\nblinding is 32 bytes hex. The descriptor must already have opened its claim\nleaf — verify it under the capsule root first, otherwise a matching pair can be\nfabricated whole. A value outside `semantic_min_encoded..semantic_max_encoded`\nreturns `false`. Without the peer dependency the function throws\n`AttestoProvenanceError`; check `pedersenAvailable()` first. This does **not**\nverify a range proof.\n\n### ZK range results: inspect, never pass through\n\nSelective disclosure v2 proves that a named detector's measurement fell inside\nan interval (`ATTESTO-ZK-RANGE-001`) without revealing it. No SDK verifies the\nbulletproof; that remains the Rust core's job. What the SDK does is refuse to\nlaunder the issuer's word as its own:\n\n```ts\nimport { inspectPredicateResult, validateRangeStatement } from \"@attesto/sdk\";\n\nconst report = inspectPredicateResult(result);\nreport.verified_here;       // { zk_predicate: \"not_checked\", capsule_inclusion: \"not_checked\" | \"verified\" | \"failed\" }\nreport.reported_by_issuer;  // whatever the issuer claimed, kept apart from what was checked here\nreport.not_claimed;         // detector_correctness_not_proven, content_truth_not_proven, ai_generation_not_proven\n\nconst width = validateRangeStatement(statement); // 8 | 16 | 32 | 64, derived from the public bounds\n```\n\n`inspectPredicateResult(result, capsuleInclusion?)` throws on a result that drops\none of the three required non-claims or carries a verdict-shaped field\n(`ai_generated`, `synthetic`, `is_fake`, `authentic`, `confidence`, `score`,\n`probability`): a proven bound says nothing about whether the content is\nmachine-generated.\n\n### Provider results and content marks\n\nCapsule evidence from an AttestoMark Image/Audio/Video provider is an\n`ATTESTO-PROVIDER-RESULT-001/0.2` object carrying `presented_matches_record`\n(ADR-0015): whether the bytes presented to detection are the exact asset that\nwas marked. A mismatch is an observation an honest transcode also produces,\nnever a refusal. A detected mark identifies a registration; it is not evidence\nof authorship, AI origin, ownership or truth. See\n[attesto-edge-provider-001](../../docs/protocol/attesto-edge-provider-001.md).\n\n### What is not in this SDK\n\n- No capsule construction, randomizer generation or envelope signing — those\n  live in the Local Vault's pinned edge core.\n- No range-proof verification (bulletproofs); only the Rust core verifies one.\n- No client wrapper yet for `POST /v2/provenance/streams`; create provenance\n  streams over plain HTTP with a system API key.\n\n## Batch Ingest\n\n```ts\nawait attesto.logEvents([\n  { type: \"inference\", ts: new Date(), latencyMs: 40 },\n  { type: \"decision\", ts: new Date(), status: \"pending\", payload: { threshold: 0.7 } },\n]);\n```\n\nThe SDK accepts up to 1000 events per batch and sends an `Idempotency-Key`\nautomatically for both single-event and batch writes.\n\nEvent fields default to the backend contract: `type: \"inference\"`,\n`status: \"verified\"`, `ts: new Date()`, and `payload: {}`. TypeScript callers can use normal\ncamelCase fields (`latencyMs`, `inputHash`, `outputHash`); the SDK also accepts\nthe Python-style aliases (`latency_ms`, `input_hash`, `output_hash`) for shared\ncross-language event builders.\n\n## Proofstream v2\n\n```ts\nimport { AttestoV2Client } from \"@attesto/sdk\";\n\nconst attesto = new AttestoV2Client({\n  apiKey: process.env.ATTESTO_API_KEY!,\n});\nconst receiptSignerPublicKeyHex =\n  process.env.ATTESTO_RECEIPT_SIGNER_PUBLIC_KEY_HEX!;\n\nconst stream = await attesto.createStream({\n  useCase: \"ai-decision-history\",\n  policyId: \"policy-2026-01\",\n});\n\nconst receipt = await attesto.logEvent(stream.streamId, {\n  sourceRef: \"upstream-event-123\",\n  eventType: \"decision\",\n  occurredAt: new Date(),\n  payload: { decision: \"approve\", score: 91 },\n});\n\nconst batch = await attesto.logEvents(stream.streamId, [\n  { sourceRef: \"upstream-event-124\", occurredAt: new Date(), payload: { score: 88 } },\n  {\n    sourceRef: \"upstream-event-125\",\n    eventType: \"decision\",\n    occurredAt: new Date(),\n    payload: { decision: \"review\" },\n  },\n]);\nconsole.log(batch.accepted, batch.receipts.length);\n\nconst stored = await attesto.getReceipt(receipt.streamEventId);\n\nconst report = await attesto.verifyReceipt({\n  receipt: stored.receipt,\n  publicKeyHex: receiptSignerPublicKeyHex,\n  streamEventId: receipt.streamEventId,\n});\n\nconst consistency = await attesto.getCheckpointConsistency(\n  \"chk_current\",\n  \"chk_previous\",\n);\n\nconst policy = await attesto.getWitnessPolicy(\"policy-ai-credit-v1\");\nconsole.log(policy.policyHash);\n\n// Bundle export is intentionally stricter than receipt ingest: every\n// checkpoint in the selected range must already have witness quorum\n// evidence and a confirmed anchor epoch.\nconst bundle = await attesto.buildVerifierBundle(\n  \"chk_previous\",\n  \"chk_current\",\n);\n\n// Present only after the checkpoint has confirmed on-chain.\nconst anchor = await attesto.getAnchorEpoch(\"aep_...\");\nconsole.log(anchor.status);\n\nconst streamReport = await attesto.verifyObject({\n  kind: \"stream\",\n  object: streamExportJson,\n});\nconsole.log(streamReport.ok);\n\nconst offline = await attesto.verifyObject({\n  kind: \"bundle\",\n  object: bundle.bundle,\n});\nconsole.log(offline.ok);\n```\n\n`AttestoV2Client` uses the production `/v2/streams`,\n`/v2/streams/{streamId}/events`,\n`/v2/streams/{streamId}/events/batch`, `/v2/receipts`, `/v2/windows`,\n`/v2/checkpoints`, `/v2/checkpoints/{checkpointId}/consistency`,\n`/v2/witness/policies/{policyId}`, `/v2/anchors/{anchorEpochId}`,\n`/v2/ivc/epochs/{ivcEpochId}`, `/v2/audit/packs`, and `/v2/verify`\nAPIs. Single and batch writes both return signed receipts. It exposes witness\npolicy and review-gated IVC epoch visibility. Receipt ingest can run before\nenforced rollout gates; verifier-bundle export requires witnessed and confirmed\nanchored checkpoints. Nova proof production remains review-gated until that\nrollout gate is enabled.\n\n### Source Time\n\nAttesto stores source-system time separately from backend ingest time. `ts` and\nProofstream `occurredAt` must include an explicit timezone offset. The\nTypeScript SDK defaults them to `new Date()` from the running source process\nwhen omitted, but production integrations should pass the real upstream event\ntimestamp whenever the source system provides one.\n\n## Typed compliance events\n\n```ts\nimport { modelDecision, payloadCommitment } from \"@attesto/sdk\";\n\nconst event = modelDecision({\n  model: \"credit-v1\", decision: \"approve\",\n  inputCommitment: await payloadCommitment(appData),\n  confidenceBp: 8700, humanInLoop: true,\n});\nawait client.logEvent(streamId, { sourceRef: \"d-1\", eventType: event.eventType, payload: event.payload });\n```\n\nBuilders (`modelDecision`, `humanOverride`, `incidentReport`, `dataAccess`)\nattach `regulation_refs` (EU AI Act / NIS2 / GDPR) and self-validate against\nthe number policy.\n\n## Testing without Attesto: createMockServer\n\nA local in-memory emulator with **real** hash-chain semantics; pass its fetch\nhandler to the client and run your full pipeline in CI with zero network:\n\n```ts\nimport { AttestoV2Client, createMockServer, verifyReceipt } from \"@attesto/sdk\";\n\nconst mock = await createMockServer();\nconst client = new AttestoV2Client({\n  apiKey: mock.apiKey, baseUrl: \"https://mock.attesto.test\",\n  fetch: mock.fetch, headStore: null,\n});\nconst stream = await client.createStream({ useCase: \"ci\", policyId: \"mock-policy\" });\nconst receipt = await client.logEvent(stream.streamId, { sourceRef: \"e1\", payload: { n: 1 } });\nconst stored = await client.getReceipt(receipt.streamEventId);\nconst report = await verifyReceipt(stored.receipt, mock.publicKeyHex);\n```\n\nMock evidence can never pass as real: every object carries `mock: true`, the\nsigner kid is `attesto-mock-ed25519`, and verification against any real\nwitness key fails.\n\n## Built-in self-test\n\nOn the first hashing operation per process the SDK verifies itself against a\nvendored copy of the cross-language parity vectors and throws\n`AttestoSelfTestError` on any divergence — a corrupted install can never\nsilently produce wrong evidence.\n\n## Iterating long listings\n\nEvery `list*` method has an `iter*` twin (async iterator) that walks\nlimit/offset pages transparently and stops on the first short page:\n\n```ts\nfor await (const event of attesto.iterTenantStreamEvents(\"str_...\", { pageSize: 200 })) {\n  process(event);\n}\n```\n\n## Verify anchors on-chain\n\n`verifyAnchorOnchain` checks an anchor epoch against the chain itself — one raw\nJSON-RPC `eth_call` to the anchoring contract's `getCommitment(batchId)`\n(comparing the on-chain merkle root) plus a transaction-receipt check (status,\nblock). No ethers/web3 dependency; the RPC endpoint is yours, so this never asks\nAttesto to confirm Attesto.\n\n```ts\nimport { verifyAnchorOnchain } from \"@attesto/sdk\";\n\nconst anchor = await attesto.getAnchorEpoch(\"aep_...\");\nconst report = await verifyAnchorOnchain({\n  anchorEpoch: anchor,\n  rpcUrl: \"https://polygon-rpc.example\", // your own RPC endpoint\n});\nif (!report.ok) throw new Error(report.problems.join(\", \"));\n```\n\n## Receiving Attesto webhooks\n\n```ts\nimport { verifyWebhook } from \"@attesto/sdk\";\n\napp.post(\"/attesto-webhook\", async (req, res) => {\n  const ok = await verifyWebhook({ body: req.rawBody, headers: req.headers, secret: WEBHOOK_SECRET });\n  if (!ok) return res.status(401).end();\n  process(req.body);\n});\n```\n\nVerification recomputes `hmac_sha256(secret, \"<timestamp>.\" + body)` from the\n`X-Attesto-Timestamp` / `X-Attesto-Signature` headers, rejects timestamps more\nthan 300 s from now (replay protection), and compares in constant time.\n\n## Signed webhook connectors\n\nUse the connector helper when an external source posts to a signed-webhook\nconnector endpoint:\n\n```ts\nimport { signedConnectorWebhookHeaders } from \"@attesto/sdk\";\n\nconst body = JSON.stringify({ sourceRef: \"evt_123\" });\nconst headers = await signedConnectorWebhookHeaders(connectorSecret, body);\n```\n\nThe helper signs `timestamp + \".\" + raw_body_bytes` and returns the exact\n`X-Attesto-Connector-*` headers expected by\n`/v2/connectors/signed-webhooks/{connectorId}/events`.\n\n## Configuration\n\n| option | default | purpose |\n|---|---:|---|\n| `apiKey` | - | Required. Must match `atto_live_<32 lowercase hex chars>` or `atto_test_<32 lowercase hex chars>`. |\n| `baseUrl` | `https://verify.attesto.eu` | Public Attesto API origin. Override only for private/staging deployments. |\n| `timeoutMs` | `10000` | Per-request timeout. |\n| `maxRetries` | `3` | Maximum attempts for retryable 408, 429, 5xx, and transport errors. |\n| `retryBaseDelayMs` | `250` | Base delay for jittered exponential backoff. |\n| `fetch` | global `fetch` | Optional custom fetch implementation for tests or runtimes. |\n\n## Security\n\nUse this SDK from server-side code only. Attesto system API keys are bearer\nsecrets and must never be embedded in browser bundles, mobile apps, logs, or\nclient-visible environment variables.\n\n## Errors\n\n```ts\nimport {\n  AttestoClient,\n  AuthError,\n  RateLimitError,\n  ServerError,\n  ValidationError,\n} from \"@attesto/sdk\";\n\ntry {\n  await attesto.logEvent({ type: \"inference\" });\n} catch (error) {\n  if (error instanceof AuthError) {\n    // 401 / 403\n  } else if (error instanceof RateLimitError) {\n    // 429 after retries\n  } else if (error instanceof ValidationError) {\n    // Other 4xx validation problems\n  } else if (error instanceof ServerError) {\n    // 5xx or transport failure after retries\n  }\n}\n```\n\n## Production Behavior\n\n- Defaults to `https://verify.attesto.eu`.\n- Validates key shape locally before making network calls.\n- Validates `baseUrl` locally and accepts only `http` or `https` origins.\n- Applies the same event defaults as the backend/Python SDK.\n- Adds `Idempotency-Key` automatically to single and batch writes.\n- Retries transient 408, 429, 5xx, and transport failures with jittered\n  exponential backoff.\n- Does not require tenant IDs, system IDs, wallets, private keys, or gas\n  handling in application code.\n","readmeFilename":"README.md"}