Skip to content

blocksign-verify/verify

Repository files navigation

blocksign-verify

Independently verify that a signed BlockSign PDF has not been altered since it was stamped on the Solana blockchain.

blocksign-verify is the reference implementation of the BlockSign v0 document-signing protocol. It is published openly so that any party — recipients, auditors, courts, archival systems — can confirm the integrity of a signed document without trusting the BlockSign platform itself.

The package ships as both a library and a CLI:

blocksign-verify contract.pdf
# VERIFIED  tx 5J7zXq9G3a1bN4mLrPzVxYwR9k7Mc2VfHd5JuBwGpEtT3sLfA1jW8oN3eQpHy4xR2zS9bV6t
#   document  0e6e3c1a-2f9b-4f4a-9a4d-c0a8b5e90f24 (v1)
#   status    completed
#   signed    2025-11-04T14:00:00.000Z

Table of contents


Why this exists

BlockSign is tamper-evident document signing on Solana. The platform's central claim is that a signed document can be independently verified by anyone — without asking BlockSign for permission, without trusting BlockSign's servers, and without a future BlockSign outage taking the verification capability with it. Cryptographic, not photographic.

To make that claim credible, the verification path has to be public. This repository is that path:

  • Open algorithm. Every step — how the PDF is hashed, how the stamp is encoded, how the memo is parsed — is documented in docs/PROTOCOL.md and implemented here.
  • Open key. The v0 protocol's XOR obfuscation key is a published public constant. It keeps casual on-chain scrapers from indexing BlockSign memos verbatim — it is not a confidentiality primitive.
  • No platform calls. Verification reads only the PDF bytes and the public Solana RPC. The BlockSign platform is never contacted; the verifier keeps working if blocksign.ink is down forever.

If you are evaluating BlockSign for a regulated workflow, this package is the artifact your security team should audit.

What is and is not open source

Only the verifier is open source — this package, the protocol specification, and the related tooling under the blocksign-verify GitHub organization. The BlockSign platform (the web app at blocksign.ink, its signing workflow, dashboard, RLS schema, payer service, and product surface) is closed source.

This split is deliberate: verification is the part of the system that has to be auditable by anyone for the on-chain proof to carry weight. Everything else is product surface that does not need to be public to be trustworthy.


Installation

Clone the repository and build it locally:

git clone https://github.com/blocksign-verify/verify.git
cd verify
pnpm install
pnpm build

Requires Node 20 or newer and pnpm 10+. The package is pure ESM.

Use it as a git dependency from another project:

pnpm add github:blocksign-verify/verify

Quick start

Library — verify by PDF

The simplest call. The transaction signature is recovered from the PDF's own embedded metadata block.

import { readFile } from 'node:fs/promises';
import { verifyPdf } from 'blocksign-verify';

const bytes = await readFile('contract.pdf');
const result = await verifyPdf(bytes);

switch (result.verdict) {
  case 'verified':
    console.log('Document matches its on-chain stamp.');
    break;
  case 'tampered':
    console.warn('Document has been altered since it was signed.');
    break;
  case 'unverifiable':
    console.error(`Could not verify: ${result.error.code}`);
    break;
}

Library — verify by transaction

When you don't trust the PDF's embedded metadata — for example, the transaction signature was shared with you out of band — pass it explicitly:

import { verifyByTransaction } from 'blocksign-verify';

const result = await verifyByTransaction(
  bytes,
  '5J7zXq9G3a1bN4mLrPzVxYwR9k7Mc2VfHd5JuBwGpEtT3sLfA1jW8oN3eQpHy4xR2zS9bV6t',
);

Library — reusable verifier

When verifying many documents, construct a BlockSignVerifier once and reuse its RPC connection:

import { BlockSignVerifier } from 'blocksign-verify';

const verifier = new BlockSignVerifier({
  rpcUrl: 'https://your-private-rpc.example.com',
  timeoutMs: 15_000,
});

for (const path of paths) {
  const bytes = await readFile(path);
  console.log(path, (await verifier.verify(bytes)).verdict);
}

CLI

$ blocksign-verify --help

Usage: blocksign-verify [options] <path-to-pdf>

Options:
  --tx <signature>     Verify against a specific transaction signature instead
                       of the one embedded in the PDF.
  --rpc <url>          Override the Solana RPC endpoint (default: public mainnet).
  --cluster <name>     One of mainnet-beta | devnet | testnet.
  --json               Emit machine-readable JSON instead of human output.
  -h, --help           Print this message.

Exit codes follow the standard pattern so you can chain blocksign-verify into shell pipelines:

Code Meaning
0 Document verified
1 Document tampered (on-chain hash does not match)
2 Document unverifiable (missing metadata, RPC error, etc.)
126 Bad invocation

