# 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 single 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 `EnvelopeRecord`s | 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 `Movement`s | 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** (`Transfer` → `Envelope` → 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](#where-book-fits-and-doesnt). | > 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: ```rust 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, book, user_data, metadata }`. Callers express *what* should happen, not *which* postings. - **`Envelope`** is the *resolved* form produced by `resolve()`: `{ consumes: Vec, creates: Vec, account_snapshots, book, … }`. It names the concrete postings to spend and create. - Committing one (`commit` / `commit_atomic`) 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** | `TransferStore` → `EnvelopeRecord` | Full posting-level detail of each committed transfer | The accounting **journal** — the source of truth for balances. | | **Event log** | `EventStore` → `LedgerEvent` | 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](glossary.md#book) for the Book model and worked examples. ## Quick reference | Classical accounting | Kuatia | Notes | |---|---|---| | Journal | Transfer log / `TransferStore` | 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.