This document demonstrates a practical workflow using Ledgerator for document attestation between multiple parties.
Three parties need to agree on a service contract:
- Alice (Service Provider) - Registers the contract
- Bob (Client) - Reviews and accepts the contract
- Carol (Witness) - Acknowledges the agreement
# Terminal 1: Start Anvil (local Ethereum node)
anvil
# Terminal 2: Deploy contract
make deploy-localEach party configures their CLI with the deployed contract address.
Alice's config (~/.ledgerator/config.toml):
[network]
rpc_url = "http://localhost:8545"
chain_id = 31337
contract_address = "0x5FbDB2315678afecb367f032d93F642f64180aa3"Private keys (Anvil test accounts):
# Alice (Anvil account #0)
export LEDGERATOR_PRIVATE_KEY="0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
# Bob (Anvil account #1)
export LEDGERATOR_PRIVATE_KEY="0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d"
# Carol (Anvil account #2)
export LEDGERATOR_PRIVATE_KEY="0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a"First, let's configure identities for the participants to make output more readable:
# Alice sets up identities for all parties
ledge identity set 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 Alice
ledge identity set 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 Bob
ledge identity set 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC Carol
# View all identities
ledge identity list
# Output:
# Identities:
# 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC: Carol
# 0x70997970C51812dc3A010C7d01b50e0d17dc79C8: Bob
# 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266: AliceCreate the contract document:
cat > service-contract.txt << EOF
Service Agreement
Provider: Alice (0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266)
Client: Bob (0x70997970C51812dc3A010C7d01b50e0d17dc79C8)
Witness: Carol (0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC)
Services: Web Development
Duration: 6 months
Payment: 50 ETH
Start Date: 2026-01-15
Terms:
1. Provider delivers working website
2. Client pays upon delivery
3. Witness validates completion
EOFRegister with Ledgerator:
ledge register service-contract.txt \
--title "Service Agreement - Alice & Bob" \
--description "6-month web development contract" \
--location "s3://shared-contracts/2026-01-02/service-agreement.txt"
# Output:
# Computing file hash...
# Registering document on blockchain...
# Uploading metadata...
# Document registered successfully!
# Hash: 0x7d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e
# Transaction hash: 0xabc123...Alice attests to the contract:
ledge attest 0x7d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e \
--type ACCEPT
# Output:
# Attesting to document 0x7d3e...
# Transaction hash: 0xdef456...
# Attestation recorded: ACCEPTAlice shares the hash with Bob and Carol via email or Slack.
Bob receives the contract file and hash from Alice.
First, verify the file matches the hash:
# Switch to Bob's private key
export LEDGERATOR_PRIVATE_KEY="0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d"
# Verify file hash
ledge verify service-contract.txt 0x7d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e
# Output:
# Computing file hash...
# Expected hash: 0x7d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e
# Computed hash: 0x7d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e
#
# ✓ Hashes match!Query the document to verify on-chain registration:
ledge query 0x7d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e
# Output:
# Querying document 0x7d3e...
#
# Blockchain Information:
# Hash: 0x7d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e
# Registrar: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 (Alice)
# Registered At: 1704240000 (Unix timestamp)
#
# Metadata:
# Title: Service Agreement - Alice & Bob
# Description: 6-month web development contract
# Retrieved From: s3://shared-contracts/2026-01-02/service-agreement.txt
#
# Attestations:
# 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 (Alice) - ACCEPT (1704240123)Bob adds his own metadata locally:
# Register the document locally with Bob's metadata
ledge register service-contract.txt \
--title "Service Agreement - Alice & Bob" \
--description "Web dev contract - reviewed and approved" \
--location "s3://shared-contracts/2026-01-02/service-agreement.txt"Bob attests acceptance:
ledge attest 0x7d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e \
--type ACCEPT
# Output:
# Attesting to document 0x7d3e...
# Transaction hash: 0x123abc...
# Attestation recorded: ACCEPTCarol switches to her private key:
export LEDGERATOR_PRIVATE_KEY="0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a"Carol verifies file hash and on-chain registration:
# Verify file hash
ledge verify service-contract.txt 0x7d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e
# ✓ Hashes match!
# Verify on-chain registration
ledge query 0x7d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e
# Output shows Alice and Bob have both accepted
# Attestations:
# 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 (Alice) - ACCEPT (1704240123)
# 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 (Bob) - ACCEPT (1704240456)Carol adds her witness attestation:
# Store metadata locally
ledge register service-contract.txt \
--title "Alice-Bob Service Agreement" \
--description "Witnessed agreement between Alice and Bob" \
--location "s3://shared-contracts/2026-01-02/service-agreement.txt"
# Witness the agreement
ledge attest 0x7d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e \
--type WITNESS
# Output:
# Attesting to document 0x7d3e...
# Transaction hash: 0x789def...
# Attestation recorded: WITNESSAnyone can now query the complete attestation history:
ledge query 0x7d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e
# Output:
# Querying document 0x7d3e...
#
# Blockchain Information:
# Hash: 0x7d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e
# Registrar: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 (Alice)
# Registered At: 1704240000 (Unix timestamp)
#
# Attestations:
# 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 (Alice) - ACCEPT (1704240123)
# 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 (Bob) - ACCEPT (1704240456)
# 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC (Carol) - WITNESS (1704240789)The blockchain now contains immutable proof that:
- Alice registered the document on 2026-01-02
- Alice accepted it immediately
- Bob accepted it a few minutes later
- Carol witnessed the agreement shortly after
If Alice wants to share her local metadata with a team member:
# Alice exports metadata
ledge export my-contracts.json
# Metadata file can be shared via email, Slack, etc.
# Team member imports
ledge import my-contracts.json
# Now they can see Alice's local metadata
ledge listEach party can list their locally tracked documents:
ledge list
# Output:
# Documents:
#
# Hash: 0x7d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e
# Title: Service Agreement - Alice & Bob
# Registrar: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
# Local Path: /Users/alice/contracts/service-contract.txt
# Retrieved From: emailed to all parties on 2026-01-02Filter by registrar:
ledge list --registrar 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266- Immutable Record: All attestations are permanently recorded on the blockchain
- Verification: Anyone can verify the document hash matches the file
- Timeline: Blockchain timestamps prove when each action occurred
- Trust: No central authority - cryptographic proof via blockchain
- Privacy: Metadata stays local unless explicitly shared
- Collaboration: Export/import enables selective metadata sharing
For production use on mainnet:
- Deploy contract to Ethereum mainnet
- Update
config.tomlwith mainnet RPC and contract address - Use production private keys (not test accounts!)
- Share document hashes through existing communication channels
- Use export/import for team metadata sharing
- Query blockchain to verify attestations at any time
- Private Keys: Never share your private key
- File Verification: Always verify hash before attesting
- Blockchain Fees: Mainnet transactions cost gas
- Metadata Privacy: Local storage means metadata stays private
- Export Security: Exported JSON contains your metadata - share carefully