How verification works

   signed.pdf                                             Solana
   ──────────                                             ──────
        │
        │ readBlockSignMetadata()
        ▼
   /BlockSign_Metadata  ──── transaction signature ────▶  getParsedTransaction
        │                                                       │
        │                                                       │ memo payload
        │                                                       ▼
        │                                                  "v0:<base64>"
        │                                                       │
        │                                                       │ parseV0Memo
        │                                                       ▼
        │                                                  base64 payload
        │                                                       │
        │                                                       │ XOR + msgpack
        │                                                       ▼
        │                                                  DocumentStamp
        │                                                       │
        │ extractContentStreams                                 │
        ▼                                                       │
   sha256 contentHash  ───── timingSafeEqual ─────────────▶ stamp.contentHash
                                       │
                                       ▼
                            ┌─────────────────────┐
                            │   verified | tampered  │
                            └─────────────────────┘

The verifier never contacts the BlockSign platform. The only network call is to the configured Solana RPC.


The v0 protocol at a glance

Full specification: docs/PROTOCOL.md.

A BlockSign-stamped PDF consists of two coupled artifacts:

  1. A Solana memo transaction carrying the encoded DocumentStamp as its sole instruction payload.
  2. A PDF with an embedded BlockSign_Metadata dictionary containing the memo's transaction signature.

The memo body is:

memo := "v0:" || base64( xor( msgpack( shortKey( stamp ) ), KEY ) )

Where:

  • shortKey rewrites long field names to two-letter shortcuts and substitutes enum strings for integer codes, fitting the stamp under Solana's 1000-byte memo cap.
  • msgpack is RFC-compliant MessagePack.
  • xor is a repeating-key XOR with the published BLOCKSIGN_V0_PROTOCOL_KEY. This is obfuscation, not encryption — its purpose is to prevent casual on-chain indexers from treating BlockSign memos as plaintext.

The integrity of the stamp itself rests on SHA-256 and Solana's immutable on-chain ordering — not on the secrecy of the XOR key.


Error model

Every failure surface returns a typed VerificationError whose code field is one of:

Code Meaning
invalid_pdf Bytes are not a parseable PDF document.
missing_metadata PDF lacks a /BlockSign_Metadata dictionary.
memo_not_found The referenced transaction has no SPL-memo instruction.
memo_malformed Memo body is not in "v<n>:<payload>" form.
unsupported_protocol_version Memo declares a protocol this verifier does not understand.
stamp_schema_invalid Decoded payload does not match the v0 schema.
rpc_unavailable Solana RPC call failed or timed out.
invalid_signature_format Transaction signature is not valid base58.
import { isVerificationError } from 'blocksign-verify';

if (result.verdict === 'unverifiable' && result.error.code === 'memo_not_found') {
  // Maybe this PDF was signed on devnet, not mainnet.
}

Using a private RPC

The default RPC is https://api.mainnet-beta.solana.com. It is rate-limited and not suitable for production verification at scale. For sustained use, pass a private RPC URL:

import { BlockSignVerifier } from 'blocksign-verify';

const verifier = new BlockSignVerifier({
  rpcUrl: 'https://my-rpc.helius-rpc.com/?api-key=...',
});

Or, if you already have a Connection instance, hand it in directly:

import { Connection } from '@solana/web3.js';
import { BlockSignVerifier } from 'blocksign-verify';

const connection = new Connection(myRpc, 'confirmed');
const verifier = new BlockSignVerifier({ connection });

Security model

What this package proves on a successful verified result:

  1. The PDF bytes you passed in produce the same SHA-256 content-stream digest as the digest the BlockSign platform committed to the Solana memo program at the time of signing.
  2. The committed memo exists on chain and is referenced by the transaction signature embedded in the PDF (or supplied explicitly).
  3. The Solana memo transaction has been observed at the configured commitment level (confirmed by default).

What this package does not prove:

  • That the signers named in the stamp are who they say they are. Signer identity is established at sign time by the producer; this verifier does not re-validate it.
  • That the document has not been altered in ways that affect only the PDF metadata block or annotation layer. The contentHash covers page content streams only.
  • That the BlockSign platform itself was not compromised at sign time. Tamper-evidence is a different property from tamper-resistance.

For the full threat model, see docs/PROTOCOL.md.

To report a security vulnerability privately, see SECURITY.md.


Compatibility

Verifier version Protocol versions Status
0.1.x v0 active

The verifier follows semantic versioning. Adding support for a new protocol version is a minor-version bump; dropping support for an old protocol version is a major-version bump.


Contributing

Contributions are welcome — see CONTRIBUTING.md. The protocol spec in docs/PROTOCOL.md is the source of truth; any change to wire format requires a spec edit before the implementation lands.


License

MIT — see LICENSE.

About

Reference verifier for the BlockSign v0 document-signing protocol. Audits a signed PDF against its on-chain Solana memo stamp without contacting the BlockSign platform. Used by https://blocksign.ink.

Topics

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors