Skip to main content

Vaulteq

CI PyPI License: MIT

The double-entry ledger you pip install — not one you sign up for.

Finance is supposed to be boring. Most "AI-native" financial tooling isn't — it lets an LLM touch the arithmetic, which means it can be confidently wrong. Vaulteq is the boring part on purpose: a deterministic, rule-enforcing ledger that an agent calls instead of calculates. Debits and credits match, or it refuses the post. No exceptions, no vibes.

Status: Alpha. Core engine and concurrency safety are tested (see below) — including a bug the tests themselves caught and fixed. Not independently audited. See Disclaimer before using this for anything involving real money.

Install

pip install vaulteq          # core library — zero dependencies
pip install "vaulteq[mcp]"   # + MCP server, for agent tool-calling

Why this and not TigerBeetle / Modern Treasury / Formance?

They're real, capable systems — for a different buyer. TigerBeetle is a standalone database server built for massive throughput; Modern Treasury and Formance are hosted platforms with contracts and onboarding. Vaulteq is for the developer who wants correctness now, in a script or an agent, with no server to run and nothing to sign up for. Smaller scope, on purpose.

Use it as a library

from vaulteq import LedgerEngine, PostRequest, JournalLineInput, Direction, AccountType

engine = LedgerEngine("mybook.db")   # or LedgerEngine() for in-memory
org = engine.create_organization("Acme Corp", base_currency="USD")
engine.create_account(org, "1001", "Cash", AccountType.ASSET, Direction.DEBIT)
engine.create_account(org, "4000", "Revenue", AccountType.REVENUE, Direction.CREDIT)

resp = engine.post(PostRequest(
    organization_id=org, idempotency_key="order_123",
    lines=[
        JournalLineInput("1001", Direction.DEBIT, 5000, "USD"),
        JournalLineInput("4000", Direction.CREDIT, 5000, "USD"),
    ]
))

Use it as an MCP server

vaulteq-mcp                                  # in-memory ledger
VAULTEQ_DB_PATH=./mybook.db vaulteq-mcp      # persistent ledger

Exposes vaulteq_create_organization, vaulteq_create_account, vaulteq_post, vaulteq_trial_balance, vaulteq_get_audit_trail, and vaulteq_verify_audit_chain as tools any MCP-compatible agent can call directly — point Claude or another agent at it and it can post a balanced journal entry without ever touching the math.

What's inside

File Purpose
vaulteq/schema.sql SQL schema, bundled as package data. Integer minor units. Hash-chained audit trail. payload_hash + UNIQUE(org, idempotency_key) for real idempotency.
vaulteq/engine.py Core engine. Zero dependencies. Explicit transaction control (isolation_level=None). Atomic idempotency with safe-retry and conflict paths. Full error taxonomy.
vaulteq/mcp_server.py Optional MCP server wrapping the engine as agent-callable tools. Only import path that needs the mcp extra.
tests/test_race.py Concurrent stress test. Two threads, two connections, same DB. Verifies exactly one journal entry lands and no raw IntegrityError leaks.

Design decisions

  • Amounts are integer minor units (BIGINT, cents) — never floats, never unconstrained decimals in storage.
  • Audit events are hash-chained (prev_event_hash → SHA-256 of previous event). This makes deleting or altering a mid-chain event detectable via verify_audit_chain() — it is not a substitute for append-only storage or an external anchor, and does not protect against someone with full write access wiping the entire audit table or truncating the most recent event. See Disclaimer.
  • Idempotency is real, not cosmetic:
    • Same key + same payload → returns cached PostResponse (safe retry)
    • Same key + different payload → DUPLICATE_IDEMPOTENCY_KEY conflict
    • Check is atomic under BEGIN IMMEDIATE with explicit transaction control (isolation_level=None)
    • Belt-and-suspenders: IntegrityError from the UNIQUE constraint is caught and resolved into proper retry or conflict
  • Error taxonomy is explicit and complete:
Code Meaning
ORGANIZATION_NOT_FOUND Referenced org doesn't exist
INVALID_JOURNAL Fewer than 2 lines, or other structural violation
UNBALANCED_JOURNAL Debits ≠ credits
ACCOUNT_NOT_FOUND Referenced account_code doesn't exist for this org
ACCOUNT_INACTIVE Account exists but is closed/inactive
DUPLICATE_IDEMPOTENCY_KEY Key already used with a different payload
CURRENCY_MISMATCH Line currency has no registered fx_rate to base_currency
PERIOD_CLOSED Attempted post to a closed accounting period (deferred)

Run the tests

python -m pytest tests/test_engine.py -v   # 16 behavioral tests — balance rules,
                                            # idempotency, audit tamper-detection
python tests/test_race.py                  # concurrent idempotency + race safety

Zero dependencies for the core library. Uses Python stdlib + SQLite.

Why the race test proves what it proves

race_test.py uses separate sqlite3 connections per thread (not a shared connection, which would serialize through Python's GIL and mask real cross-connection races) and a threading.Barrier to force both threads into post() at the same instant rather than hoping for a scheduling accident. It asserts the invariant that matters: exactly one journal entry in the database, both threads returning the same journal ID, and zero raw IntegrityError exceptions leaking to the caller. This is a legitimate concurrency test, not a token one.

What works now

  • Organization & Chart of Accounts management
  • Double-entry journal posting with strict balance validation
  • Real idempotency — safe retries return cached responses, conflicts are explicit
  • Atomic idempotency under SQLite reserved lock with explicit transaction control
  • Race-safety verified — concurrent threads with same key produce exactly one journal entry
  • Tamper-evident, hash-chained audit trail (deletion of a mid-chain event is detectable — not the same guarantee as append-only/immutable storage; see Disclaimer)
  • Trial balance query
  • Audit chain integrity verification
  • Complete error taxonomy with structured JSON responses

What's intentionally deferred

  • Multi-currency FX rates (MVP enforces base-currency only)
  • Period close / lock
  • Journal reversals
  • HTTP API layer (FastAPI wrapper)
  • Concurrent post safety at scale (SQLite serializes; prod needs row-level locking in Postgres)
  • Postgres migration (swap connection string, schema is compatible)

The one thing to remember

This is infrastructure, not a fintech. Your first customer is a developer building an AI agent that needs to post a journal without hallucinating the math.

License

MIT — see LICENSE.

Disclaimer

Vaulteq is alpha software. It has not been independently audited or reviewed by a security or accounting professional. The double-entry and concurrency guarantees described in this README have been verified with the tests in this repo, under the specific conditions those tests exercise (SQLite, single-process, the scenarios in tests/) — they have not been verified in production or at scale.

If you use Vaulteq for anything involving real money, you are responsible for your own testing, review, and risk assessment. It is provided "as is," without warranty of any kind, as stated in the LICENSE. In particular:

  • The audit trail is tamper-evident, not immutable — see the note under Design Decisions above.
  • Multi-currency, period close, and journal reversals are not implemented.
  • Concurrency safety has been tested against SQLite specifically; it has not been tested under Postgres or at production scale.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

vaulteq-1.1.1.tar.gz (19.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

vaulteq-1.1.1-py3-none-any.whl (14.4 kB view details)

Uploaded Python 3

File details

Details for the file vaulteq-1.1.1.tar.gz.

File metadata

  • Download URL: vaulteq-1.1.1.tar.gz
  • Upload date:
  • Size: 19.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.3

File hashes

Hashes for vaulteq-1.1.1.tar.gz
Algorithm Hash digest
SHA256 7632c3b5657d2deb798b25f3e6e926fa98442128e6ceb90026b4a684c5f9d161
MD5 13e72fe11b27c9651db3c01c3f931b0c
BLAKE2b-256 885dc98cf5f4dfc53baaf887d3646a064013c338199e5532036a0529ecf72ec8

See more details on using hashes here.

File details

Details for the file vaulteq-1.1.1-py3-none-any.whl.

File metadata

  • Download URL: vaulteq-1.1.1-py3-none-any.whl
  • Upload date:
  • Size: 14.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.3

File hashes

Hashes for vaulteq-1.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 f5c8aaea1a898ff4d54c576d594d070dc0f3f2e4d8a167941d9b43c9646aac64
MD5 bc201f3b71ce99e3e3265fd5568a254a
BLAKE2b-256 c33b8fddaa89d3b3dbaedf27e1278f141afed4785334a7d7bf696c0830e18635

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page