Mark Reynolds
Provides integration with the Algorand blockchain to anchor document hashes, enabling tamper-evident notarization with post-quantum security and offline verification.
PQ Verifiable Archive
Post-quantum tamper-evidence for any signed document. Anchor a SHA-256 hash to Algorand mainnet and get back a self-contained proof bundle signed with ML-DSA-65 (NIST FIPS-204). Anyone can verify it later with the open-source verifier: the signature, document hash and Merkle proof check offline against a pinned issuer key, and the anchor is confirmed against any archival Algorand indexer.
Works with any signing tool: DocuSign, HelloSign, Adobe Sign, or a plain PDF. You bring the signed document; this adds the quantum-resistant notarization layer.
Live on Algorand mainnet. Real transactions, real ML-DSA-65 signatures, working open-source verifier.
Try it in 2 minutes
With DocuSign: open https://pq-verifiable-archive.vercel.app/try, sign a demo contract (DocuSign sandbox, embedded signing, no email or account needed), and about 30 seconds later you get your archived copy, its proof bundle and a one-click verifier. The same page anchors the hash of any file for free.
Verify only:
Open https://pq-verifiable-archive.vercel.app → Verify Independently → Load sample bundle & PDF, then Verify. Everything runs in your browser against the public Algorand indexer; the PDF is never uploaded.
Change one character of the demo PDF (or use any other PDF) with the same bundle: the result flips to INVALID on the document-hash check.
Prefer a terminal?
git clonethis repo,npm install, thennpm run verify -- --bundle bundles/sample-contract-bundle.json --pdf assets/sample-contract.pdf.
Anchoring through the API costs $0.01 USDC via x402 (POST /api/anchor, see below); the /try demo is free within a daily cap. Verifying is always free.
Related MCP server: hive-mcp-sigr
Why this exists
Today's e-signature platforms use RSA or ECDSA. Both are broken by a large-enough quantum computer. NIST finalized ML-DSA (FIPS-204) in 2024 and CNSA 2.0 mandates PQC migration in federal procurement by ~2030–2035. Documents signed today need to be verifiable in 2040.
What's different
Property | DocuSign / Adobe Sign | PQ Verifiable Archive |
Post-quantum issuer signature | No (RSA / ECDSA) | Yes — ML-DSA-65 (FIPS-204) |
Verifiable without the vendor's servers | No | Yes — 3 checks offline (with a pinned key fingerprint), anchor check via any archival Algorand indexer |
Issuer pinning | Platform CA | Verifier pins the issuer's Algorand address and key fingerprint |
Data on-chain | n/a | Hashes only (pseudonymous — see compliance FAQ) |
Works with any signer | No | Yes — bring any signed PDF |
Standards | RSA / ECDSA | NIST FIPS-204, SHA-256, JCS (RFC 8785), RFC 6962-style Merkle tree |
What the verifier does not do yet: it does not verify Algorand's Falcon-512 state proofs. It trusts the indexer's report of the anchor transaction (after checking sender, round and note) and only reports state-proof coverage as information.
DocuSign
What it adds
DocuSign's own evidence (the certificate of completion and the PDF seal) is RSA-signed and checked through DocuSign or a PDF trust chain. This adds an independent record of the exact completed document: a SHA-256 hash anchored on a public ledger, inside a receipt signed with ML-DSA-65. Anyone can check it, now or in 2040, without DocuSign or this service.
How it works
An envelope is completed. DocuSign Connect posts
envelope-completedto/webhook/docusign(JSON, SIM, HMAC-signed).The webhook checks the HMAC, then asks the eSignature REST API for the envelope's status and only continues if it is
completed.It downloads the combined PDF with the certificate of completion (
documents/combined?certificate=true) and the recipients, then hashes the PDF.The hash goes into a Merkle tree anchored in an Algorand transaction. The proof bundle (
pqva/2) is signed with ML-DSA-65 and records the signers asdocusign-connectplus a signedcapturerecord (DocuSign's completion time and the archive time).The PDF and bundle are archived, and the signer gets a private link (
/d/<token>) to download both and verify.
Each delivery is idempotent (one claim per envelope across instances), a failure returns 5xx so Connect retries, and a document is never anchored twice.
DocuSign regenerates PDFs on every download
Two downloads of the same completed envelope have different SHA-256 hashes: DocuSign rewrites the XMP dates, the PDF /ID and its adbe.pkcs seal each time. A proof over "the envelope" can therefore never be checked against a later download. The design answer: the archived copy is the document of record. The archive captures it once, seconds after completion; the signer link hands out those exact bytes; and the signed capture record shows when DocuSign completed the envelope and when the copy was taken. (Normalising the PDF before hashing was rejected: it widens what the proof does not cover and every future verifier would have to reproduce the normalisation.)
What the proof shows, and what it does not
Shows: this exact file existed no later than the anchor's ledger time; the trusted issuer signed a receipt for it with a post-quantum signature; the signer list and capture times are what DocuSign reported to the issuer.
Does not show: who signed. Signer identity still rests on DocuSign's own records and audit trail. The capture times are reported by DocuSign and the issuer; only the ledger time is independent.
Not yet post-quantum end to end: the Algorand transaction itself is Ed25519-signed. Algorand's state proofs (Falcon, post-quantum) attest the ledger history, but the verifier only reports them and does not check them yet.
On-chain: hashes only. Envelope ids are hashed too. Names and emails appear only in the archived PDF and bundle, behind the private link.
Why not just…
…an RFC 3161 timestamp? A good complement, but the timestamp authority signs with RSA or ECDSA (quantum-exposed) and the proof is only as good as that authority's certificate chain staying trusted. Here the issuer signature is ML-DSA-65, and the anchor is on a public ledger that anyone can query with no certificate authority.
…DocuSign's certificate of completion? It is included in the hashed PDF. On its own it is RSA-sealed, verified through DocuSign or a PDF trust chain, and regenerated on every download.
…Algorand? About 0.001 ALGO per anchor (a fraction of a cent), ~3 s finality with no forks, and Falcon-based state proofs give a post-quantum path for the ledger history.
Numbers from the live sandbox tests (2026-10-09)
A completed demo envelope is public: open the Live example on /try, download the PDF and bundle, and verify them yourself. Full write-up of each run, with the checks, attacks and on-chain transactions: docs/live-tests.md.
Envelope completed → anchored on mainnet | 23–26 s (three envelopes, one signed by a visitor through |
Connect delivery failures | 0 |
Cost per anchor | one Algorand transaction, 0.001 ALGO |
Verification | free: browser, CLI or |
Run it against your own DocuSign developer account
Self-host this repo (see below) with your own Algorand wallet and ML-DSA key (
npm run generate-wallet,npm run register-key).Apps and Keys in the DocuSign developer console: create an integration key, generate an RSA key pair, and add a redirect URI (any, e.g.
https://localhost) for the one-time consent.Grant consent once for JWT impersonation: open
https://account-d.docusign.com/oauth/auth?response_type=code&scope=signature%20impersonation&client_id=<INTEGRATION_KEY>&redirect_uri=<REDIRECT_URI>and accept.Connect: Settings → Connect → Add configuration → Custom. URL:
https://<your-host>/webhook/docusign; data format REST v2.1 / JSON (SIM); event Envelope Signed/Completed; tick Include HMAC signature and create an HMAC key under Connect Keys; enable for all users.Environment:
DOCUSIGN_SANDBOX=true,DOCUSIGN_INTEGRATION_KEY,DOCUSIGN_USER_ID(your API user id),DOCUSIGN_ACCOUNT_ID(API account id),DOCUSIGN_PRIVATE_KEY(the RSA private key PEM, base64-encoded) andDOCUSIGN_HMAC_KEY.Send yourself an envelope and sign it. The function log shows one line per delivery,
[webhook] event=envelope-completed envelope=… outcome=anchored, and the record appears under Archived Documents with a Signer link button.
Production accounts work the same with DOCUSIGN_SANDBOX=false; the regional REST host is read from the account's base_uri. To enable the /try demo on your instance, see PQVA_DEMO_* in .env.example.
Use as an MCP tool (AI agents)
Add to claude_desktop_config.json or .claude/settings.json:
{
"mcpServers": {
"pq-verifiable-archive": {
"command": "npx",
"args": ["tsx", "/path/to/pq-verifiable-archive/src/mcp-server.ts"],
"env": {
"ALGORAND_MNEMONIC": "your 25-word mnemonic",
"PQVA_MLDSA_PUBLIC_KEY": "hex-encoded ML-DSA-65 public key",
"PQVA_MLDSA_PRIVATE_KEY": "hex-encoded ML-DSA-65 private key",
"PQVA_KEY_REGISTRATION_TXN_ID": "algorand txn id from npm run register-key"
}
}
}
}Claude (or any MCP-compatible agent) can then call:
anchor_document— anchor a SHA-256 hash to Algorand, receive a proof bundle (capped atMCP_MAX_ANCHORS_PER_DAY, default 25)verify_bundle— verify a bundle against the trusted issuer (PQVA_TRUSTED_*, default: the hosted service)
Signers passed to anchor_document or the REST API are recorded as requester-asserted: the bundle proves you claimed them, not that they signed.
Self-hosted MCP is free. You pay only Algorand's network fee (~$0.0002 per anchor).
Hosted API (pay-per-use)
If you don't want to run your own node, call the hosted REST endpoint:
# Without payment — returns 402 with payment instructions
curl -X POST https://pq-verifiable-archive.vercel.app/api/anchor \
-H "Content-Type: application/json" \
-d '{"hash":"<sha256 hex>","envelope_id":"contract-001"}'
# With x402 payment ($0.01 USDC on Algorand)
curl -X POST https://pq-verifiable-archive.vercel.app/api/anchor \
-H "Content-Type: application/json" \
-H "payment-signature: <x402 payment header>" \
-d '{"hash":"<sha256 hex>","envelope_id":"contract-001"}'Price: $0.01 per anchor. Verification is always free.
OpenAPI spec: /openapi.json — machine-readable for LLMs, tools, and code generators.
Self-hosted quick start
git clone https://github.com/m-reynaldo35/pq-verifiable-archive.git
cd pq-verifiable-archive
npm install
cp .env.example .env # fill in ALGORAND_MNEMONIC + ML-DSA keys
npm run generate-wallet # or use existing wallet
npm run register-key # registers your ML-DSA-65 key on Algorand
npm run dev # HTTP server on :3000
npm run mcp # MCP server (stdio) for AI agentsGenerate a sample document and verify it:
npx tsx scripts/generate-sample-pdf.ts
npm run verify -- --bundle bundles/sample-contract-bundle.json --pdf assets/sample-contract.pdfRequired env vars (see .env.example): ALGORAND_MNEMONIC, PQVA_MLDSA_PUBLIC_KEY,
PQVA_MLDSA_PRIVATE_KEY, PQVA_KEY_REGISTRATION_TXN_ID (the old DOCUSIGN_* names
still work but are deprecated).
With NODE_ENV=production the server also requires PORTAL_API_KEY (operator
archive UI and /api/documents) and X402_TREASURY_ADDRESS (pay-per-anchor); without
them every request returns 503. Locally the archive is stored in PQVA_ARCHIVE_DIR
(default ./archive).
Verifying your own bundles: pass your issuer to the verifier, e.g.
npm run verify -- --bundle b.json --pdf doc.pdf --issuer-address <addr> --key-reg-txn <txid> --pk-sha256 <hex>
(npm run register-key prints these values).
Deploy to Vercel
The app runs on Vercel as one serverless function (api/index.ts) with public/ served from the CDN (see vercel.json).
Import the repository as a Vercel project (framework preset: Other; no build command needed).
Storage: create a private Blob store and connect it to the project (this sets
BLOB_READ_WRITE_TOKEN). It stores archived PDFs and bundles plus the payment and webhook claims.Environment variables (Production):
ALGORAND_MNEMONIC,PQVA_MLDSA_PUBLIC_KEY,PQVA_MLDSA_PRIVATE_KEY,PQVA_KEY_REGISTRATION_TXN_ID,PORTAL_API_KEY,X402_TREASURY_ADDRESS, and for DocuSignDOCUSIGN_HMAC_KEYplus theDOCUSIGN_*API credentials. Mark the secrets as Sensitive.Optional: add a Vercel Firewall rate-limit rule for
/api/anchorand/api/verify. The in-app limits apply per function instance only.
If anything required is missing in production, every request returns 503 and the reason appears in the function logs.
How verification works
The verifier (npm run verify) exits 0 = VALID, 1 = INVALID, 2 = could not verify. Without --pdf it prints BUNDLE VALID — document not checked. By default it trusts the hosted service's issuer and says so; use --issuer-address/--key-reg-txn/--pk-sha256 for any other issuer. It does not read .env.
Step | Check | Network |
1 | ML-DSA-65 signature, and the key belongs to the trusted issuer (pinned fingerprint, or a key-registration txn sent by the issuer address). Fails closed. | None when the fingerprint is pinned |
2 | SHA-256(document) matches | None |
3 | Merkle proof from the document hash to the root | None |
4 | Anchor txn sent by the issuer, in the claimed round, with exactly this root in its note; ledger round time is the anchoring time | Algorand indexer |
5 | Informational: indexer lists a state-proof txn covering the round (not verified) | Algorand indexer |
Who uses this
HR platforms — offer letters, NDAs, termination agreements with quantum-proof audit trail
Legal tech — tamper-evident contract archive that survives vendor shutdown
Healthcare — patient consent forms (no names or content on-chain; hashes are pseudonymous, so assess under your own HIPAA/GDPR analysis)
AI agents — autonomous agents executing agreements need immutable, verifiable receipts
Anyone signing documents today that need to be verifiable in 2040
Proof bundle (example)
{
"protocol": "pqva/2",
"envelopeId": "launch-test-contract-001",
"documentHash": "7ad4c3341e5c37b4bdc1001abbb3b99ecbb3ea42deca98ea7e6b7366805be991",
"batchId": "5V47FZ65L4AO2UWOSEP73GGKGFYOQ672URVRE5F6GPGL3M7G6EHA",
"merkleRoot": "170c1f67b7709e58...",
"merkleProof": [],
"algorandTxnId": "5V47FZ65L4AO2UWOSEP73GGKGFYOQ672URVRE5F6GPGL3M7G6EHA",
"algorandRound": 65727529,
"blockTimestamp": "2026-10-06T13:41:09.000Z",
"stateProofRound": 65727744,
"issuerAddress": "XCJOXAMHVPXGFJHHKF3CUSVD7CD44Z4FB3BFXZYH46HYX6NUQ7TANSWAMM",
"keyRegistrationTxnId": "S7NGS2LDQXB73R4XSSOS6VRCS66ZL3I5S7FQWXHB4YIXLDISLZ7Q",
"signers": [],
"signerSource": "requester-asserted",
"algorithm": "ml-dsa-65",
"mldsaPublicKey": "...",
"signature": "..."
}This is a real bundle from a paid mainnet anchor (explorer).
Hosted issuers (pinned in every verifier): XCJOXAMH… (current, key registration S7NGS2LD…) and JJNDY3TL… (original, key registration BUVBKZAY…).
Full schema: docs/architecture.md
Tech stack
Component | Choice |
Post-quantum signatures |
|
Blockchain | Algorand mainnet |
AI agent interface |
|
Payments |
|
Merkle trees | built in — SHA-256, RFC 6962-style leaf/node prefixes |
Canonical JSON |
|
Runtime | TypeScript + Node.js via |
License
MIT — free to use, self-host, and fork.
This server cannot be deployed
Maintenance
Related MCP Connectors
Post-quantum, tamper-evident receipts for agent actions. Ed25519 + ML-DSA-65, offline verify.
Bitcoin-anchored, tamper-evident audit-permanence layer for AI agents, FRE 902(13)/(14)-shaped.
Tamper-evident proof creation and verification for AI agents via MCP, A2A, and REST.
Free e-signature for humans and AI agents — zero-document PDF signing from hashes only.
Related MCP Servers
- AlicenseAqualityCmaintenanceEnables AI agents to sign decisions with post-quantum cryptographic proofs and maintain secure audit trails for compliance. It provides tools for stamping events, verifying chain integrity, and exporting audit data across industries like finance and healthcare.416 npmMIT
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to sign and verify actions with ML-DSA-65 digital signatures, providing tamper-proof receipts that can be verified offline without any secrets.MIT
- AlicenseNot gradedqualityBmaintenanceProvides tools to sign and verify post-quantum attestations of AI agent actions (mint, trajectory, coherence) using ML-DSA-65 signatures, with offline verification always free.MIT

markovian-mcpofficial
AlicenseAqualityBmaintenanceBitcoin-anchored provenance for AI outputs; enables stamping, verifying, and tracing outputs with offline-verifiable canonical roots.3Apache 2.0