Experiments
A homoiconic SaaS substrate — Lisp, one kernel, one Postgres

One kernel. One Postgres. The code is rows.

Kern is a Lisp framework where the application itself is data — every resource, component, view, workflow, policy, translation, tenant customization, resident agent, and agent-written patch is a versioned, content-addressed row in the same Postgres that holds your business. The binary is a small evaluator that rarely changes. Deploying is a transaction. Rolling back is a WHERE clause. And an AI agent doesn't read your codebase through grep — it queries it, because the codebase has a schema.

./kern — the evaluator, a few MB postgres:// — truth, queue, history, and the application there is no build artifact
The tension nobody resolved

Every company runs two machineries — and governs only one

For fifty years, software has governed its data with one discipline and its code with another. Your database gives every fact a transaction, a history, an audit trail, a permission model, and a query language. Your codebase gets a text repository, a build pipeline, an artifact registry, deploy gates, feature flags, and a rollback runbook — a hand-assembled, weaker imitation of the guarantees the database already provides. That divide was tolerable when humans wrote all the code, slowly. Now agents write code fast — and the code is the least governed artifact in the company.

The data machinery — Postgres
“Every fact is transactional, versioned, audited, queryable, and bound by policy.”
The code machinery — git · CI · registry · deploys
“Except one: the application itself — shipped as an artifact, with none of those guarantees.”
Kern ends the divide: the application is data in the database. Deploying, auditing, versioning, and governing code becomes the machinery you already trust with everything else.
How it runs

The kernel has three jobs. Everything else is rows.

Homoiconicity is the license: in a Lisp, code is s-expressions, s-expressions are data, and data has a home. The kernel — a reactor, an evaluator, a verifier suite, and a wire protocol — is the only compiled artifact, and it changes on the timescale of a database engine, not an application. It ships zero business logic: it is to your product what Postgres is to your rows.

admit

Code enters by transaction

New definitions are inserted, verified, catalogued, and compiled in one transaction — with their schema migration, if they carry one. Rejected code never becomes code. Atomic, fail-closed, no pipeline.

deploy = commit · rollback = as-of
evaluate

Two tiers, one substance

Admitted definitions compile native through SBCL at admission — compile is a core-language function, not a build system. Sandbox-tier code (tenants, agents) runs in kern's own interpreter, fuel-metered: exact step and allocation budgets per evaluation.

trusted: native · untrusted: metered
resume

Everything paused is a row

Workflows, UI sessions, and signalled conditions are serialized continuations — expression + environment, stored with a wake condition. Any node resumes any of them. Crash, deploy, or drain: nothing is lost, because nothing lived only in memory.

state: postgres · memory: a cache
The kernel — stateless, boring, rarely changes
KERN BINARY
async reactor (epoll/io_uring) · the evaluator + fuel meter · SBCL compile-at-admission · verifier suite · byte-patch wire protocol
no app code inside
scale out by adding identical kernels — sessions are rows, so no node is special and none is sticky
the seam — admission only
The database — the application, and everything it has ever been
DEFINITIONS
content-addressed code rows · the catalog · resources · components · views · policies · translations · prompts · agents
RUNTIME STATE
continuations (workflows · sessions · conditions · agents) · queue · cron · history · PII vault
code crosses the seam as transactions — nothing else doesone history tier versions the data and the code that shaped it
Unique feature № 1 — the resource

Declare once. Everything derives. And the declaration is a row.

Every guarantee in this document has to know your schema — sync, policy, forms, history, the vault. So kern has one source of truth: the resource, a declaration the whole framework derives from. In a homoiconic language the idea reaches its native form: the DSL is just the language, and macroexpansion is the derivation pipeline. No DSL compiler, no code generation step — a defresource form expands into schema, migrations, history tables, sync horizons, policy checks, and form components, and the expansion is inspectable data, not compiler internals.

Because the declaration is itself a row, the catalog stops being generated documentation and becomes the storage format. Kern catalogues every definition — resources, functions, components, policies, workflows, prompts — because being in the catalog is what being code means. There is no definition an agent can see that the catalog doesn't describe, structurally, by construction.

And because code and schema live in the same database, they migrate in the same transaction. The handler that reads a new column and the migration that adds it commit together or not at all — the “deploy window” where code and schema disagree simply has no representation.

;; the declaration is data — stored, hashed, catalogued
(defresource deal
  (attributes
    (stage   stage-enum    :history :every-version)
    (amount  money)
    (owner   (pii contact) :vault pii-contact)
    (company (belongs-to company)))
  (horizon (by assignment))
  (policy  (and org-scoped (role sales))))
schematables + migrations, admitted in the same transaction as the code
historyevery version of every row — audit, as-of, and the change stream
policy + vaultorg-scope, RBAC, PII routing, masked-by-default forms
cataloga content-addressed row — the hash is the identity, the name is a pointer
apiREST + OpenAPI + MCP — the endpoint exists the moment the resource does
Unique feature № 2 — the workflow engine

