Skip to content

Command line

Every tablewalk command and flag, laid out from the help text the command prints: what each does, and where its section is. npx tablewalk --help all prints the same text, npx tablewalk --help the commands and common options on one screen, and npx tablewalk <command> --help one command’s.

The first line of the help says it: tablewalk — browse a relational database as a graph you walk.

Command What it does
tablewalk start with nothing; the page asks for a connection
tablewalk <sqlite-file> Open it in the database browser.
tablewalk postgres://user:pass@host/db Open it in the database browser.
tablewalk --db postgres://… --schema public --port 4111 Open it in the database browser.
tablewalk check --app <dir> types, typecheck and validation of an App (see below)
tablewalk build --app <dir> make an App ready for production (see below)
tablewalk new --api <name> [<dir>] an App that is only an API: a resource, a command, its event, tokens and MCP; no pages
tablewalk suite <suite.json> serve a local suite with per-App configuration; its paths are relative to the suite file’s folder
tablewalk virtual <service.ts> run a typed virtual HTTP service for tests
tablewalk flags --help explicit flag control-store provisioning
tablewalk experiments --help explicit experiment evidence-store provisioning
tablewalk analytics --help behavior-analytics store provisioning and funnels
tablewalk shot --app <dir> [<route>…] an App’s pages as PNG files, to look at (see below)
tablewalk grade [<app-dir>] an App’s pages measured against the rubric (see below)
`tablewalk theme show <look .>`
tablewalk schema --brief [<db>] the schema as one line per table, for an agent’s context
tablewalk explain <check-code> what a check code means, with a fix example
tablewalk messages extract [--app <dir>] [--locale <tag>] an App’s words for a translator: a catalog’s missing keys appended as comments, or its literal captions
tablewalk migrate <command> change the schema with versioned SQL files (see below)
tablewalk seed [--app <dir>] believable rows in an App’s writable database (see below)
tablewalk dev [<app-dir>] serve an App with a dev secret and a sign-in per role (see below)
tablewalk test init [<app-dir>] a starter suite: a journey per role, a permissions matrix (see below)
tablewalk flows start <resource> start a resource’s flows on records already there (see below)
tablewalk jobs run <name> run one of an App’s jobs at times now, once (see below)
`tablewalk tenant clone import
tablewalk docs [topic] the App reference, offline: access, relationships, dates, dashboards, reload and restart, …
tablewalk agent init put the agent skill where coding agents read it (see below)
tablewalk compare-openapi <previous.json> <current.json> compare two exported App API documents; no server
tablewalk api-token --help local token-store maintenance; no server
tablewalk mail-dns [<config.json>] the SPF, DKIM and DMARC records a tenant App’s mail is sent under; makes the DKIM key if it is missing

The server’s flags: where the data is, how it is served, and what an App adds.

Flag What it does
-d, --db <target> SQLite file path or postgres:// URL (or set DATABASE_URL, used when no config names connections)
-p, --port <n> Port to listen on (default 4111)
--host <addr> Interface to bind (default 127.0.0.1, loopback only)
--allowed-host <name> Also answer for this Host header; repeatable. Needed behind a proxy or in a container reached by name. Or set TABLEWALK_ALLOWED_HOSTS=a.example,b.example
`–trust-proxy <address CIDR>`
--trust-proxy-hops <n> Behind a managed front end with no address to name (Cloud Run, a cloud load balancer): how many proxies append to X-Forwarded-For. The client is the address n places from the right; anything the visitor wrote left of it is ignored. Rate limiting only. Or set TABLEWALK_TRUST_PROXY_HOPS=1
--trust-proxy-cloudflare Read CF-Connecting-IP as the client. Only when the server is reachable through Cloudflare alone (a tunnel or its address ranges): anyone else can write the header. Not with –trust-proxy-hops. Or set TABLEWALK_TRUST_PROXY_CLOUDFLARE=1
--auth-proxy-header <name> Behind a proxy that signs people in: the header it sets with the user (X-Forwarded-User). Requests from beyond loopback must carry it, and every write is journaled with it. Or set TABLEWALK_AUTH_PROXY_HEADER.
--schema <name> Postgres only; repeatable. Defaults to all user schemas.
--read-pool <n> PostgreSQL connections that may read at once, per database (default 4; an App’s follows its writers, at most 64). A connection’s “pool”: { “read”: n } in tablewalk.json wins. Or set TABLEWALK_READ_POOL.
--write-pool <n> Connections an App may write through at once, per PostgreSQL or MySQL database (default twice the cores, 4 to 16; at most 64); the database browser always writes through one. A connection’s “pool”: { “write”: n } wins. Or set TABLEWALK_WRITE_POOL.
--lock-timeout <s> Seconds a write waits for a row or table another transaction holds before it is refused (default 5, 0.1 to 30). Or set TABLEWALK_LOCK_TIMEOUT.
--pooler <mode> session (the default): PostgreSQL is reached directly, or through a pooler that keeps a client on one server connection. transaction: through PgBouncer in transaction mode or RDS Proxy; the read-only guard and the statement cap are then set per transaction. Or set TABLEWALK_POOLER.
--drain-delay <s> Seconds /readyz answers 503 before the listener closes on SIGTERM, so a balancer that probes takes this instance out first (default 0, up to 120). Or set TABLEWALK_DRAIN_DELAY.
--shutdown-grace <s> Seconds a response still unfinished after the drain may keep the process before it is cut (default 10, 1 to 300). Hold the drain and this inside the platform’s stop timeout. Or set TABLEWALK_SHUTDOWN_GRACE.
--config <path> Read connections from this file, and the cache, search, webhook, email (smtps://, smtp:// or log:) and OAuth services beside them; the paths in it are relative to its folder
--no-config Ignore any config file
--app <dir> Serve an app: a directory whose app.ts (or app.mjs / app.json) declares its resources, views, navigation and pages. Repeatable; with more than one, every app names its connection. A save reloads it; a change to its grants, sources or sign-in needs a restart. tablewalk docs start is where to begin. One App is served at the root (/leases/12); several each under its id (/lintel/leases/12).
--mount = Where an App is served, over that default: / makes it the main App at the root, /billing puts it there. Repeatable, one per App. Or “mount” in the config.
--base-path <path> The path a reverse proxy serves this server under, like /tools: every address, asset, API call, cookie and link carries it. Or “basePath” in the config, or set TABLEWALK_BASE_PATH.
--app-entry <file> With one –app: the manifest to read, when it is not the directory’s own app.ts. The directory stays the App’s root: its components, pages/, features/ and authority.lock.
--commands <file> The server-only default defineCommands registry of one –app, when it is not the commands.ts beside the App’s manifest, which is read without the flag. The App names its commands and is served their input from it. Requires authenticated access and an explicitly migrated writable SQLite or PostgreSQL command source; restart to reload.
--jobs <file> Load a server-only default defineJobs module with one –app and its commands: jobs that run a command, as a declared principal, when a committed event says so or at the times they say (every). Its stores are provisioned once from tablewalk/maintenance; restart to reload.
--policy <file> Load a server-only row or tenant policy for one authenticated, named-source –app whose auth.rows is “policy” or “tenant”. Tenant Apps require one existing provisioned SQLite or PostgreSQL source. Row policies support reads and explicit commands and scoped creation/updates with Undo. Delete/restore requires an explicit deleted-record policy. App and policy changes require a restart.
`–from-openapi <spec.json url>`
`–openapi <spec.json url>`
--api-url <url> With –openapi: the API’s base URL, when the spec’s servers do not give a usable one.
--api-token-env <name> With –openapi: send $name as a bearer token.
--scaffold-app <dir> Draft an App project for the active connection, in TypeScript built from the tablewalk/app helpers: list views, display rules, an Overview dashboard, record pages, tablewalk-schema.d.ts, tsconfig.json, an .oxfmtrc.json and (outside a tablewalk checkout) package.json with format scripts. A SQLite file is named by a tablewalk.json beside the App, by a path from there. It passes tablewalk check as written; a start to curate. With –config naming several connections, one App with a source per connection; a PostgreSQL or MySQL one holding several schemas is a source per schema on that one connection.
--name <name> With –scaffold-app: the App’s name, instead of one read off the database file’s.
--write-authority With tablewalk check: write each app’s authority.lock — what it grants, one fact per line, and each command’s contract — to commit beside it. The server refuses to start an app whose authority differs from its lock, and a reload that changes it.
--bump <command> With tablewalk check: move the command’s contract to its next version in authority.lock, when it changed and the App API, a job or kept receipts depend on it.
--reindex [resource] With –app and a search provider plugged in (search providers are packs, after 1.0): read every searched resource (or the one named) from the database into its search index, then exit. Run it after declaring search, and after writes made outside the App.
--demo Build a throwaway sample database and open it. Nothing of yours is touched; it lives in the temp directory.
--open Open a browser once the server is up
--dev Serve the dev observer at /_tablewalk/observe: every request’s trace with its SQL and parameters, commands with their input and refusal cause, logs, page errors. Loopback only, kept in memory, credentials masked. tablewalk observe prints its address. With an App, the theme bench too, at /_theme.
--mcp Speak MCP on stdio for coding agents, instead of HTTP: the database browser’s tools, which serve no App
--tools <name> Offer one job’s worth of tools instead of all of them: explore, migrate, seed, or full (the default).
--output-schema Advertise each tool’s output shape in tools/list. Off by default: it is half the listing’s size, and most agents never read it. Answers carry the same fields either way.
--export <fmt> Print the schema as md, mermaid, json, brief or types, then exit. types emits a .d.ts of this connection’s own table and column names for the app DSL — save it next to the app and “satisfies App” squiggles a typo the editor can catch before the server has to. openapi exports one App’s API document; it enables no API routes and requires –api-integers. asyncapi exports its AsyncAPI 3 document: feed events, webhooks and, with –jobs, inbound endpoints.
--api-integers <kind> With –export openapi or asyncapi, –api-reads or –app-mcp: decimal-string or number. No default; number refuses unsafe integer responses.
--api-reads Serve one authenticated App’s API and its MCP (…/$mcp): its reads, and with –commands the targeted commands it exposes. Requires integers and rate budget; bearer tokens only; SQLite or PostgreSQL. Serving must pass native readiness. On loopback, or beyond it (–host, –allowed-host, –trust-proxy) only for an App whose api declares network: true. A tenant App is served under its –policy, each token bound to one membership; a row-policy App under its –policy, each token reading its own account’s rows. A “kind”: “oauth” entry lets a signed-in person give an agent a token.
--api-requests-per-minute <count> Explicit –api-reads rate budget per token, 1–1000000; no default. Counted in this process.
--app-mcp Speak one authenticated App’s API as MCP tools on stdio instead of serving HTTP: its reads, and with –commands the targeted commands it exposes. The token is read from TABLEWALK_API_TOKEN, never the command line. Requires –api-integers. SQLite or PostgreSQL; a tenant or row-policy App under its –policy.
--lint [fmt] Report what the shape will cost — missing keys, unindexed references, naive timestamps — as text or json, then exit.
--fail-on <sev> With –lint, exit non-zero when a finding is at least this severe: high, medium or low. For CI.
--diff <conn> Compare the active connection’s shape with this one, print the differences, then exit. Exit code 1 when they differ, the way diff(1) does.
-h, --help Print the short help and stop; –help all prints this
-v, --version Print the version and stop

The one command of the loop.

tablewalk check --app <dir> [--app-entry <file>]
[<db> | --db <target> | --config <path>]
[--commands <file>] [--jobs <file>] [--policy <file>] [--bump <command>]
[--routes [--mount <app>=<path>] [--base-path <path>]]
[--design] [--fix] [--defaults] [--fail-on-design-warning] [--warnings]
[--pending [--pending-on <url>] [--migrations <dir>] [--history-table <name>]]
[--deploy [--replicas <n>]] [--json | --brief]
Three or four steps, each with its own verdict line. Schema types: regenerates
the App's tablewalk-schema.d.ts from the database when it has drifted (a
file tablewalk did not generate is left alone; an App validation refuses is
still typed from what it declares, a refused segment, computed column or
tags included, so its typecheck does not bury the one problem), its
tablewalk-commands.d.ts
from its commands module, which types the command names the App uses, and its
tablewalk-components.d.ts, a props type for each custom component from its
declaration (SalesSummaryProps). Typecheck: the App's
tsconfig.json, or the flags the agent guide gives, errors said as
file:line, the mismatch and a hint. Validation: the App loaded against its
database, with its problems and its warnings; an App with an authority.lock
beside its manifest fails when what it grants differs from the lock. Components, for an App that declares custom
components: each compiled, its sources failed on markup or code from text
and the server's environment, warned on what a look cannot dress. Every
step runs; the errors print first, and warnings are advisory (unless
--fail-on-design-warning is set) and print as one line a kind unless --warnings
lists them in full.
--routes also prints every address the App answers to (/{app}/leads/{id},
/{app}/leads/new, ...) and what each one opens. --design reads its pages,
forms and shell for compositions that come out basic (a list cut beside an
aside, a page that opens on a block of fields), each a warning with its fix:
tablewalk docs design; with --fix it first deletes from the App's source
every rule a restated warning names. --fix also writes into an App theme's
source each color or chart order its contrast and color-blind failures
say passes, and checks again; the themes step comes first, a line per
theme and mode, and fonts says the faces against their 150 KB budget. --defaults prints what each column is
drawn by when the App says nothing, and the layout each page is drawn in.
--pending first applies the pending migrations (tablewalk migrate) to a
scratch copy of the database — SQLite: a copy of the file; PostgreSQL:
CREATE DATABASE … TEMPLATE, which needs CREATEDB and no other session on
the database — and every step reads the copy. --pending-on <url> (which
implies --pending) runs them on the scratch database that URL names instead,
on any engine: a branch or restore of the real one, or an empty one the whole
history can build; for a hosted role without CREATEDB. It is never dropped
and never the App's own database; a \${NAME} in the URL is expanded. --deploy adds a step that
judges whether the App survives a container being replaced: it refuses a
source or auth.store on a SQLite file, the default auth.store, a missing
BETTER_AUTH_SECRET and a \${NAME} the environment lacks (run it with the
deployment's environment), and with --replicas <n> above one it warns that
rate limits are per process without a cache service. --json prints one JSON
document instead of the lines (schema/check.schema.json): every finding
of every step with its code, file, line, column, definition path and fix,
for an agent; the exit code is the same. --brief prints one line per
problem, "code file:line message — fix: …", and nothing else; repeated
warnings are a line a code and resource with a count (--warnings lists each).
Exit codes: 1 could not check, 2 schema types, 3 typecheck, 4 validation,
5 components, 6 a pending migration failed on the copy, 7 --deploy refused. What each step
checks and the common fixes: tablewalk docs check-codes.

Its pages as PNG files, for the agent that built it.

tablewalk shot --app <dir> [<route>…] [<db> | --db <target> | --config <path>]
[--commands <file>] [--jobs <file>] [--policy <file>]
[--theme <id|all>] [--palette <id>]
[--compare <look>] [--recipes <part>] [--dark | --light] [--phone | --desk | --both]
[--click <name>] [--type <field> <text>] [--press <key>] [--wait-for <text>] [--matrix]
[--width <px>] [--full] [--scale <1-3>] [--as <role|email> [--tenant <id>]]
[--out <dir>] [--text] [--browser <path>]
Serves the App on a free port with a temporary config directory (none of
yours is read or written), opens each route in Chromium once it has settled
and prints the path of each PNG. A route is /{app}/leads/12 or leads/12;
with none, every view and page the App declares, a page on its first record.
--theme <id|all> with shot or grade: a built-in look (lumen, mono,
sage) or one of the App's own themes instead of the
one it wears; with shot, all shoots every built-in
look, then the App's. The menu stands where that
theme suggests (shell.placement) unless the App's
ui.shell says, which a note says
--palette <id> with shot or grade: one of that theme's palettes
--compare <look> the App's theme (or --theme) beside that look, one
picture of the two, <page>-<mode>.png,
the look on the left
--recipes <part> one page (the home, or the route) in each value of
that recipe part, one contact sheet at
<part>-<page>-<mode>.png; a note
names values that draw the page nearly alike
--dark, --light
the scheme a reader picked: the page wears it as if
chosen in its menu, whatever the machine is set to
(without either, the machine's, light)
--click <name> before the picture, press the button, link, tab, menu
item or option with that accessible name, else the
visible text; css:<selector> names an element by
selector
--type <field> <text>
type into the box with that label or placeholder
(css:<selector> likewise)
--press <key> press a key (Enter, Escape, ArrowDown)
--wait-for <text> wait until that text is visible. These four run in
the order given, repeatable, on every route; the page is
settled (requests done, nothing busy) before the first
and after each, and a name that matches nothing in 15
seconds is refused with the step and the address
--matrix every built-in look and the App's own, light and dark, at a
desk and on a phone, for the routes: <page>-<look>-
<light|dark>-<desk|phone>.png and an index.html laying
them out. Takes no --theme, --palette, --dark, --light
or size flag
--phone, --desk, --both
390 wide, 1280 wide (the default), or both
--width <px> the window's width instead (320 to 2560), with --desk
or --phone: a narrow desk, a tablet
--full the whole page, not the window
--scale <1-3> device pixels per CSS pixel (default 1; 2 for a sharp
picture): the page lays out the same, the PNG is larger
--as <role> sign in as an account of that role the App's local
store holds (one with a person row first, then the
earliest made), in a copy of it (its password reset
there only), else as a fresh one, which owns no rows;
never a service role, which no person signs in as;
without it, signed out
--as <email> sign in as that account, already made (by tablewalk dev
or a local start), with the password in
TABLEWALK_AS_PASSWORD; its store is read, never written
--tenant <id> with --as on a tenant App: the tenant the role is held
in (a platform role needs none). The account is granted
a temporary membership in the App's tenant store,
journaled as maintenance:tablewalk-shot and revoked when
the shot ends; each page opens in that context
--out <dir> with shot: where every file goes, from the current
directory (default tablewalk-shots/<app> in the system's
temporary directory, where --compare, --recipes and
--matrix each write to compare/, recipes/ or matrix/)
--text with shot: print what each page shows (title, headings,
table columns and row count, actions, empty states, page
errors) as compact text instead of writing PNG files
--browser <path> a Chromium or Chrome to shoot with (default: the CHROME
variable, else Playwright's own, the machine's Chrome,
then any Chromium another Playwright version installed)
Needs Playwright's Chromium: install the playwright package where you run it,
then npx playwright install chromium; the machine's Chrome serves when
Playwright's browser is missing. Exit codes: 1 could not shoot, 2 a page threw or a
section could not be drawn.

A draft judged on believable data, with no seed script.

tablewalk seed [--app <dir>] [<db> | --db <target> | --config <path>]
[--connection <name>] [--rows <n>] [--seed <n>] [--append]
Fills the App's database (or every table, without --app) through its
adapter, parents before children: CHECK vocabularies, keys, NOT NULL and
unique columns hold. A status the App moves with a machine (or, without
an App, one read off the vocabulary) is spread over its states — most work
open, about a third done, a little called off — each record with the
stamps of the states it passed through. Dates fall across the last 90
days, and some open work is due today or overdue. Names, emails
(example.com) and phone numbers (555-01xx) come from a small word list.
Writes only to a connection whose config says "writable": true.
--rows <n> rows per main table (default 40); staff and lookups
get fewer, lines more
--seed <n> the same rows again, on the same day (default: one
chosen and printed)
--append add to tables that already hold rows; without it, a
table with rows is refused before anything is written
Exit codes: 1 could not run, 2 refused (read-only, or rows already there).

The rubric measured in a browser, not looked at.

tablewalk grade [<app-dir>] [<db> | --db <target> | --config <path>]
[--commands <file>] [--jobs <file>] [--policy <file>] [<route>…]
[--as <role|email|all> [--tenant <id>]] [--theme <id|all> [--palette <id|all>]]
[--json] [--browser <path>]
Serves the App as shot does and opens every view and a record of each page
at 1280, 390 and 320 wide, and at 390 in the dark scheme. Each is measured:
sideways scroll, figures and words cut off, controls drawn over each other,
targets under 40px on a phone, text and field edges below the contrast
rule, layout shift after the first paint, page errors and failed requests,
empty sections, two number formats in one surface, chart axes that are not
round, a record's name past two lines on a phone. One line per finding,
"code file:line page@widths message — fix: …", then a score out of 100.
The App is <app-dir>, else the current directory. An App with sign-in is
graded as the role that reads the most, unless --as names one (or an
account, as shot's --as <email> does).
--json with grade: one JSON document of the findings and score
--theme <id> with grade: grade in a built-in look or one of the App's
own themes instead of the one it wears; a finding
the theme causes is placed at the theme, and any
error fails it; all grades every look as one table,
a row each and a column each window
--palette all and --as all add a row for each palette (at 390, light and
dark, its colors only) and for signed out and each role. A finding of the
theme contract says its promise, C1 to C13: tablewalk docs theme-contract.
Exit codes: 1 could not grade, 2 an error-level finding. The codes and their
fixes: tablewalk docs design.

Every color, recipe, type and shape after extends.

tablewalk theme show <look|theme-id|.> [--app <dir>] [--diff <look|theme-id>] [--json]
A built-in look (lumen, mono, sage), or a theme of the App in --app (else
the current directory); . is the one the App wears. Every color in light
and dark and each palette's changes, every recipe, the type and the shape,
each value it inherits marked with where from: the look it extends, its
Default palette, the plain structure's default, or derived from its other
colors.
--diff <look> only what differs from that look or theme
--json with theme show: one JSON document

Versioned SQL files in Flyway’s format, forward only.

tablewalk migrate new <name> [--repeatable]
tablewalk migrate status | up | validate | baseline | repair
tablewalk migrate diff <name> | check | squash
[--app <dir>] [<db> | --db <target> | --config <path>]
[--connection <name>] [--schema <name>] [--migrations <dir>]
[--history-table <name>] [--dry-run] [--tool flyway]
[--schema-file <path>] [--dev-url <url>]
V3__add_payoff_quote.sql runs once, in version order, in its own
transaction (MySQL commits DDL as it goes: a failure there stops the run
and is recorded as failed); R__views.sql runs again whenever it changes.
up applies what is pending; with --app it then regenerates the App's schema
types and runs check, which names the view, page or command a dropped or
renamed column breaks. status lists what is applied and pending, the changes
made outside migrations (drift), and whether the App's types are older than
the database. validate fails on a changed, missing or failed file, a pending
one, or drift. baseline records a database that already has tables as
version 0, touching nothing, and writes its schema beside the migrations
(baseline.schema.json, never run). repair removes failed entries, accepts
changed files and marks missing ones deleted.
diff, check and squash read the schema you want from schema.sql beside the
migrations (db/migrations -> db/schema.sql). diff writes the next V file
holding what the migrations still lack to build it, for review: a rename
comes out as a drop and an add. The first is schema.sql itself, written
with no download. check fails (exit 2) when the migrations,
applied to a scratch database, do not build schema.sql. squash folds the V
files into one V1__baseline.sql, before a database worth keeping exists.
tablewalk's own __tablewalk_* tables are left out. The comparison is Atlas's
community build, pinned and checked by sha256, fetched on first use into
.tablewalk/bin with its update check off (TABLEWALK_ATLAS names your own);
serving never fetches it. PostgreSQL and SQLite.
--migrations <dir>
where the files are (default: migrations/ in the App, or
the connection's "migrations": { "dir": … } in
tablewalk.json, relative to that file)
--history-table <name>
the history table: Flyway's layout, named
__tablewalk_schema_history unless this or the
connection's "migrations": { "table" } says otherwise
(flyway_schema_history, to take over from Flyway)
--connection <name>
with several connections and no --app: which one
--dry-run with up, baseline or repair: print what would run
(the SQL, for up) and change nothing
--tool flyway run the command with an installed Flyway instead (on the
PATH, or TABLEWALK_FLYWAY), over the same files and history
--repeatable with new: an R__ file
--schema-file <path>
with diff, check or squash: the schema you want (default
schema.sql beside the migrations directory)
--dev-url <url> with diff, check or squash on PostgreSQL: the server
where two scratch databases are made and dropped
(default: the connection's; needs CREATEDB)
A run holds a lock — PostgreSQL: the advisory lock Flyway takes for the same
table; MySQL: GET_LOCK; SQLite: its write lock — so two deploys never migrate
at once. Scoped to the App's schemas (or the connection's, or --schema).
Exit codes: 1 could not run, 2 files and history disagree, 3 a migration
failed, 4 applied but the App no longer checks.
tablewalk migrate stores status | up
[--suite <suite.json>] [--app <dir> [--jobs <file>] [--policy <file>]]
[<db> | --db <target> | --config <path>] [--connection <name>]
The layouts tablewalk keeps in a database of its own (auth, tokens, history,
inbox, command journal and sequences, tenant stores, flags, experiments, analytics)
move forward only here: serving never migrates, and refuses a store behind
its layout with this command in the message. status prints one line per
store; up applies each pending step in its own transaction (PostgreSQL: under
an advisory lock) and is safe to run twice. --suite adds the suite's identity,
grant, preference, flag and experiment stores and each App's connections.
With --app, up also creates every store the App uses: the inbox its notify
rules write, the record history it shows, the command journal and event
store its commands need, their effects' store; --jobs <file> adds the jobs
module's stores and delivery destinations. The one step before it serves.
A tenant App's stores are bound to its policy: with --policy <file>, up
also makes the tenant store and the journals the policy declares.
--app may name a build (tablewalk build): its own bundled commands and jobs
are read, and a --jobs or --commands must name the build's own.
Exit codes: 1 could not run, 2 a store refused, 3 a step failed.
tablewalk migrate files status | up | sweep --app <dir>
[<db> | --db <target> | --config <path>] [--connection <name>]
[--policy <file>] [--dry-run]
An App's files move to where each resource's storage says: up copies each
database-kept file to the bucket and then empties its content, or back; a
file is read from wherever its row says throughout, so up can stop and run
again anywhere. sweep deletes the bucket's objects no row names (a deleted
record's, an upload whose row was refused) that are older than an hour. status
counts both sides; --dry-run says what up or sweep would do. A tenant App
names its --policy. Credentials come from TABLEWALK_S3_ACCESS_KEY_ID and
TABLEWALK_S3_SECRET_ACCESS_KEY, else from an ECS task role or (with
TABLEWALK_S3_INSTANCE_ROLE=1) an EC2 instance role; a server whose App names a
bucket does not start without them. Served with --jobs, an App is swept daily by the
server itself (a tenant App tenant by tenant); otherwise run sweep daily
from cron.
Exit codes: 1 could not run, 3 some files did not move.

A backfill, run once beside the server.

tablewalk flows start <resource> --app <dir> --jobs <file>
[--where <query>] [--flow <id>] [--tenant <id> --policy <file>]
[--dry-run] [<db> | --db <target> | --config <path>]
Start a resource's created flows on the records already there, as though
each had just been created: one keyed dispatch per record, in the App's job
store, which the server started with --jobs runs. Each flow's condition
still decides. --where narrows the records (the query language over their
own columns), --flow to one flow; a tenant App runs one --tenant at a time,
whose owner column its --policy names. Running it again starts nothing new.
Exit codes: 1 could not run.

One of an App’s jobs at times, once, by hand.

tablewalk jobs run <name> --app <dir> --jobs <file> [--wait <seconds>]
[<db> | --db <target> | --config <path>]
Enqueues one run of the job in the App's job store, marked manual beside
the slots the schedule enqueues, for the server started with --jobs to run
as the job's principal, journaled as a slot's run is; waits --wait seconds
(default 60; 0 returns at once) and says how it ended. A job at times runs
one at a time: a manual run waits while the schedule's run of the job holds
its lease. A name that is not one of the App's jobs at times is refused with
the names that are. A job store with no run of a job yet starts at its next
slot: a first start never runs a missed one.
Exit codes: 1 could not run, 2 still waiting when --wait ran out, 3 the run
did not succeed.

A copy of a tenant to try changes in.

tablewalk tenant clone <source> <sandbox> --app <dir> --policy <file> [--samples]
tablewalk tenant import <sandbox> --app <dir> --policy <file>
tablewalk tenant clock <sandbox> --app <dir> --policy <file> --ahead <duration> [--rate <n>]
[--operator <name>] [--commands <file>] [<db> | --db <target> | --config <path>]
clone makes a new sandbox tenant from a copy of the source's rows of every
resource the App marks clone: 'config' (and 'sample' with --samples), its
ids rewritten so the copy names only its own rows. import prints what
importing the sandbox into its source would change and what conflicts; the
source's owner approves it in Manage tenants. clock sets a sandbox's dev
clock, which its machine timers fall due on; a live tenant has none.
Exit codes: 1 could not run.

Optional; an App also runs from its source.

tablewalk build --app <dir> [--app-entry <file>] [--out <dir>]
[--config <path>] [--commands <file>] [--jobs <file>]
[--policy <file>] [--dockerfile <file>]
Typechecks the App (its tsconfig.json, with the App's or tablewalk's
TypeScript), validates it as a start does, bundles its definition and
server entries (with rolldown when it is installed; otherwise each module
is type-stripped on its own), and writes app.json, tablewalk-build.json
and server/<kind>.mjs, and an App's custom components as components/.
--out <dir> Where the build goes, from the current directory as
every path on the command line is (default
build/<dir name> in the current directory, the project
the build must stay inside to resolve tablewalk; or
<dir name>-build beside an App built from its own
directory). Replaced whole on success; refused unless
it is new, empty or an earlier build.
--dockerfile <file> Build nothing: write the Dockerfile that builds this
App with these options in a build stage and serves it
(tini, uid 1000, PORT, a /healthz health check). Run
docker build from the project root; --config is copied
in, not built with.
A --config may list this App (and no other): its apps[].env is what the
definition is built with. With --config reaching the App's database, it
also writes the generated types and runs tablewalk check's validation on the build. Serve
it with --app <out>, and the build's own server/<kind>.mjs for --commands,
--jobs or --policy. A build is pinned to the tablewalk version that made
it and never watched.
Exit codes: 1 could not build, 2 typecheck, 3 validation, 4 bundle.
tablewalk compare-openapi <previous.json> <current.json>
Reads two bounded local JSON files, prints a conservative change report,
and exits without loading a config, database or command handlers.
Requires no-follow/nonblocking file-open support; Windows is not supported.
Exit 0: version requirement met; 1: a required version increase is missing
or the version decreased; 2: invalid input or unsupported document.
Findings remain visible after a version increase. This is not a general
OpenAPI compatibility proof, and it does not enable API routes.
tablewalk docs list the topics and every shipped page
tablewalk docs <topic> print one (access-model, secrets, relationships, …)
tablewalk docs <topic> --brief its headings and the first sentence under each, one screen
tablewalk docs --search <words> every line of the shipped pages that says them
tablewalk agent init [--dir <project>] [--tool claude|codex|cursor|copilot|all] [--force]
Copies the skill tablewalk ships (a decision guide for building an App:
which block fits, the design language, the choices that are hard to undo,
and a rubric for screenshots) into the project, and points at it from a
marked block in AGENTS.md. Claude Code reads .claude/skills/tablewalk;
Codex, Cursor and Copilot read .agents/skills/tablewalk; all (the default)
writes both.
--dir <project> where to install (default: the current directory)
--tool <name> claude, codex, cursor, copilot or all; comma-separated
--force replace a copy the project has changed
A re-run updates a copy it installed and says so; one the project changed
is left alone, and the exit code is 1.

Running an App on this machine, and testing it

Section titled “Running an App on this machine, and testing it”
tablewalk dev [<app-dir>] [<db> | --db <target> | --config <path>] [options]
Serves the App (default: the current directory) as --app <dir> --dev does,
with a throwaway session secret and an account per declared role, each
sign-in printed. They are kept in <app-dir>/.tablewalk/ (the auth store too),
so a restart signs the same people in; delete it to start over. Local and
for development only: it refuses to run with NODE_ENV=production. The
database is the App's tablewalk.json unless one is named. Where auth.me
names the table people are rows of, each role's account takes the email of
one of its rows, so a view of "= me" shows that person's records. A
tenant App, started with its --policy, gets an account per role that is a
member of the first two tenants of its profile table, or of the platform
realm for a platform role.
tablewalk test init [<app-dir>] [--config <path>] [--force]
Writes tests/start.ts, tests/journeys.test.ts and tests/permissions.test.ts
into the App, on tablewalk/testing alone: a journey per role (its views, a
record, a command it is granted, each machine verb it holds), each job at
times run now, and every role × resource and command, allowed or refused as
authority.lock says, for Node's test runner (tests/*.test.ts). A row-policy
App is served with the policy.ts and jobs.ts beside its app.ts. A tenant
App gets tests/tenants.test.ts instead: for two of its tenants, each
resource a tenant owns, each command a tenant role runs and each platform
role's reads, served with its tenant policy and commands.ts. A file
already there is refused unless --force.
--config <path> with test init: the tablewalk.json the suite copies its
database from (default: the App's own); the suite reads
TABLEWALK_TEST_CONFIG first, when it is set

Connections can also come from a tablewalk.json

Section titled “Connections can also come from a tablewalk.json”
{ "connections": [ { "name": "app", "url": "postgres://localhost/app" } ] }

An App’s definition reads no environment unless the file lists it

Section titled “An App’s definition reads no environment unless the file lists it”
{ "apps": [ { "dir": "apps/ops", "env": ["REGION"] } ] }
Looked for in ./tablewalk.json, ./.tablewalk.json, then
$XDG_CONFIG_HOME/tablewalk/connections.json.
tablewalk observe [--url <base>] print the running dev server's observer address
tablewalk observe --mcp [--url <base>]
speak its telemetry to a coding agent on stdio:
check (tablewalk check --json; no server needed),
recent_errors, list_traces, get_trace, search_logs,
slow_queries, n_plus_one, explain_access,
page_vitals, tail
OpenTelemetry is on when the standard variables say so: set
OTEL_EXPORTER_OTLP_ENDPOINT (and OTEL_EXPORTER_OTLP_HEADERS, OTEL_SERVICE_NAME,
OTEL_RESOURCE_ATTRIBUTES, OTEL_TRACES_SAMPLER as your collector, Datadog or
Dynatrace asks) and traces, metrics and logs are sent over OTLP/HTTP. Nothing
private is in them: no bodies, row values, SQL parameters, emails or tokens.
GET /healthz and /readyz answer an orchestrator (under --base-path, as every
route is); /robots.txt and /sitemap.xml a crawler, with an App's public
addresses where it has auth.public.
Set TABLEWALK_DIAGNOSTICS=1 for best-effort HTTP JSONL diagnostics on stderr
and generated X-Request-ID response headers. Disabled by default. These records
omit URLs, bodies, identities, SQL and error messages; existing human CLI logs
are separate.
Browsing is read-only. Editing is off by default and enabled in the UI.
Flag Said in
--ahead A tenant App’s sandboxes
--allowed-host Options
--api Commands
--api-integers Options
--api-reads Options
--api-requests-per-minute Options
--api-token-env Options
--api-url Options
--app Options, Commands, Checking an App while authoring it, Looking at an App, Rows to look at, A theme as it is worn, Changing the schema, Starting flows on records already there, Running a job now, A tenant App’s sandboxes, Building an App for production, Running an App on this machine, and testing it
--app-entry Options, Checking an App while authoring it, Building an App for production
--app-mcp Options
--append Rows to look at
--as Looking at an App, Grading an App
--auth-proxy-header Options
--base-path Options, Checking an App while authoring it, Watching what a –dev server did
--both Looking at an App
--brief Commands, Checking an App while authoring it, Reading the reference offline
--browser Looking at an App, Grading an App
--bump Options, Checking an App while authoring it
--click Looking at an App
--commands Options, Checking an App while authoring it, Looking at an App, Grading an App, Changing the schema, A tenant App’s sandboxes, Building an App for production
--compare Looking at an App
--config Options, Checking an App while authoring it, Looking at an App, Rows to look at, Grading an App, Changing the schema, Starting flows on records already there, Running a job now, A tenant App’s sandboxes, Building an App for production, Running an App on this machine, and testing it
--connection Rows to look at, Changing the schema
--dark Looking at an App
--db Options, Commands, Checking an App while authoring it, Looking at an App, Rows to look at, Grading an App, Changing the schema, Starting flows on records already there, Running a job now, A tenant App’s sandboxes, Running an App on this machine, and testing it
--defaults Checking an App while authoring it
--demo Options
--deploy Checking an App while authoring it
--design Checking an App while authoring it
--desk Looking at an App
--dev Options, Running an App on this machine, and testing it
--dev-url Changing the schema
--diff Options, A theme as it is worn
--dir Giving a coding agent the App skill
--dockerfile Building an App for production
--drain-delay Options
--dry-run Changing the schema, Starting flows on records already there
--export Options
--fail-on Options
--fail-on-design-warning Checking an App while authoring it
--fix Checking an App while authoring it
--flow Starting flows on records already there
--force Giving a coding agent the App skill, Running an App on this machine, and testing it
--from-openapi Options
--full Looking at an App
--help Options, Commands
--history-table Checking an App while authoring it, Changing the schema
--host Options
--jobs Options, Checking an App while authoring it, Looking at an App, Grading an App, Changing the schema, Starting flows on records already there, Running a job now, Building an App for production
--json Checking an App while authoring it, Grading an App, A theme as it is worn, Watching what a –dev server did
--light Looking at an App
--lint Options
--locale Commands
--lock-timeout Options
--matrix Looking at an App
--mcp Options, Watching what a –dev server did
--migrations Checking an App while authoring it, Changing the schema
--mount Options, Checking an App while authoring it
--name Options
--no-config Options
--open Options
--openapi Options
--operator A tenant App’s sandboxes
--out Looking at an App, Building an App for production
--output-schema Options
--palette Looking at an App, Grading an App
--pending Checking an App while authoring it
--pending-on Checking an App while authoring it
--phone Looking at an App
--policy Options, Checking an App while authoring it, Looking at an App, Grading an App, Changing the schema, Starting flows on records already there, A tenant App’s sandboxes, Building an App for production, Running an App on this machine, and testing it
--pooler Options
--port Options, Commands
--press Looking at an App
--rate A tenant App’s sandboxes
--read-pool Options
--recipes Looking at an App
--reindex Options
--repeatable Changing the schema
--replicas Checking an App while authoring it
--routes Checking an App while authoring it
--rows Rows to look at
--samples A tenant App’s sandboxes
--scaffold-app Options
--scale Looking at an App
--schema Options, Commands, Changing the schema
--schema-file Changing the schema
--search Reading the reference offline
--seed Rows to look at
--shutdown-grace Options
--suite Changing the schema
--tenant Looking at an App, Grading an App, Starting flows on records already there
--text Looking at an App
--theme Looking at an App, Grading an App
--tool Changing the schema, Giving a coding agent the App skill
--tools Options
--trust-proxy Options
--trust-proxy-cloudflare Options
--trust-proxy-hops Options
--type Looking at an App
--url Watching what a –dev server did
--version Options
--wait Running a job now
--wait-for Looking at an App
--warnings Checking an App while authoring it
--where Starting flows on records already there
--width Looking at an App
--write-authority Options
--write-pool Options