Kizaki
Concepts

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:

  1. you define your app in Inspire
  2. Kizaki compiles that schema into runtime artifacts and generated TypeScript packages
  3. your app code adds server workflows with /** @expose */
  4. the browser uses the generated client and query stack
  5. 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 assets

The 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:

  1. Managed runtime bootstrap — public installs start from a thin kizaki binary. On the first kizaki init, kizaki compile, or kizaki dev, the CLI downloads the exact matching managed runtime into ~/.kizaki/toolchains/<cli-version>/<platform>/ and switches the stable ~/.kizaki/toolchain symlink.
  2. 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.
  3. Schema compilation — the compiler reads your .inspire files and produces artifact.json, schema.sql, and the generated TypeScript packages.
  4. 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.
  5. Database engine startup — the engine loads artifact.json and connects to Postgres. It applies the current schema and begins enforcing policies.
  6. Function runtime startup — your @expose functions are loaded and connected to the database engine. The runtime provides query(), getContext(), getPrincipal(), and other SDK functions via async context.
  7. App server startup — HTTP and WebSocket listeners come online. Routes declared in Inspire are registered. Static assets are served. The dev dashboard becomes available.
  8. File watcher — the dev server watches your .inspire files 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:

  1. The browser calls useQuery(select(Project)) from @kizaki/sdk.
  2. The SDK sends the query over a WebSocket connection to the app server.
  3. The app server forwards it to the database engine.
  4. The database engine identifies the principal (the authenticated user) and loads the policy grants from artifact.json.
  5. 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.
  6. 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:

  1. The browser calls createProject({ name: "Northlight" }), an imported function from @kizaki/client.
  2. The call is sent as an RPC to the app server, which routes it to the function runtime.
  3. The function runtime invokes your /** @expose */ async function createProject(name: string) with the principal set in async context.
  4. Inside that function, your code calls query(insert(Project).values({ name, ownerId: getPrincipal().userId })).
  5. The query() call forwards the insert to the database engine, which checks write policies from artifact.json.
  6. If the policy allows the write, the database engine executes the SQL INSERT and returns the result. If not, it returns a permission error.
  7. 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:

  1. kizaki deploy compiles the schema and bundles your application code.
  2. The compiled artifact and bundle are uploaded to the platform.
  3. Pending migrations are applied to the target database. If a migration contains destructive changes (dropping a column with data), the deploy requires --allow-destructive as a confirmation step.
  4. The database engine reloads with the new artifact.json.
  5. The function runtime restarts with the new application code.
  6. 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

On this page