A few lines of TypeScript.
A complete App.
Describe your records, screens and rules. mistri serves the App on PostgreSQL, with lists, record pages, forms, dashboards, sign-in and roles, and an assistant.
Built for the agent loop: mistri agent init teaches your coding agent the DSL, mistri check names what is wrong and how to fix it, and mistri shot shows it the screen.
- write
- check
- see
machine: machine('status', { draft: { on: { signLease: 'active' } }, active: { on: { giveNotice: 'notice' } }, notice: { on: { endLease: 'ended' } }, ended: { final: true },}),page('lease', [ header({ status: 'status', facts: ['start_on', 'end_on', 'rent', 'deposit'] }), fields(['balance', 'charged', 'paid']), related('Charges', 'charge', 'lease_id', /* … */), // … payments card('Home', 'unit_id', ['property_id', 'bedrooms', 'sqft']), card('Resident', 'resident_id', ['email', 'phone']),]),

Start a new App
Section titled “Start a new App”Two tables and twenty-one lines of TypeScript are an App you can list, search, filter and edit. Write the schema you want and the screens over it.
create table customer ( id serial primary key, name text not null, email text);
create table invoice ( id serial primary key, customer_id integer not null references customer (id), total numeric(10, 2) not null, paid boolean not null default false);{ "connections": [{ "name": "billing", "url": "postgres://localhost/billing", "writable": true }]}import { defineApp, listView } from 'tablewalk/app';
export default defineApp({ id: 'billing', name: 'Billing', connection: 'billing', resources: { customer: { access: 'write' }, invoice: { access: 'write' }, }, views: { invoices: listView('Invoices', 'invoice', { show: ['customer_id', 'total', 'paid'], }), customers: listView('Customers', 'customer', { show: ['name', 'email'], }), }, nav: ['invoices', 'customers'], home: 'invoices',});npm init -y && npm pkg set type=module && npm install tablewalk typescriptcreatedb billingnpx mistri migrate diff init --app . # writes migrations/V1__init.sql from schema.sqlnpx mistri migrate up --app . # applies it, then checks the App against itnpx mistri dev # serves it, and reloads on every saveThe first migration is written for you to read before it runs, and the next
comes the same way when you change schema.sql. No front end to write, no API
to wire, no build step to serve.
Quickstart: an App from a schema
Starting with nothing? npx mistri new billing makes a starter App with a
database of its own. Get started
Already have a database? mistri drafts the App from it. Quickstart: an App from your database
A few lines is a screen
Section titled “A few lines is a screen”Lintel, the flagship example, runs a property company’s three Denver buildings: leases, a rent ledger, repairs, a storeroom, a public listings site and a resident portal. It is about 1,500 lines of TypeScript, commands and jobs included, not counting blank lines, comments or tests, checked against the database before anyone opens a page. Three cuts from it:
A list people work in
units: listView('Rent roll', 'unit', { icon: 'buildings', // … editable: ['market_rent', 'available_on', 'listed'], // … tabs: [An asking rent, a date and a listing change in place; the tabs are the rent roll’s segments, each counted.

Buttons from the road a lease travels
machine: machine('status', { draft: { on: { signLease: 'active' } }, // … ended: { final: true },Each state names the verbs that leave it. They become the page’s buttons and its stepper, and the server refuses any other change of status.

A card for each number that matters
metric('Rent collected', 'charge', { query: 'kind = rent and status = paid', over: 'due_on', measure: 'sum amount as collected', better: 'up', // …}),Over the window the reader picks, with its change from the period before and its trend across the window, drawn unasked.

Beautiful out of the box
Section titled “Beautiful out of the box”Every list, record page, form and dashboard is drawn for you, in a design system made for work people do all day.

assistant() entry. How it works
listView call. How it works
page(). How it works
metric or chart call each. How it works- Keys for everything. ⌘K finds any view, and j and k step through the records of the list you came from. Navigation
- An address for every state. A list’s tab, search, filters and sort, a record, a dashboard’s window: copy the address and share it. Addresses
- Edit in place. Name the columns a list may change in its cells, and a record’s side panel edits where it reads. Lists
- Forms from the schema. Required fields marked, references searched by name, a switch for a yes or no. Forms
- Phone layouts. On a phone the menu folds into the bar, dialogs fill the screen and a record’s side panel follows the page. Navigation
- Light and dark, three looks. Lumen, Mono and Sage; each reader picks a look and System, Light or Dark under Preferences. Looks
Same app. Your brand.
Section titled “Same app. Your brand.”


The same Lintel, data and addresses, re-dressed by one validated 61-line theme file that changes 98 of Lumen’s
values. mistri check tests every color pair it makes in light and dark, and refuses one it cannot
read, naming a color that passes; mistri grade then measures the rendered pages, 26 of them at four widths: 99 of 100, and no finding the theme causes.
A look of your own
Yours to change
Section titled “Yours to change”When a default is not enough, each layer has a hook, and what you write is still checked against the database.
A component of your own
export const warehouseBins = custom('Floor', binMap, { rows: { bins: { from: 'bin', whose: 'warehouse_id', /* … */ }, },});A floor map drawn in React with tablewalk/ui: typed rows in, read under the
reader’s grants, changes only through commands, in every look.

defineTheme: colors by purpose, faces and corners, light and dark.CommandsServer operations with declared inputs, a typed db and refusals of their own, in one transaction.Record pagesSections, child tabs and a side panel, your own components among them.DashboardsMetrics, charts, breakdowns and waiting work, over a window the reader picks.Custom componentsA React component fed by checked reads, drawn with the looks’ tokens.FormsGroups, defaults, help and conditions for the New form and record editing.Built for the agent loop
Section titled “Built for the agent loop”mistri is made for a coding agent to build with: little to write, a precise answer when it is wrong, and a way to see what the running App did. Install the skill, give the agent a brief, and it works against the same checks you would.
- The skill where your agent reads it.
mistri agent initinstalls it for Claude Code, Codex, Cursor and GitHub Copilot. Build with a coding agent - The schema in its context.
mistri schema --briefprints one line per table, with keys, references and allowed values. - Wrong fails before a page opens.
mistri checktypechecks the App against the generated schema types and validates it against the database: a stable code, the file, and the fix, as lines or JSON. Check codes - It sees what it did.
--devrecords every request’s SQL, each command’s input and refusal cause, logs, N+1 and slow statements and page vitals, andmistri observe --mcphands them to the agent as tools;mistri shotdraws the App’s pages as PNG files. Observe
$ npx mistri agent init$ npx mistri schema --brief…customer(id* int, name text, email text?)invoice(id* int, customer_id int → customer, total num, paid bool)$ npx mistri check --app . --briefunknown-column app.ts view "invoices": This view's show cannot be resolved on "invoice": "amount" ("amount" is not a column on invoice.). — fix: use a column the table has (the message names the nearest; mistri schema --brief lists them)An App’s own reads and commands reach an agent as MCP tools too, under a scoped token and the same grants as its screens. In production the same traces go to your collector over OpenTelemetry. App API · Observe and operate
Also built in
Section titled “Also built in”mistri build checks and bundles once, for a container.What is stable and what comes next: Status and roadmap.
Three sample Apps to run and read, and not only for the back office: Tick, a to-do list in one file; Lintel, a staff desk and a resident portal in one App, with its own tests; and Ladle, a cooking school’s own React site with the App inside it. A CRM, a service desk, a warehouse and the like are patterns in the agent skill’s recipes, not samples. Sample Apps
And tablewalk, a database browser. npx tablewalk postgres://… opens any PostgreSQL, MySQL/MariaDB or SQLite database read-only, its foreign keys links to follow; a package of its own, with no App in it. The database browser
MIT license (the LICENSE file in the npm package)


