Everything you need to deploy Adduce for any country’s legal aid system. Real on-chain infrastructure, no simulations. Each module is independently configurable and verifiable on Solana Explorer.
Verify in 60 seconds. Click any link below to confirm this is real, deployed infrastructure.
Adduce is an on-chain legal-aid plugin. It provides credential issuance, document anchoring, and payment settlement primitives that integrate into existing government legal-aid portals. Adduce does not replace your portal: it replaces the parts of your stack that are slow, opaque, or non-interoperable: identity attestations, document audit trails, and payment rails.
Protocol, not portal. In production, a government integrator (e.g. Government Justice Portal) embeds Adduce SDK calls into their existing system. The reference UI at /dashboard is a demo showing what those calls look like end-to-end.
Issues on-chain attestations that serve as the digital equivalent of a legal aid eligibility certificate: Berechtigungsschein (DE), Aide Juridictionnelle certificate, or any country’s legal aid entitlement document. Verifiable by any party without contacting the issuing authority.
On-chain state machine tracking each case through its lifecycle: Open, InProgress, Closed, Paid. Jurisdiction-scoped configurations. Authority-gated transitions. All state changes produce verifiable transactions.
Lawyers anchor SHA-256 hashes of case documents on-chain. No PII is stored: only the cryptographic fingerprint. Any auditor can verify a document’s integrity by comparing hashes without accessing the original.
Holders prove statements about credentials: “jurisdiction is DE, credential not expired”: without revealing other fields. A custom Circom circuit generates 256-byte Groth16 proofs verified on-chain via Solana’s alt_bn128 pairing. Same BN254 curve as Light Protocol and Ethereum ZK rollups. Mathematical privacy, not access-control privacy.
Compressed audit logs reduce storage costs by 98.7% compared to standard Solana PDAs. At government scale (100K+ cases/year), this means $22,000+ annual savings while maintaining full immutability and verifiability.
Instant stablecoin disbursement upon case closure and credential verification. The x402 protocol handles the HTTP-native payment flow: GET returns 402 Payment Required, POST verifies credential + payment signature.
Background polling agent that scans for closed cases, verifies credentials, executes USDC transfers, creates compressed audit logs, and marks cases as paid. Runs as a cron job or continuous service.
Legal aid certificates cross institutional boundaries: issued by one authority, verified by another, audited by a third. A private chain requires every participant to join the same network. A public chain lets any court, any lawyer, any auditor verify independently.
Private chains win when strict data-residency is required (all data must stay within national borders), when regulation explicitly mandates a permissioned ledger, or when participating institutions already share network infrastructure. Adduce is designed for the opposite scenario: lightweight credential verification that must work across jurisdictions without bilateral agreements.
The protocol is a pipeline. Each stage produces an on-chain artifact that the next stage can verify independently.
Program ID: 3f1yBTY6xb6ESdzzb9LxAozv7uVsj9Y9AMEpnAwKJRNV
Every legal aid system in the world has three actors, regardless of jurisdiction. Each has different permissions, different daily tasks, and different pain points. Adduce maps each role to specific on-chain capabilities.
Government employee at the court or ministry. Reviews eligibility applications, issues legal aid certificates (eligibility certificates), monitors case progress, and approves payment disbursements. Role-separated: authority calls initialize and open_case, reviewer (or delegate) calls link_credential and close_case, payer calls mark_paid. Up to 3 delegates can be added via add_delegate. Custodial cases use open_case_custodial. Lawyers can be reassigned via reassign_lawyer.
Private attorney who takes legal aid cases. Must hold a valid SAS credential (the digital eligibility certificate). Receives case assignments, submits case documents by anchoring their SHA-256 hash on-chain, verifies credential validity, and claims payment after case closure. Can call anchor_document (must be the assigned lawyer; credential liveness re-checked on-chain).
Citizen seeking legal aid. Files an application at the court, receives an eligibility certificate if eligible, and takes it to a lawyer. The applicant has zero blockchain interaction. the system is invisible to them. Their data never touches the chain. Only entitlement metadata (jurisdiction, tier, expiry) is stored as a verifiable credential.
The court operator is the system’s authority. They control the full case lifecycle: from certificate issuance to payment approval.
In a typical system, issuing an eligibility certificate follows a Review → Editing → Verification procedure. In Adduce:
The lawyer is the primary user of the system. They receive legal aid assignments, verify their eligibility certificate, submit case documents, and claim payment once the case is closed.
Pain point solved: In the current paper system, a lawyer must phone the issuing court to verify a eligibility certificate. With Adduce, verification is a single on-chain PDA read: no phone calls, no hold queues, no business-hour dependency.
Sensitive document handling: Case documents (billing forms, court filings, settlement records) are encrypted end-to-end using X25519 ECDH key agreement + AES-256-GCM. The encrypted blob is stored permanently on Arweave via Irys (pay once, stored forever). Only the assigned lawyer can decrypt using their wallet key. The plaintext SHA-256 hash is anchored on Solana for integrity verification. No sensitive content ever touches the public ledger. See scripts/encrypt-and-anchor.ts for the full working implementation.
The applicant is the citizen seeking legal aid. They interact with the court and the lawyer: never with the blockchain directly. Adduce is invisible to them.
Pain point solved: Today, applicants carry a paper certificate that can be forged, lost, or expired without anyone knowing. An on-chain attestation cannot be forged, cannot be lost (it lives on the blockchain), and expiry is checked automatically at verification time.
This is the complete flow from a citizen’s application to the lawyer’s payment. Each step shows who performs it, what happens on-chain, and the real Solana Devnet transaction from our sample case (Reference: 123 UR II 143/22).
Every step after #3 produces a verifiable Solana transaction. The full cycle: from certificate issuance to payment : completed in under 2 minutes on-chain. In the paper system, the same cycle takes 6–12 months.
Legal aid infrastructure across Europe shares the same failure modes. Each is a direct consequence of paper-based, siloed processes.
A paper eligibility certificate can be photocopied and reused. No court can tell if the certificate was already consumed by another lawyer.
Lawyers submit paper forms, wait for manual review, and receive payment via bank transfer weeks or months later. 40% of lawyers stop taking legal aid cases.
A lawyer in Munich cannot verify a BS issued by a court in Hamburg without calling them during business hours. Cross-border is impossible.
A lawyer can submit the same BS for multiple payment claims. Detection relies on manual spreadsheet reconciliation.
Document submissions have no tamper-proof audit trail. Forms are lost, filing cabinets are inaccessible, and there is no proof of when something was submitted.
Court clerks manually match incoming billing forms to cases and payment records. Error-prone at any scale.
The fastest path. Install the published SDK and call the already-deployed program on devnet. No cloning, no deploying, no Anchor setup. Works from any TypeScript/Node.js project. View on npm
npm install @adduce/sdk
All 14 program instructions are callable through the SDK. The program is already deployed at 3f1yBTY6xb6ESdzzb9LxAozv7uVsj9Y9AMEpnAwKJRNV. You connect to it, not deploy your own.
import { AdduceClient, Keypair } from "@adduce/sdk";
const wallet = Keypair.fromSecretKey(/* your keypair */);
const adduce = new AdduceClient({ cluster: "devnet", wallet });
// Open a case
const { casePda, lawyerCommitment } = await adduce.openCase({
caseId: "CASE-DE-2025-001",
lawyerPubkey: lawyerWallet.publicKey,
applicant: citizenWallet.publicKey,
authorizedAmount: 8500,
}, "DE");
// Read case status
const caseData = await adduce.getCase("CASE-DE-2025-001");
console.log(caseData.status); // "Open"For running scripts, modifying the program, or building the ZK circuit locally. Tested with the German jurisdiction but works for any of the 9 supported countries by changing the jurisdiction parameter.
nvm use 22)solana airdrop 2)Clones the repository, installs all dependencies (including snarkjs and circomlibjs for ZK proofs), compiles the Anchor program, and deploys it to Solana Devnet. The program ID is printed after deploy. Your wallet at ~/.config/solana/id.json becomes the upgrade authority.
git clone https://github.com/SAHU-01/legal_aid.git && cd legal_aid nvm use 22 && npm install cp .env.example .env # add your Helius API key anchor build && anchor deploy --provider.cluster devnet
Deploys the eligibility credential schema to the Solana Attestation Service (SAS). The schema defines three fields:jurisdiction (12 bytes), eligibility_tier (12 bytes), and expiry_date (8 bytes). These fields are the same regardless of country. Output: a schema address saved to scripts/schema-address.json.
npx ts-node scripts/create-schema.ts
Issues an eligibility certificate as an SAS attestation. The script fetches the schema, serializes the credential data (jurisdiction, tier, expiry), and creates the attestation PDA on-chain bound to the citizen’s wallet. This is the digital equivalent of a court issuing a paper certificate. Output: credential info saved to scripts/credential-info.json.
npx ts-node scripts/issue-credential.ts
Executes the complete lifecycle on Devnet with real transactions: open_case → link_credential → anchor_document → close_case → USDC transfer → mark_paid. Every step produces a Solana transaction with an Explorer link. The report includes cost analysis and timing for each instruction.
npx ts-node scripts/e2e-full-pipeline.ts
Independently verifies the full chain: SAS credential is valid and not expired, case status is Paid, document hash matches, and payment was disbursed. This is what an external auditor or a different jurisdiction would run to verify a case without any access to the issuing court’s internal systems.
npx ts-node scripts/verify-credential.ts
Compiles the Groth16 circuit, runs the trusted setup ceremony, then generates and verifies a ZK proof that proves “jurisdiction is DE and credential is not expired” without revealing any other fields. The proof is 256 bytes and verifiable on-chain via verify_zk_disclosure.
cd circuits && npm install && ./build.sh cd .. && npx ts-node scripts/zk-disclosure-demo.ts
Adduce is designed to be called from any government portal via REST API. The on-chain program and TypeScript scripts are deployed and working today. The REST API wrapper (/api/gov/*) is the planned integration layer that makes these callable from Java, .NET, or any HTTP client. No existing system needs to be replaced.
The operator approves a legal aid application in the existing portal. The portal’s backend adds one call to create the on-chain credential. The TypeScript path works today. The REST path shows the planned API design.
Your Java/Spring backend calls this endpoint after the operator clicks “Approve.” The API handles all Solana transaction construction, signing, and confirmation. Your portal receives the credential address and transaction link for its internal records.
POST /api/gov/credentials/issue
Content-Type: application/json
{
"jurisdiction": "DE",
"eligibilityTier": "TIER_1",
"expiryDate": "2027-05-04",
"citizenWallet": "Citizen4K...pubkey",
"lawyerCommitment": "a3b2c1d4...sha256hex"
}
# Response: { credentialAddress, transactionSignature, explorerUrl }For TypeScript/Node.js integrations, call the Anchor program directly. The sas-lib package handles credential creation. The Anchor client handles case management.
import { getCreateAttestationInstruction, deriveAttestationPda } from "sas-lib";
// 1. Derive the credential PDA
const [attestationPda] = await deriveAttestationPda({
credential: credentialAddress,
schema: schemaAddress,
nonce: citizenWallet,
});
// 2. Build and send the attestation instruction
const ix = getCreateAttestationInstruction({
authority: courtWallet,
schema: schemaAddress,
nonce: citizenWallet,
data: serializedFields, // jurisdiction + tier + expiry
expiry: expiryTimestamp,
});
// 3. Open the case with lawyer commitment
await program.methods
.openCase(caseId, lawyerCommitment, citizenWallet, authorizedAmount)
.accounts({ config: configPda, caseFile: casePda, authority: courtWallet.publicKey })
.signers([courtWallet])
.rpc();The lawyer uploads case documents through the existing portal. The portal hashes the document and anchors the hash on-chain. Documents stay in the existing DMS. Only the hash goes on-chain.
The portal computes SHA-256 of the uploaded document, then sends it along with the lawyer’s salt (generated during case assignment). The API verifies the lawyer commitment and anchors the hash. The document itself never leaves the portal’s DMS.
POST /api/gov/cases/{caseId}/anchor
Content-Type: application/json
{
"documentHash": "175b1e36...sha256hex",
"lawyerWallet": "Lawyer5K...pubkey",
"lawyerSalt": "random32bytehex..."
}
# The lawyer's salt proves they are the assigned lawyer
# (their pubkey + salt hashes to the commitment stored on-chain)
# Response: { transactionSignature, explorerUrl }A court in one country needs to verify a credential issued by another country. No bilateral agreement needed. One API call.
Any system in any country can verify a credential by passing its on-chain address. No Solana SDK required. No consortium membership. The API reads the SAS attestation PDA and returns structured JSON.
GET /api/gov/credentials/verify?address=C8B4AJp5...credentialPDA
# Response:
{
"valid": true,
"jurisdiction": "DE",
"eligibilityTier": "TIER_1",
"expiryDate": "2027-05-04",
"isExpired": false,
"issuer": "Court7K...pubkey",
"explorerUrl": "https://explorer.solana.com/address/C8B4AJp5...?cluster=devnet"
}The existing payment system (bank transfer, SEPA, etc.) processes the actual payment. Then the portal callsmark_paid to record it on-chain with the ERP payment reference. The on-chain program validates that the disbursed amount does not exceed the authorized amount.
# Close the case (reviewer or delegate)
POST /api/gov/cases/{caseId}/close
{ "signerWallet": "Reviewer3K...pubkey" }
# Record payment after bank transfer completes
POST /api/gov/cases/{caseId}/pay
{
"disbursedAmount": 8500,
"paymentReference": "SAP-INV-2025-04-28-00142",
"signerWallet": "Payer9K...pubkey"
}
# Response: { status: "Paid", transactionSignature, explorerUrl }Program ID: 3f1yBTY6xb6ESdzzb9LxAozv7uVsj9Y9AMEpnAwKJRNV
The Solana Attestation Service (SAS) issues verifiable on-chain eligibility credentials. The same schema works across all 9 supported jurisdictions: the Aide Juridictionnelle in France, the Toevoeging in Netherlands, and so on. Configurable per jurisdiction.
scripts/create-schema.tsscripts/schema-address.jsonscripts/issue-credential.ts or programmatically via the dashboard# Deploy schema (one-time per jurisdiction) npx ts-node scripts/create-schema.ts # Issue credential to a lawyer wallet npx ts-node scripts/issue-credential.ts # Verify a credential on-chain npx ts-node scripts/verify-credential.ts
The Anchor program manages the full case lifecycle on-chain with 14 instructions, 4 events, and 26 error codes. The core flow (open, link, anchor, close, pay) is extended with custodial support, lawyer reassignment, delegation, escalation, extended case statuses, payment validation, and ZK proof verification.
# Build the Anchor program anchor build # Deploy to devnet anchor deploy --provider.cluster devnet # Run integration tests anchor test
Adduce uses a layered storage architecture. No personal data or document content touches the public ledger. The chain stores only cryptographic proofs of existence. Actual documents stay where they are today.
anchor_documentFor cases where documents need to be shared securely between the court and the assigned lawyer, Adduce provides end-to-end encrypted storage via Arweave (permanent, decentralized) through the Irys upload service.
The court encrypts the document so only the assigned lawyer can read it. The plaintext hash is anchored on-chain (for integrity verification). The ciphertext is stored on Arweave (permanent, censorship-resistant). The lawyer decrypts using their wallet key. See scripts/encrypt-and-anchor.ts for the full working example.
import { encryptDocument, deriveEncryptionKeypair } from "./lib/privacy";
import { uploadToArweave } from "./upload-to-arweave";
// 1. Derive encryption keys from Solana wallet (deterministic)
const senderKeys = deriveEncryptionKeypair(courtWallet.secretKey);
const recipientKeys = deriveEncryptionKeypair(lawyerWallet.secretKey);
// 2. Encrypt document (X25519 ECDH + AES-256-GCM)
const envelope = encryptDocument(
documentBuffer,
recipientKeys.publicKey, // only the lawyer can decrypt
courtWallet.secretKey // court signs the encryption
);
// 3. Upload ciphertext to Arweave via Irys (permanent storage)
const arweaveUrl = await uploadToArweave(envelope);
// 4. Anchor the plaintext hash on-chain (proves document existed)
// envelope.plaintextHash goes into anchor_document()
// The actual document is encrypted on Arweave, hash is on SolanaLight Protocol compresses on-chain state using zero-knowledge proofs, reducing storage costs by 98.8%. This is critical at government scale: a country processing 100,000 legal aid cases per year saves over $22,000 annually on infrastructure costs alone.
createRpc() from @lightprotocol/stateless.js and call compress()The compression script uses Helius as both RPC and Photon indexer. All three parameters to createRpc() point to Helius because it serves JSON-RPC, prover, and indexer functions from a single endpoint. See scripts/anchor-document-compressed.ts for the full implementation.
import { createRpc, compress } from "@lightprotocol/stateless.js";
// 1. Connect to Helius (required for Light Protocol)
const rpc = createRpc(HELIUS_RPC_URL, HELIUS_RPC_URL, HELIUS_RPC_URL);
// 2. Compress SOL into Light Protocol state tree
const compressLamports = 10_000;
await compress(rpc, payer, compressLamports, payer.publicKey);
// 3. Anchor case metadata as compressed memo
const memo = JSON.stringify({
case_id: "BS-DE-143/22",
document_hash: "175b1e36...",
timestamp: Date.now(),
status: "closed"
});
// Memo program anchors this in compressed state
// Cost: ~$0.004 vs ~$0.30 for standard PDAHELIUS_RPC_URL in your .env file@lightprotocol/stateless.js and @lightprotocol/compressed-token packagesThe x402 protocol enables HTTP-native payment flows. When a lawyer claims payment, the system verifies their SAS credential and the on-chain payment transaction before confirming. Settlement happens in USDC (or EURC for eurozone deployments).
mark_paid to finalize the caseHTTP/1.1 402 Payment Required X-Payment-Amount: 85000000 X-Payment-Asset: USDC X-Payment-Chain: solana:devnet X-Payment-Recipient: <authority-wallet> X-Credential-Required: SAS X-Credential-Schema: 7uKMGSgup1UZ26MnptCCMCac6rqpe6vBNXET338cDeid
The agent runs as a background service, polling the on-chain program every 15 seconds for cases in Closed status. When it finds one, it executes the full disbursement pipeline automatically: credential verification, USDC transfer, compressed log creation, and status update.
mark_paid to finalize the case on-chain# Run the production automation agent npx ts-node scripts/agent.ts # Or run the full e2e pipeline (demo mode) npx ts-node scripts/e2e-full-pipeline.ts # Run the German BS sample case npx ts-node scripts/sample-german-bs-case.ts
This is a real case executed on Solana Devnet following the exact German Beratungshilfe workflow. Every transaction below is clickable and verifiable on Solana Explorer. Reference: 123 UR II 143/22.
Adduce is jurisdiction-agnostic by design. The protocol uses a two-character jurisdiction code as the seed for all jurisdiction-scoped accounts. To deploy for a new country, you configure three things: the jurisdiction code, the SAS credential schema, and the payment asset.
# 1. Set jurisdiction code in your environment export JURISDICTION="FR" # France # 2. Deploy SAS credential schema for the jurisdiction # Edit scripts/create-schema.ts — change schema fields if needed: # - jurisdiction: "FR" # - eligibility_tier: mapped to local categories # - expiry_date: per local regulation npx ts-node scripts/create-schema.ts # 3. Initialize the on-chain jurisdiction config # The Anchor program creates a ProgramConfig PDA # seeded with ["config", "FR"] # This is done automatically on first case open # 4. Configure payment asset # USDC for dollar-pegged, EURC for eurozone # Set in .env: PAYMENT_MINT=<token-mint-address> # 5. Issue credentials to registered lawyers npx ts-node scripts/issue-credential.ts # 6. Start the automation agent npx ts-node scripts/agent.ts
Full setup from clone to running sample case in under 10 minutes.
# Clone the repository git clone https://github.com/SAHU-01/legal_aid.git cd legal_aid # Install dependencies (requires Node >= 22) nvm use 22 npm install # Configure environment cp .env.example .env # Edit .env — add your Helius API key: # HELIUS_RPC_URL=https://devnet.helius-rpc.com/?api-key=YOUR_KEY # Build the Anchor program anchor build # Deploy to Solana devnet anchor deploy --provider.cluster devnet # Run the German sample case (creates real on-chain txs) npx ts-node scripts/sample-german-bs-case.ts # Start the Next.js dashboard npm run dev # Open http://localhost:3000/dashboard
nvm use 22cargo install --git https://github.com/coral-xyz/anchor avmsh -c "$(curl -sSfL https://release.anza.xyz/stable/install)"solana airdrop 2 (fund your local wallet)A custom Circom circuit enables true zero-knowledge credential verification. Holders prove statements about credential fields without revealing them. Proofs are verified on-chain using Solana’s native alt_bn128 pairing syscall.
cd circuits npm install # installs circomlib + snarkjs ./build.sh # compile + Powers of Tau + phase 2 setup + export VK node test_proof.js # generate + verify a test proof locally # Run the full ZK demo cd .. && npx ts-node scripts/zk-disclosure-demo.ts
How external systems (government portals, case management systems, ERP platforms) integrate with Adduce. Every instruction is callable via the Anchor TypeScript SDK.
import { PublicKey } from "@solana/web3.js";
const PROGRAM_ID = new PublicKey("3f1yBTY6xb6ESdzzb9LxAozv7uVsj9Y9AMEpnAwKJRNV");
// Config PDA (one per jurisdiction)
const [configPda] = PublicKey.findProgramAddressSync(
[Buffer.from("config"), Buffer.from("DE")],
PROGRAM_ID
);
// Case PDA (one per case ID)
const [casePda] = PublicKey.findProgramAddressSync(
[Buffer.from("case"), Buffer.from("BS-DE-143/22")],
PROGRAM_ID
);import { createHash, randomBytes } from "crypto";
// Generate commitment (done once during open_case)
const lawyerPubkey = lawyerKeypair.publicKey.toBytes();
const salt = randomBytes(32);
const commitment = createHash("sha256")
.update(Buffer.concat([lawyerPubkey, salt]))
.digest();
// Store salt securely — lawyer needs it for anchor_document
// commitment goes on-chain, salt stays off-chainimport { createHash } from "crypto";
// Hash the citizen's national ID (never stored in plaintext)
const citizenIdHash = createHash("sha256")
.update("DE-PERSONALAUSWEIS-L01234567")
.digest();
// Court acts as custodian — citizen never touches blockchain
await program.methods
.openCaseCustodial(
"BS-DE-143/22",
lawyerCommitment,
Array.from(citizenIdHash),
custodianPubkey,
85_000_000 // authorized amount in atomic units
)
.accounts({ config: configPda, caseFile: casePda, authority: courtWallet.publicKey })
.signers([courtWallet])
.rpc();# Required HELIUS_RPC_URL=https://devnet.helius-rpc.com/?api-key=YOUR_KEY NEXT_PUBLIC_HELIUS_RPC_URL=https://devnet.helius-rpc.com/?api-key=YOUR_KEY # Program addresses (set after deployment) NEXT_PUBLIC_PROGRAM_ID=3f1yBTY6xb6ESdzzb9LxAozv7uVsj9Y9AMEpnAwKJRNV NEXT_PUBLIC_SAS_PROGRAM_ID=22zoJMtdu4tQc2PzL74ZUT7FrwgB1Udec8DdW4yw4BdG # Network NEXT_PUBLIC_NETWORK=devnet
Each jurisdiction has a single ProgramConfig PDA with anauthority pubkey. This authority is the only signer that can open cases, close cases, mark payments, and issue SAS credentials. In production, this is the court or ministry keypair. Multi-sig (e.g. Squads Protocol) can be used for shared authority.
SAS attestations support revocation natively. The issuing authority can close or revoke an attestation at any time. On the next verification check, fetchMaybeAttestation() returnsexists: false and the payment claim is rejected. No phone calls, no manual registry updates.
bump field for forward-compatible PDA derivation