create-dillo
v1.0.0
Published
Dillo — the edge-native typed backend. Create and check standalone Dillo applications.
Readme
Dillo
Cheap zero-fuss infra for apps, events, AI, and decisions.
Your app has a better story than “we spent the week wiring up auth.”
Dillo is a typed TypeScript backend you describe in three files, then run locally or on Cloudflare. Accounts, sessions, teams, permissions, records, files, and server functions ship with the engine. Agents and humans build on the same contract. Private by default. Infrastructure you control.
schema.ts What your data looks like. Who can access it.
backend.ts What your app does. Functions, middleware, integrations.
armadillo.config.ts Where it runs. Infrastructure, assets, secrets, deployment.Typed from schema to client. Private records by default. Deploy when you are ready.
Golden path ↓ · See the code ↓ · Install honestly ↓ · What works ↓ · Deploy ↓ · llms.txt (generated per project via npm run llms:generate; not shipped in the tarball) · Patterns
Status (read this)
This checkout is create-dillo 1.0.0 tagged v1.0.0-rc.1 — a release candidate, not a production 1.0 claim.
| Fact | Reality |
| --- | --- |
| npm | Available under the alpha dist-tag. npm init dillo@alpha my-app to scaffold a project; npm install create-dillo@alpha for the library. The latest tag is never published. |
| Repo | github.com/29-Court/create-dillo |
| Public API | Promise client + schema words. Effect is internal only (pins Effect 4.0.0-rc.112). Do not rewrite apps in Effect or teach Effect as the product. |
| Deploy tooling | Included Cloudflare recipe uses Alchemy 2.0.0-beta.* (beta). |
| Cloud records | JSON-backed application records on D1; column-backed cloud deploy and D1 callback transactions are not supported. |
| Microapps | No create-dillo/microapps package. docs/microapps.md is a proposal only. Codegen via prompt pack / backend.promptPack() / GET /v1/discovery*. |
| Structure discovery | Default ON: GET /v1/schema, /v1/discovery, /v1/discovery/prompt. Turn off with discovery: false. Does not dump secrets, raw SQL, or a full MCP server (backend.mcp() = functions OpenAPI only). |
| CI | No CI on the alpha by design; pre-release verification is the local npm run release:verify gate. |
Local Node/SQLite and Cloudflare Workers/D1/R2 paths are covered by the release:verify script and prior live proofs. See release evidence before production rollout.
LLM indexers: prefer generated llms.txt (npm run llms:generate / llms:check; not shipped in the tarball) over hand-written summaries. Do not hand-edit llms.txt.
Three files. One backend.
Same shape as the examples/typed.
The
examples/curriculum lives in the repo checkout — the npm tarball shipsdocs/+ templates, not the examples.
1. schema.ts — data and access once
import { boolean, owner, schema, string, table } from "create-dillo/schema";
export default schema({
Project: table({
name: string().min(1).max(120),
active: boolean().default(true),
}).permissions({
view: owner,
edit: owner,
}),
});Validated records, CRUD/query API, and inferred client types. Permissions are enforced on every request. Add relations, computed fields, files, or team policies in the same file. Words like anyone, owner, members, roles(...), recordOwner, and nobody are listed in the examples/README.md#who-can-view-and-who-can-edit.
2. backend.ts — behavior that makes it yours
import { defineBackend, defineFunction } from "create-dillo/backend";
import { string } from "create-dillo/schema";
import schema from "./schema.ts";
export default defineBackend({
schema,
functions: {
greet: defineFunction()
.input({ name: string().min(1) })
.output({ message: string() })
.handler((_context, input) => ({ message: `Hello ${input.name}` })),
},
});Canonical spelling is defineBackend (same function as Armadillo.backend). Inputs and outputs are validated; types flow to the client. Inside a handler, context.records and context.files respect the caller’s permissions. Middleware, capabilities, extensions, and secret requirements live here—not in provider bindings.
3. armadillo.config.ts — give it a home
import { app } from "create-dillo/deploy/cloudflare";
import backend from "./backend.ts";
export default app({
id: "my-app",
backend,
signup: "open",
});The Alchemy recipe generates the Worker entrypoint, wires D1 and R2, applies core migrations, and configures scheduled maintenance. Add static assets, application migrations, and mail as you grow. Prefer armadillo.config.ts; alchemy.run.ts also works (templates use either). Closed signup + bootstrap secret for invitation-only apps.
Now use it
import { Armadillo } from "create-dillo/client";
import type backend from "./backend.ts";
const api = Armadillo.client<typeof backend>({ url: "http://localhost:8787" });
await api.auth.signUp({
name: "Ada",
email: "[email protected]",
password: "correct-horse-battery-staple",
});
const project = await api.tables.Project.create({ name: "Garden" });
const active = await api.tables.Project.query().where({ active: true }).limit(20).find();
const greeting = await api.functions.greet({ name: active[0]?.name ?? project.name });
// greeting.message → "Hello Garden"One schema. Typed calls through. Wrong fields and unknown functions fail at compile time. The backend import is type-only, so server code stays out of the browser bundle. Prefer HTML? Serve /client.js or /armadillo/client.js. Prefer another language? REST under /v1/auth/*, /v1/tables/:table (writes take a {"data": {...}} wrapper), /v1/files, /v1/functions/:name, plus POST-only /v1/query/:table. Browser sessions ride the HttpOnly cookie set at signup — there is no session bearer token; machine callers pass a scoped API key (POST /v1/api-keys) as the Authorization: Bearer token. Frontend can live in its own package — frontend integration.
What works today
| Need | Ships |
| --- | --- |
| Identity | Accounts, sessions, magic links, scoped API keys, external-auth seam |
| Shared work | Teams, memberships, roles, record access policies |
| Data | Validation, defaults, relations, computed fields, queries, client type inference |
| Files | MIME/size constraints, ownership, access via authorized record links |
| Behavior | Validated functions, caller-scoped data, middleware, trusted capabilities |
| Mail | Providers for magic links / invites (Resend, CF Email, webhook, local stub) |
| Connect | Events, webhooks, extensions; typed realtime in examples/09-live-board (not a default on-ramp) |
| Run | Cloudflare (Workers + D1 + R2 via Alchemy) and local Node/SQLite/filesystem |
Shared media with real access rules:
import { file, members, recordOwner, relation, roles, schema, table } from "create-dillo/schema";
export default schema({
Media: table({
partyId: relation.team(),
content: file()
.accept("image/png", "image/jpeg", "application/pdf")
.maxSize("10 MB"),
}).permissions({
view: members,
edit: roles("owner", "admin").or(recordOwner),
}),
});permissions() compiles to the one policy the engine enforces; explain() prints it back. Unprovable rules are rejected at schema define time. Prove policies with npm run permission-lab and npm run permission-evals (permission lab). Trusted writes use context.trusted.records after authority: "trusted" — see architecture.
Install (this checkout)
From the published alpha: npm install create-dillo@alpha. From a clone of this repo (Node 22.19+ / npm 11.6+):
npm ci
npm run release:verifyThat packs the verified tarball under artifacts/. Wire examples (or your app) against them:
(cd examples/minimal && npm install --ignore-scripts ../../artifacts/create-dillo-1.0.0.tgz)
(cd examples/block-party && npm install --ignore-scripts ../../artifacts/create-dillo-1.0.0.tgz)Many in-repo examples also use file:../.. against a built root package (npm run build). The public path is npm init dillo@alpha.
Examples
These examples live in the repo checkout (
git clone https://github.com/29-Court/create-dillo.git) — the npm tarball shipstemplates/+docs/, notexamples/. Registry users: start withnpx create-dillo@alpha my-app --template minimal.
Golden path (one path above the fold): examples/example-html → examples/00-guestbook → examples/README.md. Same order as examples/README.md.
examples/example-html— shortest honest app: one HTML file, cookie auth,/client.js, no schema yet. Local HTTP only.examples/00-guestbook— first real three-file lesson (schema→backend→config+ plain HTML;view: anyone,edit: owner). Walk: patterns.- Then the numbered curriculum in
examples/README.md(permission words, teams,nobody, files, …).
Also supported (not the default on-ramp):
examples/minimal/examples/typed— auth, private CRUD, typed functions; CLI /release:verifyfixtures against the packed tarball (not a registry install).examples/block-party— larger neighborhood app + Alchemy deploy guide.examples/29-court-setup— invite-only onboarding control plane; product surface, not a generic template.examples/11-agent-bridge— DEV-only enrollment notes. No prod deploy claim. Preferexamples/10-agent-relayfor a runnable agent lesson.
Kitchen sink and custom components stay demos of breadth, not the first lesson. Do not invent microapps from those demos.
Local wonder path after install/build:
# One File
node --experimental-strip-types examples/run/example-html.ts
# Guestbook
npm --prefix examples/00-guestbook install
npm --prefix examples/00-guestbook run devNo Cloudflare account required for local Node/SQLite. Guestbook data persists under examples/00-guestbook/.data/.
Deploy with Alchemy
Alchemy is the included Cloudflare path. Your application owns its infrastructure.
Workers for compute, D1 for data, R2 for files, plus resources your backend requires. Deployment tooling is an app development dependency — it does not ship in the browser client or the library runtime graph. Effect RC and Alchemy beta are intentional pins; plan upgrades.
From an example that already has the recipe (e.g. Block Party), after Cloudflare credentials, secrets, and mail:
npm run plan -- dev
npm run deploy -- dev
npm run deploy -- prod # when ready
npm run logs -- prodnpm run dev starts the persistent local adapter in the minimal template; the typed template's dev script is alchemy dev and needs Cloudflare credentials. Cloud deploy needs an explicit stage; planning does not apply migrations.
| Component | Cloudflare adapter | Local adapter | | --- | --- | --- | | HTTP | Workers | Node.js | | Database | D1 | SQLite | | Files | R2 | Filesystem |
Bring your own cloud via public /runtime + /testing contracts — components, examples/custom-components.
Visual journal
Milestone illustrations live in docs/assets/illustrations/ — local→cloud, cloud launch, staged edge auth, bring-your-own backbone, data observatory, and the armored-backend emblem. Link them; do not orphan new art outside that catalog.
Deeper reading
- Patterns · Architecture · Operations
- LLM prompt pack · generated
llms.txt(not shipped in the tarball) · v1 doctrine - Components · UI shells · microapps proposal (not shipped)
- Release evidence · Contributing · Agent onboarding
Verify this tree:
npm ci && npm run build && npm run build:cli && npm run type-checknpm run test:all adds the runtime suite, report-citation checks, and CLI tests; release:verify builds and installs real tarballs, scaffolds an app, checks typed consumers and browser isolation, and writes artifacts/. The full suite runs in CI. Neither publishes nor deploys.
MIT licensed. Build something worth inviting people to.
