Documentation

VectorLedger Docs

Everything you need to install, configure, and operate VectorLedger from a first-run quick start to production HSM deployment and compliance reporting.

Table of Contents
Introduction

Overview

VectorLedger is a purpose-built, append-only financial ledger written entirely in Rust. Every journal entry is linked by a tamper-evident BLAKE3 hash chain, every page of data is encrypted at rest with AES-256-GCM, and every query result can carry a cryptographic Merkle proof that the returned data has not been modified since it was written.

Historical tampering is cryptographically detectable any modification to a past record invalidates every hash in the chain from that point to the present, provided that verification checkpoints are independently protected (which the HSM architecture is specifically designed to enforce).

Built by VectorGuard Labs.

Why VectorLedger?

Traditional relational databases treat audit trails as an afterthought: triggers that can be disabled, log tables that can be truncated, and backup files that can be silently replaced. For organizations operating under SOC 2, PCI-DSS, financial regulation, or internal zero-trust policies, this is not good enough.

  • A row written five years ago cannot be changed without invalidating every hash in the chain from that point to the present.
  • Every SELECT response optionally carries a Merkle proof that any client can independently verify.
  • The audit log is WORM-append-only each event is hashed into the next, forming a second independent tamper-evident chain.
  • The compliance engine generates machine-generated technical evidence supporting SOC 2 Type II and PCI-DSS v4 control assessments — not pre-written documentation. This evidence supports an auditor's work; it does not by itself make an organization compliant. Organizational compliance requires additional controls, policies, and independent auditor assessment beyond what any database engine can provide.
Scope note: VectorLedger generates machine-generated technical evidence supporting SOC 2 and PCI-DSS control assessments. This evidence is a technical input to an audit it does not by itself make an organization compliant. Organizational compliance requires additional policies, procedures, personnel controls, and independent auditor assessment that are outside the scope of any database engine.

Deployment Architecture

VectorLedger supports two PyHSM deployment models. Model 1 is suitable for development and single-server production. Model 2 isolates key material on a dedicated host with no public IP, which is the recommended configuration for production.

VectorLedger │ ┌──────────┴──────────┐ │ │ Local PyHSM Remote PyHSM (Model 1) (Model 2) │ │ Unix socket TLS 1.3 + mTLS /tmp/pyhsm.sock port 8443 │ │ ▼ ▼ PyHSM PyHSM same host separate host (private subnet, no public IP)

See Model 1 Local PyHSM and Model 2 Remote PyHSM for full setup instructions.

Setup

Prerequisites

RequirementMinimum VersionNotes
Rust toolchain1.80Install via rustup.rs
macOS, Linux, or WindowsmacOS and Linux are fully supported; Windows 10/11 and Windows Server 2019/2022 (x86_64 and ARM64) are supported
GitAny recentTo clone the repository

No other runtime dependencies are required. All cryptographic libraries are statically linked via Cargo.

Setup

Installation

Option 1 Install via curl (recommended)

The fastest way to install VectorLedger. The installer detects your OS and architecture, downloads the correct pre-built binary, verifies its SHA-256 checksum, and places vledger on your PATH.

curl --proto '=https' --tlsv1.2 -sSf \ https://raw.githubusercontent.com/pavondunbar/VectorLedger/main/install.sh | bash

Install a specific version

curl --proto '=https' --tlsv1.2 -sSf \ https://raw.githubusercontent.com/pavondunbar/VectorLedger/main/install.sh \ | VLEDGER_VERSION=v0.1.0 bash

Install to a custom directory

curl --proto '=https' --tlsv1.2 -sSf \ https://raw.githubusercontent.com/pavondunbar/VectorLedger/main/install.sh \ | VLEDGER_INSTALL_DIR="$HOME/.local/bin" bash

Installer environment variables

VariableDefaultDescription
VLEDGER_VERSIONlatestRelease tag to install, e.g. v0.1.0
VLEDGER_INSTALL_DIR/usr/local/binDirectory to place the vledger binary
VLEDGER_NO_MODIFY_PATH0Set to 1 to skip adding the install dir to your shell profile

After installation, verify it works:

vledger --version vledger self-test

Windows (PowerShell)

Run this in PowerShell 5.1+ or PowerShell 7+:

irm https://raw.githubusercontent.com/pavondunbar/VectorLedger/main/install.ps1 | iex

The installer detects your architecture (x86_64 or ARM64), downloads the signed release zip, verifies its SHA-256 checksum, installs vledger.exe to %LOCALAPPDATA%\vledger\bin, and adds it to your user PATH.

Windows installer parameters

ParameterDefaultDescription
-VersionlatestRelease tag, e.g. v0.1.0. Also reads $env:VLEDGER_VERSION.
-InstallDir%LOCALAPPDATA%\vledger\binDirectory to install vledger.exe
-NoPathUpdateoffSkip adding the install dir to your user PATH
Windows-specific notes: PyHSM uses TCP instead of a Unix socket on Windows. Start the PyHSM daemon with $env:PYHSM_TCP_PORT = 7777 and pass --pyhsm-socket 127.0.0.1:7777 to vledger init. File permissions (0o600) are not enforced on Windows protect your data directory using NTFS ACLs. Graceful shutdown responds to CTRL-C; use Stop-Process or the service manager instead of SIGTERM.

Option 2 Build from source

1 Install Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh source "$HOME/.cargo/env" rustc --version # should be 1.80 or newer cargo --version
2 Clone the repository
git clone https://github.com/pavondunbar/VectorLedger.git cd VectorLedger
3 Build
# Development build cargo build # Optimized production binary cargo build --release

The binary is written to target/debug/vledger (dev) or target/release/vledger (release). Install to your PATH:

cargo install --path crates/vledger # Or manually: sudo cp target/release/vledger /usr/local/bin/vledger
4 Verify the build
./target/release/vledger self-test ./target/release/vledger self-test-phase3

Expected output:

── VectorLedger Phase 2 Self-Test ─────────────── [1/7] Hash chain ... ✓ [2/7] AES-256-GCM encryption ... ✓ [3/7] Merkle proofs ... ✓ [4/7] WAL-backed ledger ... ✓ [5/7] Page encryption ... ✓ [6/7] SQL engine ... ✓ [7/7] Verifiable query proof ... ✓ ✓ All Phase 2 self-tests passed. ── VectorLedger Phase 3 Self-Test ─────────────── [1/7] Audit log (WORM + chain) ... ✓ [2/7] Four-eyes workflow ... ✓ [3/7] Compliance report (SOC 2) ... ✓ [4/7] Compliance report (PCI-DSS) ... ✓ [5/7] Backup & restore round-trip ... ✓ [6/7] PgWire message encoding ... ✓ [7/7] SQL optimizer (agg + window) ... ✓ ✓ All Phase 3 self-tests passed.
Setup

Quick Start

VectorLedger uses PyHSM a VectorGuard Labs product as its default key management backend. PyHSM acts as a local software HSM: VectorLedger's master encryption key is sealed inside PyHSM's encrypted keystore and never touches disk in plaintext.

The steps below walk through the full setup from scratch. If you are not using PyHSM and prefer a different key backend, jump to Key Source Backends.

1 Install and start PyHSM

PyHSM ships as both a Python package and a TypeScript daemon. VectorLedger connects to the TypeScript daemon via a Unix domain socket.

Install Node.js (18+) if you don't have it

brew install node # Or download from https://nodejs.org

Clone PyHSM and install dependencies

git clone https://github.com/pavondunbar/PyHSM.git ~/PyHSM cd ~/PyHSM/pyhsm-ts npm install

Generate a PyHSM master password

openssl rand -base64 32
Important: Store this password somewhere safe before proceeding. If it is lost, the keystore cannot be unlocked.

Start the PyHSM daemon

cd ~/PyHSM PYHSM_MASTER_PASSWORD="<your-password>" \ PYHSM_KEYSTORE_PATH="$HOME/PyHSM/pyhsm-keystore.enc" \ PYHSM_AUDIT_LOG_PATH="$HOME/PyHSM/pyhsm-audit.jsonl" \ npx tsx pyhsm-ts/process.ts

Expected output: [PyHSM] Listening on /tmp/pyhsm.sock

Confirm the socket is ready in a new terminal:

ls -la /tmp/pyhsm.sock # srw------- 1 you wheel 0 ... /tmp/pyhsm.sock
Important: PyHSM must always start before VectorLedger. If the daemon is not running when vledger start is called, startup will fail with a clear error.
2 Initialise the database
cd /path/to/VectorLedger vledger init --data-dir ./vledger-data --key-source pyhsm

Verify the backend was recorded correctly:

