🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@graneth/verify

Package Overview
Dependencies
Maintainers
1
Versions
4
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@graneth/verify

Offline verifier for Graneth's signed governance receipts — checks the Ed25519 signature against a public key you already have. Zero network calls, zero runtime dependencies.

latest
npmnpm
Version
0.2.1
Version published
Weekly downloads
447
445.12%
Maintainers
1
Weekly downloads
 
Created
Source

@graneth/verify

Offline verifier for Graneth's signed governance receipts. Recomputes the canonical bytes and checks the Ed25519 signature against a public key you already have — no network calls, no account, no dependencies beyond Node's built-in crypto/fs.

npx @graneth/verify receipt.json --key graneth-key.json

Don't have the key yet? Download it once (this tool won't fetch it for you):

curl -o graneth-key.json https://graneth.com/.well-known/graneth-attestation-key.json

--key accepts either that key document or a raw PEM public key.

Four outcomes, not two

ExitWordWhat it means
0PASSThe signature was checked, and it is valid.
1FAILThe signature was checked, and the file does not match it — the document was altered after it was signed.
2UNCHECKEDNothing was checked, and the obstacle is on your side: a missing or wrong key, an unreadable file, a mistake in the command. This one says nothing about the receipt.
3MALFORMEDThis is not a well-formed Graneth receipt — no signature, or a signed field removed since it was issued. Not evidence of anything.

Only 0 means verified.

Forgetting --key, pointing at a key file that isn't there, or using a key document downloaded before the signing key was rotated are all mistakes on the reader's side — and a verifier that answers them with the same word it uses for a forgery is telling the reader something false about a receipt that may be perfectly good.

2 and 3 are separated for the opposite reason, and the line between them is load-bearing. UNCHECKED is the one state a reader is explicitly reassured about, so anyone who can steer a document into it gets to choose the sentence the reader sees.

The line is drawn once, by artifact rather than by case:

Before the tool holds bytes claimed to be a receipt — no path, no file, an unreachable URL, a mistyped command — the obstacle is yours, and UNCHECKED is honest. Once it holds those bytes, a defect in them is the document's, and it is MALFORMED.

Not because we can tell who damaged the bytes: because one corrupted byte is indistinguishable, from in here, from an edited one — and the person editing a receipt is the one who benefits from the doubt. The default side is the one whose sentence is not a lie under either reading, and "this is not a well-formed Graneth receipt" is true even when you simply pointed at the wrong file.

Your copy of the key stays your artifact end to end, so every defect in it — unreadable, not JSON, no matching key — is UNCHECKED. The same unparseable file is therefore MALFORMED when passed as a receipt and UNCHECKED when passed with --key.

Whether a file is a well-formed receipt is a property of that file, so it is answered without a key at all: graneth-verify receipt.json with no --key still reports MALFORMED for a document that is not one. (In 0.2.0 those checks sat behind key resolution, so the same file answered MALFORMED with a key and UNCHECKED without — under a sentence that blamed the reader. Fixed in 0.2.1.)

Two consequences of the same rule: every receipt Graneth issues names its signing key inside the signature, so a document declaring a version and an algorithm but no keyId has had that field removed and is MALFORMED; and the copy of keyId in the receipt envelope is never consulted for a verdict, because no signature covers it.

--help also exits 2, because exit 0 is reserved for a receipt that verified.

Programmatically, verifyReceipt() returns outcome: "valid" | "mismatch" | "malformed" | "unverifiable" alongside the original valid boolean, which still means exactly outcome === "valid".

What a PASS tells you — and what it does not

PASS — Ed25519 signature is valid.
  scan #4821  acme-corp/payments-api
  verdict: PASSED  (critical 0, warnings 2)
  keyId:   3ed76e31c05aecdb

The signature covers the object it was given, so a receipt can be validly signed and still not state a repository or a verdict. When that happens the tool says so rather than printing a blank:

PASS — Ed25519 signature is valid.
  scan #4821
  NOT ATTESTED: repository, verdict — the signature is valid, but
  this receipt's signed object does not carry those facts.

Exit code is still 0 in that case: the signature really is valid, and scripts key on that. What changes is that the human reading it is not left to assume the receipt attested to more than it did.

NOT ATTESTED is a different axis from PASS/FAIL/UNCHECKED: it describes what a valid signature covers, not whether a check happened.

Programmatic use: import { verifyReceipt } from "@graneth/verify".

License

MIT

Keywords

graneth

FAQs

Package last updated on 28 Jul 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts