accounting-mapping.md 9.1 KB

Accounting Mapping: Classical Double-Entry ↔ Kuatia

Kuatia provides double-entry-style safety using a UTXO-style model. Value is held as signed postings, and every committed transfer must satisfy per-asset conservation. The accounting goal is the same as classical bookkeeping; the mechanical model is different:

Classical double-entry Kuatia
Σ debits = Σ credits sum(consumed) == sum(created) per asset

This page maps classical accounting vocabulary onto Kuatia's types and clears up the terms that are easy to conflate.

The most important correction: in classical accounting a journal is the append-only book of original entry, while a journal entry is one committed accounting event. In Kuatia, the closest equivalent to the classical journal is the transfer log; the closest equivalent to a journal entry is a committed Transfer/Envelope. Kuatia's Book is neither. It is a transfer policy scope, not the accounting journal.

Core mapping

Classical accounting Kuatia Notes
Journal (book of original entry) Transfer log, TransferStore of EnvelopeRecords Append-only, ordered source of truth for committed transfers.
Journal entry (one balanced event) Committed Transfer (intent) → Envelope (resolved) One atomic accounting event.
Compound journal entry Transfer with multiple Movements One event touching many accounts/assets.
Entry line / leg Posting effect (often derived from a Movement) A concrete account-level value fragment. A Movement is two-sided intent {from, to, asset, amount} that resolves into consumed/created postings, not a 1:1 debit/credit line.
Σ debits = Σ credits Per-asset conservation sum(consumed) == sum(created) Enforced in validate_and_plan; ConservationViolation otherwise.
Ledger (accounts + running balances) Accounts + active postings Balances are projections over Active postings, never stored.
Posting a transaction (the verb) resolve + commit (TransferEnvelope → apply) Confusing collision: in Kuatia a posting is a noun (a value fragment), not the act of recording.
Accounting book no direct equivalent unless modeled separately Kuatia Book is not this.
Transfer policy scope Book Gates which accounts/assets may participate. See below.

A journal entry is one committed accounting event. In many accounting texts this event is also called a transaction, a word that is overloaded in a ledger library (database transaction, business transaction, atomic transfer), so this doc prefers "committed accounting event."

A journal entry is multi-account

Double-entry entries are inherently multi-account; that is the entire point. A minimal entry has two legs; a compound entry has more. So a Kuatia transfer touching many accounts is not a mismatch. It is a (compound) journal entry.

Classical compound journal entry:

2026-06-26  Cash sale of goods
  Dr  Cash ................. 115
      Cr  Revenue ..........     100
      Cr  Sales tax payable .      15

The equivalent business effects as a Kuatia transfer, multiple movements committed atomically:

let transfer = TransferBuilder::new()
    .book(sales_book)
    .pay(customer, revenue, usd, Cent::from(100))
    .pay(customer, tax_payable, usd, Cent::from(15))
    .build();
// One Transfer → one Envelope → one EnvelopeRecord in the transfer log.

Note: this is not a literal debit/credit translation. It shows the business effects as movements. In a production POS model, cash, revenue, and tax might be separate effects routed through system/offset accounts. (A literal multi-hop customer → cash → revenue chain inside one transfer would require spending a posting created earlier in the same envelope, which the resolver does not do; it selects from already-committed postings.)

Both are a single balanced event. In the classical entry, Σ Dr (115) = Σ Cr (115). In Kuatia, the resolved Envelope satisfies per-asset conservation: sum(consumed) == sum(created) for USD.

One entry vs the journal: Transfer/Envelope vs the transfer log

These differ in grain: one record vs. the collection of all records.

  • Transfer / Envelope = one record (one journal entry).
    • Transfer is the intent: { movements: Vec<Movement>, book, user_data, metadata }. Callers express what should happen, not which postings.
    • Envelope is the resolved form produced by resolve(): { consumes: Vec<PostingId>, creates: Vec<NewPosting>, account_snapshots, book, … }. It names the concrete postings to spend and create.
    • Committing one (commit / commit_envelope) returns a Receipt { transfer_id } identifying the committed envelope, the EnvelopeId, which is content-addressed (the double-SHA-256 of the canonical envelope bytes).
  • Transfer log = the accounting journal. The append-only, ordered sequence of every committed envelope, persisted by TransferStore as EnvelopeRecord { envelope, receipt, created_at }. Each transfer is one entry in it.

Transfer/Envelope : transfer log :: one journal entry : the journal.

"The system is trivially auditable by replaying the transfer log" means: re-apply every EnvelopeRecord in order and you reconstruct all balances. There is no stored balance that can drift.

Two append-only logs: don't conflate "log"

Kuatia keeps two distinct append-only sequences. Only the first is the accounting journal.

Log Type Records Role
Transfer log TransferStoreEnvelopeRecord Full posting-level detail of each committed transfer The accounting journal, the source of truth for balances.
Event log EventStoreLedgerEvent High-level lifecycle notifications Projections / subscribers; not the journal.

LedgerEvent { seq, timestamp, kind } carries a monotonic seq and a kind of TransferCommitted | AccountCreated | AccountFrozen | AccountUnfrozen | AccountClosed. It tells you that something happened; the transfer log tells you exactly which postings moved.

The UTXO wrinkle

Classical ledgers post an entry by mutating each account's running balance. Kuatia is UTXO-style and posting-based, so the mechanism differs while the event grain is identical. Because postings are signed, debit/credit is not the native primitive; resolution works in terms of consuming and creating postings:

  • In a simple movement, the source side is resolved by consuming Active postings from the source account, creating a change posting if the selected postings exceed the amount.
  • The destination side is represented by newly created postings on the destination account.
  • An account's balance is the sum of its Active postings, computed on demand, never stored.

So an entry line maps to a Posting effect, usually derived from a Movement (two-sided intent) that resolves into one or more postings (a created posting, consumed postings, and possibly change). The balancing rule is unchanged: per asset, sum(consumed) == sum(created).

Where Book fits (and doesn't)

Book is the one Kuatia concept with no classical counterpart. It is a transfer policy scope: it gates which accounts and assets may participate in a transfer (BookPolicy { allowed_assets, allowed_flags, allowed_accounts }).

It is explicitly not:

  • the journal (that is the transfer log),
  • a journal entry (that is a Transfer/Envelope),
  • a balance partition (balances are global; a Book only gates participation).

Despite the name, a Kuatia Book must not be confused with an accounting book. The accounting journal is the transfer log; Book is purely a policy scope.

See the glossary for the Book model and worked examples.

Quick reference

Classical accounting Kuatia Notes
Journal Transfer log / TransferStore<EnvelopeRecord> Append-only source of truth for committed transfers.
Journal entry Committed Transfer / Envelope One atomic accounting event.
Entry line / leg Posting effect A concrete account-level value fragment; movements are intent that resolve into postings.
Compound journal entry Transfer with multiple movements One event touching many accounts/assets.
Σ debits = Σ credits Per-asset conservation sum(consumed) == sum(created) per asset.
Ledger Accounts + active postings Balances are projections over active postings.
Posting a transaction (verb) Resolve + commit Avoid confusion: Kuatia Posting is a noun.
Accounting book No direct equivalent unless modeled separately Kuatia Book is not this.
Transfer policy scope Book Gates allowed accounts/assets.
Proof a txn was recorded Receipt { transfer_id } Content-addressed EnvelopeId.
Lifecycle notifications Event log (LedgerEvent) Separate from the transfer log.

In one line: Kuatia's transfer log is the accounting journal; each committed envelope is a journal entry; Kuatia's Book is a policy scope, not a journal or a balance partition.