cat vledger-data/keys/key_source.json # Must contain "backend": "py_hsm"
Never run vledger init without --key-source pyhsm in production. The default without this flag uses an environment variable backend which stores key material in your shell environment.
3 Lock down the data directory
chmod 700 vledger-data/ chmod 700 vledger-data/keys/ chmod 700 vledger-data/catalog/ chmod 700 vledger-data/audit/ chmod 700 vledger-data/wal/ chmod 700 vledger-data/pages/
4 Start the server
vledger start --data-dir ./vledger-data

On first start, credentials are written to ./vledger-data/catalog/.admin_initial_credentials (mode 0600) not printed to the terminal.

5 Change the admin password
cat vledger-data/catalog/.admin_initial_credentials vledger user set-password --username admin --data-dir ./vledger-data rm vledger-data/catalog/.admin_initial_credentials
6 Verify integrity
vledger verify --data-dir ./vledger-data
7 Run your first queries
vledger sql --data-dir ./vledger-data --username admin

Once in the REPL, use \x to toggle expanded (vertical) display — useful for wide rows like ledger entries. Use \? to see all available meta-commands.

-- Create accounts INSERT INTO accounts (code, name, account_type, currency, domain) VALUES ('CASH', 'Cash - USD', 'asset', 'USD', 'main'); INSERT INTO accounts (code, name, account_type, currency, domain) VALUES ('REVENUE', 'Revenue', 'income', 'USD', 'main'); -- Post a journal entry INSERT INTO ledger (description, debit_account, credit_account, amount, currency, domain) VALUES ('Customer payment', 'CASH', 'REVENUE', 100000, 'USD', 'main'); -- Check balance (amounts are in minor units — cents for USD) SELECT BALANCE('CASH'); -- balance -- ------- -- 100000 -- Verify the hash chain SELECT VERIFY_CHAIN(); -- status | entries_verified -- -------+----------------- -- OK | 1 -- Query all entries (one row per entry) SELECT * FROM ledger; -- Query entries in traditional accounting format (one row per debit/credit line) SELECT * FROM ledger_lines LIMIT 10; -- date | sequence | description | account_id | dr_cr | amount | currency | domain -- -----------+----------+------------------+------------+--------+--------+----------+------- -- 2026-08-17 | 1 | Customer payment | <uuid> | Debit | 1.00 | USD | main -- 2026-08-17 | 1 | Customer payment | <uuid> | Credit | 1.00 | USD | main
8 Also start the PostgreSQL wire-protocol listener
vledger start --data-dir ./vledger-data --pgwire # Then connect with any PostgreSQL client: psql "host=127.0.0.1 port=5432 user=admin sslmode=require"
Operations

Reset / Starting Over

If you lose the admin password, or need to wipe the database and start fresh, delete the data directory and re-initialise. Make sure PyHSM is running before you init.

This is destructive. All data in the data directory will be permanently deleted. Stop the server first.
# Stop the server first (Ctrl-C in the terminal where it is running) rm -rf ./vledger-data vledger init --data-dir ./vledger-data --key-source pyhsm vledger start --data-dir ./vledger-data

The first start after a fresh init writes a new admin credential file. Read it, change the password, and delete the file before doing anything else.

CLI Reference

vledger init

Initialise a new database.

vledger init [OPTIONS] --data-dir <PATH> Data directory (default: ./vledger-data) --force Reinitialise an existing database --key-source <BACKEND> env | file | vault | aws_kms | pyhsm | remote-pyhsm (default: pyhsm) --vault-addr <URL> Vault server address --vault-mount <MOUNT> Vault KV v2 mount path (default: secret) --vault-path <PATH> Vault secret path (default: vledger/master_key) --kms-key-id <ARN> AWS KMS key ARN or alias --kms-region <REGION> AWS region (default: us-east-1) # Model 1 local PyHSM (same server) --pyhsm-socket <PATH> PyHSM Unix socket path (default: /tmp/pyhsm.sock) --pyhsm-caller-id <ID> Caller ID written to PyHSM audit log (default: vledger) # Model 2 remote PyHSM (same-region, separate server) --pyhsm-endpoint <URL> Remote PyHSM HTTPS endpoint; selects remote-pyhsm automatically when set Example: https://pyhsm.internal.example.com:8443 Env: PYHSM_ENDPOINT --pyhsm-ca-cert <PATH> CA certificate PEM to verify the PyHSM server Env: PYHSM_CA_CERT --pyhsm-client-cert <PATH> mTLS client certificate PEM (VectorLedger's identity) Env: PYHSM_CLIENT_CERT --pyhsm-client-key <PATH> mTLS client private key PEM Env: PYHSM_CLIENT_KEY --pyhsm-timeout-ms <MS> Per-request timeout in ms (default: 5000) Env: PYHSM_TIMEOUT_MS --pyhsm-max-retries <N> Max retries on transient errors (default: 3)
CLI Reference

vledger start

Start the server. Responds to SIGTERM and CTRL-C with a graceful drain — in-flight connections are allowed to complete before the process exits.

vledger start [OPTIONS] --data-dir <PATH> Data directory (default: ./vledger-data) --bind <ADDR> Bind address (default: 127.0.0.1:5433) --pgwire Also start the PostgreSQL wire-protocol listener on port 5432 --with-proofs Attach Merkle proofs to every SELECT response --max-connections <N> Override the connection semaphore limit --wal-sync-mode <MODE> group_commit | per_record | no_sync (default: group_commit) --group-commit-delay-ms Flush interval in ms for group_commit mode (default: 2) --tls-cert-path <PATH> Path to TLS certificate PEM (replaces self-signed) --tls-key-path <PATH> Path to TLS private key PEM (replaces self-signed)
Integrated replication: If replication.json exists in the data directory with "role": "primary", vledger start automatically activates the WAL shipper alongside the SQL server — no separate start-primary process is required. Every committed journal entry is shipped to connected replicas automatically.

When started with --with-proofs, every SELECT response in the vledger sql REPL includes a Merkle proof and root hash.

WAL sync modes

# Default group commit (2 ms flush interval) vledger start # Per-record fsync (safest, lower TPS) vledger start --wal-sync-mode per_record # Tune the flush interval vledger start --wal-sync-mode group_commit --group-commit-delay-ms 5
CLI Reference

vledger sql

Interactive SQL REPL or single-statement execution. When vledger start is running, the CLI automatically detects it and connects over TLS. Use --server to point at a non-default address.

vledger sql [OPTIONS] --data-dir <PATH> Data directory --query <SQL> Run a single statement and exit (omit for interactive mode) --username <USER> Username (or set VLEDGER_CLI_USER) --password <PASS> Password (or set VLEDGER_CLI_PASSWORD, or enter interactively) --server <ADDR> Connect to a running server at host:port

REPL meta-commands

CommandDescription
\xToggle expanded (vertical) display. Useful for wide rows.
\q or exitQuit the REPL.
\? or \helpShow available meta-commands.

The prompt changes to vledger (expanded)> while expanded mode is active. Use \x again to toggle back.

CLI Reference

vledger verify

Verify WAL integrity and the ledger hash chain.

vledger verify --data-dir <PATH>
CLI Reference

vledger status

Show database version, WAL segment count, and active segment.

vledger status --data-dir <PATH>
CLI Reference

vledger backup

Create a point-in-time backup archive with a BLAKE3 manifest. Private key material is excluded only public keys are archived.

vledger backup --data-dir <PATH> [--output <FILE.tar>]
CLI Reference

vledger restore

Restore a backup archive. Verifies every file's BLAKE3 hash against the manifest before completing. --force is required to overwrite an existing data directory.

vledger restore --from <FILE.tar> [--target <PATH>] [--force]
CLI Reference

vledger rotate-keys

Rotate all HSM key slots and record audit events. Old key versions are archived for decryption of existing data; new versions are used for all new writes.

vledger rotate-keys [OPTIONS] --data-dir <PATH> Data directory (default: ./vledger-data) --caller-id <ID> Caller ID written to audit log # Model 1 local PyHSM --hsm-socket <PATH> PyHSM Unix socket path (default: /tmp/pyhsm.sock) # Model 2 remote PyHSM --pyhsm-endpoint <URL> Remote PyHSM HTTPS endpoint --pyhsm-ca-cert <PATH> CA certificate PEM --pyhsm-client-cert <PATH> mTLS client certificate PEM --pyhsm-client-key <PATH> mTLS client private key PEM --pyhsm-timeout-ms <MS> Per-request timeout in ms (default: 5000) --pyhsm-max-retries <N> Max retries on transient errors (default: 3)
CLI Reference

vledger audit-export

Export the WORM audit log, optionally filtered by date range.

vledger audit-export --data-dir <PATH> [--format json|csv] [--output <FILE>] [--from <RFC3339>] [--to <RFC3339>]

Audit log export is tier-gated:

TierDate range allowed
FreeLast 30 days
StarterLast 90 days
Growth / EnterpriseUnlimited
CLI Reference

