# Substrat > The hard parts, hosted. A runtime-enforced substrate for building vertical B2B SaaS. ## What Substrat is Substrat hosts the parts of a business application that are catastrophic to get wrong — multi-tenancy, permissions, audit, migrations — and enforces them **at runtime**, in the platform. That is the whole distinction: a template hands you correct code once and every edit after erodes it, whereas here a vertical *cannot* query across tenants or skip a permission check, because the kernel will not serve the query. Three layers, and you write only the third: - **Kernel** — tenancy, permissions, events and audit, migrations. One **scope** is one isolated SQLite database; there is no cross-tenant API to misuse. - **Engines** — headless, versioned packages owning domain invariants (work orders, invoicing, bookings, protocols, invites, absence, metering). You either **compose** one (import it; its in-scope functions run inside *your* transaction) or **feed** one (emit a fat event; it consumes, with no import). Engines never import each other. - **Verticals** — the application: vocabulary, pricing, roles, screens. This is the layer you own, and the only one an agent should be writing. A vertical is built by scaffolding one (`npm create substrat`) and reshaping the working reference vertical it ships into your domain. It runs locally against SQLite with no platform in the loop, and deploys to Cloudflare Durable Objects unchanged. That vocabulary — scope, engine, vertical, module, operation — is what the page titles below assume you already have. ## Before you read These docs describe **@substrat-run/kernel 0.115.0**. Substrat is pre-1.0 and interfaces change without notice, so a page cached from two minors ago is the failure mode this file exists to prevent. Check the version you have (`npm ls @substrat-run/kernel`, or the `@substrat-run/kernel` entry in your `package.json`) against the version above. - If they match, everything below describes your install. - [`https://substrat.net/llms-0.115.0.txt`](https://substrat.net/llms-0.115.0.txt) is this same file, at a version-pinned URL. Fetching `https://substrat.net/llms-.txt` returns 200 only while the published docs still describe your kernel; a 404 means they have moved on, and this file is the one to re-read. Every link below is **raw markdown**. Fetch those `.md` URLs directly — do not fetch the HTML page at the same path, which wraps the same prose in navigation and theme markup. Everything at once: [`https://substrat.net/llms-full.txt`](https://substrat.net/llms-full.txt). ## Start here If you are building on Substrat, read this one page before any other: - [**Agent rules**](https://substrat.net/guide/agent-rules.md): the always-on contract — the three layers, the ten non-negotiable module-code rules, the gates to run, and the two checkpoints you may never self-approve. Most of what a generated vertical gets wrong is on that page, and where it restates the summary above, that page is the authoritative one. The rest of this index is reference. Fetch from it as the task needs, rather than reading it through — the sections below are ordered for a person learning the platform, not for an agent with a job to do. ## The book - [How to read this](https://substrat.net/book/index.md): This is the book. The rest of these docs are a reference — you arrive with a question, find the page that answers it, and leave. - [1. Why a substrate at all](https://substrat.net/book/01-why-a-substrate.md): Every piece of business software for a specific trade — a field-service app, a workshop system, a clinic's booking tool — is mostly the same software. - [2. Tenants, scopes, and one database each](https://substrat.net/book/02-tenants-and-scopes.md): Everything in Substrat hangs off one structural decision, and it is the one to understand first, because every later mechanism is shaped by it. - [3. The path of one request](https://substrat.net/book/03-one-request.md): Someone opens a browser, types a hostname, and clicks a button that creates a work order. This chapter is every hop between that click and the committed row, in order. - [4. What a handler can and cannot do](https://substrat.net/book/04-inside-an-operation.md): We left the last chapter one step from the handler. This chapter is that step: what an operation is handed, what it is denied, and why each denial is worth its inconvenience. - [5. The life of one event](https://substrat.net/book/05-one-event.md): This is the chapter the reference genuinely does not have. Every fact in it is published somewhere… - [6. Permissions and identity](https://substrat.net/book/06-permissions-and-identity.md): Authentication is a seam; authorization is kernel-owned. Permission keys, roles at nodes, entity-narrowed grants, the fixed four-rule tuple algebra, and why a check never leaves the scope. - [7. Engines, verticals, composition](https://substrat.net/book/07-engines-and-verticals.md): Chapter 1 named the three layers. This chapter is the middle and top ones in working detail: what belongs in an engine, what belongs in a vertical, and the rules that keep seven engines from becoming… - [8. The life of one deploy](https://substrat.net/book/08-one-deploy.md): A change is committed. This chapter follows it from a laptop to a scope's database. - [9. The two clocks](https://substrat.net/book/09-the-two-clocks.md): Everything so far has been driven by a request. This chapter is the work nothing asks for: retries, recurring jobs, reconciliation, reaping. - [10. Seeing what happened](https://substrat.net/book/10-seeing-what-happened.md): Sampled router traffic, tenant-stamped invocation logs, opt-in traces, and the event spine read through history, cause, effects and same-call — and which question each answers. - [11. The audit trail and the lake](https://substrat.net/book/11-audit-and-the-lake.md): The four audit witnesses — outbox, denials, admin log, access log — and how events drain from every scope into an Iceberg table in R2 that R2 SQL can query. - [12. Metering and billing](https://substrat.net/book/12-metering-and-billing.md): A vertical metering and invoicing its own customers, and the platform metering a tenant: what is counted, what is priced, and what (like storage) is not counted yet. - [13. Operating it](https://substrat.net/book/13-operating-it.md): The last twelve chapters were how the system works and what it records. This one is how it behaves when something is wrong, which is a different subject. ## Introduction - [What is Substrat?](https://substrat.net/guide/what-is-substrat.md): Substrat is a substrate for building vertical B2B SaaS — the software a property manager, an installation firm, or a point-of-sale chain actually runs their business on. - [Why runtime enforcement?](https://substrat.net/guide/why-substrat.md): Templates, code generators and BaaS platforms all deliver their guarantees as conventions, and conventions erode on the next edit. Why Substrat enforces them in the runtime instead. - [How Substrat compares](https://substrat.net/guide/comparisons.md): Substrat sits in a category most people meet for the first time here, so the fastest way to understand it is against the tools you already know. - [What Substrat doesn't have (yet)](https://substrat.net/guide/what-substrat-lacks.md): A page that only lists strengths is a document nobody trusts twice. This one names the gaps, says which are deliberate refusals and which are simply unbuilt, and separates the two… - [FAQ](https://substrat.net/guide/faq.md): Short answers, each pointing at the page that has the long one. - [Architecture](https://substrat.net/guide/architecture.md): Substrat is one decomposition seen two ways: a stack of layers (what the code is) and a request path across isolated databases (how it runs). Both pictures are below. ## Build - [Getting started](https://substrat.net/guide/getting-started.md): Two ways in: scaffold a working vertical with `npm create substrat`, or build a scope host, a module and one operation from the packages up in ten minutes. - [Walkthrough: the todo app](https://substrat.net/guide/walkthrough-todo.md): One vertical end to end, as whole files: the one-line brief, the interview, the approved concept, the declared model, what is derived, and the business logic that is all you write. The todo app. - [Agent rules](https://substrat.net/guide/agent-rules.md): The always-on contract for building a Substrat vertical: the three layers, the ten non-negotiable module-code rules, the gates to run, and the two checkpoints an agent may never self-approve. - [The Claude Code plugin](https://substrat.net/guide/agent-plugin.md): A scaffolded Substrat project already teaches an agent what it needs: npm create substrat writes the rules, the build playbook, and a session hook that pins the agent to the docs for the kernel… - [Building for AI agents](https://substrat.net/guide/ai-agents.md): What makes the platform legible to a coding agent: bring your own model, docs published as markdown, a project that announces its own rules, and self-describing module manifests. - [Where AI mistakes stop](https://substrat.net/guide/ai-guardrails.md): Nothing here claims an agent cannot write a bug — only that one class of bug is unreachable rather than discouraged. The six guards a change passes through, in the order they fire. - [Running locally](https://substrat.net/guide/running-locally.md): Getting started builds one host in one script. This page is the other end: the entire flow — a vertical, the shared control plane, and the admin console… ## Ship - [Deploying a vertical](https://substrat.net/guide/deploying.md): Running locally ends on a promise: the SQLite adapter you run on your laptop and the Cloudflare adapter you deploy on are the same kernel above the scope-host contract… - [Environments & previews](https://substrat.net/guide/environments-and-previews.md): A vertical you own has exactly one channel — prod (the deploy model). There is no dev, no staging. That is not a missing feature; it is the point. - [Ask the docs](https://substrat.net/guide/support.md): Ask the Substrat documentation a question — a ticket0 support desk, embedded on every page of this site. ## Concepts - [Tenants & scopes](https://substrat.net/concepts/tenancy.md): Substrat tenancy is two levels, tree-shaped: the tenant is the business that pays you (a property-management firm, a retail chain, a publisher); beneath it are scopes… - [The platform layer](https://substrat.net/concepts/platform.md): A Substrat vertical is not a SaaS product that happens to share some libraries. It is an application deployed onto a platform… - [Operations & the scope host](https://substrat.net/concepts/scope-host.md): The scope-host contract is the heart of the kernel: module code registers operations; callers reach a scope only through a capability stub. - [Permissions](https://substrat.net/concepts/permissions.md): The permission model is kernel-owned — it is enforcement input, never delegated to an auth provider. - [Authentication & identity](https://substrat.net/concepts/identity.md): The kernel authorizes; it never authenticates. Every operation runs against an ambient PrincipalId that is already established by the time the kernel sees it… - [Events & audit](https://substrat.net/concepts/events.md): Every mutation in a Substrat system emits a domain event. Events are the audit trail, the integration surface between engines, and the feed for reporting — one mechanism, three jobs. - [Snapshots & test copies](https://substrat.net/concepts/snapshots.md): You're about to upgrade Acme HR to a new version, and the new version changes the database schema. If the migration mangles a table, "undo" is not a button — the data has moved. - [The deploy model](https://substrat.net/concepts/deploying.md): A vertical you build on Substrat runs as a hosted app: one Durable Object per tenant scope, its own SQLite, served by code you pushed. - [Reads & scaling](https://substrat.net/concepts/reads.md): Every scope runs a strictly serialized executor: one operation at a time, run to completion (Operations & the scope host). - [The model](https://substrat.net/concepts/model.md): A vertical's model is one TypeScript module — spec/model.ts — declaring what exists: its entities, the operations over them, and the permissions those operations check. - [Lifecycles](https://substrat.net/concepts/lifecycle.md): An entity with a status column has a state machine. Before #844 that machine was written down twice: once as the column's enum, and once as guards scattered through the operations that move it. - [Modules & the manifest](https://substrat.net/concepts/modules.md): Engines and verticals join a scope host the same way — as modules bundling a manifest, migrations, operations and consumers. Covers envSpec, schedules, freshness, and attachment contracts. - [What a good API looks like](https://substrat.net/concepts/api-design.md): Substrat is opinionated about API shape on purpose. Not because there is one true REST, but because the alternative is that every vertical re-decides pagination, error bodies, time and identifiers… - [The MCP surface](https://substrat.net/concepts/mcp.md): A vertical whose routes come from mountOperations serves an MCP endpoint at /api/mcp, and writes nothing to get it — one tool per operation, derived from the same declarations the route table is. - [Money](https://substrat.net/concepts/money.md): Substrat ships one money representation and one sanctioned way to compute with it, because "every engine invents its own money handling" is how reconciliation dies. ## Engines - [What is an engine?](https://substrat.net/engines/index.md): An engine is domain machinery shared across verticals but too domain-shaped for the kernel: work orders, invoicing, scheduling, ticketing, protocols/checklists. - [Work orders › Overview](https://substrat.net/engines/workorder/index.md): @substrat-run/engine-workorder — one engine covering work orders, time reporting, and material reporting. One state machine, append-only reporting, and a billable snapshot frozen at completion. - [Work orders › Domain model & invariants](https://substrat.net/engines/workorder/model.md): Declared, in engines/workorder/src/lifecycle.ts, and emitted to engines/workorder/model.json — this is the first engine to adopt a lifecycle. - [Work orders › Operations & permissions](https://substrat.net/engines/workorder/surface.md): The work-order engine's two surfaces: operations invoked through a scope stub, and in-scope functions a vertical calls inside its own transaction — plus its permission keys and entitlement. - [Work orders › Events](https://substrat.net/engines/workorder/events.md): The engine emits 7 and consumes none — it is a source, not a sink. Nothing upstream drives a work order; the vertical does, through operations. - [Work orders › Composing & extending](https://substrat.net/engines/workorder/composing.md): That's the whole registration surface. registerModule takes exactly one argument, and it's a frozen constant. - [Bookings › Overview](https://substrat.net/engines/booking/index.md): @substrat-run/engine-booking — reservations as allocation against capacity over an interval. - [Bookings › Domain model & invariants](https://substrat.net/engines/booking/model.md): A resource held over a time interval, with the invariant that concurrent allocations never exceed that resource's capacity over any overlapping interval. - [Bookings › Operations & permissions](https://substrat.net/engines/booking/surface.md): Registered bindings, each one a permission check plus a call into the in-scope function below. - [Bookings › Events](https://substrat.net/engines/booking/events.md): The twelve events the booking engine emits, from `booking.held` to `booking.no-show`, and the PII split that keeps participant identity out of every payload. Consumes nothing. - [Bookings › Composing & extending](https://substrat.net/engines/booking/composing.md): Registering the booking engine, wrapping `hold` and `confirm` with your own rules, and the timezone boundary: the engine compares instants and never does calendar arithmetic. - [Invoicing › Overview](https://substrat.net/engines/invoicing/index.md): @substrat-run/engine-invoicing — accumulates invoice bases (Swedish: invoice basis) from billable events and makes them immutable once exported. - [Invoicing › Domain model & invariants](https://substrat.net/engines/invoicing/model.md): The invoicing engine's two tables, the immutable-after-export invariant, why lines snapshot their source rather than join to it, and the rule that one document carries one currency. - [Invoicing › Operations & permissions](https://substrat.net/engines/invoicing/surface.md): An engine has no endpoints. It exposes operations (invoked through a scope stub) and in-scope functions (called by a vertical inside its own transaction). - [Invoicing › Events](https://substrat.net/engines/invoicing/events.md): This engine is mostly a sink. Lines arrive by event, never by call — which is why the consumed side is the more interesting half. - [Invoicing › Composing & extending](https://substrat.net/engines/invoicing/composing.md): Then emit the events it consumes — that's the whole integration. There is no wiring step between the work-order engine and this one, because there is no connection between them: one emits, the kernel… - [Protocols › Overview](https://substrat.net/engines/protocol/index.md): @substrat-run/engine-protocol — protocols and checklists with the sign → immutable invariant: self-inspections, installation protocols, service checklists, per-item condition reports. - [Protocols › Domain model & invariants](https://substrat.net/engines/protocol/model.md): Templates, instances, responses and signature requests across five tables: immutable template versions, the checklist and document content kinds, and provider-agnostic signature evidence. - [Protocols › Operations & permissions](https://substrat.net/engines/protocol/surface.md): An engine has no endpoints. It exposes operations (invoked through a scope stub) and in-scope functions (called by a vertical inside its own transaction). - [Protocols › Events](https://substrat.net/engines/protocol/events.md): The engine emits 9 and consumes none — protocols are driven by operations, not by upstream facts. - [Protocols › Composing & extending](https://substrat.net/engines/protocol/composing.md): Registering the protocol engine and supplying the templates it deliberately ships none of, wrapping instantiation in your own operations, and the completion guard in both its forms. - [Invites › Overview](https://substrat.net/engines/invites/index.md): @substrat-run/engine-invites — how a person joins an organization they are not already in. - [Invites › Domain model & invariants](https://substrat.net/engines/invites/model.md): Terminal states are terminal. An accepted invitation cannot be revoked, a revoked one cannot be accepted, and an expired one cannot be revived — a new invitation is a new row. - [Invites › Operations & permissions](https://substrat.net/engines/invites/surface.md): Each is the thin binding the engine convention requires: a permission check plus one exported in-scope function. - [Invites › Events](https://substrat.net/engines/invites/events.md): None. consumes is empty: this engine is a pure producer. An invitation is started by a call, never by another module's event. - [Invites › Composing & extending](https://substrat.net/engines/invites/composing.md): Calling the invites engine from your own operations, delivering the invitation yourself — the engine sends nothing — and a worked example of the join flow, with what not to do. - [Absence › Overview](https://substrat.net/engines/absence/index.md): @substrat-run/engine-absence — approved absence as an append-only entry ledger over an opaque subject. - [Absence › Domain model & invariants](https://substrat.net/engines/absence/model.md): The append-only absence ledger and its five entry kinds, the request state machine, and why balances are computed in whole days rather than instants. - [Absence › Operations & permissions](https://substrat.net/engines/absence/surface.md): Registered bindings, each one a permission check plus a call into the in-scope function below. - [Absence › Events](https://substrat.net/engines/absence/events.md): The six events the absence engine emits, the fat payloads that spare consumers a cross-module read, and the subject key that keeps crypto-shredding possible. Consumes nothing. - [Absence › Composing & extending](https://substrat.net/engines/absence/composing.md): Registering the absence engine, the subject boundary that keeps it free of any employee directory, and why accrual, carryover and the calendar stay in your vertical. - [Metering › Overview](https://substrat.net/engines/metering/index.md): @substrat-run/engine-metering — billable usage as an append-only, idempotent meter ledger whose closed periods are frozen billing evidence. - [Metering › Domain model & invariants](https://substrat.net/engines/metering/model.md): The append-only meter ledger, the counter and gauge kinds frozen at meter creation, the dedupe key that makes ingest idempotent, and why entries are stamped as instants. - [Metering › Operations & permissions](https://substrat.net/engines/metering/surface.md): Registered bindings, each one a permission check plus a call into the in-scope function below. - [Metering › Events](https://substrat.net/engines/metering/events.md): The three events the metering engine emits, why a deduped replay emits nothing at all — leaving the stream itself dedupe-clean — and the period-closed billing hand-off. Consumes nothing. - [Metering › Composing & extending](https://substrat.net/engines/metering/composing.md): Registering the metering engine, recording usage inside the operation that already did the work, tagging entries without overloading the meter key, and the pricing hand-off to invoicing. ## Connectors - [What is a connector?](https://substrat.net/connectors/index.md): A connector is the platform's bridge to a third-party service a tenant uses — Scrive for e-signing, Fortnox for accounting, a BankID provider for identity. - [Scrive (e-signing)](https://substrat.net/connectors/scrive.md): Turns a signature request from the protocol engine into a real signing flow at Scrive, authenticated with Swedish BankID. - [Fortnox (accounting)](https://substrat.net/connectors/fortnox.md): Reads a company's bookkeeping out of Fortnox as a SIE4 export, sums it per account, cost centre and month, and lands the result into a scope through the consuming vertical's own operation. - [Planima (facility maintenance)](https://substrat.net/connectors/planima.md): Reads a maintenance plan out of Planima — facilities, buildings, components, and the costed actions a property owner intends to carry out in a given year… ## Verticals - [What is a vertical?](https://substrat.net/verticals/index.md): A vertical is the business: the software a service firm, a workshop, a shop, or an HR team actually runs on. - [Callout (field service)](https://substrat.net/verticals/callout.md): demos/callout — a small Swedish field-service firm (ElMontage AB, el/VVS installation): work orders, technician time & material, checklists, and invoice basis. - [Handlebar (bike workshop)](https://substrat.net/verticals/handlebar.md): demos/handlebar — a one-workshop bike-repair shop (Kedja & Kugghjul Cykelverkstad AB, Stockholm): customers bring bikes in, mechanics repair them, the shop prices and invoices the job. - [Kallkälla (coffee shop)](https://substrat.net/verticals/shop.md): demos/shop — a small-batch coffee roaster in Stockholm (Kallkälla Kaffe AB) running a web shop: beans (whole/ground × 250 g/1 kg) and brewing gear, checkout mot faktura (on invoice). - [Meridian (HR)](https://substrat.net/verticals/meridian.md): demos/meridian — a multi-country HR vertical: leave and absence, project time reporting, expenses, onboarding, and the anställningsavtal, in one role-adaptive app that is an employee's self-service… - [Manyfold (headless CMS)](https://substrat.net/verticals/manyfold.md): demos/manyfold — a multi-scope headless CMS: content types authored as data, a draft → review → publish editorial lifecycle, append-only revisions, and a frozen published projection served by (type,… - [Todo (shared lists)](https://substrat.net/verticals/todo.md): demos/todo — the smallest vertical that is still a real one: lists, the items on them, and sharing a list with one person by email. No engine, no money, one role, two tenants. - [ticket0 (support desk)](https://substrat.net/verticals/ticket0.md): demos/ticket0 — an AI-assisted support desk: a chat widget a company embeds on its own site with one