Durable workflows are continuations, not checkpoint-replay

Durable-execution engines persist step results and re-execute deterministic code to recover position. It works — and it drags a constraint through everything: workflow code must be replay-safe, and versioning a workflow mid-flight is the problem that haunts the whole category. A Lisp interpreter can do the honest thing instead: serialize the actual continuation. The paused program is an expression plus an environment — which is to say, a row.

Versioning dissolves into the history tier: a paused workflow resumes against the exact code hashes it started with, because old definitions are immortal rows. “As-of” for code solves what replay-compatibility gymnastics never quite did. Steps that touch the database commit their effect and their checkpoint in one transaction — exactly-once, guaranteed by Postgres itself.

And the condition system — Lisp's resumable-error discipline — becomes the human-in-the-loop primitive. A failed step doesn't throw; it signals a durable condition whose restarts are data, presented in the operator plane (or to an agent) as choices. Pick one; the continuation resumes. “Workflow errored, an operator chooses a restart, work continues” is the true shape of SaaS operations, and no mainstream engine has it as a language feature.

(defworkflow follow-up (deal-id)
  (step  (send-intro deal-id))
  (sleep (days 3))  ; continuation → row · survives deploys
  (when (await 'approval; human or agent — same row
    (step (send-contract deal-id))))
 
;; a failed step signals a DURABLE CONDITION —
;; restarts are data, offered in the operator plane
(restart-case (step (charge-card invoice))
  (retry-with-new-card (card) …)
  (write-off () …))
versioningpaused code resumes against its exact hashes — as-of, not replay gymnastics
restartserrors pause into choices; operators or agents pick; the continuation resumes
exactly-oncestep effect + checkpoint commit in one transaction — Postgres guarantees it
queues & cronrows — concurrency, priority, dedup, no orchestrator cluster
triggersrecord events, cron, webhook, manual — declared as data; every run is a queryable row
Unique feature № 3 — the reactive layer

Server-driven UI — with durable sessions and dependency-exact patches

Handlers never emit markup. Components are typed sexps drawn from a closed vocabulary of ~25 semantic componentspage, form, table, card, badge, and kin — headless, themed by CSS variables, composed freely. A macro splits every component into static parts (sent once) and dynamic bindings (tracked) at expansion time; the server diffs and ships byte-sized patches over an owned wire protocol to a vendored ~15 KB client. The closed set is load-bearing: it is what makes derived forms, automatic PII masking, and a future native-renderer lane possible.

Then kern goes past diffing. Because every component's reads are analyzable forms against catalogued resources, the framework knows statically which fields each component depends on. A change to deal.stage invalidates exactly the components bound to it — no per-socket re-render-and-diff of everything in scope. Server-side diffing was always a clever recovery from not knowing dependencies; kern knows them.

And the session itself is a continuation. Hot state lives in the reactor's memory; on disconnect, deploy, or drain it checkpoints to a row — so any node resumes any session, and the reconnect storm that haunts stateful-socket frameworks becomes a rehydration, not a re-mount stampede. Serializable-continuation web serving has two decades of prior art; marrying it to server-diffed reactive UI and a database is the opening.

;; components emit meaning, not markup
(defcomponent deal-card (deal)
  (card
    (badge (deal :stage))  ; dynamic: tracked
    (label "Amount")     ; static: sent once
    (money (deal :amount)))) ; dynamic: tracked
 
;; the catalog knows deal-card reads deal.stage +
;; deal.amount — invalidation is exact, patches are bytes
browsersbyte patches over SSE — real HTML first paint, no SPA, no hydration
invalidationdependency-exact, derived from the catalog — not diff-everything
sessionscontinuations — deploys and node loss don't drop UI state
native, laterthe closed vocabulary keeps the native-renderer lane open
Unique feature № 4 — governed evaluation

The database has a type system for code

Grounding an agent on a catalog stops schema hallucination: a column that isn't in the catalog can't be referenced, because the reference fails verification. Kern takes grounding to its limit — admission. Agent-written code is data submitted to the database, and the verifier suite — catalog parity, capability audit, PII flow, contracts — runs inside the insert transaction. Rejected code never becomes code. And the grounding surface can't rot, because it is populated by the agent's own admitted work: being catalogued is what admission means.

Policy enforcement moves from checks to environments. Code runs in an environment where only its granted capabilities are bound — an unauthorized call isn't rejected, it's unnameable. There is no ambient authority to escalate, no forgotten middleware, no bypass path: the environment is the policy.

This closes the quiet failure mode of every hand-built framework: a policy that is declared, exported, documented — and wired into nothing. A permission model that no query path actually calls looks identical, in the source and in the demo, to one that holds; the gap only surfaces as a breach. Because kern's verifiers run at admission, “declared but unenforced” is a build failure, not a latent hole — a resource whose policy no admitted path consults fails catalog parity and never becomes code. The guarantee isn't that engineers wire enforcement correctly; it's that unwired enforcement can't be admitted.

One mechanism, not two: tenant customization and agent patches are the same primitive — governed evaluation. Sandboxed expressions, capability-scoped environments, fuel-metered, catalog-verified, audit-rowed. The customization ladder every serious SaaS eventually builds and the agent-in-the-loop every SaaS is about to need collapse into a single thing to secure.

And the framework ships its own MCP server: agents query the catalog, fetch definitions by hash, submit patches as sexps, and receive verifier verdicts as structured data. Today every coding agent reverse-engineers a codebase through file reads and grep; kern's answer is that the codebase is already a database with a schema. The IDE is an API.

;; an agent's patch is data — admission runs the
;; verifiers IN the insert transaction
(admit! patch-7f3a
  :capabilities '(crm.read billing.read) ; else unnameable
  :fuel  1e6  ; sandbox tier — metered exactly
  :verify '(catalog-parity capability-audit
           pii-flow contracts))
 