vledger audit-package

Generate a portable, self-contained cryptographic audit evidence package. Default (commitment-only) mode computes the Merkle root over all entries in a single O(n) pass, signs it with the database Ed25519 key, and writes a compact JSON commitment. Completes in seconds at any scale.

vledger audit-package [OPTIONS] --data-dir <PATH> Data directory (default: ./vledger-data) --output <FILE> Output JSON file (default: ./vledger-audit-package-<ts>.json) --tenant <NAME> Organisation name to embed in the package Example: --tenant "Acme Financial" --description <TEXT> Description of this audit package Example: --description "Q3 2026 regulatory audit" --period-start <DATE> Start of the reporting period (YYYY-MM-DD or RFC 3339) --period-end <DATE> End of the reporting period (YYYY-MM-DD or RFC 3339) --include-entries Also embed all entries and per-entry Merkle proofs (only practical for ledgers with < ~10,000 entries)
vledger audit-package \ --data-dir ./vledger-data \ --tenant "Acme Financial" \ --description "Q3 2026 regulatory audit" \ --period-start 2026-07-01 \ --period-end 2026-09-30 \ --output audit-q3-2026.json

The output JSON meta block includes all provided fields:

{ "meta": { "tenant": "Acme Financial", "description": "Q3 2026 regulatory audit", "period_start": "2026-07-01", "period_end": "2026-09-30", "entry_count": 1000000, "merkle_root": "804efb54...", "root_signature": "...", "generated_at": "2026-09-30T23:59:59Z" } }
CLI Reference

vledger audit-proof

Generate a single-entry inclusion proof against a commitment package. Produces a self-contained JSON file the auditor can verify without any database access.

vledger audit-proof [OPTIONS] --data-dir <PATH> Data directory --commitment <FILE> Commitment package JSON (from vledger audit-package) --sequence <N> Sequence number of the entry to prove --output <FILE> Output proof JSON (default: ./vledger-entry-proof-<N>.json)
vledger audit-proof \ --data-dir ./vledger-data \ --commitment audit.json \ --sequence 406340 \ --output entry-406340-proof.json
CLI Reference

vledger verify-audit-package

Verify an audit package or entry proof. Requires no database access, no server, and no key files. Works on all three package types: commitment, entry_proof, and full.

vledger verify-audit-package --file <FILE>
Package typeWhat is verified
commitmentEd25519 root signature
entry_proofContent hash + chain hash + Merkle inclusion proof
fullAll of the above + all entries

Example output:

── VectorLedger Audit Verifier ───────────────── Type : entry_proof Entries : 1 [1/3] Content hash ✓ [2/3] Chain hash ✓ [3/3] Merkle proof ✓ ✓ VERIFIED — 1 entries, all checks passed. Merkle root : 804efb54ea31539a...

Auditors who cannot install software can download a pre-built binary from the GitHub release page (no Rust toolchain required):

curl --proto '=https' --tlsv1.2 -sSf \ https://raw.githubusercontent.com/pavondunbar/VectorLedger/main/install.sh | bash vledger verify-audit-package --file entry-406340-proof.json
CLI Reference

vledger compliance-report

Generate a SOC 2 or PCI-DSS compliance evidence report. Reports are generated by running checks against real filesystem state not pre-written text.

vledger compliance-report --data-dir <PATH> [--standard soc2|pci-dss] [--format markdown|json] [--output <FILE>] # Example generate a PCI-DSS report as Markdown: vledger compliance-report --data-dir ./vledger-data \ --standard pci-dss \ --format markdown \ --output pci-report.md
CLI Reference

vledger license

Show the active license tier, features, and expiry. Place the signed license.json file provided by VectorGuard Labs into your data directory to unlock paid features. If no file is present, the engine runs in Free tier.

vledger license --data-dir <PATH> # Example output (Growth tier): # ── VectorLedger License ──────────────────────── # Tier : Growth # Licensee : Acme Corp # Expires : 2027-08-07 # Status : Active (365 days remaining)
CLI Reference

vledger start-primary

Start a WAL replication primary listener. Requires a Growth or Enterprise license.

Tip: In most deployments you don't need this command. If replication.json exists with "role": "primary", vledger start automatically activates the WAL shipper. Use start-primary only when you want a shipper running without a SQL server.
vledger start-primary [OPTIONS] --data-dir <PATH> Data directory (default: ./vledger-data) --bind <ADDR> Bind address for the WAL shipper (overrides replication.json; default: 127.0.0.1:5434)

Reads <data-dir>/replication.json. If the file does not exist, a default config is written on first run. The primary auto-generates replication_secret.hex (mode 0o600) on first start — copy this file to every replica before starting them.

CLI Reference

vledger start-replica

Start a WAL replication replica. Requires a Growth or Enterprise license. The replication_secret.hex file must already be present (copied from the primary) before this command will succeed.

vledger start-replica [OPTIONS] --data-dir <PATH> Data directory (default: ./vledger-data) --primary <ADDR> Primary host:port to connect to (overrides replication.json)

The replica connects to the primary, performs the BLAKE3 HMAC handshake, and streams WAL records continuously. It reconnects automatically on disconnection with exponential backoff.

CLI Reference

vledger user

Manage user accounts. All subcommands read the user store directly from the data directory the server does not need to be running.

SubcommandDescription
set-passwordChange a user's password (revokes all active sessions)
createCreate a new user account
listList all accounts
set-enabledEnable or disable an account
deleteDelete an account
# Change a password vledger user set-password --data-dir <PATH> --username admin # Create a user (roles: admin, operator, auditor, readonly) vledger user create --data-dir <PATH> --username alice --role operator # List users vledger user list --data-dir <PATH> # Disable an account vledger user set-enabled --data-dir <PATH> --username alice --enabled false # Delete an account vledger user delete --data-dir <PATH> --username alice
Key Source Backends

Environment Variable

Best for containerised environments where secrets are injected as environment variables (Kubernetes Secrets, AWS ECS task definitions, etc.).

export VectorLedger_MASTER_KEY="$(openssl rand -hex 32)" vledger init --key-source env
Key Source Backends

HashiCorp Vault KV v2

VAULT_TOKEN must be set in the environment before starting the server. VectorLedger checks the token's TTL at startup and logs a warning if it expires within 24 hours.

# One-time setup: write the key to Vault vault kv put secret/vledger/master_key value="$(openssl rand -hex 32)" # Initialise with Vault backend export VAULT_TOKEN="<your-vault-token>" vledger init \ --key-source vault \ --vault-addr http://127.0.0.1:8200 \ --vault-mount secret \ --vault-path vledger/master_key
Key Source Backends

AWS KMS

On first start, VectorLedger calls GenerateDataKey and caches the encrypted ciphertext blob locally (kms_data_key.enc). On subsequent restarts, it calls Decrypt against the cached blob. The cache file is protected with an HMAC-SHA256 integrity check a tampered blob is detected before any network call.

export AWS_ACCESS_KEY_ID="..." export AWS_SECRET_ACCESS_KEY="..." # AWS_SESSION_TOKEN is also supported for temporary credentials vledger init \ --key-source aws_kms \ --kms-key-id "arn:aws:kms:us-east-1:123456789012:key/mrk-..." \ --kms-region us-east-1
Key Source Backends

File (development only)

Generates a random key and writes it to vledger-data/keys/master_key.hex with mode 0o600.

Not recommended for production. Move to Vault, AWS KMS, or PyHSM before deployment.
vledger init --key-source file

Key Backend Summary

Backend--key-sourceKey never on diskExternal dependency
PyHSM local Model 1pyhsmPyHSM daemon on same host
PyHSM remote Model 2remote-pyhsmPyHSM daemon on private subnet + TLS certs
Environment variableenv✗ (in env)None
Disk filefileNone
HashiCorp VaultvaultVault server + token
AWS KMSaws_kmsAWS credentials + KMS key
Key Source Backends

PyHSM (Recommended)

PyHSM is a VectorGuard Labs product and the default key backend for VectorLedger. The master encryption key is sealed inside PyHSM's AES-256-GCM-SIV encrypted keystore and never touches disk in plaintext.

Two ways to install PyHSM

Option A TypeScript daemon (required for VectorLedger integration)

This is what VectorLedger connects to. The daemon listens on a Unix domain socket and handles all key operations via IPC.

git clone https://github.com/pavondunbar/PyHSM.git ~/PyHSM cd ~/PyHSM/pyhsm-ts npm install # Start the daemon: PYHSM_MASTER_PASSWORD="<your-password>" \ PYHSM_KEYSTORE_PATH="$HOME/PyHSM/pyhsm-keystore.enc" \ PYHSM_AUDIT_LOG_PATH="$HOME/PyHSM/pyhsm-audit.jsonl" \ npx tsx ~/PyHSM/pyhsm-ts/process.ts

