The ledger
Append-only, double-entry, with corrections posted as reversals. Nothing updates a posted transaction, including us, which is what makes every balance above it reconstructible.
Platform · platform & data
Most AI-in-ERP products are a language model placed next to an existing application, sharing its credentials and its blind spots. The interesting architectural question is not which model is used — it is what sits between the model and the ledger, and whether anything can go around it.
What it does
Append-only, double-entry, with corrections posted as reversals. Nothing updates a posted transaction, including us, which is what makes every balance above it reconstructible.
Customers, vendors, contracts, projects, documents, and people as objects with typed relationships, holding both what you run here and what we read from connected systems.
Every write — from a person, an agent, an API key, or a portal — passes the same evaluation of actor, authority, threshold, and conflict. There is no second path.
Agents propose actions to the policy engine like any other actor. They hold no elevated credential and cannot reach the ledger directly, which is what makes the audit trail mean something.
Every layer writes into the same append-only log with the same schema, so an action is traceable from the agent that proposed it to the journal line that resulted.
The application, the API, MCP, webhooks, and portals are all clients of the same layers. None of them has a capability the others could not be granted.
If the policy engine sits beside the application rather than beneath it, then anything that is not the application — an integration, a script, a support tool, an agent — has a path that skips it. That is the architecture of nearly every system where an unexplained journal entry has ever appeared.
Putting policy between every actor and the ledger costs latency and forecloses shortcuts we would occasionally like to take. It is also the only structure in which the statement “nothing posts without authority” is a property of the system rather than a description of current practice.
A ledger knows about accounts and amounts. It does not know that this invoice belongs to this contract, which belongs to this customer, who is a subsidiary of that one, and that the work was delivered by these people on that project.
Those relationships are what make a question like “why did margin fall in Denver” answerable without a report having been built in advance. Holding them as typed objects rather than as foreign keys in an accounting schema is the difference between a system you can query and one you can only report from.
Analytical queries run against a read model derived from the ledger, so a heavy report cannot slow down posting and cannot lock a table somebody is trying to close against. The read model is rebuilt from the ledger rather than maintained independently, so it cannot drift into a second version of the truth.
Where the read model lags — it is usually under a second — the interface says so rather than presenting a stale figure as current.
Tenant isolation is enforced in the data access layer and again by row-level security in Postgres. A query without an actor and a scope does not return the wrong rows; it fails. That is deliberately stricter than checking permissions in an endpoint, because the endpoint approach depends on every future developer remembering.
Not a plugin runtime — third-party code executing inside the policy boundary would undo the guarantee the boundary exists for. Not an event-sourced everything; the ledger is append-only because accounting is, not because the pattern is fashionable. And not a microservice per noun, which for a system of this size buys operational complexity in exchange for organisational independence we do not need.
Limits
Every write passes it. That is a few milliseconds we spend deliberately and will not offer a bypass for, at any volume.
Usually under a second, occasionally more under load. The interface shows staleness rather than hiding it.
Extensions run as API clients, not as plugins. It closes off some integration patterns and keeps the policy guarantee intact.
Questions
Send the questions it raises. We answer architecture questions in writing, including the awkward ones.