Skip to content

Blockchain-based document attestation system for creating verifiable, tamper-proof records of legal document acceptance. Multi-party workflow support with on-chain attestations and off-chain metadata storage.

License

Notifications You must be signed in to change notification settings

christophercampbell/ledgerator

Repository files navigation

Ledgerator

Document attestation system for verifiable workflows between parties.

Overview

Ledgerator combines blockchain immutability with local metadata storage for document verification and attestation. The system enables multiple parties to cryptographically verify, acknowledge, or attest to documents in workflows minimizing trust requirements through auditability.

Key Features:

  • Blockchain-based document registry with cryptographic proof
  • Local SQLite metadata storage for privacy and simplicity
  • Hash-based document verification
  • Multiple attestation types (ACCEPT, ACKNOWLEDGE, WITNESS, REJECT)
  • Multi-network support with easy switching
  • Metadata export/import for collaboration
  • Simple CLI interface

Architecture

Ledgerator consists of two components:

  1. Smart Contract - EVM-based registry for document hashes and attestations
  2. CLI Tool - Command-line application with local SQLite storage

Design Philosophy

On-chain (Immutable):

  • Document hash (keccak256)
  • Registrar address
  • Registration timestamp
  • Attestations (who, what type, when)

Off-chain (Local):

  • Document title and description
  • Local file path
  • Retrieval location (URL identifier: s3://, ipfs://, https://, etc.)
  • Registrar address (cached from blockchain)

This separation balances permanence with privacy—attestation records stay on-chain forever, while practical metadata stays local and can be shared selectively.

Quick Start

Prerequisites

  • Rust 1.75+
  • Foundry (for smart contracts)

Installation

# Build
cargo build --release

# Install globally
cargo install --path .

# Or use directly
cargo run -- --help

Configuration

Initialize configuration (creates ~/.ledgerator/config.toml with local network):

ledge config init

This creates a default configuration with a local network profile for Anvil development.

Add additional networks:

ledge config add mainnet \
  --rpc-url "https://eth-mainnet.alchemyapi.io/v2/YOUR_KEY" \
  --chain-id 1 \
  --contract-address "0x..."

ledge config add sepolia \
  --rpc-url "https://sepolia.infura.io/v3/YOUR_KEY" \
  --chain-id 11155111 \
  --contract-address "0x..."

Switch between networks:

ledge config use mainnet

View current configuration:

ledge config show

Set private key:

export LEDGERATOR_PRIVATE_KEY="0x..."

Local metadata is stored at ~/.ledgerator/metadata.db (created automatically).

Usage

Register a Document

ledge register contract.pdf \
  --title "Service Agreement" \
  --location "s3://bucket/contracts/service-agreement.pdf"

This:

  1. Computes keccak256 hash of the file
  2. Registers hash on blockchain
  3. Stores metadata locally (including absolute file path)

Attest to a Document

ledge attest 0xabc123... --type ACCEPT

Attestation types:

  • ACCEPT - Approve or accept the document
  • ACKNOWLEDGE - Confirm receipt or awareness
  • WITNESS - Observed or verified
  • REJECT - Reject or dispute

Query Document

ledge query 0xabc123...

Shows:

  • Blockchain data (hash, registrar, timestamp, attestations)
  • Local metadata (title, description, file path, location)

Verify File

ledge verify contract.pdf 0xabc123...

Confirms the file matches the expected hash.

List Documents

# All documents
ledge list

# Filter by registrar
ledge list --registrar 0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb

Share Metadata

# Export
ledge export metadata.json

# Import (on another machine)
ledge import metadata.json

Metadata files are portable JSON containing all your local document records.

Manage Identities

Map Ethereum addresses to human-readable names for easier attestation tracking:

# Set identity
ledge identity set 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 Alice

# Get identity
ledge identity get 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266

# List all identities
ledge identity list

# Delete identity
ledge identity delete 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266

Identities are stored locally and displayed in query output alongside addresses.

Development

Build

# All components
make build

# Contracts only
make build-contracts

# CLI only
make build-cli

Test

# All tests
make test

# Contract tests
make test-contracts

# CLI tests
make test-cli

# Integration test
make integration

Local Development

# Start local Ethereum node
anvil

# Deploy contract (in another terminal)
make deploy-local

# Initialize configuration
cargo run -- config init

# Use CLI
cargo run -- --help

Code Quality

# Check compilation
make check

# Run linters
make lint

# Format code
make format

Smart Contract

File: contracts/src/Ledgerator.sol

Functions

  • registerDocument(bytes32 hash) - Register document hash
  • attest(bytes32 hash, AttestationType type) - Add attestation
  • getDocument(bytes32 hash) - Get document info
  • getAttestations(bytes32 hash) - Get all attestations
  • getAttesters(bytes32 hash) - Get unique attesters

Events

  • DocumentRegistered(bytes32 indexed hash, address indexed registrar, uint256 timestamp)
  • Attested(bytes32 indexed hash, address indexed attester, AttestationType attestationType, uint256 timestamp)

Tests

Comprehensive test suite in contracts/test/Ledgerator.t.sol covers:

  • Document registration and uniqueness
  • Multiple attestation types
  • Query operations
  • Gas optimization

CLI Tool

Location: src/

Commands

ledge config init               Initialize configuration
ledge config show               Show configuration
ledge config add <name>         Add network
ledge config use <name>         Switch active network
ledge register <file>           Register document
ledge attest <hash> --type      Add attestation
ledge query <hash>              Query document
ledge verify <file> <hash>      Verify file
ledge list [--registrar]        List documents
ledge export <file>             Export metadata
ledge import <file>             Import metadata
ledge identity set <addr> <nm>  Set identity for address
ledge identity get <addr>       Get identity for address
ledge identity list             List all identities
ledge identity delete <addr>    Delete identity

Storage

Local SQLite database at ~/.ledgerator/metadata.db:

CREATE TABLE documents (
    hash TEXT PRIMARY KEY,
    title TEXT,
    description TEXT,
    local_path TEXT,        -- Absolute path on filesystem
    location TEXT,          -- URL identifier (s3://, ipfs://, https://, etc)
    registrar TEXT,
    created_at TEXT,
    updated_at TEXT
);

CREATE TABLE identities (
    address TEXT PRIMARY KEY,
    name TEXT NOT NULL,
    created_at TEXT,
    updated_at TEXT
);

Configuration

Config file: ~/.ledgerator/config.toml

[networks.local]
rpc_url = "http://localhost:8545"
chain_id = 31337
contract_address = "0x5FbDB2315678afecb367f032d93F642f64180aa3"

[networks.mainnet]
rpc_url = "https://..."
chain_id = 1
contract_address = "0x..."

[settings]
active_network = "local"

Environment:

  • LEDGERATOR_PRIVATE_KEY - Private key for transactions

Integration Test

Full end-to-end test in integration-test.sh:

./integration-test.sh

Tests:

  1. Start Anvil (local EVM)
  2. Deploy smart contract
  3. Register document
  4. Attest to document
  5. Query document
  6. Verify file
  7. List documents
  8. Export metadata
  9. Import metadata
  10. Contract tests

Deployment

Smart Contract

# Local (Anvil)
make deploy-local

# Testnet/Mainnet
cd contracts
forge script script/Deploy.s.sol \
  --rpc-url <RPC_URL> \
  --private-key <KEY> \
  --broadcast

After deploying to a new network, add it to your configuration:

ledge config add <network-name> \
  --rpc-url <RPC_URL> \
  --chain-id <CHAIN_ID> \
  --contract-address <DEPLOYED_ADDRESS>

ledge config use <network-name>

CLI Tool

# Install system-wide
make install

# Or distribute binary
cargo build --release
# Binary at: target/release/ledge

Example Workflow

# Alice registers a contract
ledge register service-agreement.pdf \
  --title "Service Agreement Q1 2025" \
  --location "s3://legal-docs/q1-2025/service-agreement.pdf"

# Output: Hash: 0xabc123...

# Alice shares hash with Bob (email, Slack, etc)

# Bob receives file, verifies hash
ledge verify received-contract.pdf 0xabc123...
# ✓ Hashes match!

# Bob queries to verify on-chain registration
ledge query 0xabc123...
# Shows: Alice registered at timestamp X

# Bob accepts (which also confirms the document exists on-chain)
ledge attest 0xabc123... --type ACCEPT

# Anyone can verify the chain of attestations
ledge query 0xabc123...
# Shows: Alice registered, Bob accepted, timestamps

# Alice exports metadata to share with Carol
ledge export contracts-q1.json

# Carol imports metadata
ledge import contracts-q1.json
ledge list  # See all Q1 contracts

Use Cases

  • Contract Approval: Multi-party contract signing with verifiable timestamps
  • Document Verification: Prove document existence at a specific time
  • Audit Trails: Immutable record of document attestations
  • Supply Chain: Track document flow through verification stages
  • Compliance: Regulatory documents with attestation history

Design Documentation

See docs/plans/2026-01-01-ledgerator-design.md for detailed specifications.

License

MIT

About

Blockchain-based document attestation system for creating verifiable, tamper-proof records of legal document acceptance. Multi-party workflow support with on-chain attestations and off-chain metadata storage.

Topics

Resources

License

Stars

Watchers

Forks