Option B Python CLI

The Python package installs the vectorguard-pyhsm command a key management CLI for generating, rotating, and inspecting keys. VectorLedger does not connect to it; use it to administer the keystore independently.

pip install vectorguard-pyhsm # List keys in the keystore vectorguard-pyhsm --store ~/PyHSM/pyhsm-keystore.enc list # Rotate the VectorLedger wrapping key vectorguard-pyhsm --store ~/PyHSM/pyhsm-keystore.enc rotate vledger.master-key # Verify audit log integrity vectorguard-pyhsm --store ~/PyHSM/pyhsm-keystore.enc audit --verify
Summary: Install both if you want the full toolkit. The TypeScript daemon is what VectorLedger needs to run. The Python CLI is what you use to administer the keystore.

PyHSM environment variables

VariableDefaultDescription
PYHSM_MASTER_PASSWORDRequired. Password that unlocks the PyHSM keystore.
PYHSM_KEYSTORE_PATH./pyhsm-keystore.encPath where the encrypted keystore is stored. Set this to a persistent, backed-up location.
PYHSM_AUDIT_LOG_PATH<keystore>.audit.jsonlPath for PyHSM's own tamper-evident audit log.
PYHSM_SOCKET_PATH/tmp/pyhsm.sockUnix socket path the daemon listens on (Model 1).
PYHSM_CALLER_SECRETOptional shared secret for IPC caller authentication.
PYHSM_RATE_LIMIT100Max operations per rate window.
PYHSM_RATE_WINDOW_MS60000Rate window in milliseconds.

How VectorLedger uses PyHSM across restarts

EventWhat happens
First vledger init --key-source pyhsmVectorLedger generates a 32-byte master key in-process, asks PyHSM to encrypt it, stores only the encrypted blob at vledger-data/keys/pyhsm_master_key.enc with an HMAC-SHA256 integrity seal.
Every vledger startHMAC verified locally first, blob sent to PyHSM for decryption, plaintext key used briefly for key derivation then immediately zeroized from memory.
PyHSM daemon not running at startupvledger start fails immediately with a clear error no data is touched.
Cache file tamperedHMAC check fails, startup aborts before any IPC call.
Remote PyHSM unreachable (Model 2)Startup fails closed VectorLedger never falls back to a weaker key source.
HSM Deployment

Model 1 Local PyHSM Same Server

PyHSM and VectorLedger run on the same host. Communication uses a Unix domain socket at /tmp/pyhsm.sock. Zero network overhead. Suitable for development, CI, and single-server production deployments.

┌──────────────────────────────┐ │ Server │ │ │ │ VectorLedger │ │ │ │ │ ▼ /tmp/pyhsm.sock │ │ PyHSM daemon │ └──────────────────────────────┘
vledger init --data-dir ./vledger-data --key-source pyhsm
HSM Deployment

Model 2 Remote PyHSM Separate Server

Implementation status Beta: The VectorLedger CLI fully supports --key-source remote-pyhsm and all associated flags. However, the default PyHSM TypeScript daemon (pyhsm-ts/process.ts) currently binds only to the local Unix socket (/tmp/pyhsm.sock) and does not expose an HTTPS/TLS listener out of the box. To run Model 2 in production you must supply the TLS environment variables documented in the setup guide (PYHSM_TLS_CERT, PYHSM_TLS_KEY, PYHSM_TLS_CA, PYHSM_LISTEN) and verify that PyHSM is responding on port 8443 before calling vledger init --key-source remote-pyhsm. Do not label a Remote PyHSM deployment production-ready until you have tested the full mTLS handshake end-to-end.

PyHSM runs on a dedicated server in the same region's private subnet. VectorLedger connects over TLS 1.3 with mutual certificate authentication (mTLS) on port 8443. PyHSM's private key material is never accessible from the VectorLedger host. Recommended for production.

Private subnet (e.g. AWS VPC) ┌────────────────────┐ ┌────────────────────┐ │ Server A │ │ Server B │ │ VectorLedger │──TLS 1.3 ──▶│ PyHSM daemon │ │ encrypted data │ + mTLS │ port 8443 │ │ WAL / application │ │ no public IP │ └────────────────────┘ └────────────────────┘

Model 2 environment variables

VariableDescription
PYHSM_ENDPOINTHTTPS endpoint of the remote PyHSM daemon
PYHSM_CA_CERTPath to the CA certificate PEM
PYHSM_CLIENT_CERTPath to the mTLS client certificate PEM
PYHSM_CLIENT_KEYPath to the mTLS client private key PEM
PYHSM_TIMEOUT_MSPer-request timeout in milliseconds
HSM Deployment

Model 2 Step-by-Step Setup

This section walks through every step required to connect VectorLedger on Server A to PyHSM running on Server B, on the same-region private subnet.

Prerequisites: Both servers are in the same AWS VPC (or equivalent private network). Server B has no public IP. openssl is available on whichever machine you generate certificates on.
1 Generate TLS certificates
# 1a. Create the Certificate Authority openssl genrsa -out ca.key 4096 openssl req -new -x509 -key ca.key -sha256 -days 3650 \ -subj "/CN=VectorLedger-PyHSM-CA" \ -out ca.crt # 1b. Create the PyHSM server certificate # Replace 10.0.1.50 with Server B's actual private IP. openssl genrsa -out pyhsm-server.key 4096 openssl req -new -key pyhsm-server.key \ -subj "/CN=pyhsm.internal" \ -out pyhsm-server.csr openssl x509 -req -in pyhsm-server.csr -CA ca.crt -CAkey ca.key \ -CAcreateserial -days 3650 -sha256 \ -extfile <(printf "subjectAltName=IP:10.0.1.50,DNS:pyhsm.internal") \ -out pyhsm-server.crt # 1c. Create the VectorLedger mTLS client certificate openssl genrsa -out vledger-client.key 4096 openssl req -new -key vledger-client.key \ -subj "/CN=vledger-client" \ -out vledger-client.csr openssl x509 -req -in vledger-client.csr -CA ca.crt -CAkey ca.key \ -CAcreateserial -days 90 -sha256 \ -out vledger-client.crt
90-day validity on the client cert is intentional. Short-lived client certificates limit the blast radius if the private key is ever exposed. Set a calendar reminder to rotate before expiry.
2 Distribute certificates
# On Server B (PyHSM server): sudo mkdir -p /etc/pyhsm/tls sudo cp ca.crt pyhsm-server.crt pyhsm-server.key /etc/pyhsm/tls/ sudo chmod 600 /etc/pyhsm/tls/pyhsm-server.key # On Server A (VectorLedger server): sudo mkdir -p /etc/vledger/pyhsm sudo cp ca.crt vledger-client.crt vledger-client.key /etc/vledger/pyhsm/ sudo chmod 600 /etc/vledger/pyhsm/vledger-client.key
3 Open the security group
FieldValue
TypeCustom TCP
Port8443
SourceServer A's security group ID (or its private IP /32)

No other inbound rule is needed on Server B. There must be no public IP on Server B and no 0.0.0.0/0 rule for port 8443.

4 Start PyHSM in TLS mode on Server B
PYHSM_MASTER_PASSWORD="<your-pyhsm-password>" \ PYHSM_KEYSTORE_PATH="/var/lib/pyhsm/pyhsm-keystore.enc" \ PYHSM_AUDIT_LOG_PATH="/var/log/pyhsm/pyhsm-audit.jsonl" \ PYHSM_TLS_CERT="/etc/pyhsm/tls/pyhsm-server.crt" \ PYHSM_TLS_KEY="/etc/pyhsm/tls/pyhsm-server.key" \ PYHSM_TLS_CA="/etc/pyhsm/tls/ca.crt" \ PYHSM_LISTEN="0.0.0.0:8443" \ npx tsx ~/PyHSM/pyhsm-ts/process.ts
5 Initialise VectorLedger with remote-pyhsm

Run on Server A. Replace 10.0.1.50 with Server B's private IP:

vledger init \ --data-dir ./vledger-data \ --key-source remote-pyhsm \ --pyhsm-endpoint https://10.0.1.50:8443 \ --pyhsm-ca-cert /etc/vledger/pyhsm/ca.crt \ --pyhsm-client-cert /etc/vledger/pyhsm/vledger-client.crt \ --pyhsm-client-key /etc/vledger/pyhsm/vledger-client.key

Verify key_source.json shows "backend": "remote_py_hsm". If it shows "env" or "file", the connection to PyHSM failed delete the data directory, confirm PyHSM is reachable, and re-run init.

6 Start VectorLedger
vledger start --data-dir ./vledger-data

On every start, VectorLedger connects to the remote PyHSM, decrypts the master key blob, uses the key briefly for derivation, and immediately zeroizes it. If PyHSM is unreachable, startup fails closed it never falls back to a weaker key source.

