{"_id":"@aurite-ai/attestation-verifier","_rev":"6-58dc754211f1d489f298026d6ef4c6af","name":"@aurite-ai/attestation-verifier","dist-tags":{"latest":"0.8.0"},"versions":{"0.3.0":{"name":"@aurite-ai/attestation-verifier","version":"0.3.0","keywords":["attestation","audit","verification","ed25519","kahuna"],"license":"MIT","_id":"@aurite-ai/attestation-verifier@0.3.0","maintainers":[{"name":"wilcoxr","email":"wilcoxryan26@gmail.com"},{"name":"dreamwvr","email":"terry@weaversoftware.dev"},{"name":"jitenoswal","email":"jiten.p.oswal@gmail.com"}],"homepage":"https://github.com/Aurite-ai/kahuna-runtime#readme","bugs":{"url":"https://github.com/Aurite-ai/kahuna-runtime/issues"},"bin":{"kahuna-verify":"dist/cli.js","attestation-verifier":"dist/cli.js"},"dist":{"shasum":"b896028ce030f9d3d01f2e8a33dbb4253d0821ec","tarball":"https://registry.npmjs.org/@aurite-ai/attestation-verifier/-/attestation-verifier-0.3.0.tgz","fileCount":19,"integrity":"sha512-vcfH62yzz5vZ8yvLFuWYyayH/e+EcwxD3Sd7UCR3P+MPVu2Tgh6IsjKJImWv4XHzZoV04AhNTeUxo6kNHzJIbg==","signatures":[{"sig":"MEUCIQDsk1zcVty7fxyEKh/AMRDawQH5r3G7UgpvY3MRAAl8LQIgIAurVIsGG9gf3wwp8sr2FnRxlYLBUICrOqCvHRUpO8I=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":133068},"main":"./dist/index.js","type":"module","_from":"file:/private/tmp/claude-501/-Users-jitenoswal-Downloads-Agentic-Layer-Proto/f6e5f8a1-9413-4d11-bb66-7cc1f2669a9b/scratchpad/aurite-ai-attestation-verifier-0.3.0.tgz","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"scripts":{"build":"node scripts/build.mjs","clean":"rm -rf dist .turbo","test:unit":"vitest run --passWithNoTests","typecheck":"tsc -p tsconfig.json --noEmit"},"_npmUser":{"name":"jitenoswal","email":"jiten.p.oswal@gmail.com"},"_resolved":"/private/tmp/claude-501/-Users-jitenoswal-Downloads-Agentic-Layer-Proto/f6e5f8a1-9413-4d11-bb66-7cc1f2669a9b/scratchpad/aurite-ai-attestation-verifier-0.3.0.tgz","_integrity":"sha512-vcfH62yzz5vZ8yvLFuWYyayH/e+EcwxD3Sd7UCR3P+MPVu2Tgh6IsjKJImWv4XHzZoV04AhNTeUxo6kNHzJIbg==","repository":{"url":"git+https://github.com/Aurite-ai/kahuna-runtime.git","type":"git","directory":"packages/attestation-verifier"},"_npmVersion":"10.9.8","description":"Standalone offline verifier for Kahuna attestation evidence packages. Verifies ed25519 signatures and chain linkage using public keys embedded in the package — no network access to Kahuna required.","directories":{},"_nodeVersion":"22.22.3","dependencies":{"uuid":"11.0.3","json-canon":"1.0.1","@noble/hashes":"1.6.1","@noble/ed25519":"2.1.0","@opentelemetry/api":"1.9.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"2.1.4","@types/node":"22.9.0","@kahuna/shared-types":"0.0.0","@kahuna/shared-attestation":"0.0.0"},"_npmOperationalInternal":{"tmp":"tmp/attestation-verifier_0.3.0_1786567077912_0.3552399624149294","host":"s3://npm-registry-packages-npm-production"}},"0.5.0":{"name":"@aurite-ai/attestation-verifier","version":"0.5.0","keywords":["attestation","audit","verification","ed25519","kahuna"],"license":"MIT","_id":"@aurite-ai/attestation-verifier@0.5.0","maintainers":[{"name":"wilcoxr","email":"wilcoxryan26@gmail.com"},{"name":"dreamwvr","email":"terry@weaversoftware.dev"},{"name":"jitenoswal","email":"jiten.p.oswal@gmail.com"}],"homepage":"https://github.com/Aurite-ai/kahuna-runtime#readme","bugs":{"url":"https://github.com/Aurite-ai/kahuna-runtime/issues"},"bin":{"kahuna-verify":"dist/cli.js","attestation-verifier":"dist/cli.js"},"dist":{"shasum":"690de3c92bd4d4a51dc111c909adb5492df3df4d","tarball":"https://registry.npmjs.org/@aurite-ai/attestation-verifier/-/attestation-verifier-0.5.0.tgz","fileCount":19,"integrity":"sha512-y5MBqyP5Ok3Y8+AgN1Z5qaCG+qdE5QghX1LF2P2nkxJtEd4S1OmL39aR0bzABCchLv6hZRSTt1xIFOqDpVFUpA==","signatures":[{"sig":"MEQCIASs3Ka0oSc+SomLPDpjMjN+bJjpAqtdOEAscqY3sFjpAiAPmcUvvNYMYEqOqZxSkPd91FUl1BjuI1f2XcA9Ed1lOA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":149626},"main":"./dist/index.js","type":"module","_from":"file:/private/tmp/claude-501/-Users-jitenoswal-Downloads-Agentic-Layer-Proto/f6e5f8a1-9413-4d11-bb66-7cc1f2669a9b/scratchpad/aurite-ai-attestation-verifier-0.5.0.tgz","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"scripts":{"build":"node scripts/build.mjs","clean":"rm -rf dist .turbo","test:unit":"vitest run --passWithNoTests","typecheck":"tsc -p tsconfig.json --noEmit"},"_npmUser":{"name":"jitenoswal","email":"jiten.p.oswal@gmail.com"},"_resolved":"/private/tmp/claude-501/-Users-jitenoswal-Downloads-Agentic-Layer-Proto/f6e5f8a1-9413-4d11-bb66-7cc1f2669a9b/scratchpad/aurite-ai-attestation-verifier-0.5.0.tgz","_integrity":"sha512-y5MBqyP5Ok3Y8+AgN1Z5qaCG+qdE5QghX1LF2P2nkxJtEd4S1OmL39aR0bzABCchLv6hZRSTt1xIFOqDpVFUpA==","repository":{"url":"git+https://github.com/Aurite-ai/kahuna-runtime.git","type":"git","directory":"packages/attestation-verifier"},"_npmVersion":"10.9.8","description":"Standalone offline verifier for Kahuna attestation evidence packages. Verifies ed25519 signatures and chain linkage using public keys embedded in the package — no network access to Kahuna required.","directories":{},"_nodeVersion":"22.22.3","dependencies":{"uuid":"11.0.3","json-canon":"1.0.1","@noble/hashes":"1.6.1","@noble/ed25519":"2.1.0","@opentelemetry/api":"1.9.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"2.1.4","@types/node":"22.9.0","@kahuna/shared-types":"0.0.0","@kahuna/shared-attestation":"0.0.0"},"_npmOperationalInternal":{"tmp":"tmp/attestation-verifier_0.5.0_1786597839457_0.3076209002946688","host":"s3://npm-registry-packages-npm-production"}},"0.5.2":{"name":"@aurite-ai/attestation-verifier","version":"0.5.2","keywords":["attestation","audit","verification","ed25519","kahuna"],"license":"MIT","_id":"@aurite-ai/attestation-verifier@0.5.2","maintainers":[{"name":"wilcoxr","email":"wilcoxryan26@gmail.com"},{"name":"dreamwvr","email":"terry@weaversoftware.dev"},{"name":"jitenoswal","email":"jiten.p.oswal@gmail.com"}],"homepage":"https://github.com/Aurite-ai/kahuna-runtime#readme","bugs":{"url":"https://github.com/Aurite-ai/kahuna-runtime/issues"},"bin":{"kahuna-verify":"dist/cli.js","attestation-verifier":"dist/cli.js"},"dist":{"shasum":"f43607c982d4443376dd110e09c0ca9063b28509","tarball":"https://registry.npmjs.org/@aurite-ai/attestation-verifier/-/attestation-verifier-0.5.2.tgz","fileCount":19,"integrity":"sha512-vC6QFqbU+dPTMMGvB6fNUn35IwJO/2eJLuOBPgleFKfh6drMdkgCRH0cgfjmcEhGpy2docQ4QCk5Syhj9uTluQ==","signatures":[{"sig":"MEQCIDAPAB4t65jBy7mN0r/aX/ldmQndVL8+5ykDkDJyZKHFAiAnxzs/4EludZg0OIAVHMp4XqXI37ZzjRTsv2aEymZDJQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":150318},"main":"./dist/index.js","type":"module","_from":"file:/home/runner/work/_temp/aurite-ai-attestation-verifier-0.5.2.tgz","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"scripts":{"build":"node scripts/build.mjs","clean":"rm -rf dist .turbo","test:unit":"vitest run --passWithNoTests","typecheck":"tsc -p tsconfig.json --noEmit"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:5a4d61bf-cea8-4b71-9bab-49d4050c371e"}},"_resolved":"/home/runner/work/_temp/aurite-ai-attestation-verifier-0.5.2.tgz","_integrity":"sha512-vC6QFqbU+dPTMMGvB6fNUn35IwJO/2eJLuOBPgleFKfh6drMdkgCRH0cgfjmcEhGpy2docQ4QCk5Syhj9uTluQ==","repository":{"url":"git+https://github.com/Aurite-ai/kahuna-runtime.git","type":"git","directory":"packages/attestation-verifier"},"_npmVersion":"12.0.2","description":"Standalone offline verifier for Kahuna attestation evidence packages. Verifies ed25519 signatures and chain linkage using public keys embedded in the package — no network access to Kahuna required.","directories":{},"_nodeVersion":"22.23.1","dependencies":{"uuid":"11.0.3","json-canon":"1.0.1","@noble/hashes":"1.6.1","@noble/ed25519":"2.1.0","@opentelemetry/api":"1.9.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"2.1.4","@types/node":"22.9.0","@kahuna/shared-types":"0.0.0","@kahuna/shared-attestation":"0.0.0"},"_npmOperationalInternal":{"tmp":"tmp/attestation-verifier_0.5.2_1786611113377_0.12506196124287938","host":"s3://npm-registry-packages-npm-production"}},"0.7.0":{"name":"@aurite-ai/attestation-verifier","version":"0.7.0","keywords":["attestation","audit","verification","ed25519","kahuna"],"license":"MIT","_id":"@aurite-ai/attestation-verifier@0.7.0","maintainers":[{"name":"wilcoxr","email":"wilcoxryan26@gmail.com"},{"name":"dreamwvr","email":"terry@weaversoftware.dev"},{"name":"jitenoswal","email":"jiten.p.oswal@gmail.com"}],"homepage":"https://github.com/Aurite-ai/kahuna-runtime#readme","bugs":{"url":"https://github.com/Aurite-ai/kahuna-runtime/issues"},"bin":{"kahuna-verify":"dist/cli.js","attestation-verifier":"dist/cli.js"},"dist":{"shasum":"491db41b277d0efccfdc8e1746baa0e8007664bf","tarball":"https://registry.npmjs.org/@aurite-ai/attestation-verifier/-/attestation-verifier-0.7.0.tgz","fileCount":22,"integrity":"sha512-r7AFGWWM5tq+msjivbr2yzxBIgOWvwC84DW6apQcFw/v6MJ69di492hZliFPL+biWkc3Wg+9aKpp26d+ATHNqg==","signatures":[{"sig":"MEUCIQDqboDDjwj0yJnhxgIX9fwpES9SzEPDY4rA1RkFLk8/CwIgG+6hRFOnUXb6syuGJuEnVUzxzKOwDXJXKuJ726H4iNI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":184768},"main":"./dist/index.js","type":"module","_from":"file:/home/runner/work/_temp/aurite-ai-attestation-verifier-0.7.0.tgz","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"scripts":{"build":"node scripts/build.mjs","clean":"rm -rf dist .turbo","test:unit":"vitest run --passWithNoTests","typecheck":"tsc -p tsconfig.json --noEmit"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:5a4d61bf-cea8-4b71-9bab-49d4050c371e"}},"_resolved":"/home/runner/work/_temp/aurite-ai-attestation-verifier-0.7.0.tgz","_integrity":"sha512-r7AFGWWM5tq+msjivbr2yzxBIgOWvwC84DW6apQcFw/v6MJ69di492hZliFPL+biWkc3Wg+9aKpp26d+ATHNqg==","repository":{"url":"git+https://github.com/Aurite-ai/kahuna-runtime.git","type":"git","directory":"packages/attestation-verifier"},"_npmVersion":"12.0.2","description":"Standalone offline verifier for Kahuna attestation evidence packages. Verifies ed25519 signatures and chain linkage using public keys embedded in the package — no network access to Kahuna required.","directories":{},"_nodeVersion":"22.23.2","dependencies":{"uuid":"11.0.3","json-canon":"1.0.1","@noble/hashes":"1.6.1","@noble/ed25519":"2.1.0","@opentelemetry/api":"1.9.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"2.1.4","@types/node":"22.9.0","@kahuna/shared-types":"0.0.0","@kahuna/shared-attestation":"0.0.0"},"_npmOperationalInternal":{"tmp":"tmp/attestation-verifier_0.7.0_1787887605544_0.8370760221922293","host":"s3://npm-registry-packages-npm-production"}},"0.7.1":{"name":"@aurite-ai/attestation-verifier","version":"0.7.1","keywords":["attestation","audit","verification","ed25519","kahuna"],"license":"MIT","_id":"@aurite-ai/attestation-verifier@0.7.1","maintainers":[{"name":"wilcoxr","email":"wilcoxryan26@gmail.com"},{"name":"dreamwvr","email":"terry@weaversoftware.dev"},{"name":"jitenoswal","email":"jiten.p.oswal@gmail.com"}],"homepage":"https://github.com/Aurite-ai/kahuna-runtime#readme","bugs":{"url":"https://github.com/Aurite-ai/kahuna-runtime/issues"},"bin":{"kahuna-verify":"dist/cli.js","attestation-verifier":"dist/cli.js"},"dist":{"shasum":"c10158d4ca15a14c5d01fda501b9fb61b5335e72","tarball":"https://registry.npmjs.org/@aurite-ai/attestation-verifier/-/attestation-verifier-0.7.1.tgz","fileCount":23,"integrity":"sha512-Wm8bmybSTZV6W1lQyif54y1YYgUPrpbPSiJT4A0hIEt3CLZN97NV83JQdOfZ/1xN+SQeJucBPPaDwSQW1ZrxEQ==","signatures":[{"sig":"MEUCIQC5uWD7vARMHj7aoQd2YAbMW61jQwMfz1BFQcp6T67YXwIgB/TIuwkV+RaqywsaEYqFXGim3W4Mf2RFL/z9S60vn9g=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":197516},"main":"./dist/index.js","type":"module","_from":"file:/home/runner/work/_temp/aurite-ai-attestation-verifier-0.7.1.tgz","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"scripts":{"build":"node scripts/build.mjs","clean":"rm -rf dist .turbo","test:unit":"vitest run --passWithNoTests","typecheck":"tsc -p tsconfig.json --noEmit"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:5a4d61bf-cea8-4b71-9bab-49d4050c371e"}},"_resolved":"/home/runner/work/_temp/aurite-ai-attestation-verifier-0.7.1.tgz","_integrity":"sha512-Wm8bmybSTZV6W1lQyif54y1YYgUPrpbPSiJT4A0hIEt3CLZN97NV83JQdOfZ/1xN+SQeJucBPPaDwSQW1ZrxEQ==","repository":{"url":"git+https://github.com/Aurite-ai/kahuna-runtime.git","type":"git","directory":"packages/attestation-verifier"},"_npmVersion":"12.0.2","description":"Standalone offline verifier for Kahuna attestation evidence packages. Verifies ed25519 signatures and chain linkage using public keys embedded in the package — no network access to Kahuna required.","directories":{},"_nodeVersion":"22.23.2","dependencies":{"uuid":"11.0.3","json-canon":"1.0.1","@noble/hashes":"1.6.1","@noble/ed25519":"2.1.0","@opentelemetry/api":"1.9.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"2.1.4","@types/node":"22.9.0","@kahuna/shared-types":"0.0.0","@kahuna/shared-attestation":"0.0.0"},"_npmOperationalInternal":{"tmp":"tmp/attestation-verifier_0.7.1_1787889347831_0.3497522070044705","host":"s3://npm-registry-packages-npm-production"}},"0.8.0":{"name":"@aurite-ai/attestation-verifier","version":"0.8.0","type":"module","description":"Standalone offline verifier for Kahuna attestation evidence packages. Verifies ed25519 signatures and chain linkage using public keys embedded in the package — no network access to Kahuna required.","license":"MIT","keywords":["attestation","audit","verification","ed25519","kahuna"],"repository":{"type":"git","url":"git+https://github.com/Aurite-ai/kahuna-runtime.git","directory":"packages/attestation-verifier"},"bugs":{"url":"https://github.com/Aurite-ai/kahuna-runtime/issues"},"main":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"publishConfig":{"access":"public"},"engines":{"node":">=20"},"dependencies":{"@noble/ed25519":"2.1.0","@noble/hashes":"1.6.1","@opentelemetry/api":"1.9.0","json-canon":"1.0.1","uuid":"11.0.3"},"devDependencies":{"@types/node":"22.9.0","vitest":"2.1.4","@kahuna/shared-types":"0.0.0","@kahuna/shared-attestation":"0.0.0"},"scripts":{"build":"node scripts/build.mjs","typecheck":"tsc -p tsconfig.json --noEmit","test:unit":"vitest run --passWithNoTests","clean":"rm -rf dist .turbo"},"bin":{"attestation-verifier":"dist/cli.js","kahuna-verify":"dist/cli.js"},"_id":"@aurite-ai/attestation-verifier@0.8.0","homepage":"https://github.com/Aurite-ai/kahuna-runtime#readme","_integrity":"sha512-GCp4QqfRhjaB/oZsEBK+9BVGeHevTPXwjFllfeBWj0ftryJ1g99m+Vo+GX4XQ6ZR8LburVREuRlcXMvkx/TMTA==","_resolved":"/home/runner/work/_temp/aurite-ai-attestation-verifier-0.8.0.tgz","_from":"file:/home/runner/work/_temp/aurite-ai-attestation-verifier-0.8.0.tgz","_nodeVersion":"22.23.2","_npmVersion":"12.0.2","dist":{"integrity":"sha512-GCp4QqfRhjaB/oZsEBK+9BVGeHevTPXwjFllfeBWj0ftryJ1g99m+Vo+GX4XQ6ZR8LburVREuRlcXMvkx/TMTA==","shasum":"b57a0ce223dc748d2165d63ff8aeaa930a358a7a","tarball":"https://registry.npmjs.org/@aurite-ai/attestation-verifier/-/attestation-verifier-0.8.0.tgz","fileCount":24,"unpackedSize":210230,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIFmH/9yAURaODCONq9tIQbs2fwHpoVdwOBfnLShzEorgAiBG8yUneVR2uSpy3GvfPB6z3uHyWdnwLJmweYcdzjk2Hw=="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:5a4d61bf-cea8-4b71-9bab-49d4050c371e"}},"directories":{},"maintainers":[{"name":"wilcoxr","email":"wilcoxryan26@gmail.com"},{"name":"dreamwvr","email":"terry@weaversoftware.dev"},{"name":"jitenoswal","email":"jiten.p.oswal@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/attestation-verifier_0.8.0_1787910456652_0.7327455232734272"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-12T20:37:57.768Z","modified":"2026-08-28T09:47:36.997Z","0.3.0":"2026-08-12T20:37:58.059Z","0.5.0":"2026-08-13T05:10:39.629Z","0.5.2":"2026-08-13T08:51:53.539Z","0.7.0":"2026-08-28T03:26:45.695Z","0.7.1":"2026-08-28T03:55:47.975Z","0.8.0":"2026-08-28T09:47:36.780Z"},"bugs":{"url":"https://github.com/Aurite-ai/kahuna-runtime/issues"},"license":"MIT","homepage":"https://github.com/Aurite-ai/kahuna-runtime#readme","keywords":["attestation","audit","verification","ed25519","kahuna"],"repository":{"type":"git","url":"git+https://github.com/Aurite-ai/kahuna-runtime.git","directory":"packages/attestation-verifier"},"description":"Standalone offline verifier for Kahuna attestation evidence packages. Verifies ed25519 signatures and chain linkage using public keys embedded in the package — no network access to Kahuna required.","maintainers":[{"name":"wilcoxr","email":"wilcoxryan26@gmail.com"},{"name":"dreamwvr","email":"terry@weaversoftware.dev"},{"name":"jitenoswal","email":"jiten.p.oswal@gmail.com"}],"readme":"# @aurite-ai/attestation-verifier\n\nExternal verifier package for Kahuna attestation events.\n\n## What this is\n\nThis package lets any party — Kahuna customers, third-party auditors, compliance scanners — independently verify the integrity of attestation chains produced by Kahuna agents. It wraps the verify operations from `@kahuna/shared-attestation` with a public API designed for standalone consumption.\n\nThe verifier is **pure** — given the same inputs (an event and a public key bundle), it returns the same outputs. No network calls during verification. No environment dependencies. No side effects.\n\n### Two questions, both answered offline\n\n| Question | Command | Needs |\n|---|---|---|\n| Are these events authentic and linked? | `verify evidence.json` | the package |\n| Is this **all** of them? | `verify evidence.json --countersigned-head head.json` | the package + a file from the customer |\n\n**Neither touches the network.** The signing keys travel inside the package, and\na countersigned head is a local file. Adding the scope check does not make\nverification less offline — it adds a second local input.\n\nThe two questions are genuinely different, and only the first is answerable from\nthe package alone. A curated subset answers \"yes\" to the first and hides the\nsecond.\n\n## Why open verification matters\n\nPer the architectural decision baked into the v1 schema (see `docs/attestation/event-schema.md` §11), Kahuna attestation chains are designed for **independent verification**. The cryptographic guarantee — \"events were produced by the named signing key\" — only matters if parties other than Kahuna can verify it.\n\nFor evidence packages, the keys travel *inside* the package, so verification needs no network access at all and no cooperation from whoever operates the chain. That is the point: the party you are auditing is not in the loop when you check their work.\n\n> **Correction (2026-08-27).** This section previously said *\"Public key bundles\n> are openly accessible (no API key required). Anyone can fetch any\n> organization's bundle.\"* **That was not true and has never been true.** The\n> `/.well-known/kahuna/public-keys/<org>` route exists and is unauthenticated\n> on the server, but in a deployed cluster it sits behind an authenticating\n> load balancer, so an unauthenticated request is redirected to a login page\n> and never reaches it. Making it publicly reachable is tracked as G51(a) and\n> is **not** in this release.\n>\n> Nothing about offline verification depended on it: the keys are in the\n> package. What it affects is only the optional `--against` cross-check below.\n\n## Checking the scope claim — read this before trusting \"COMPLETE chain\"\n\nEverything an evidence package says about **its own extent** is written by the\nparty being audited. A genuine 10-event prefix of a 60-event chain can declare\nitself the complete chain and pass every offline check — real signatures, linked\nhashes, self-consistent metadata. This was demonstrated, not theorised.\n\nOffline verification answers *\"are these events authentic and linked?\"* It does\n**not** answer *\"is this all of them?\"* Those are different questions and only\nthe first is answerable from the file alone.\n\n### The countersigned chain head\n\nThe second question needs one number the package cannot contain: a chain head\nobserved by someone other than the operator. A **countersigned head** is a\nsigned statement by the customer about what they saw:\n\n```json\n{\n  \"assertion\": {\n    \"kind\": \"kahuna.chain-head-attestation/v1\",\n    \"organization_id\": \"3f2504e0-4f89-11d3-9a0c-0305e82c3301\",\n    \"chain_head_position\": 59,\n    \"head_event_hash\": null,\n    \"observed_at\": \"2026-08-20T10:00:00.000Z\",\n    \"observer_id\": \"spiffe://customer.example/agent/attestation_consumer\"\n  },\n  \"signature\": \"<base64 Ed25519 over the JCS serialization of `assertion`>\",\n  \"signer_public_key\": \"<base64 raw Ed25519 public key>\"\n}\n```\n\n**It is not a public key**, though it carries one so the signature is checkable.\nIt is a dated, signed claim that *the chain had reached position 59*.\n\n`head_event_hash` may be `null`. The observer records a hash only if it saw the\nhead event; observing only a position is honest and still sufficient for the\ncheck below. It is never invented.\n\n### Why it works\n\nThe chain is append-only, so head position only increases. If the customer\nobserved position **59** at time *T*, then a package assembled at or after *T*\nclaiming the **complete** chain ends at position **9** is self-contradictory.\nThat contradiction is what the check reports.\n\n### Producing one (customer side)\n\nRuns as a workload in the customer's own environment, signing with a\n**customer-held** Ed25519 key — not a Kahuna key, and not the workload's SVID\nkey, which is short-lived by design.\n\n```ts\nimport { startHeadObserver } from \"@aurite-ai/attestation-verifier\";\n\nstartHeadObserver({\n  serverBase: \"http://kahuna-server:9100\", // in-cluster; no ingress needed\n  organizationId: \"<org-uuid>\",\n  signingKey: customerEd25519PrivateKey,   // 32 bytes, customer-held\n  observerId: \"spiffe://customer.example/agent/attestation_consumer\",\n  intervalMs: 60 * 60 * 1000,\n  onAttestation: (a) => persistSomewhereTheCustomerControls(a),\n});\n```\n\n**Retention is the whole mechanism.** An attestation stored only where the\noperator can reach it buys nothing — the point is a reference they cannot\nrevise. Frequency sets resolution: hourly bounds any curated package to within\nan hour of the truth. Infrequent is fine, because the chain only grows, so an\nold attestation stays valid evidence of a position already reached.\n\n### Using one (auditor side)\n\n```bash\nnpx @aurite-ai/attestation-verifier verify evidence.json \\\n  --countersigned-head head.json\n```\n\n**Obtain `head.json` from the customer, not from the package.** A countersigned\nhead that arrived inside the evidence bundle proves nothing — the operator\nassembled both.\n\nOutcomes:\n\n| Result | Meaning |\n|---|---|\n| exit 0, *\"a reference this platform does not control\"* | The package's scope claim is consistent with what the customer observed |\n| exit 2, *\"claims the COMPLETE chain ends at N\"* | **Contradicted.** The customer had already observed a later position |\n| exit 2, *\"No verdict was reached\"* | The package claims to predate the observation, so nothing follows. Get an attestation observed *before* the package was assembled |\n| exit 2, *\"countersignature did not verify\"* | The file is not usable as evidence |\n\nNote the third row: a check that cannot reach a verdict **fails**. Silence must\nnot read as agreement.\n\n### What this still does not close\n\n- **Whose key is it?** The verifier checks that the signature matches the\n  embedded key. It cannot know the key is the customer's. That is the auditor's\n  job, and it is why the file must come from the customer through a channel the\n  operator does not control.\n- **`assembled_at` is operator-supplied.** An operator can claim a package\n  predates the attestation and escape the comparison — which reports\n  `stale_attestation` rather than passing. Closing it needs a timestamp the\n  operator does not control (a transparency log). Tracked as G51(c).\n\n### `--against <server>` — deferred\n\nAn earlier design fetched the live head from the server. It is weaker (the same\nparty serves the package and the head, so it defeats a curated *file* but not a\ndetermined operator) and it is **not reachable in a deployed cluster** — see the\ncorrection above. Deferred to a future release; the countersigned head is the\nstronger check and needs no ingress change.\n\n## Setting this up (customer, once)\n\nThree steps. After this, every audit is a file handover.\n\n### 1. Generate a signing key you control\n\n```bash\nnpx @aurite-ai/attestation-verifier keygen --out observer-key.json\n```\n\nWrites a `0600` file and prints the public key:\n\n```\nWrote observer-key.json (mode 0600).\n\n  PRIVATE half — never send this to Kahuna or anyone else.\n  PUBLIC key, base64 — give this to your auditors:\n\n    iptR2KuIx9Nye3VbFUqM34dwUBbuQRZCAW1WzPrgaoM=\n```\n\nIt runs entirely on your machine. **Kahuna never sees the private half** — that\nis the property the whole mechanism rests on, which is also why there is no\n\"generate a key\" button in the Kahuna console: a console page would put our code\nin your key path, and an auditor could not rule out that we kept a copy.\n\nLoad `observer-key.json` into your observer workload the way you load any other\nsecret — a Kubernetes Secret, your secret manager, a mounted file.\n\n> **Losing this key is cheap.** Everything already signed stays valid, and old\n> attestations remain good evidence of positions the chain had already reached.\n> Generate a new one and carry on. That is also why we do not derive it from a\n> passphrase: the key would be only as strong as the phrase, a guessed phrase\n> forges countersignatures, and there is no recovery need to justify the risk.\n\n### 1b. Declare the public half to Kahuna (optional, and not a trust root)\n\nRecording your public key with Kahuna lets you check that the key we show\nalongside an evidence package is the one you actually hold. Declaring requires\na **possession proof**:\n\n```sh\nnpx @aurite-ai/attestation-verifier prove \\\n  --key observer-key.json \\\n  --org <your-organization-id> \\\n  --observer-id <your-reference>\n```\n\nSend the printed JSON as `possession_proof` to\n`POST /management/v1/observer-keys`. The proof contains your **public** key and\na signature; your private key is used to sign and never leaves the process. It\nis valid for five minutes.\n\n**Why a proof is required.** An Ed25519 private key and a public key are both\n32 base64 bytes, so nothing about the value itself distinguishes them. Pasting\nthe `private_key` line where `public_key` belongs would pass every structural\ncheck and disclose the half that makes your countersignature mean anything —\nand you would not find out until verification failed later, looking like a\nbroken key rather than a leaked one. The proof catches it immediately.\n\n> **This does not make our copy of your key authoritative.** We are the party\n> being audited; a key an auditor learns from us is a key we could have\n> substituted. The sound path is still the one in step 3 — you hand the key to\n> your auditor directly. Declaring it here buys you the ability to notice a\n> substitution, and buys an auditor nothing on its own.\n\n### 2. Run the observer in your cluster\n\nAny long-running workload will do — a small Deployment of its own is the usual\nshape. It needs network access to `kahuna-server` and read access to the key.\n\n```ts\nimport { loadObserverKey, startHeadObserver } from \"@aurite-ai/attestation-verifier\";\n\nconst key = loadObserverKey(\"/etc/kahuna/observer-key.json\");\n\nstartHeadObserver({\n  serverBase: \"http://kahuna-server:9100\", // in-cluster; no ingress needed\n  organizationId: process.env.KAHUNA_ORG_ID!,\n  signingKey: key.privateKey,\n  observerId: \"spiffe://your-org.example/agent/attestation-observer\",\n  intervalMs: 60 * 60 * 1000,              // hourly is plenty — see below\n  onAttestation: async (a) => {\n    // `a` serialized IS the head.json an auditor is later handed.\n    await appendToYourOwnStorage(`${JSON.stringify(a)}\\n`);\n  },\n  onError: (e) => log.warn({ e }, \"head observation failed; will retry\"),\n});\n```\n\nEach attestation looks like this — and this object, written to a file, is\nexactly what `--countersigned-head` consumes:\n\n```json\n{\n  \"assertion\": {\n    \"kind\": \"kahuna.chain-head-attestation/v1\",\n    \"organization_id\": \"…\",\n    \"chain_head_position\": 59,\n    \"head_event_hash\": null,\n    \"observed_at\": \"2026-08-20T10:00:00.000Z\",\n    \"observer_id\": \"spiffe://your-org.example/agent/attestation-observer\"\n  },\n  \"signature\": \"…\",\n  \"signer_public_key\": \"iptR2KuIx9Nye3VbFUqM34dwUBbuQRZCAW1WzPrgaoM=\"\n}\n```\n\nHourly is generous. The chain only grows, so an old attestation stays valid\nevidence of a position already reached; frequency sets how tightly a curated\npackage can be bounded, not whether the check works.\n\n### 3. Retain the output somewhere Kahuna cannot reach\n\n**This is the mechanism, not an implementation detail.** An attestation stored\nwhere the operator could revise it proves nothing. Object storage in your\naccount, your log pipeline, anywhere under your control.\n\nYou do not need all of them. One attestation older than the package under audit\nis enough.\n\n## Receiving an audit (auditor)\n\nYou should be handed **three** things. Ask for the third if it is missing.\n\n| | What | Why |\n|---|---|---|\n| 1 | `evidence.json` | the events and the keys that signed them |\n| 2 | `head.json` | a countersigned chain head, **from the customer** |\n| 3 | the customer's public key, **through a channel the operator does not control** | so #2 means something |\n\nThen:\n\n```bash\nnpx @aurite-ai/attestation-verifier verify evidence.json \\\n  --countersigned-head head.json\n```\n\nCheck the key yourself — the file carries `signer_public_key`, and it should\nequal what the customer gave you separately:\n\n```bash\njq -r .signer_public_key head.json     # must match the key you were given\n```\n\nIf those differ, stop. A countersigned head whose key you learned only from the\nfile proves nothing about who observed the chain.\n\n> **Why the separate channel matters.** The verifier checks that the signature\n> matches the embedded key. It cannot know whose key that is. If the operator\n> supplied the package, the head file *and* the key, they supplied the entire\n> chain of custody and the check is theatre.\n\n## Installation\n\n```bash\nnpm install @aurite-ai/attestation-verifier\n```\n\n## CLI\n\n```bash\n# Verify an exported evidence package — fully offline\nnpx @aurite-ai/attestation-verifier verify evidence.json\n\n# Export a package from a running Kahuna server\nnpx @aurite-ai/attestation-verifier export --org <uuid> --server <url> --out evidence.json\n```\n\nExit codes: `0` passed · `1` usage / unreadable file / malformed JSON ·\n`2` verification **failed** (signature, chain position, or hash linkage).\n\nBoth `attestation-verifier` and `kahuna-verify` are installed as binaries; they are the same tool.\n\n### What a pass proves — and what it does not\n\nA pass proves every event **in the file** is authentically signed and that the events form an unbroken run.\n\nIt does **not**, on its own, prove the file contains the whole chain. A window is verified against its own first event, so a partial export passes exactly like a complete one. Compare `metadata.chain_position_start` and `metadata.chain_position_end` against the range you expected. Do not rely on `metadata.window_complete` — see tracker G31; it is not currently trustworthy.\n\n## Library usage\n\n### Verify a single event\n\n```typescript\nimport { verifyEvent, type PublicKeyBundle } from \"@aurite-ai/attestation-verifier\";\n\nconst bundle: PublicKeyBundle = await fetchYourOrgBundle();\nconst event = await fetchEventFromKahuna();\n\nconst result = await verifyEvent(event, bundle);\nif (!result.valid) {\n  console.error(\"Verification failed:\", result.failure.reason);\n  console.error(\"Message:\", result.failure.message);\n}\n```\n\n### Verify a chain segment\n\n```typescript\nimport { verifyChainSegment } from \"@aurite-ai/attestation-verifier\";\n\nconst events = await fetchChainSegment(); // events in chain_position order\n\nconst result = await verifyChainSegment(events, bundle);\nif (result.valid) {\n  console.log(\"Chain head:\", result.chainHead);\n} else {\n  console.error(\n    `Verification failed at segment index ${result.failure.failed_at_segment_index}:`,\n    result.failure.reason,\n  );\n}\n```\n\nNote that `verifyChainSegment` accepts an optional third argument, `startingState`. **Without it, the segment is verified against its own first event** — which is why a partial window passes like a complete one. Pass a `ChainStartingState` when you know where the segment is supposed to attach.\n\n### Verify an evidence package\n\n```typescript\nimport { verifyEvidencePackage, type EvidencePackage } from \"@aurite-ai/attestation-verifier\";\n\nconst summary = await verifyEvidencePackage(pkg);\nif (!summary.valid) throw new Error(summary.failure?.message);\n```\n\n## Failure reasons\n\nThe verifier returns structured failures per schema §9.3. The full set:\n\n- `schema_invalid` — event is malformed (missing fields, wrong types, etc.)\n- `signature_invalid` — Ed25519 signature didn't verify\n- `key_unknown` — the named signing key isn't in the bundle\n- `chain_position_gap` — adjacent events have non-consecutive positions\n- `chain_position_duplicate` — two events share a position\n- `previous_hash_mismatch` — a chain link is broken\n- `organization_mismatch` — events span multiple organizations\n- `internal_inconsistency` — an internal-consistency check failed (e.g., `recorded_at < occurred_at`)\n- `bounds_exceeded` — an event exceeds a size or cardinality bound\n\n## Status\n\n**Current:** v0.3.0. The verify operations are real and tested. `fetchPublicKeyBundle()` is implemented against `GET <server>/.well-known/kahuna/public-keys/<organizationId>`.\n\n**Not yet done:** the package is **not published to any registry**. Until it is, `npx @aurite-ai/attestation-verifier` cannot work for anyone outside this repository — see tracker G30, which is what that row exists to close.\n\n## Building and publishing (maintainers)\n\n`pnpm build` runs `scripts/build.mjs`, which bundles the library and CLI with esbuild and emits declarations with tsc. The two `@kahuna/*` workspace packages are **inlined** into the bundle — they do not exist on npm, so a published package that imported them would fail on the customer's first run. The build asserts this rather than assuming it, and also asserts that every remaining bare import is a declared runtime dependency.\n\n**Publish with `pnpm publish`, never `npm publish`.** The manifest points `main`/`types`/`bin` at `src/` for in-repo consumers and overrides them to `dist/` under `publishConfig`. pnpm applies those overrides; npm does not, so `npm pack` produces a tarball whose entry points reference TypeScript sources that are not shipped.\n\nTo check the artifact the way a customer receives it:\n\n```bash\npnpm pack --pack-destination /tmp\nmkdir /tmp/clean && cd /tmp/clean && npm init -y\nnpm install /tmp/aurite-ai-attestation-verifier-0.3.0.tgz\nnpx @aurite-ai/attestation-verifier verify <some-package.json>\n```\n\nRun that from **outside** this repository. Every defect tracked in G30 was invisible from inside it.\n\n## License\n\nMIT — see [LICENSE](./LICENSE). This package is MIT even though the wider Kahuna repository is not: a verifier that third parties are told to run has to be one they are actually permitted to run.\n","readmeFilename":"README.md"}