Skip to content

Walkthrough — from zero to running app

We’re going from nothing to a running Kumiko app with one custom feature. Three commands, one file to edit, ~5 minutes.

At the end you have:

  • a fresh app workspace under ./my-notes/
  • one feature notes with an entity and a starter handler
  • a clean boot: [runProdApp] boot validation OK

Prerequisite: Bun ≥ 1.2.20. Postgres + Redis are NOT required for the boot-validation path used here.

Terminal window
bunx @cosmicdrift/kumiko-cli new app my-notes
cd my-notes
bun install

This creates package.json, tsconfig.json, src/run-config.ts, bin/main.ts, .env.example, README.md. No mkdir, no hand-typed config.

src/run-config.ts already mounts secrets + sessions as foundation. config, user, tenant, and auth-email-password are auto-mounted by composeFeatures because bin/main.ts passes auth: { admin: { … } }.

Terminal window
bunx @cosmicdrift/kumiko-cli add feature notes

This creates src/features/notes/feature.ts and src/features/notes/index.ts, and auto-mounts the feature in src/run-config.ts:

import { notesFeature } from "./features/notes";
export const APP_FEATURES = [
createSecretsFeature(),
createSessionsFeature(),
notesFeature,
] as const;

You never edit run-config.ts by hand — the CLI does it via ts-morph.

Terminal window
cp .env.example .env

Edit .env and set:

Terminal window
JWT_SECRET=$(openssl rand -base64 32)
KUMIKO_SECRETS_MASTER_KEY_V1=$(openssl rand -base64 32)

Then boot:

Terminal window
bun run boot

Expected output:

[runProdApp] booting Kumiko stack on port 3000…
[runProdApp] boot validation OK (7 features, 7 registry entries)

That’s the boot-validation path (KUMIKO_DRY_RUN_ENV=boot): every feature mounted, every r.requires resolved, every entity schema validated — without touching DB or Redis. The 7 features = 3 you mounted (secrets, sessions, notes) + 4 auto-prepended (config, user, tenant, auth-email-password, because bin/main.ts passes auth: { admin: { … } }). The next step would wire real services, but everything above this line is already working.

FileWhat it does
src/run-config.tsSingle source of truth: which features your app mounts.
bin/main.tsProduction bootstrap. Reads env, calls runProdApp({ features, auth, … }).
src/features/notes/feature.tsYour defineFeature("notes", (r) => …) body. Add entities, write/query handlers, hooks.

Open src/features/notes/feature.ts. It looks like this:

import { defineFeature } from "@cosmicdrift/kumiko-framework/engine";
export const notesFeature = defineFeature("notes", (r) => {
r.entity("notes-item", {
fields: {
title: { type: "text", required: true },
},
});
});

Add a body field and a pinned flag:

r.entity("notes-item", {
fields: {
title: { type: "text", required: true },
body: { type: "text" },
pinned: { type: "boolean", default: false },
},
});

Re-run bun run boot to validate: the schema still composes cleanly.

  • Add a write handler with r.writeHandler(...) — see Patterns for the r.* API.
  • Add a query handler with r.queryHandler(...) to read your data.
  • Wire screens + nav for an admin UI (r.screen, r.nav).
  • Pull in bundled featuresaudit, delivery, cap-counter, subscription-stripe, … — by editing src/run-config.ts. (Or scaffold your own with bunx @cosmicdrift/kumiko-cli add feature <name>.)

The previous walkthrough required you to mkdir, hand-write package.json, hand-write tsconfig.json, hand-mount features in run-config.ts. Each step was a chance to mistype something. The new path:

  • 3 commands, 0 hand-typed config
  • defineFeature(...) is the only place a feature’s name lives
  • Boot is one command, validates everything, fails fast with a clear error

If you hit something that doesn’t work as described, open an issue.