HSM Deployment

Rotating the Client Certificate

The mTLS client certificate should be rotated before it expires (90-day validity recommended). No data migration is required only the transport credential changes.

# 1. Generate a new client cert signed by the same CA openssl genrsa -out vledger-client-new.key 4096 openssl req -new -key vledger-client-new.key \ -subj "/CN=vledger-client" -out vledger-client-new.csr openssl x509 -req -in vledger-client-new.csr -CA ca.crt -CAkey ca.key \ -CAcreateserial -days 90 -sha256 -out vledger-client-new.crt # 2. Copy to Server A sudo cp vledger-client-new.crt /etc/vledger/pyhsm/vledger-client.crt sudo cp vledger-client-new.key /etc/vledger/pyhsm/vledger-client.key sudo chmod 600 /etc/vledger/pyhsm/vledger-client.key # 3. Restart VectorLedger to pick up the new certificate # (no vledger init needed key_source.json paths are unchanged)
HSM Deployment

Replay-Attack Prevention (Model 2)

Every request sent to a remote PyHSM over TLS includes two additional fields that PyHSM should validate:

FieldValuePurpose
requestIdUUID v4PyHSM rejects duplicate IDs within its replay window (recommended: 5 min)
timestampRFC 3339 UTCPyHSM rejects requests more than 2 minutes stale or in the future

These fields are injected automatically by VectorLedger — no configuration required. They are not present on Model 1 (local socket) requests, where replay is not a meaningful threat.

Note: Replay-attack prevention applies to Model 2 (remote PyHSM) only. Ensure PyHSM is configured to validate requestId uniqueness within a 5-minute window and reject stale timestamp values. This is also a requirement in the Production Deployment Checklist.
HSM Deployment

PyHSM on Windows

PyHSM uses TCP instead of a Unix socket on Windows. Start the daemon with PYHSM_TCP_PORT and pass the TCP address to VectorLedger:

$env:PYHSM_MASTER_PASSWORD = "your-password" $env:PYHSM_TCP_PORT = 7777 npx tsx ~/PyHSM/pyhsm-ts/process.ts # Init with Model 1 TCP loopback: vledger init --key-source pyhsm --pyhsm-socket 127.0.0.1:7777
Operations

Compliance Reporting

Important scope note: VectorLedger generates machine-generated technical evidence supporting SOC 2 and PCI-DSS control assessments. This evidence is a technical input to an audit — it does not by itself make an organization compliant. Organizational compliance requires additional policies, procedures, personnel controls, and independent auditor assessment.

SOC 2 Type II controls covered

CC6.1, CC6.2, CC6.3, CC6.6, CC6.7, CC7.2, CC8.1, A1.1

PCI-DSS v4 controls covered

Req 2.2, 3.4, 3.5, 4.2, 7.1, 10.2, 10.3, 10.5, 11.5

Generate reports

# SOC 2 report as Markdown vledger compliance-report --data-dir ./vledger-data \ --standard soc2 --format markdown --output soc2-report.md # PCI-DSS report as JSON vledger compliance-report --data-dir ./vledger-data \ --standard pci-dss --format json --output pci-report.json
Operations

Cryptographic Audit Package

VectorLedger can generate a portable, self-contained cryptographic audit evidence package that any third party can verify independently — no database access, no server, no credentials required.

Three-tier design:

Tier 1 — Commitment package (default, fast at any scale)

Computes the Merkle root over all entries in a single O(n) pass, signs it with the database Ed25519 key, and writes a compact JSON commitment. Completes in seconds regardless of ledger size.

vledger audit-package \ --data-dir ./vledger-data \ --tenant "Acme Financial" \ --description "Q3 2026 regulatory audit" \ --period-start 2026-07-01 \ --period-end 2026-09-30 \ --output audit-q3-2026.json

Tier 2 — On-demand entry proof (prove one specific entry)

Generates a single Merkle inclusion proof proving that a specific entry belongs to the committed root. The auditor receives a self-contained file they can verify without database access.

vledger audit-proof --data-dir ./vledger-data \ --commitment audit.json \ --sequence 406340 \ --output entry-proof.json

Tier 3 — Full export (small ledgers only)

Embeds all entries and per-entry proofs. Only practical for ledgers with fewer than ~10,000 entries.

vledger audit-package --data-dir ./vledger-data --include-entries

Verification (no database access required)

vledger verify-audit-package --file audit.json vledger verify-audit-package --file entry-proof.json

Example output:

[1/3] Content hash ✓ [2/3] Chain hash ✓ [3/3] Merkle proof ✓ ✓ VERIFIED — 1 entries, all checks passed. Merkle root : 804efb54ea31539a...

Auditors who cannot install software can use the pre-built binary from the GitHub release (no Rust toolchain required):

curl --proto '=https' --tlsv1.2 -sSf \ https://raw.githubusercontent.com/pavondunbar/VectorLedger/main/install.sh | bash vledger verify-audit-package --file entry-proof.json
SQL Reference

SQL Reference

VectorLedger supports a financial-ledger SQL dialect over both the native TLS connection (port 5433) and the PostgreSQL wire protocol (port 5432). It is PostgreSQL-compatible — not PostgreSQL — so standard PostgreSQL system catalog queries (\l, \dt, pg_catalog.*) are not supported.

Scan Safety — Default Row Cap

Unbounded full-table scans on a large ledger load all matching rows into memory before returning, which can exhaust server RAM. VectorLedger protects against this with an automatic 10,000-entry cap on non-point-lookup queries that have no explicit LIMIT.

Query patternBehaviour
SELECT * FROM ledger WHERE sequence = NNo cap — returns exactly 1 entry
SELECT * FROM ledger WHERE external_ref = 'X'No cap — point lookup
SELECT * FROM ledger LIMIT 500Exactly 500 rows — explicit limit honoured
SELECT * FROM ledgerCapped at 10,000 rows + pagination notice
SELECT * FROM ledger WHERE domain = 'x'Capped at 10,000 rows + pagination notice
SELECT * FROM ledger WHERE status = 'Posted'Capped at 10,000 rows + pagination notice

When the cap fires you will see: 10000 rows (capped at 10000 — use LIMIT n or WHERE sequence = x to paginate)

To page through a large dataset, use sequence-based pagination:

-- Page 1: entries 1 – 10,000 SELECT * FROM ledger LIMIT 10000; -- Page 2: entries 10,001 – 20,000 SELECT * FROM ledger WHERE sequence > 10000 LIMIT 10000; -- Page 3: entries 20,001 – 30,000 SELECT * FROM ledger WHERE sequence > 20000 LIMIT 10000;
SQL Reference

Tables

ledger — one row per journal entry

Columns: sequence, id, status, description, domain, effective_at, posted_at, external_ref, content_hash, chain_hash, lines

The lines column contains all debit and credit lines as a single semicolon-separated string. Use ledger_lines for the traditional accounting view.

-- Point lookups (no cap — always safe) SELECT * FROM ledger WHERE sequence = 406340; SELECT * FROM ledger WHERE external_ref = 'TXN-001'; -- Bounded queries (always safe) SELECT * FROM ledger LIMIT 100; SELECT * FROM ledger WHERE domain = 'main' LIMIT 500; -- Full scans (auto-capped at 10,000 — paginate for more) SELECT * FROM ledger; SELECT * FROM ledger WHERE status = 'Posted';

ledger_lines — one row per debit/credit line

Returns each debit and credit as its own row — the standard double-entry accounting view that accountants expect.

Columns: date, sequence, entry_id, description, domain, account_id, dr_cr, amount, currency, status

Amounts are displayed as decimals (e.g. 1.00) rather than minor units. The cap applies to the number of entries before line expansion — a cap of 10,000 entries yields up to 20,000 rows for a standard two-line transaction.

-- Point lookups (no cap — always safe) SELECT * FROM ledger_lines WHERE sequence = 406340; -- Bounded queries (always safe) SELECT * FROM ledger_lines LIMIT 100; SELECT * FROM ledger_lines WHERE domain = 'main' LIMIT 500; -- Full scans (auto-capped at 10,000 entries) SELECT * FROM ledger_lines; SELECT * FROM ledger_lines WHERE status = 'Posted';

Example output:

date | sequence | entry_id | description | domain | account_id | dr_cr | amount | currency | status -----------+----------+----------+------------------+--------+------------+--------+--------+----------+------- 2026-08-17 | 1 | <uuid> | Customer payment | main | <uuid> | Debit | 1.00 | USD | Posted 2026-08-17 | 1 | <uuid> | Customer payment | main | <uuid> | Credit | 1.00 | USD | Posted

accounts — chart of accounts

Columns: id, code, name, account_type, currency, status, domain, balance

SELECT * FROM accounts; SELECT * FROM accounts WHERE domain = 'main';
SQL Reference

