How Kizaki Works
Kizaki turns one schema into a full application workflow: database shape, runtime rules, generated client surfaces, and deployment artifacts.
Kizaki starts with the schema.
You define your application model in Inspire. The rest of the system preserves that model as it moves through local development, runtime enforcement, generated code, and deploys.
At a high level, the flow is:
- you define your app in Inspire
- Kizaki compiles that schema into runtime artifacts and generated TypeScript packages
- your app code adds server workflows with
/** @expose */ - the browser uses the generated client and query stack
- deploy promotes the same app shape into hosted environments
The Architecture
Three layers, connected by a single compilation step:
.inspire schema
-> Compiler
-> artifact.json (policies + schema for the database engine)
-> schema.sql (PostgreSQL DDL)
-> @kizaki/schema (typed entity objects for server code)
-> @kizaki/client (typed RPC stubs for the browser)
Database engine — loads artifact.json, enforces policies on every query
Function runtime — executes your @expose functions, calls the database engine
App server — handles HTTP, WebSocket, routes, and static assetsThe compiler is the only component that reads your .inspire files. Everything downstream works from the compiled outputs. The database engine never interprets Inspire at runtime. It works from a pre-compiled artifact, which is faster and easier to audit.
The three runtime layers communicate over internal protocols. Your application code interacts with two surfaces: @kizaki/sdk on the server and @kizaki/client in the browser.
What Happens When You Run kizaki dev
The dev server starts the full stack locally, in this order:
- Managed runtime bootstrap — public installs start from a thin
kizakibinary. On the firstkizaki init,kizaki compile, orkizaki dev, the CLI downloads the exact matching managed runtime into~/.kizaki/toolchains/<cli-version>/<platform>/and switches the stable~/.kizaki/toolchainsymlink. - Embedded PostgreSQL — Kizaki manages its own Postgres instance inside that managed runtime. On first bootstrap, it downloads a platform-appropriate Postgres bundle (~50MB) and initializes a data directory. Subsequent starts reuse the existing database.
- Schema compilation — the compiler reads your
.inspirefiles and producesartifact.json,schema.sql, and the generated TypeScript packages. - Migration check — the dev server compares your current schema against committed migrations. If they have diverged, it stops and tells you to generate a new migration plan. This prevents local drift.
- Database engine startup — the engine loads
artifact.jsonand connects to Postgres. It applies the current schema and begins enforcing policies. - Function runtime startup — your
@exposefunctions are loaded and connected to the database engine. The runtime providesquery(),getContext(),getPrincipal(), and other SDK functions via async context. - App server startup — HTTP and WebSocket listeners come online. Routes declared in Inspire are registered. Static assets are served. The dev dashboard becomes available.
- File watcher — the dev server watches your
.inspirefiles and TypeScript source. When a file changes, it recompiles, regenerates packages, and hot-reloads the database engine and function runtime without restarting Postgres.
After the first run, startup takes a few seconds. You do not need to install or configure Postgres separately.
What Happens On A Request
Reads
A read flows from the browser to the database engine and back:
- The browser calls
useQuery(select(Project))from@kizaki/sdk. - The SDK sends the query over a WebSocket connection to the app server.
- The app server forwards it to the database engine.
- The database engine identifies the principal (the authenticated user) and loads the policy grants from
artifact.json. - It constructs a SQL query with WHERE clauses derived from those grants. If the principal has no read access to
Project, the query returns zero rows. If the principal has scoped access (e.g.,@grant read to owner), the WHERE clause restricts results to matching rows. - The result flows back through the app server to the browser.
The application code that called useQuery never participates in the authorization decision. The database engine handles it.
Writes
A write flows through the function runtime:
- The browser calls
createProject({ name: "Northlight" }), an imported function from@kizaki/client. - The call is sent as an RPC to the app server, which routes it to the function runtime.
- The function runtime invokes your
/** @expose */ async function createProject(name: string)with the principal set in async context. - Inside that function, your code calls
query(insert(Project).values({ name, ownerId: getPrincipal().userId })). - The
query()call forwards the insert to the database engine, which checks write policies fromartifact.json. - If the policy allows the write, the database engine executes the SQL INSERT and returns the result. If not, it returns a permission error.
- The result flows back through the function runtime to the browser.
Both paths pass through the same policy enforcement layer. There is no way to bypass it from application code.
What Happens When You Deploy
Deployment promotes your local application shape to a hosted environment:
kizaki deploycompiles the schema and bundles your application code.- The compiled artifact and bundle are uploaded to the platform.
- Pending migrations are applied to the target database. If a migration contains destructive changes (dropping a column with data), the deploy requires
--allow-destructiveas a confirmation step. - The database engine reloads with the new
artifact.json. - The function runtime restarts with the new application code.
- The app server performs a zero-downtime swap. New requests go to the updated stack while in-flight requests complete on the previous version.
The same migration history applies across all environments. There is no separate migration path for staging vs. production. The migration files committed to version control are the portable unit.
What The Platform Eliminates
In a typical stack, each of these concerns lives in a different system:
- database schema
- auth provider configuration
- authorization middleware
- backend endpoints
- frontend API typing
- migration tooling
- deployment configuration
Each system has its own configuration surface, its own failure modes, its own versioning story. The glue code between them is where most bugs live. Not in any single system, but in the assumptions one system makes about another.
Kizaki pulls those concerns together. It does not remove application code, but it removes the glue code that sits between core systems. The schema becomes the shared source of truth, and the compiler ensures that the database shape, the policy rules, the TypeScript types, and the client API all agree.
The Practical Consequence
When the model is coherent:
- local development matches the shape of deploys
- generated packages match the schema
- policies apply consistently across reads, writes, routes, and live queries
- schema changes move through committed migrations instead of ad hoc SQL
- a type error in the browser means the schema and the client are out of sync, not that someone forgot to update a DTO