;; rejected code never becomes code.
;; admitted code IS the catalog.
capabilitiesthe environment is the policy — unbound means unnameable
fuelexact step + allocation budgets per evaluation — untrusted code is priced, not trusted
reviewstructural diffs in the same UI as record audit — code changes get audit rows
MCPcatalog queries, hash-addressed fetches, verdicts as data — built in
Every agent framework bolts guardrails onto a codebase that is still text. In kern the codebase is a database with a schema — the guardrails are the storage format. A substrate agents can't hallucinate off of is a category of one.
Unique feature № 5 — the mutable surface

Change the application while it runs — editing is admission

The runtime-metadata products — Salesforce's config plane, Airtable, Twenty — proved something loud: users want to reshape live software. Custom objects, fields, views, and automations, created from Settings, live in seconds, no deploy. And every one of them is built the same way: a metadata engine interpreting rows at runtime, running beside a codebase that is still text shipped through CI. That seam is the product's ceiling — what Settings can't express needs a pull request, and the two substances version, audit, and migrate under different laws. Kern doesn't have the seam. Metadata was always code that lived in the database. In kern, all of it does.

So the no-code surface is not a second engine — it is a thin client of admission. Attributes draw from a closed vocabulary of semantic types — money, email, phone, address, select, relation, and kin — each carrying its validation, rendering, masking, and locale behavior. A Settings edit arrives as an extend-resource form and passes the same gate as everything else: verified, versioned, audit-rowed, live on commit. A trigger-action workflow drawn in the builder lands as the same defworkflow sexp an engineer would write. Roles are the same story — a role is a named bundle of capabilities, a row, grantable to a human, a key, or an agent alike. The Settings UI is an agent — a well-behaved one whose patches happen to be forms filled in by humans.

Tenant customization is the same mechanism, one column deeper. Scope is a column on the definition row — product, package, org, team, user — and the catalog resolves through the chain: a tenant sees the base product plus their org's overlay; a rep sees personal views above the team's. When an org admin adds a field to deal from Settings, the admitted form is scoped to their org — forms, views, policy, API, and history know it seconds later, and no other tenant's catalog moves at all. Extension needs no reserved prefixes and no hand-assigned “universal identifiers” to survive deploys — identity is the hash, and an extension references the exact base definition it extends. Who may edit is the same machinery one level up: schema-editing is a capability granted through roles, not a hardcoded admin flag. And because the overlay is code, the customization ladder has no cliff — a field today, a computed field tomorrow, a validation, a workflow, a component — the same substance throughout, priced in capability grants, not in “call us” tiers.

And the surface users shape most — views, layouts, dashboards — is rows by the same argument. A saved view is a catalogued query plus a projection: table, board, and calendar are three renderings of one form; filters, sorts, and groupings are data; a dashboard is widgets over the same queries; record pages compose the closed component vocabulary. Because views are definitions, they inherit the machinery unasked — dependency-exact invalidation, policy scoping, per-role sharing, as-of history. “Who changed the pipeline board” is the same query as “who changed the record.”

And here is what the substrate uniquely buys the builder: round-trip. Every visual builder ships with an eject cliff — the UI writes config until config runs out, someone drops to code, and the builder can never render that object again; from that day the product has a made-in-the-UI half and a made-by-engineers half, drifting. Kern's builders are projectional editors over the catalog: the workflow canvas, the form designer, and the view editor read and write the same sexps engineers do, and the canonical printer guarantees the rendering is one-to-one. Whatever the UI writes, an engineer can open in a buffer; whatever an engineer admits, the UI renders back as editable structure — a form the closed vocabulary can't express appears as an inline code node on the same canvas, not a locked door. Drafts are rows; activating one is admission; and because a running workflow is a continuation, the canvas can open a live run — showing which step it sleeps on, with a durable condition's restarts rendered as buttons an operator presses.