Write Commands

Post a journal entry

INSERT INTO ledger (description, debit_account, credit_account, amount, currency, domain) VALUES ('Wire transfer', 'CASH', 'REVENUE', 100000, 'USD', 'main');
  • amount is in minor units (cents for USD — 100000 = $1,000.00)
  • debit_account and credit_account accept either account code or UUID
  • Optional fields: external_ref, idempotency_key
  • Entries are append-onlyUPDATE and DELETE are not supported

Create an account

INSERT INTO accounts (code, name, account_type, currency, domain) VALUES ('CASH', 'Cash - USD', 'asset', 'USD', 'main'); -- Account types: asset, liability, equity, income, expense

Post a correction (reversal)

Corrections are made by posting a new reversal entry, never by modifying the original:

INSERT INTO ledger (description, debit_account, credit_account, amount, currency, domain) VALUES ('Reversal of TXN-001', 'REVENUE', 'CASH', 100000, 'USD', 'main');
SQL Reference

Financial Functions

-- Account balance (returns minor units) SELECT BALANCE('CASH'); SELECT BALANCE('account-uuid-here'); -- Verify the entire BLAKE3 hash chain SELECT VERIFY_CHAIN(); -- Verify a range of entries SELECT VERIFY_CHAIN(1, 100000); SELECT VERIFY_CHAIN(1000000); -- Verify a single entry's content and chain hashes SELECT VERIFY_ENTRY(406340);
SQL Reference

Aggregates & Window Functions

SELECT COUNT(sequence) FROM ledger; SELECT SUM(amount) FROM ledger GROUP BY domain; SELECT AVG(amount) FROM ledger; SELECT MIN(sequence), MAX(sequence) FROM ledger; SELECT ROW_NUMBER() OVER () AS rn FROM ledger; SELECT RANK() OVER () FROM ledger;
SQL Reference

Joins

Supports INNER JOIN and LEFT OUTER JOIN.

SELECT * FROM ledger JOIN accounts ON ledger.domain = accounts.domain;
SQL Reference

Compatibility Queries

These queries are supported for ORM and connection pooler health checks:

SELECT 1; SELECT version(); SELECT current_user(); SELECT current_database();
SQL Reference

What Is Not Supported

OperationWhy
UPDATEAppend-only — entries are permanent
DELETEAppend-only — entries are permanent
CREATE TABLE / DROP TABLESchema is fixed
pg_catalog.* system tablesNot PostgreSQL internally
\l, \dt, \du psql meta-commandsRely on pg_catalog
Multiple databases or schemasSingle-database engine
Stored procedures, triggers, sequencesNot implemented
Operations

Replication

VectorLedger supports synchronous hot-standby WAL replication with three independent security layers: TLS 1.3, optional mTLS, and BLAKE3-keyed HMAC challenge-response inside TLS.

License requirement: WAL replication requires a Growth or Enterprise license. Running vledger start-primary or vledger start-replica on a Free or Starter license returns a feature-not-entitled error.

Additional integrity guarantees:

  • Replica verifies the BLAKE3 hash of every received WAL record before applying it — a tampered record is rejected and the connection closed.
  • Exponential reconnect backoff (500 ms → 30 s) with faster escalation on auth failures (max 60 s).
  • Replication secret generated from OsRng, stored at mode 0o600.
  • Divergence detection via periodic DivergenceCheckpoint messages carrying a rolling BLAKE3 WAL chain hash — a mismatch means the replica must be re-seeded.
Operations

Replication Setup

1 Start the WAL shipper on the primary
# Option A — integrated mode (recommended): # If replication.json is present with role=primary, vledger start # automatically activates the WAL shipper alongside the SQL server. vledger start --data-dir ./vledger-data # Option B — standalone shipper only (no SQL server): vledger start-primary --data-dir ./vledger-data # Override the bind address: vledger start-primary --data-dir ./vledger-data --bind 0.0.0.0:5434

The primary auto-generates a 32-byte HMAC secret at vledger-data/replication_secret.hex (mode 0o600) on first run. This secret must be copied to every replica.

2 Copy the secret to the replica
scp vledger-data/replication_secret.hex replica-host:/path/to/vledger-data/replication_secret.hex
3 Create replication.json on the replica
{ "role": "replica", "replication_addr": "<primary-host>:5434", "ack_timeout_ms": 5000, "heartbeat_interval_ms": 1000, "send_buffer_bytes": 67108864, "tls": { "enabled": true, "server_hostname": "vledger-primary", "ca_cert": "/path/to/replication-ca.pem" } }
Development note: For development on a single machine with a self-signed primary cert, set "ca_cert": null and build with the dev-insecure-replication Cargo feature. Do not do this in production.
4 Start the replica
# Start the replica (connects to primary and streams WAL continuously) vledger start-replica --data-dir ./vledger-data # Override the primary address vledger start-replica --data-dir ./vledger-data --primary <primary-host>:5434

The replica connects to the primary, performs the HMAC handshake, and begins streaming WAL records. It reconnects automatically on disconnection.

Operations

replication.json Reference

FieldDefaultDescription
role"primary""primary" or "replica"
replication_addr"127.0.0.1:5434"Primary: bind address. Replica: primary's host:port
ack_timeout_ms5000How long the primary waits for a replica ACK (ms)
heartbeat_interval_ms1000Heartbeat frequency (ms)
send_buffer_bytes67108864Max bytes buffered per replica connection (64 MiB)
secret_pathnullPath to HMAC secret; null = <data_dir>/replication_secret.hex
tls.enabledtrueEnable TLS on the replication channel
tls.server_certnullPrimary TLS cert PEM; null = auto-generate self-signed
tls.server_keynullPrimary TLS key PEM; null = auto-generate
tls.server_hostname"vledger-primary"SNI hostname used by replica to verify the primary's cert
tls.ca_certnullReplica: CA cert PEM to verify the primary. Required in production
tls.client_certnullReplica client cert PEM for mTLS (optional)
tls.client_keynullReplica client key PEM for mTLS (optional)
Operations

Replication Limitations

Failover promotion is manual. There is no automatic primary election. If the primary crashes, an operator must explicitly reconfigure a replica as the new primary. Automated promotion via consensus (Raft/Paxos) is planned but not yet implemented.
Split-brain prevention is network-layer only. VectorLedger relies on the operator's network segmentation (e.g. AWS security groups, private subnets) to prevent two nodes from simultaneously acting as primary. There is no fencing or STONITH mechanism in the replication layer itself.
Replica lag is observable but not bounded. Heartbeat ACKs carry the replica's last applied LSN, allowing the primary to compute lag. There is no automatic write-pause when lag exceeds a threshold.

These limitations do not affect the security properties of the WAL integrity or tamper-evidence guarantees.

Operations

Client SDKs

Native client libraries are included for three languages, all located under clients/ in the repository.

LanguageLocation
Pythonclients/python/
TypeScript / Node.jsclients/typescript/
Goclients/go/

Any standard PostgreSQL client library can also connect to the PostgreSQL wire-protocol listener on port 5432 when the server is started with --pgwire (Starter tier and above).

Operations

Licensing

VectorLedger uses a tiered license model. The binary enforces feature availability at startup by verifying a signed license.json file in your data directory. If no license file is present, the engine runs in Free tier mode.

Licenses are issued by VectorGuard Labs. Purchase at vectorguardlabs.com/pricing — your license.json is generated automatically and delivered to the email on your account.

Pricing

TierPriceBest for
Free$0 / monthDevelopment, evaluation, internal tools
Starter$199 / monthEarly-stage teams that need PostgreSQL client compatibility
Growth$999 / monthProduction fintechs and SaaS companies under SOC 2 or PCI-DSS
EnterpriseContact SalesBanks, payment processors, PCI-DSS Level 1, hardware HSM requirements

Annual billing available on all paid tiers — pay for 10 months, get 12. Contact sales@vectorguardlabs.com for multi-instance or custom pricing.

Installing a license

cp acme-license.json ./vledger-data/license.json vledger license --data-dir ./vledger-data

Feature availability by tier

FeatureFreeStarterGrowthEnterprise
Core ledger + SQL REPL
AES-256-GCM encryption at rest
BLAKE3 hash chain + Merkle proofs
Four-eyes dual-control workflow
WORM audit log + chain verification
Backup & restore
Audit log export (date range)30 days90 daysUnlimitedUnlimited
PostgreSQL wire protocol (--pgwire)
WAL replication (hot standby)
Compliance reports (SOC 2 / PCI-DSS)
Hardware HSM PKCS#11 integration
Multi-node deployment

Annual billing available on all paid tiers pay for 10 months, get 12. Contact sales@vectorguardlabs.com for multi-instance or custom pricing.

Operations

Production Deployment Checklist

Before putting VectorLedger in front of production traffic:

