{"_id":"@cognoshift/sanad-verify","name":"@cognoshift/sanad-verify","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@cognoshift/sanad-verify","version":"0.1.0","description":"Offline verification library for Sanad Sign attestations — Ed25519 + CycloneDX + SHA-256 hash chain. Zero dependencies, works in Node 18+.","main":"./lib/index.js","types":"./lib/index.d.ts","engines":{"node":">=18"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/anupam9091/cognoshift-sentinel.git","directory":"cli/sanad-verify"},"homepage":"https://sanad.cognoshift.in","bugs":{"url":"https://github.com/anupam9091/cognoshift-sentinel/issues","email":"support@cognoshift.in"},"author":{"name":"COGNOSHIFT PRIVATE LIMITED","email":"support@cognoshift.in","url":"https://cognoshift.in"},"keywords":["sbom","verification","ed25519","cyclonedx","attestation","sigstore","cert-in","audit","india","sanad","cognoshift"],"publishConfig":{"access":"public"},"dependencies":{},"gitHead":"421660d462bf1fab88d320db7e462e65019099e4","_id":"@cognoshift/sanad-verify@0.1.0","_nodeVersion":"24.14.1","_npmVersion":"11.12.1","dist":{"integrity":"sha512-uykpBTWPaGN0Kc74FsrJvsSDPXwm3IHfRwQeS/b/n5QxcQI8j+sq20CMnVmALhll96FcBFEsckFfTGtPZo9suQ==","shasum":"b32491565d30980ff6e3b0adce83550c5f465ca7","tarball":"https://registry.npmjs.org/@cognoshift/sanad-verify/-/sanad-verify-0.1.0.tgz","fileCount":5,"unpackedSize":11498,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIE789fLBRpqUdKXPEDy9lp1jHOG3BeFKfaPT9JhzeCwPAiA4GUTCo9KQbqBLiuIjsUJFBqFJB8bNR+28xHOIqYiCug=="}]},"_npmUser":{"name":"cognoshift1","email":"founder@cognoshift.in"},"directories":{},"maintainers":[{"name":"cognoshift1","email":"founder@cognoshift.in"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/sanad-verify_0.1.0_1776863331266_0.2317773777673573"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-22T13:08:51.180Z","0.1.0":"2026-04-22T13:08:51.442Z","modified":"2026-04-22T13:08:51.635Z"},"maintainers":[{"name":"cognoshift1","email":"founder@cognoshift.in"}],"description":"Offline verification library for Sanad Sign attestations — Ed25519 + CycloneDX + SHA-256 hash chain. Zero dependencies, works in Node 18+.","homepage":"https://sanad.cognoshift.in","keywords":["sbom","verification","ed25519","cyclonedx","attestation","sigstore","cert-in","audit","india","sanad","cognoshift"],"repository":{"type":"git","url":"git+https://github.com/anupam9091/cognoshift-sentinel.git","directory":"cli/sanad-verify"},"author":{"name":"COGNOSHIFT PRIVATE LIMITED","email":"support@cognoshift.in","url":"https://cognoshift.in"},"bugs":{"url":"https://github.com/anupam9091/cognoshift-sentinel/issues","email":"support@cognoshift.in"},"license":"Apache-2.0","readme":"# @cognoshift/sanad-verify\n\nOffline verification library for Sanad Sign attestations. **Zero dependencies.** Node 18+. Works anywhere (CI, auditor laptops, air-gapped environments).\n\nAny Sanad Sign attestation can be verified with only the stored row — no network, no license key, no trust in Sanad servers. This package is 3 KB of Node's built-in `crypto` module plus the canonicalization rules.\n\n## Install\n\n```bash\nnpm install @cognoshift/sanad-verify\n```\n\n## Verify a single attestation\n\nGiven an attestation row fetched from `GET /api/sign/registry/<id>`:\n\n```js\nconst { verifyAttestation } = require(\"@cognoshift/sanad-verify\");\n\nconst row = await fetch(\"https://sanad.cognoshift.in/api/sign/registry/<id>\").then(r => r.json());\n\nconst result = verifyAttestation({\n  sbom:       row.sbom,\n  signature:  row.signature,\n  public_key: row.public_key,\n  sbom_hash:  row.sbom_hash,   // optional — recomputed anyway\n});\n\nconsole.log(result.valid);            // true | false\nconsole.log(result.signature_valid);  // Ed25519 check\nconsole.log(result.hash_match);       // claimed vs recomputed SHA-256\nconsole.log(result.computed_hash);    // \"a3f5...\"\n```\n\n## Replay the hash chain\n\nGiven all attestations for a tenant (ordered ascending by `created_at, id`):\n\n```js\nconst { verifyChain } = require(\"@cognoshift/sanad-verify\");\n\nconst result = verifyChain({ attestations: rows });\n\nconsole.log(result.valid);           // true when chain is intact\nconsole.log(result.break_at);        // id of first broken row, or null\nconsole.log(result.first_mismatch);  // detailed diagnostic\n```\n\nThe genesis constant is `SANAD_SIGN_GENESIS_2026` (exported as `GENESIS`). Every per-tenant chain starts from this value; if you operate a private deployment with a different genesis, pass it explicitly:\n\n```js\nverifyChain({ attestations: rows, genesis: \"MY_PRIVATE_GENESIS\" });\n```\n\n## How verification works\n\n1. **Canonicalize** the SBOM — recursively sort object keys, keep arrays in order. Matches RFC 8785 JCS for the subset we use.\n2. **Hash** the canonical string with SHA-256. This is the `sbom_hash`.\n3. **Verify** the Ed25519 signature over the hex-encoded `sbom_hash`, using the public key stored on the row (SPKI format, base64).\n4. **Chain replay:** for every row in a tenant's ledger, recompute `SHA-256(event_hash || previous_chain_hash)` and match against the stored `chain_hash`. `previous_chain_hash` is `GENESIS` for the first entry.\n\nAll four steps are deterministic and reproducible in any language. This library is the reference Node implementation.\n\n## Port to other languages\n\nThe verification protocol is open. A Go or Python implementation is ~40 lines — read [the source](./lib/index.js) for reference. Contributions welcome.\n\n## TypeScript\n\nTypes ship in the package. `import { verifyAttestation, verifyChain } from \"@cognoshift/sanad-verify\";` works out of the box.\n\n## License\n\nApache-2.0 — use freely including in commercial audits.\n","readmeFilename":"README.md","_rev":"1-7a2c737a6187ddaa97bced84db3a8c0b"}