Metadata-Version: 2.4
Name: vaulteq
Version: 1.1.1
Summary: Zero-infra, embeddable, agent-native double-entry ledger. pip install it — don't sign up for it.
Author: Basie Pharedi
License: MIT
Keywords: ledger,accounting,double-entry,ai-agents,mcp,fintech
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: mcp
Requires-Dist: mcp>=1.0.0; extra == "mcp"
Provides-Extra: dev
Requires-Dist: pytest; extra == "dev"
Dynamic: license-file

# Vaulteq

[![CI](https://github.com/christianpharedi-boop/vaulteq/actions/workflows/ci.yml/badge.svg)](https://github.com/christianpharedi-boop/vaulteq/actions/workflows/ci.yml)
[![PyPI](https://img.shields.io/pypi/v/vaulteq)](https://pypi.org/project/vaulteq/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/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](#disclaimer) before using this for anything involving real money.

## Install

```bash
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

```python
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

```bash
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](#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

```bash
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

- [x] Organization & Chart of Accounts management
- [x] Double-entry journal posting with strict balance validation
- [x] **Real idempotency** — safe retries return cached responses, conflicts are explicit
- [x] **Atomic idempotency** under SQLite reserved lock with explicit transaction control
- [x] **Race-safety verified** — concurrent threads with same key produce exactly one journal entry
- [x] **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)
- [x] Trial balance query
- [x] Audit chain integrity verification
- [x] 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](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](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.