Core setup

  • PyHSM daemon running with a persistent, backed-up keystore (PYHSM_KEYSTORE_PATH points to a durable location)
  • vledger init completed with --key-source pyhsm (Model 1) or --key-source remote-pyhsm (Model 2)
  • key_source.json shows "backend": "py_hsm" or "backend": "remote_py_hsm" not "env" or "file"
  • keys/MASTER_KEY_PLACEHOLDER.txt deleted (its presence fails the PCI-DSS compliance check)
  • Admin credential file read, password changed, and catalog/.admin_initial_credentials deleted
  • Data directory permissions locked (chmod 700 on vledger-data/ and all subdirectories)
  • Volume encryption enabled on the disk hosting vledger-data/
  • Replace the self-signed TLS certificate with a CA-signed one pass --tls-cert-path and --tls-key-path at startup
  • Configure replication with a secondary node (replication.json) or document a backup-based HA strategy
  • Install a valid license.json for your paid tier (vledger license --data-dir ./vledger-data to confirm)
  • Test a full backup and restore drill: vledger backupvledger restorevledger verify
  • Schedule regular vledger backup runs (cron or your orchestrator)
  • Schedule regular vledger verify runs (recommended: after each backup)
  • Ship audit/audit.log to an append-only off-host destination in real time
  • Run compliance reports and confirm zero FAIL items: vledger compliance-report --standard pci-dss

Additional items for Model 2 (remote PyHSM)

  • PyHSM server has no public IP accessible only via private subnet
  • Security group / firewall allows VectorLedger → PyHSM (port 8443) only no other inbound
  • CA certificate, client certificate, and client key stored at paths that survive reboots and are mode 0600
  • PYHSM_CA_CERT, PYHSM_CLIENT_CERT, PYHSM_CLIENT_KEY environment variables set (or paths baked into key_source.json)
  • mTLS client certificate has a short validity period (90 days recommended) with a rotation schedule
  • PyHSM configured to validate requestId (reject duplicates within 5-minute window) and reject stale timestamp values
  • Verified that vledger start fails cleanly when PyHSM is unreachable never falls back silently
Operations

Testing & Verification

This section documents every testing mechanism available in VectorLedger. Run these commands on your own server to independently verify correctness, durability, and tamper-evidence before deploying to production.

Testing

Prerequisites

Start the server before running any tests that require a live connection:

export VectorLedger_MASTER_KEY=$(openssl rand -hex 32) ./target/release/vledger init --key-source env cat vledger-data/catalog/.admin_initial_credentials # note the generated password ./target/release/vledger start --max-connections 200 --pgwire & sleep 5
Testing

1. Benchmark Tests (TPS)

Measures transactions per second across INSERT, SELECT, and mixed workloads. Restart the server between each workload run to clear connection state.

# INSERT workload - write heavy cargo run --release --package vledger-bench -- \ --username admin --password <password> \ --clients 50 --transactions 5000 --workload insert
# Restart between runs pkill vledger && sleep 2 && ./target/release/vledger start --max-connections 200 --pgwire & sleep 5
# SELECT workload - read heavy cargo run --release --package vledger-bench -- \ --username admin --password <password> \ --clients 50 --transactions 5000 --workload select
# MIXED workload - 70% INSERT, 30% SELECT cargo run --release --package vledger-bench -- \ --username admin --password <password> \ --clients 50 --transactions 5000 --workload mixed
Recommended instance: AWS c7g.xlarge (Graviton3, 4 vCPU, 8 GB RAM). Avoid t3/t4g burstable instances - CPU credit throttling produces misleading results.
Testing

2. PostgreSQL Wire Protocol Compatibility

Verifies that VectorLedger accepts connections from standard PostgreSQL clients. Start the server with --pgwire and connect with psql:

psql "host=127.0.0.1 port=5432 user=admin dbname=vledger sslmode=require"
SELECT 1; SELECT version(); SELECT current_user(); SELECT current_database(); SHOW server_version; SHOW server_encoding; SHOW TimeZone; SELECT COUNT(*) FROM ledger; SELECT * FROM ledger LIMIT 5; SELECT * FROM accounts LIMIT 5; SELECT VERIFY_CHAIN(); BEGIN; SELECT COUNT(*) FROM ledger; COMMIT;
Testing

3. Concurrent Transaction Test

Runs the benchmark in one terminal while querying live from another to confirm no torn reads, duplicate sequences, or chain failures under concurrent load.

Terminal 1

cargo run --release --package vledger-bench -- \ --username admin --password <password> \ --clients 10 --transactions 1000 --workload mixed &

Terminal 2 (while benchmark runs)

psql "host=127.0.0.1 port=5432 user=admin dbname=vledger sslmode=require"
SELECT COUNT(*) FROM ledger; SELECT COUNT(DISTINCT sequence) FROM ledger; -- must equal COUNT(*) SELECT VERIFY_CHAIN(); -- must return OK
Testing

4. WAL Corruption Test

Confirms that VectorLedger detects and rejects corrupted WAL data during recovery.

pkill vledger && sleep 2 # Corrupt a byte in the last WAL segment python3 -c " import os wal_dir = 'vledger-data/wal' segments = sorted(os.listdir(wal_dir)) target = os.path.join(wal_dir, segments[-1]) size = os.path.getsize(target) mid = size // 2 with open(target, 'r+b') as f: f.seek(mid) b = f.read(1) f.seek(-1, 1) f.write(bytes([b[0] ^ 0xFF])) print('WAL corruption written at offset', mid) " # Attempt restart - server will reject or truncate at the corrupt record nohup ./target/release/vledger start --max-connections 200 --pgwire & sleep 10 cat nohup.out | tail -10 # Restore the WAL (XOR with 0xFF again to flip back) python3 -c " import os wal_dir = 'vledger-data/wal' segments = sorted(os.listdir(wal_dir)) target = os.path.join(wal_dir, segments[-1]) size = os.path.getsize(target) mid = size // 2 with open(target, 'r+b') as f: f.seek(mid) b = f.read(1) f.seek(-1, 1) f.write(bytes([b[0] ^ 0xFF])) print('WAL restored at offset', mid) " # Restart and verify full recovery pkill vledger && sleep 2 nohup ./target/release/vledger start --max-connections 200 --pgwire & sleep 120 # wait for WAL replay psql "host=127.0.0.1 port=5432 user=admin dbname=vledger sslmode=require"
SELECT COUNT(*) FROM ledger; SELECT VERIFY_CHAIN();
Testing

5. Logical Tampering Test

Confirms that the BLAKE3 hash chain detects in-memory data manipulation. TAMPER_ENTRY mutates an entry's description without updating its hash - simulating what a malicious actor would need to do to falsify a record.

-- Establish baseline SELECT VERIFY_CHAIN(); -- Tamper with a specific entry SELECT TAMPER_ENTRY(999999, 'THIS RECORD HAS BEEN FALSIFIED'); -- Hash chain must now detect the mutation SELECT VERIFY_CHAIN(); -- Expected: ERROR: INTEGRITY FAILURE: Hash chain broken at sequence 999999 -- Confirm the specific entry is marked corrupted SELECT VERIFY_ENTRY(999999); -- Expected: status = CORRUPTED
Testing

6. Crash / Restart Recovery Test

Confirms that committed transactions survive a hard kill mid-write and that uncommitted transactions are rolled back cleanly.

# Start benchmark in background cargo run --release --package vledger-bench -- \ --username admin --password <password> \ --clients 10 --transactions 10000 --workload insert & BENCH_PID=$! # Hard-kill the server while writes are in flight sleep 10 kill -9 $(pgrep -f "vledger start") echo "Server killed mid-write" wait $BENCH_PID # Restart - WAL replay recovers all committed transactions nohup ./target/release/vledger start --max-connections 200 --pgwire & sleep 120 psql "host=127.0.0.1 port=5432 user=admin dbname=vledger sslmode=require"
SELECT COUNT(*) FROM ledger; SELECT VERIFY_CHAIN(); -- Chain must be OK. Some in-flight transactions may be missing (expected). -- All committed transactions must be present and valid.
Testing

7. Integrity Self-Test Suite

The built-in self-test runs five automated phases against a completely isolated temporary database. Your production data is never touched.

# Quick smoke test - 1K entries, instant ./target/release/vledger verify --self-test --entries 1000 2>/dev/null # Dev run - 10K entries, ~5 seconds ./target/release/vledger verify --self-test --entries 10000 2>/dev/null # Standard - 100K entries, ~30-60 seconds (default) ./target/release/vledger verify --self-test 2>/dev/null # Enterprise stress test - 1M entries, ~10-15 minutes ./target/release/vledger verify --self-test --entries 1000000 2>/dev/null # Keep the test database for manual inspection ./target/release/vledger verify --self-test --entries 10000 --keep-data 2>/dev/null

