Once upon a component
Small pieces, a whole application.
Ajo is a UI library built on JavaScript generators, and a small platform around it: Kit for routes and data, Engine to run sealed applications, and Server to host them on a machine you control.


Chapter I · Ajo, the interface
State is just a variable.
A small UI library built on JSX and JavaScript generators. A stateful component is a generator: what you declare before its loop persists, and this.next() renders it again. No hooks, no dependency arrays, no rules about call order.
- Control over reconciliation
- key, memo and skip decide what is compared; everything else is updated where it stands.
- Server rendering
- ajo/html renders the same components to HTML, and the browser continues from that DOM.
- Cloves
- Shared behavior is a plain function of the component host, with no ordering rules and no registry.
Status Published on npm.
0 of 5 lanterns lit
import type { Stateful } from 'ajo'
export const Lanterns: Stateful = function* () {
let lit = 0
const light = () => this.next(() => lit = lit === 5 ? 0 : lit + 1)
while (true) yield (
<>
<p>{lit} of 5 lanterns lit</p>
<div class="lanterns">
{[1, 2, 3, 4, 5].map(n => <span key={n} class={n <= lit ? 'lit' : ''} />)}
</div>
<button set:onclick={light}>{lit === 5 ? 'Start over' : 'Light one'}</button>
</>
)
}

Chapter II · Ajo Kit, the framework
Routes are files. Data is server truth.
A full-stack framework for Ajo: file routes and nested layouts, server loaders and form actions, SQLite migrations through Kysely, and live route updates over server-sent events. Vite runs development; the build stages a closed module graph for the engine.
- Loaders are server truth
- handler.ts returns the data a page renders; parent() reads what its layouts loaded.
- SQLite and migrations
- Kysely over SQLite, with up and down migrations from your App and its plugins in one history.
- Live routes
- Loaders track topics, actions emit them, and open pages receive a fresh route payload over SSE.
Status Published on npm. Pre-1.0: its API is still being refined.
import type { ActionContext, Request, Response } from 'ajo-kit'
import { db } from '/src/data'
export async function page(req: Request) {
req.track?.('notes')
const notes = await db().selectFrom('notes').select(['id', 'text']).execute()
return { notes }
}
export const actions = {
async add(req: Request, _res: Response, action: ActionContext) {
await db().insertInto('notes').values({ text: String(req.body.text) }).execute()
action.emit('notes')
return { ok: true }
},
}

Chapter III · Ajo Engine, the runtime
Sealed before it sails.
A focused runtime built on QuickJS-NG. Its compiler turns a Kit build into precompiled bytecode with a manifest of hashes, capabilities and filesystem roots; the engine loads nothing else and refuses to start when anything does not match.
- Parserless production
- The runtime executes precompiled QuickJS-NG bytecode. It has no parser, eval, REPL or Node module loader.
- Declared authority
- Runtime modules, filesystem roots and FIFOs must be declared; a module grant never bypasses a filesystem check.
- Fail-closed startup
- Missing or mismatched files, an unknown pin or missing required environment stop the App before it runs.
Status Pre-release. The native packages are verified on Linux x64 (Alpine musl and Freedesktop glibc).
- kit build.ajo/compiler.json
A closed server graph, the client assets and the App’s declared authority.
- ajo-engine-compilerdist/ajo/manifest.json
One bytecode chunk per module, each with its SHA-256, plus the exact engine pin.
- ajo-engine dist/ajoHTTP, SSE, SQLite
Every file, hash, capability and root is checked before the entry module runs.


Chapter IV · Ajo Server, the host
A lighthouse of your own.
An Alpine Linux host image with nginx and TLS, rootless Podman and an administration App. Deploy a named version from a Git branch, wait for the host’s receipt, and keep the previous image for rollback.
- A reviewed image
- An Alpine host built without network access from a reviewed package lock, with nginx, TLS and rootless Podman.
- An administration App
- Passkeys, live host data, typed operations and alerts, itself an Ajo Kit App running on the engine.
- Named versions
- Every arrival is versioned. If a new version does not answer, the host attempts to restore the previous image.
Status Experimental. Hosts come from signed releases, on this machine or on DigitalOcean; there is no automated host replacement yet.
pnpm add -D ajo-kit-server
pnpm add --save-optional --save-exact ajo-engine@0.2.0 ajo-engine-compiler@0.2.0
pnpm kit host create --provider digitalocean --name apps.example.com
pnpm kit deploy

Ajo means garlic
A bulb is made of cloves.
Every piece is its own package on npm. Take only the ones your application needs.
- ajo0.2.0Generator components, in-place DOM reconciliation and server rendering.
- ajo-kit0.5.0File routes, loaders, actions, SQLite migrations and live updates.
- ajo-kit-auth0.7.1Sessions, CSRF, guards, scoped tokens, passkeys and invitations.
- ajo-kit-mail0.4.1One validation boundary for mail, with SMTP, HTTP and capture transports.
- ajo-cloves0.2.1Reusable behaviors: controlled state, dismissal, keyboard focus and sensors.
- ajo-ui0.3.0Unstyled, accessible component families with stable styling hooks.
- ajo-ui-playa0.3.0Themed components and a build-time UnoCSS preset.
- ajo-engine0.2.0The native runtime for sealed applications.
- ajo-engine-compiler0.2.0Seals a Kit build into precompiled, hash-pinned bytecode.
- ajo-kit-server0.3.1kit deploy: ship an App to an Ajo Server host.


Every story begins at the shore.
Start with a single page, then follow the path as far as your application needs to go.

















