Development Guide
This guide covers everything you need to develop applications with Aqua Protocol, including signing methods, witnessing options, and SDK usage.
We recommend completing the Quick Start Guide before diving into development details.
Choose Your SDK
Aqua Protocol offers SDKs for different use cases:
Rust SDK (v4)
Beta - Template system, WASM support, cross-platform
Best for: New projects, performance-critical applications
JavaScript SDK (v3)
Stable - Production-ready, Node.js/Web/React Native
Best for: Web applications, existing JavaScript projects
CLI Tool (v4)
Command-line tool for quick operations
Best for: Testing, CI/CD, scripting
Aquafier API (v3)
Web-based API for notarization
Best for: No-code integration, demos
Signing Methods
Aqua Protocol supports multiple signature types for different use cases:
| Method | Protocol Version | Use Case | Trust Model |
|---|---|---|---|
| DID (JWS) | v3, v4 | Decentralized identities | W3C DID standard |
| Ethereum (EIP-191) | v3, v4 | Blockchain-native apps | Ethereum addresses |
| RSA | v4 only | Traditional PKI | Certificate authorities |
| P12 Certificates | v3 only | Enterprise/legal docs | Certificate authorities |
Signing Setup Details
What you need: A DID key pair
How to generate:
Option 1: Using JavaScript
1npm install @digitalcredentials/did-method-key1import { Ed25519VerificationKey2020 } from '@digitalcredentials/ed25519-verification-key-2020';2import { X25519KeyAgreementKey2020 } from '@digitalcredentials/x25519-key-agreement-key-2020';3 4const keyPair = await Ed25519VerificationKey2020.generate();5const did = `did:key:${keyPair.publicKeyMultibase}`;6console.log('DID:', did);7console.log('Private Key:', keyPair.privateKeyMultibase);Option 2: Online Generator
- Visit EBSI DID Generator
- Click "Generate Keys"
- Save your DID and private key securely
Protocol Support: v3, v4
What you need: Ethereum wallet with private key or mnemonic
Two supported methods:
1. MetaMask Browser Extension (Recommended for development)
- Install MetaMask in your browser
- Create or import a wallet
- The SDK will prompt for signature when needed
2. Mnemonic/Private Key in Credentials File
Create a credentials.json file:
1{2 "mnemonic": "your twelve word mnemonic phrase here...",3 "wallet_address": "0xYourEthereumAddress"4}Or with private key:
1{2 "private_key": "0xYourPrivateKeyHex",3 "wallet_address": "0xYourEthereumAddress"4}Never commit credentials.json to version control! Add it to .gitignore.
Check your SDK documentation (Rust SDK | JavaScript SDK) for how to pass credentials.
Protocol Support: v3, v4
What you need: RSA key pair (2048-bit or higher)
Generate RSA keys:
1# Generate private key (2048-bit)2openssl genrsa -out private.pem 20483 4# Extract public key5openssl rsa -in private.pem -pubout -out public.pem6 7# View keys8cat private.pem9cat public.pemFor production: Use keys from your organization's PKI infrastructure or certificate authority.
Protocol Support: v4 only
What you need: PKCS#12 certificate file (.p12/.pfx) with password
For Development (self-signed certificate):
1# Generate private key2openssl genrsa -out private.key 20483 4# Create certificate signing request5openssl req -new -key private.key -out certificate.csr6 7# For Windows/Git Bash, specify config:8# openssl req -new -key private.key -out certificate.csr -config "C:\Program Files\Git\usr\ssl\openssl.cnf"9 10# Generate self-signed certificate (valid for 365 days)11openssl x509 -req -days 365 -in certificate.csr -signkey private.key -out certificate.crt12 13# Create P12 file (you'll be prompted for export password)14openssl pkcs12 -export -out certificate.p12 -inkey private.key -in certificate.crtConvert to Base64 (required for SDK):
1# macOS/Linux2base64 -i certificate.p12 -o certificate_base64.txt3 4# Windows (PowerShell)5[Convert]::ToBase64String([IO.File]::ReadAllBytes("certificate.p12")) | Out-File certificate_base64.txt6 7# Linux alternative8base64 certificate.p12 > certificate_base64.txtFor Production (trusted certificate):
1. Purchase from Certificate Authority:
- DigiCert, Sectigo, GlobalSign, etc.
- Request "Code Signing" or "Document Signing" certificate
- They'll provide a .p12/.pfx file directly
2. If you receive separate files (.crt + .key + intermediate):
1openssl pkcs12 -export -out certificate.p12 \2 -inkey privatekey.key \3 -in certificate.crt \4 -certfile intermediate.crtUsage in SDK:
1{2 "p12_content": "base64_encoded_p12_content_here",3 "p12_password": "your_export_password"4}Protocol Support: v3 only
Witnessing Methods
Witnessing anchors your Aqua revisions to external timestamping services for provable timestamps:
| Method | Networks | Cost | Verification |
|---|---|---|---|
| Ethereum | Mainnet, Sepolia, Holesky | Gas fees (testnet: free) | Blockchain explorer |
| TSA | RFC 3161 services | Free (public TSAs) | TSA verification |
| Nostr | Nostr relays | Free | Nostr event verification |
Witnessing Setup Details
What you need: A TSA service URL
Free Public TSA Services:
1# DigiCert2http://timestamp.digicert.com3 4# Sectigo (formerly Comodo)5http://timestamp.sectigo.com6http://timestamp.comodoca.com7 8# GlobalSign9http://timestamp.globalsign.com/scripts/timstamp.dll10http://timestamp.globalsign.com/tsa/r6advanced111 12# Entrust13http://timestamp.entrust.net/TSS/RFC3161sha2TSUsage: Simply provide the TSA URL to your SDK when witnessing.
Benefits:
- No blockchain required
- Widely accepted standard (RFC 3161)
- Free for most public TSAs
- Instant timestamps
Protocol Support: v3, v4
What you need:
- Ethereum wallet (MetaMask or mnemonic/private key)
- ETH for gas fees (mainnet) or testnet ETH (Sepolia/Holesky)
- Alchemy API key for witness verification
Networks Supported:
- Mainnet: Production use, requires real ETH for gas
- Sepolia: Testnet, free test ETH from faucets
- Holesky: Testnet, free test ETH from faucets
Wallet Setup (same as signing):
Option 1: MetaMask
- Install MetaMask
- Ensure you're on the correct network (mainnet/sepolia/holesky)
- Have sufficient ETH for gas fees
Option 2: Credentials File
1{2 "mnemonic": "your twelve word mnemonic phrase here...",3 "wallet_address": "0xYourEthereumAddress"4}Get Test ETH (for testnets):
- Sepolia: sepoliafaucet.com
- Holesky: holesky-faucet.pk910.de
Alchemy API Key (for verification):
- Visit alchemy.com
- Create free account
- Create new app
- Copy API key
- Detailed guide
Protocol Support: v3, v4
What you need: Nostr secret key (nsec)
How to get a Nostr key:
Option 1: Nostr Client App
- Download a Nostr app:
- Create new account or log in
- App generates a key pair automatically
- Your secret key starts with
nsec1... - Save it securely - you'll need it for witnessing
Option 2: Web Generator
- Visit nostr.com or nostrtool.com
- Click "Generate Keys"
- Save your secret key (
nsec1...)
Your Nostr secret key is permanent. Anyone with it can post as you on Nostr relays. Keep it secure!
Benefits:
- Fully decentralized (no blockchain fees)
- Censorship-resistant
- Fast propagation across relays
- Free to use
Protocol Support: v3, v4
JavaScript SDK Usage (v3)
- Node.js version 19 or higher
- Install aqua-js-sdk:
npm install aqua-js-sdk
Complete Workflow Example
Follow these steps to notarize a file, sign it, and witness it using the JavaScript SDK (v3):
Create Genesis Revision
1import Aquafier, { FileObject } from 'aqua-js-sdk';2import { readFileSync } from 'fs';3 4// Read file content5const testFileContent = readFileSync("./test.txt", 'utf-8');6 7// Create file object8const aquaFileObject: FileObject = {9 fileName: "test.txt",10 fileContent: testFileContent,11 path: "./test.txt"12};13 14// Initialize Aquafier15const aquafier = new Aquafier();16 17// Create genesis revision (notarize the file)18const genesisResult = await aquafier.createGenesisRevision(aquaFileObject);19 20if (genesisResult.isOk()) {21 console.log("Genesis created successfully!");22 console.log("Aqua Tree:", genesisResult.data.aquaTree);23} else {24 console.error("Error creating genesis:", genesisResult.error);25}Sign the Aqua Tree
1import { AquaTreeWrapper, CredentialsData } from 'aqua-js-sdk';2 3// Prepare credentials (for MetaMask, can be empty object)4const creds: CredentialsData = {};5 6// Or for CLI signing with mnemonic:7// const creds: CredentialsData = {8// mnemonic: "your twelve word mnemonic here",9// wallet_address: "0xYourAddress"10// };11 12// Create wrapper (empty string for revision means sign latest)13const aquaWrapper: AquaTreeWrapper = {14 aquaTree: genesisResult.data.aquaTree,15 fileObject: aquaFileObject,16 revision: "" // Sign latest revision17};18 19// Sign with MetaMask (or use "cli", "did", "p12")20const signedResult = await aquafier.signAquaTree(21 aquaWrapper,22 "metamask", // Signature type23 creds,24 true // Auto-increment25);26 27if (signedResult.isOk()) {28 console.log("Signature added successfully!");29} else {30 console.error("Signing failed:", signedResult.error);31}Witness on Blockchain
1// Update wrapper with signed tree2const witnessWrapper: AquaTreeWrapper = {3 aquaTree: signedResult.data.aquaTree,4 fileObject: aquaFileObject,5 revision: "" // Witness latest revision6};7 8// Witness on Ethereum Sepolia testnet9const witnessResult = await aquafier.witnessAquaTree(10 witnessWrapper,11 "eth", // Witness type (eth, tsa, or nostr)12 "sepolia", // Network (sepolia, mainnet, holesky)13 "metamask", // Signing method14 creds,15 true // Auto-increment16);17 18if (witnessResult.isOk()) {19 console.log("Witnessing successful!");20 console.log("Transaction:", witnessResult.data.witness_transaction_hash);21} else {22 console.error("Witnessing failed:", witnessResult.error);23}Verify the Aqua Tree
1// Fetch aqua tree from storage, database, or state2const aquaTree = witnessResult.data.aquaTree;3 4// Prepare credentials for verification (needs Alchemy key for Ethereum witness)5const verifyCreds: CredentialsData = {6 alchemy_key: "your_alchemy_api_key"7};8 9// Verify the complete tree10const verificationResult = await aquafier.verifyAquaTree(11 aquaTree,12 [aquaFileObject], // Array of file objects13 verifyCreds14);15 16if (verificationResult.isOk()) {17 console.log("✓ Verification successful!");18 console.log("Details:", JSON.stringify(verificationResult.data, null, 2));19} else {20 console.log("✗ Verification failed!");21 console.log("Details:", JSON.stringify(verificationResult.data, null, 2));22}More Examples
Complete Examples
Browse full working examples in the repository
API Documentation
Detailed API reference for JavaScript SDK
Rust SDK Usage (v4)
- Rust 1.70 or higher
- Add to Cargo.toml:
aqua-rs-sdk = { git = "https://github.com/inblockio/aqua-verifier-rs" }
Quick Example
1use aqua_rs_sdk::primitives::Method;2use aqua_rs_sdk::schema::file_data::FileData;3use aqua_rs_sdk::Aquafier;4use std::path::PathBuf;5 6#[tokio::main]7async fn main() -> Result<(), Box<dyn std::error::Error>> {8 // Read file content9 let filename = "test.txt".to_string();10 let file_content = tokio::fs::read(&filename).await?;11 12 // Create file data13 let file_data = FileData::new(14 filename.clone(),15 file_content,16 PathBuf::from(format!("./{}", filename)),17 );18 19 // Initialize Aquafier20 let aquafier = Aquafier::new(None, None);21 22 // Create genesis revision (notarize the file)23 let result = aquafier.create_genesis_revision(file_data, Method::Scalar);24 25 match result {26 Ok(tree) => {27 println!("✓ Aqua chain created successfully!");28 println!("{}", serde_json::to_string_pretty(&tree)?);29 }30 Err(e) => {31 eprintln!("Error: {:#?}", e);32 }33 }34 35 Ok(())36}