What the self-test verifies

PhaseWhat it tests
A - BaselineInserts N deterministic entries with varied amounts, verifies the hash chain immediately
B - WAL IntegrityCorrupts a WAL byte, confirms server detects and rejects it, restores the byte
C - Crash RecoveryReopens the database, confirms 100% of entries recovered with chain intact
D - Logical IntegrityMutates an entry in memory without updating its hash, confirms VERIFY_CHAIN() detects it
E - Entry VerificationSpot-checks five entries spread across the ledger with VERIFY_ENTRY()

Inspecting the self-test database manually

# Run with --keep-data to retain the database after the test ./target/release/vledger verify --self-test --entries 10000 --keep-data 2>/dev/null # Note the directory printed at the end, then start a server against it cat /path/to/vledger-self-test-<timestamp>/catalog/.admin_initial_credentials ./target/release/vledger start \ --data-dir /path/to/vledger-self-test-<timestamp> \ --max-connections 10 --pgwire & sleep 5 psql "host=127.0.0.1 port=5432 user=admin dbname=vledger sslmode=require"
SELECT COUNT(*) FROM ledger; SELECT VERIFY_CHAIN(); SELECT VERIFY_ENTRY(1); SELECT VERIFY_ENTRY(5000); SELECT VERIFY_ENTRY(10000); \x SELECT * FROM ledger ORDER BY sequence LIMIT 10; SELECT * FROM ledger WHERE sequence = 5000; SELECT * FROM ledger_lines WHERE sequence = 5000;
Testing

8. Chain Range Verification

Verify a specific range of entries rather than the full chain:

-- Verify entries 1 through 100,000 SELECT VERIFY_CHAIN(1, 100000); -- Verify from 1,000,000 to end SELECT VERIFY_CHAIN(1000000); -- Verify the full chain SELECT VERIFY_CHAIN();
Testing

9. Direct SQL Queries (without psql)

Query the production database directly from the terminal without starting psql:

./target/release/vledger sql --query "SELECT COUNT(*) FROM ledger" ./target/release/vledger sql --query "SELECT VERIFY_CHAIN()" ./target/release/vledger sql --query "SELECT * FROM ledger WHERE sequence = 500000" ./target/release/vledger sql --query "SELECT VERIFY_ENTRY(500000)" ./target/release/vledger sql --query "SELECT VERIFY_CHAIN(1000000, 1100000)" ./target/release/vledger sql --query "SELECT * FROM ledger LIMIT 10" ./target/release/vledger sql --query "SELECT * FROM ledger_lines WHERE sequence = 500000"

Each vledger sql invocation opens a fresh TLS connection, authenticates, runs the query, and closes. There is no persistent session between calls, so credentials are prompted every time by default.

To avoid retyping credentials on every query, set environment variables:

export VLEDGER_CLI_USERNAME=admin export VLEDGER_CLI_PASSWORD=<your-password> # All subsequent queries run without prompting ./target/release/vledger sql --query "SELECT COUNT(*) FROM ledger" ./target/release/vledger sql --query "SELECT VERIFY_CHAIN()" ./target/release/vledger sql --query "SELECT VERIFY_ENTRY(500000)"

Alternatively, use psql for an interactive session - authenticate once and run as many queries as you want:

psql "host=127.0.0.1 port=5432 user=admin dbname=vledger sslmode=require" # Type \q to exit
Testing

10. Audit Package (Cryptographic Evidence Export)

Generate a portable audit evidence package and verify it independently — no database access required for verification.

# Generate commitment (fast — works at any scale) ./target/release/vledger audit-package \ --data-dir ./vledger-data \ --output audit-commitment.json # Prove a specific entry to an auditor ./target/release/vledger audit-proof \ --data-dir ./vledger-data \ --commitment audit-commitment.json \ --sequence 500000 \ --output entry-500000-proof.json # Verify — no database access required ./target/release/vledger verify-audit-package --file audit-commitment.json ./target/release/vledger verify-audit-package --file entry-500000-proof.json
Testing

Running the Test Suite

# Unit and integration tests cargo test --workspace # Self-test (exercises the full engine end-to-end) cargo run --release -- self-test cargo run --release -- self-test-phase3 # Security audit (checks for known vulnerabilities in dependencies) cargo install cargo-audit cargo audit
Reference

WAL Sync Modes

VectorLedger ships with three WAL sync modes selectable at startup.

ModeDurabilityTypical Use
group_commitUp to one flush window of data loss on hard crashDefault recommended for most deployments
per_recordZero data loss every write fsynced immediatelyStrict regulatory environments
no_syncNoneDevelopment and CI only
# Default group commit (2 ms flush interval) vledger start # Per-record fsync (safest, lower TPS) vledger start --wal-sync-mode per_record # Tune the flush interval vledger start --wal-sync-mode group_commit --group-commit-delay-ms 5
Reference

Connection Resource Controls

Per-listener resource controls to prevent resource exhaustion:

ControlNative (5433)PgWire (5432)
max_connections semaphore12864
Per-IP token bucket (burst=10, refill=2/s)YesYes
Auth timeout30 s30 s
Idle timeout5 min5 min
Request frame size limit4 MiB16 MiB
Graceful shutdown drainYesYes
Status & Performance

Production Status

This matrix reflects the current tested and verified state of each capability. Status labels are conservative a capability is only marked Production after it has been exercised under real or realistic load with the full mTLS / key pipeline in place.

Capability Status Notes
Core ledger (append, query, BLAKE3 chain) Production Exercised by full self-test suite and phase3 tests
WAL (group commit + per-record fsync) Production Both sync modes stable; no_sync is dev/CI only
Cryptographic integrity (AES-256-GCM, Merkle proofs) Production All proofs verified by vledger verify and self-test
Local PyHSM (Model 1 Unix socket) Production Default configuration; recommended for single-server deployments
Remote PyHSM (Model 2 TLS 1.3 + mTLS) Beta CLI flags fully implemented. process.ts TLS listener requires manual env-var configuration verify end-to-end before calling production-ready. See Model 2 warning.
PostgreSQL wire protocol (--pgwire) Production Compatible with psql, standard PG drivers, and ORMs
AWS KMS Supported GenerateDataKey + Decrypt with HMAC-protected local cache
HashiCorp Vault KV v2 Supported Token-based auth; TTL expiry warning at startup
WAL replication (hot standby) Beta Failover is manual; no automatic primary election. See Replication limitations.
Compliance evidence (SOC 2 / PCI-DSS) Technical evidence Machine-generated evidence supporting control assessments not a substitute for full audit. See scope note.
Status definitions: Production tested, stable, and safe to operate under production load. Beta functional but with known limitations or incomplete test coverage; treat with care. Supported the integration is implemented and working; operational maturity depends on your external service. Technical evidence output is real and machine-generated, but audit scope extends beyond the database engine.
Status & Performance

Performance Benchmarks

Benchmarked on Apple Silicon (MacBook, macOS) running in group_commit WAL mode with a mixed read/write workload (10 concurrent clients, 1,000 transactions each, 70% INSERT / 30% SELECT):

MetricValue
Throughput430 TPS
Min latency311 µs
p50 latency23 ms
p95 latency36 ms
p99 latency42 ms
Errors0 / 10,000
Do not use the 430 TPS figure for production capacity planning. It was measured on a single MacBook with 10 concurrent clients. Server-class NVMe storage, higher concurrency, and network-attached clients will produce materially different numbers — in both directions depending on workload shape. These numbers represent a conservative baseline on development hardware. Production performance has not yet been independently characterized on server-class hardware. The primary bottleneck in the write path is fsync latency, which varies significantly between storage devices and operating systems.

Benchmark environment matrix

The table below will be populated as benchmarks are run on server-class hardware. Contributions and independent reproduction are welcome. All measurements use --wal-sync-mode group_commit (default) with a 70% INSERT / 30% SELECT mixed workload unless noted otherwise.

EnvironmentStorageConcurrencyTPSp50p95p99
Apple M-series (dev baseline)NVMe1043023 ms36 ms42 ms
AWS Graviton3 (c7g)EBS gp310
AWS Graviton3 (c7g)EBS gp3100
AWS x86 (c6i)EBS gp310
AWS x86 (c6i)EBS gp3100
AWS x86 (c6i)EBS gp31,000
Bare metalNVMe100
Bare metalNVMe1,000
Want to run your own benchmarks? Once results are published, the exact test script and methodology will be linked here so you can reproduce the numbers on your own hardware.
Reference

Built With

ComponentLibrary
Async runtimetokio
Symmetric encryptionaes-gcm (AES-256-GCM)
Hashingblake3
Signinged25519-dalek
Key derivationhkdf
Password hashingargon2
TLSrustls
SQL parsingsqlparser
Secret managementreqwest (Vault / AWS KMS)