So make it official: the builder is a first-class object. Every kern product ships a workflow builder for its tenants not as a feature someone built but as a derived surface — the canvas, the palette, and the property panels are catalogued components like everything else: admitted, versioned, scoped, as-of queryable. Cash that out. The palette is a query — it offers exactly the step types the current user's capabilities can admit, so unnameable extends to the UI: a step you may not use is not greyed out, it is absent. A package that ships a new action ships its palette entry and property panel as fields on the same definition row — installed, it appears on every canvas allowed to name it, with no builder release. Property panels derive the way forms derive from resources, because def-forms are themselves typed, catalogued grammar — the builder is what happens when the derivation machinery is pointed at the language itself. And the fixed point holds: the builder's own definition opens on its own canvas. Smalltalk shipped its browser inside the image fifty years ago; kern ships its builder inside the catalog, where a product team, a tenant, or an agent can patch it — and roll it back — like any other rows.

;; a tenant admin adds a field — an admission like any other
(admit! (extend-resource deal
          (win-chance percent))
  :scope (org acme)  ; their overlay — no other tenant sees it
  :by (member 312) :via settings)  ; or :via agent — same gate
 
;; a saved view is a catalogued query + a projection
(defview pipeline (deal)
  (board :group-by stage :sum amount) ; table · board · calendar
  (where (mine owner))
  (show stage amount company close-date))
live schemathe field exists in seconds — forms, policy, history, and the API already know it
scope chainproduct · package · org · team · user — one column, resolved per request; upgrades re-verify every overlay
viewstable · board · calendar — one query, three projections; filters and sorts are data
layoutsrecord pages and dashboards compose the closed vocabulary — no plugin runtime
the builderitself rows — the palette is a capability query, panels derive from the grammar, the canvas opens its own definition
one gateuser edit, agent patch, developer deploy — the same transaction, the same audit row
The eject cliff is deleted: “config” and “code” were always one substance rendered two ways. The builder writes sexps, engineers write sexps, agents write sexps — one gate admits them, and one canvas renders them all back — a canvas that is itself rows.
Unique feature № 6 — the derived edge

The API, every webhook, every locale — projections of the catalog

An admitted resource is already an API. List, get, create, update, delete — over REST and the sexp wire — with filters and sorts drawn from the same query forms views use, executing inside capability environments so an endpoint cannot out-permission the application. The OpenAPI document is a query against the catalog: it can't drift from the schema because it is the schema, projected. Declare a resource and the endpoint exists seconds later — not a codegen pipeline, a SELECT. And API keys are capability grants — scoped, expiring, audit-rowed — not ambient tokens with a name.

Outbound crossings hold the same discipline. A webhook is a subscription row on the change stream — transactional with the write that caused it, durable, replayable from history when the receiver was down. An import is a governed transaction: a CSV maps onto resource forms, runs the same verifiers as every other write, dry-runs as a transaction that reports instead of committing — then lands whole or not at all, provenance on every row.

Translations complete the argument. Components emit meaning, not markup, so every human-visible string has one home: a label field on a catalogued definition. A translation is a row — (hash, locale) → string — bound at patch time, while the semantic types localize themselves: money, dates, and addresses format per locale from the type, not the template. A locale's coverage is a completeness query; an agent can translate the catalog overnight and the verifier reports exactly what's missing. No extraction pipeline, no .po files, no string that escaped them.

;; the endpoint exists because the resource does —
;; GET /api/deal?stage=won&sort=-amount · policy-scoped
;; openapi.json is a SELECT against the catalog
 
(defhook ledger-sync (deal :on (create update))
  :post "https://ledger.example/hooks" :sign hmac)
 
;; a translation is a row — types carry the formats
(translations deal :nl
  (stage "fase") (close-date "sluitdatum"))
rest + openapiendpoints and docs derive from the catalog — keys are capability grants
webhookssubscription rows — transactional with the write, replayable from history
import / exportmap, verify, dry-run, commit whole — one transaction, provenance kept
localestranslations are rows, coverage is a query — semantic types carry the formats
Unique feature № 7 — the resident agent

An agent isn't a client at the seam — it's a resident inside it

Strip the frameworks and an agent is embarrassingly small: a recursive function over a growing list of messages, whose one tool is eval, whose memory is JSON on a disk. Governed evaluation — the admission gate — already governs the code such an agent writes, treating it as an author standing at the seam. This is the other half, and the inversion: the agent itself becomes a resident object. Because every part it is made of already has a first-class home here — the loop is a continuation, the tool is governed evaluation, the memory is the catalog, the audit is the history tier — making it native is almost entirely reuse. The one new thing is a name for the composition: defagent.

The loop is a continuation. The accumulated message history is its environment, each model call is a step, and the whole conversation is a row — so it survives a deploy, parks for three days on a human's approval at zero cost, and resumes on any node. The article threads that state by hand and writes it to disk in twenty lines; the continuation store already holds workflows and sessions exactly this way, and the agent simply joins them. And the tool is admission. The article's most powerful move — one tool, eval — carries its one caveat: eval requires a sandbox, so the author runs it in Docker. Kern is that sandbox, as the storage format — reasoning-time evals run fuel-metered in the interpreter tier, where an ungranted capability is unnameable, not refused. There is no box to bolt on, because isolation is where the code already lives.

And the skills. Handed an API key mid-conversation, the article's agent writes a brave-search function into its running image — a real capability, invented at runtime — but it lives only in the transcript: a fresh session must re-read its own history and re-eval its own code to get it back. Capability is just a story the agent tells itself. Kern makes learning admission: a skill worth keeping passes the same gate as engineer code — catalog parity, capability audit, PII flow, contracts, inside the insert transaction — and becomes a content-addressed row, verified once, named by hash, scoped to its org, revocable, and live in every future session with no rehydration. The story it tells itself becomes a row it can query — and the transcript still holds the reasoning that built it, as provenance and an as-of trail of exactly how the skill was learned, not code that must be re-run to exist.

What is genuinely new is the identity. A defagent is a definition like a resource or a workflow — a row binding a catalogued prompt, a role (its capability bundle, and so its blast radius), a fuel budget, a model endpoint held as a metered capability, and a tool surface: the exact set of definitions it may name. It is content-addressed, scope-columned, versioned, as-of queryable, and delegable by attenuation — a user grants it a subset of their own capabilities and never more. So the agent is a principal, the same kind of row a human or an API key is, the same substance as the application it operates on; a role was already a named bundle of capabilities grantable to a human, a key, or an agent alike — this is the agent that phrase was always describing. Its self-authored skills stay sandbox-tier — metered, interpreted, capability-scoped — because admitted native code shares the kernel's heap; promotion to the native tier is a capability the agent does not hold, gated behind an operator's restart. Self-extension is real, and it is fenced exactly where the honest edges draw the line.

;; an agent is a definition — a row: prompt, role, budget, scope
(defagent deal-desk
  (prompt sales-copilot)        ; a catalogued prompt — by hash
  (model  (opus :budget 2e5 tokens)) ; a metered capability
  (grant  (role sales-agent))     ; its tools ARE its capabilities
  (fuel   1e6) (scope (org acme))) ; sandbox tier · one overlay
 
