Milestone 2 — Financial Immutability and State Machine Enforcement
Kernel Milestone: 2 of 6 Previous: Milestone 1 — Tenant Isolation (5.14.0) Next: Milestone 3 — Monetary Domain Migration
System Impact
-------------
Ledger integrity: strengthened
Ledger mutation: structurally impossible after posting
Tenant isolation: unchanged
Financial immutability: enforced at database layer
State machine: forward-only transitions enforced
Cascade deletion: closed on core financial tables
Breaking changes: none
Replay determinism: preserved
Invariant Change
----------------
Axiom 2.1 (Posted Set) is now mechanically enforced at the database layer.
Why This Matters
An accounting system’s most fundamental guarantee is that posted financial records cannot change. Without this property the ledger is not a ledger. It is a mutable table that happens to hold numbers. Auditors, regulators, and the mathematics of double-entry bookkeeping all depend on the same invariant: once a transaction is posted, its lines are permanently fixed.
Before this release that guarantee existed only at the application layer. The application code respected immutability, but the database did not enforce it. A direct database connection, a future developer shortcut, or a defect in application logic could silently modify or delete posted transaction lines. No error would be raised. The ledger would simply drift.
This release moves that guarantee to the only layer that cannot be bypassed: the database itself.
There is also a deeper architectural reason this matters. If posted records never change, the entire financial history of a tenant can be reconstructed from first principles by replaying the ordered ledger event stream. Deterministic replayability is the prerequisite for the cryptographic ledger verification arriving in Milestone 6.
Financial Immutability
Posted Transaction Line Protection (Axiom 2.1 — The Posted Set)
The SpeyBooks kernel defines the Posted Set: once a transaction is posted, its lines are immutable at three independent enforcement layers:
- Database triggers (this release)
- Ledger append-only policy (Milestone 6)
PostedTransactionTypeScript type with no mutation methods
These layers eliminate any single point of failure. Bypassing one does not bypass the others.
Two trigger functions now execute before any modification or deletion of a transaction line. If the parent transaction is posted or void, the operation is blocked before execution with a fatal exception.
Modification blocked Amounts, account assignments, directions, and all other values on a posted line are permanently fixed. Reversal is performed through the void mechanism, which generates a compensating entry rather than mutating history.
Deletion blocked Lines belonging to posted or voided transactions cannot be removed. Financial records are never destroyed. This is enforced by database exception, not application convention.
Orphan detection If a trigger fires on a line whose parent transaction cannot be found, an integrity exception is raised. In a correctly functioning system this state is impossible. Failures surface loudly rather than passing silently.
The Void Principle (Axiom 2.2 — No Cascade Destruction)
Financial records in SpeyBooks are never destroyed. This release eliminates the primary destruction pathway: cascade deletion on core financial tables.
Three foreign key constraints have been converted from CASCADE to RESTRICT, creating a three-sided enclosure around every posted transaction:
- Lines cannot be modified or deleted individually (Axiom 2.1 triggers)
- Parent transactions cannot be deleted while lines exist (Axiom 2.2 restrict)
- Lines cannot be cascade-destroyed through parent deletion (Axiom 2.2 restrict)
There is no remaining deletion path.
State Machine Enforcement
Forward-Only Transitions (Axiom 4.1 — The Arrow of Time)
Transactions follow a strict lifecycle: draft > posted > void
Before this release the database validated allowed status values but did not enforce transition direction. A posted transaction could be reverted to draft. A voided transaction could theoretically be reposted.
A new trigger enforces forward-only transitions at the database layer:
draft > posted— permitted, subject to existing zero-sum and line-count preconditionsposted > void— permitted- All other transitions — fatal exception before execution
Invoices receive equivalent protection. Once issued, an invoice cannot revert to draft.
This enforcement matters for deterministic replay. If transactions can be un-posted, the relationship between transaction state and ledger history becomes ambiguous. The hash chain in Milestone 6 becomes unverifiable.
Threat Closure
| Threat | Status Before | Status After | Enforcement Layer |
|---|---|---|---|
| Posted transaction line modification | Open | Closed (Class M) | Trigger guard (Axiom 2.1) |
| Posted transaction line deletion | Open | Closed (Class M) | Trigger guard (Axiom 2.1) |
| Cascade destruction of financial lines | Open | Closed (Class M) | FK RESTRICT (Axiom 2.2) |
| Illegal state transition (posted to draft) | Open | Closed (Class M) | State machine trigger (Axiom 4.1) |
| Invoice reversion to draft | Open | Closed (Class M) | State machine trigger |
| Orphaned line silent pass | Open | Closed (Class M) | Integrity exception |
Security Posture Change
Financial mutation pathways previously guarded only by application logic are now structurally closed at the database layer.
This release converts several operational guarantees into mechanically enforced invariants (Class O to Class M). Database enforcement ensures the ledger cannot be altered even if application logic fails or is bypassed.
Verification Record
Pre-flight:
4/4 validation checks clean
No invalid transaction or invoice states detected
CASCADE constraints confirmed before migration
Migration:
COMMIT
All three invariant validation blocks passed
Post-commit:
V1 immutability triggers present on transaction_lines
V2 state machine triggers present on transactions and invoices
V3 foreign keys converted to RESTRICT
Adversarial review:
9.6 / 10 Production Gold
Amendment:
Orphan detection guard added to both immutability triggers
Architectural Context
Kernel invariants are introduced in dependency order. Tenant isolation must precede immutability because immutability of cross-tenant data is not a useful property. Both must precede the monetary domain migration, which operates on a tenant-isolated and immutable schema.
After Milestone 2, three foundational invariants are enforced mechanically at the database layer:
- Tenant isolation — row-level security enforced for all roles including table owners
- Financial immutability — posted records cannot be modified or destroyed
- State machine correctness — transactions and invoices move forward only through their lifecycle
These three properties together make deterministic ledger replay possible.
Operational Impact
Immutability: Posted transaction lines immutable at the database layer. Orphan detection prevents silent corruption.
State machine: Forward-only transaction lifecycle enforced. Invoice reversion to draft prevented.
Data safety: Cascade deletion removed from core financial tables. No deletion path exists for posted transactions by any route.
Deployment: Migration fully transactional. Invariant checks executed inside migration. Adversarial review applied before deployment.
Kernel Status
| Milestone | Description | Status |
|---|---|---|
| M1 | Tenant Isolation | Complete |
| M2 | Financial Immutability and State Machines | Complete |
| M3 | Monetary Domain Migration | Pending |
| M4 | Provenance | Pending |
| M5 | Schema-Derived Categorical Boundary | Pending |
| M6 | Append-Only Cryptographic Ledger | Pending |
Files Changed
Backend:
api/db/migrations/069-milestone-2-immutability-state-machines.sql— transaction line immutability triggers (Axiom 2.1), transaction and invoice state machine triggers (Axiom 4.1), cascade to restrict conversion on core financial foreign keys (Axiom 2.2), structural invariant validation blocks, post-migration verification queries
Milestone 3 — Monetary Domain Migration — introduces integer minor-unit currency representation with deterministic reduction functions and algebraic database constraints.