;; the loop isn't written — it's a continuation over the thread;
;; a skill it learns is an admission, not a story it re-tells
(defworkflow turn (deal-desk thread)
  (let ((said (step (complete deal-desk thread)))) ; model call · metered · journaled
    (case (kind said)
      (:say   said)               ; base case — answered in words
      (:learn (admit! (skill said)    ; it WROTE a capability, mid-loop —
              :verify '(catalog-parity capability-audit pii-flow contracts)) ; a row now, not a re-eval
              (await (turn deal-desk (add thread said))))
      (:do    (await (turn deal-desk (add thread (step (invoke said))))))))) ; ungranted is unnameable
the loopa continuation over the message history — survives deploys, parks on await at zero cost, resumes anywhere
the toolgoverned eval, fuel-metered — the Docker box the article bolts on is the storage format
skillsadmitted catalog rows — verified, hash-named, scoped, revocable; queried by hash, not re-eval'd from a transcript
memorythe history tier — the transcript is provenance, as-of: replay the exact skills that ran that day
identitya defagent principal — content-addressed, scoped, versioned, delegable by attenuation
exactly-oncethe invoked tool's effect + the appended message commit in one transaction — the tool never double-fires on a crash
λ
Every agent framework hand-rolls the loop, the memory, and the sandbox — because its substrate offers none. Kern offers all three as its normal behavior, so the agent was never a client at the seam: it is a resident — its loop a continuation, its skills the catalog, its memory the history tier, its identity a row you can SELECT.
Trust, baked into the schema

Audit everything. Forget anyone. Including the code.

A (pii …) field never enters the history stream — it lives in a mutable, per-subject-keyed vault, with keys held in an external KMS outside the backup surface; everything downstream carries only a token. The derived components know what the schema knows, so a PII field renders masked by default — plaintext materializes only behind an explicit, expiring reveal grant, approved by a second party and written to a tamper-evident audit row. Erasure is crypto-shredding: destroy a subject's key and every copy of their personal data — live, replica, history, backup — becomes permanently undecryptable ciphertext, while the history itself still replays perfectly.

One deliberate exception to “nothing else”: the vault's cipher and the password hash bind to a vetted, audited implementation — never a hand-rolled one. The two primitives that actually guard secrets — authenticated symmetric encryption and the password KDF — are also the two whose subtle errors are invisible to ordinary tests: a broken cipher still round-trips, a wrong hash still verifies against itself. A vault is exactly where “from scratch” stops being a virtue, so kern uses an authenticated construction (AEAD, not unauthenticated CBC) from a reviewed library and treats the primitive as a dependency to pin and audit, not to write.

The operator plane derives from the same resources as the product: your support console, account views, and billing rollups are queries against the objects you already ship, not a second admin app built six months later. An operator impersonating a tenant sees the tenant's real UI with PII masked by default — support without disclosure, structurally.

And because code is rows, the audit story finally covers the whole system. Who changed this workflow, when, what did it look like before, who approved it — the questions a SOC 2 auditor calls “change management” and every other stack answers with screenshots of a CI dashboard — are, in kern, the same query as “who changed this record.” One audit machinery, both substances.

;; right-to-erasure, in one line
(vault-shred! subject-key)
;; history intact · PII unrecoverable
 
;; change management is a query, not a screenshot
(select (definitions :as-of "2026-03-01")
  :where (= name 'follow-up))
;; → the exact workflow that ran that day,
;; who admitted it, and what the verifiers said
the vaultpersonal data, mutable, per-subject keys — tokens everywhere downstream
maskingthe default render, derived from the declaration — reveals are granted, expiring, logged
the operator planethe same objects, masked impersonation — support without disclosure
change managementcode has audit rows and as-of — the compliance answer is a query
The language

Designed for machine writing, human auditing

“World-class for AI” is a language-design spec, not a marketing line, and kern's dialect is built to it. Every definition is stored content-addressed — the idea Unison proved — so the hash is the identity and names are catalog pointers. Renames are metadata. Drift is impossible. An agent's patch references exact hashes; there is no “which status did it mean.”

One canonical printer. Exactly one rendering of every form — no formatting diffs, no style debates, minimal tokens. Structural diffs are the only diffs; the review UI shows semantic change or nothing. A deliberately small, boring core: no reader macros in the application dialect, a fixed special-form set, effects only through capabilities. Every constraint on human expressiveness is a gift to machine verifiability — and in this substrate, humans audit more than they type. Contracts, docstrings, and examples are machine-readable fields on the definition's row, not comments beside it — so the reference manual is a projection of the catalog, never stale, and because views and components are definitions too, its screenshots are live renders, not images that rot.

The host split is pragmatic: the kernel is Common Lisp on SBCL — a mature native compiler available at runtime, plus the condition system the workflow engine is built on. The application dialect is kern's own, CPS-transformed so continuations serialize, compiled through SBCL at admission for the hot path. The host provides an engine; the dialect is the product.

exists
SBCL

A world-class native compiler where compile is a core function — compile-on-admission is a language feature, not a build system. Conditions, restarts, and thirty years of image-based living systems.

exists
Postgres primitives

LISTEN/NOTIFY as the bus, SKIP LOCKED queues, partitioned history, transactional DDL — pubsub, presence, cron, and the change stream are queries, not services. The database is the distribution layer.

prior art
Smalltalk · Unison · Racket · LiveView · DBOS · Ash · Twenty

The browser inside the image, content-addressed code, serializable-continuation web serving, the static/dynamic split, durable execution over rows, resource derivation, runtime-mutable metadata with APIs generated from it. Proven separately, elsewhere — kern is where they meet. Credits, not dependencies.

kern
The evaluator + fuel meter

Kern's own dialect: CPS-transformed, capability-scoped environments, exact step and allocation budgets for sandbox tiers, native compilation for admitted code.

kern
The admission pipeline

Verifiers in the insert transaction: catalog parity, capability audit, PII flow, contracts. The type system the dynamic dialect must earn — fail-closed, per transaction.

kern
The continuation store

Workflows, sessions, and conditions serialized as rows with wake conditions — resumable by any node, versioned against exact code hashes.

kern
The reactive layer

Expansion-time static/dynamic split, dependency-exact invalidation derived from the catalog, byte patches on an owned wire, a closed ~25-component vocabulary.

kern
The vault + operator plane

Per-subject keyed PII vault, crypto-shred erasure, masked-by-default rendering, expiring reveal grants, and an operator plane derived from the same resources — trust as schema, not process.

kern
The mutable surface

Semantic field types, views · boards · dashboards as catalogued queries, a workflow canvas that is itself catalogued components, and a Settings client that submits admission transactions — no-code and code are one substance.

kern
The derived edge

REST + OpenAPI projected from the catalog, API keys as capability grants, webhook subscription rows, per-locale translation rows bound at patch time.

kern
The resident agent

A defagent binds a catalogued prompt, a role, a fuel budget, and a metered model endpoint — its loop a continuation, its learned skills admitted rows, its memory the history tier. Not a subsystem: a composition of the rows above.

Simplicity, measured in deletions

Infrastructure you will never run

Every box below is a thing most stacks deploy, version, secure, monitor, and pay for. In kern, each one is a table, a query, or the evaluator's normal behavior — inside the two things you already have. The last row is the unusual one: kern deletes the machinery of shipping software itself, because “ship” stopped being a concept the moment the code became rows.

Redis · broker · cron

→ postgres — queues, pubsub, and schedules are rows and NOTIFY, transactional with your data

Orchestrator cluster

→ continuations — durable execution is the evaluator's normal behavior, checkpoints are rows

Session store

→ rows — sessions are continuations; deploys and node loss don't drop them

Deploy pipeline

→ a transaction — admit, don't ship; the verifiers are the gate, commit is the release

Artifact registry

→ the history tier — every version of every definition, content-addressed, immortal

Rollback choreography

→ as-of — the previous application is a WHERE clause, not a runbook

Workflow-versioning machinery

→ as-of — paused workflows resume against the exact hashes they started with

Frontend build pipeline

→ gone — no bundler, no node_modules; a vendored ~15 KB patch applier

ORM + admin scaffold

→ the resource — schema, queries, forms, policy, and the operator plane derive from declarations

Code search / IDE indexer

→ the catalog — the codebase has a schema; agents query it over MCP

git as truth

→ a projection — the repo is a deterministic view of the code tables; the image is truth

Guardrail bolt-ons

→ admission — capability environments + verifiers in the transaction; governance is the storage format

Agent framework · JSON memory · sandbox VM

→ a resident — the loop is a continuation, skills are admitted rows, isolation is the storage format

Metadata engine · EAV layer

→ the substrate — custom objects are resource forms; no second engine interpreting a second schema

API codegen · gateway config

→ the catalog — endpoints, OpenAPI, webhooks, and keys are projections and grants

i18n extraction · .po pipeline

→ rows — labels live on definitions; a locale is a coverage query, translating it is an agent's chore

Who it serves

Sized for almost everyone — built for the agent era

A single well-provisioned Postgres sustains thousands of governed write-interactions per second; the median SaaS never sees one percent of that. B2B SaaS, vertical products, internal tools, and — above all — products where an AI agent does the work: an agent operating on live customer data needs a catalog it can't hallucinate off of, capabilities that bound what it may touch, a vault so it never lands a name in the clear, and an audit row for every action it takes. That is not a feature list to assemble; in kern it is the storage format.

Scale up by buying a bigger Postgres — the most boring, best-understood move in the industry. Scale out by adding identical stateless kernels: sessions are rows, so no node is special, none is sticky, and any node resumes anything. You will exit this envelope around the time you can afford a platform team to build the next one.

The two-things test, passed twice: your infrastructure is a kernel and a Postgres — and your entire application, past and present, is something you can SELECT.
What we won't pretend

The honest edges

Named, not waved away
  • The runtime pond is us. No BEAM, no JVM, no Node underneath: kern owns its scheduler, its reactor, and its GC profile. SBCL's generational GC pauses at large heaps — mitigated by the architecture itself (state lives in Postgres, sessions checkpoint out, per-node heaps stay small) — but when it pages, it pages us.
  • Shared heap, stated plainly. Compiled, admitted code has no memory isolation from the kernel. Fuel metering covers the sandbox tiers; admitted native code is trusted-by-verification — which makes the verifier suite and capability environments load-bearing, and says exactly where the security boundary is. That is the trade for exact metering and one runtime.
  • A model call is an effect the fuel meter can't price. Fuel counts interpreter steps and allocations; an agent's dominant cost is tokens, dollars, and turns — a loop can be cheap in fuel while burning a fortune, or looping for days. So the metered model endpoint carries its own budget axis, a turn/token/dollar ceiling above the step meter, and each completion is journaled before the turn commits, so a resume replays the recorded answer instead of re-calling and re-billing the model — save for the one narrow window, between the call returning and the journal landing, where a crash forces a single re-call. That is the one honest extension the resident agent asks of the substrate; everything else it needs, kern already had.
  • Capabilities bound the blast radius, not the judgment. A resident agent is bounded by construction — it cannot name what it wasn't granted, cannot admit outside its capabilities, cannot land a name in the clear. But a prompt-injected agent is still an agent acting inside its grant, and everything inside a grant is, by construction, allowed: an injection turns untrusted data into a within-capability action the verifiers pass, because they type-check the code, not the wisdom of the instruction behind it. An action that stays in the database is a reversible row — as-of rewinds it, the audit row attributes it, an incident cut down to a WHERE clause. But a granted egress — a webhook it may post, a mail it may send — crosses the seam and is gone; no clause un-sends it. That an action should have been taken is a guarantee no substrate can offer. Least-capability grants, the narrowest possible egress surface, and human escalation on high-consequence restarts are the mitigation; provable judgment is on offer from no one, and we won't pretend otherwise.
  • A dynamic dialect can't say “it doesn't compile.” A statically-typed language makes a hallucinated field a compiler error; kern rebuilds that guarantee from contracts and verifiers at admission time. The verifier suite is the type system, and its coverage is the real boundary of every claim in this document — a guarantee the verifiers don't check is a guarantee the framework doesn't have, however confidently the DSL declares it. That is not hypothetical: the default outcome of hand-built frameworks is a policy layer that reads as enforced and is wired into nothing, and the whole point of admission is to make that specific failure impossible. But it relocates trust rather than removing it: kern is exactly as safe as its verifiers are complete, so their coverage is versioned, tested adversarially, and stated as the security boundary it is. A gradually-typed layer is the eventual reinforcement, not the v1 promise.
  • Continuation serialization is the deepest engineering bet. Closures capturing environments must serialize stably across years. Content-addressing helps — checkpoints reference immortal hashes — but capture discipline has to be designed into the dialect from day one. This burden is owned, tested red-path-first, and shipped before anything else gets clever.
  • Bootstrap cost is real, and it lives in the hardening, not the happy path. The surface is smaller than it looks — Postgres does queue, cron, pubsub, presence, sessions, and history, so the kernel is a reactor, an evaluator, a verifier, and a wire protocol — and a working end-to-end substrate is a few thousand lines, not a moonshot. The distance is between “passes its own tests” and “survives the network”: a database driver that resynchronizes cleanly after a mid-query error instead of poisoning a pooled connection, a connection layer that can't be exhausted by idle clients holding workers, correct UTF-8 at every byte boundary, TLS on every wire, protocol edge cases handled the way the RFC demands and not the way the demo needed. Mature runtimes carry a decade of exactly that scar tissue; kern re-earns it deliberately, red-path-first, and does not pretend the green path is the hard part.
  • git interop is mandatory, and the inversion will scare people. A deterministic two-way projection keeps review, CI, and editors working — but the image is truth and the repo is a view, and that reversal terrifies before it comforts. The projection ships in v1 or trust never arrives.
  • One canonical style, no exceptions. The printer's single rendering deletes formatting diffs and token waste — and personal style with them. Deliberate: in this substrate humans audit far more than they type. A trade stated, not hidden.
  • A new dialect is a small hiring pond. The counterweight — that the marginal author of code is increasingly an agent, and the language is optimized for machine writing and human auditing — is a thesis, not a fact. We say so, and we ship the git projection and the canonical docs so a strong generalist is productive in days, not months.
  • Postgres has ceilings. Partitioning and rollups carry most products indefinitely; past ~100M+ raw history rows with ad-hoc scan appetite, a columnar mirror is a per-product bolt-on decision. The primary's write budget is the sharding signal — and because every row is org-scoped, the shard key is the tenant. Nothing about code-as-rows changes either number.
  • Runtime schema change is live DDL. A tenant's field-add is a migration on a hot table. Admission serializes and verifies it, but lock behavior, backfill cost, and per-tenant divergence are operational budgets, not free moves — additive changes are instant, destructive ones are gated, and the mutable surface is rate-limited by design.
  • A mutable surface is a stability contract. Once tenants build views, imports, and webhooks on a field, rename and delete become deprecation events. Content-addressing makes renames metadata inside kern — but external callers hold names, so the derived API carries aliases and sunset windows, and deletion is a staged retirement, not a DROP.
  • Tenant overlays meet product upgrades head-on. Every base release re-verifies every org's overlay against the new catalog. The win is where breakage surfaces — at admission, per tenant, as a named verifier failure before the upgrade lands, never as a runtime surprise after. The cost is fleet machinery: cohort rollouts, migration paths, a support lane for the overlay whose computed field leaned on a column the new base retired. Salesforce built an empire on this being hard. Kern makes it a query instead of a mystery — not free.
  • Derivation buys correctness, not delight. A property panel derived from the grammar is complete and current — and generic. The surfaces people live in still earn hand-built components; the difference in kern is that the hand-built replacement is admitted into the same slot as rows, so polish overlays derivation instead of forking it.
  • Erasure is as final as your backup posture. The vault's key store rides outside the WAL and backup surface precisely so a restore resurrects ciphertext, never keys — and a shredded subject lingers in cold backups exactly as long as your stated retention. Name it.
  • Workflows are for mutations, never reads. Each step costs a Postgres write; the render path stays out of the engine. A design rule, not a tip.
  • A device-resident kernel ships later, deliberately. Code syncs like rows, so an offline-capable local kernel is a natural consequence of the architecture, not a promise bolted on. v1 is server-rendered SaaS; the device lane is its own product, scoped after the change stream has earned it in production.
The pitch, in one sentence

Your app, your schema, your views, your workflows, your policies, your API, your translations, your agents and the patches they write, and your tenants' customizations are one substance — versioned expressions in one Postgres, evaluated by one kernel. Deploying, auditing, syncing, localizing, and governing them is one mechanism instead of ten.