Walkthrough, customize your starter app
If you ran the Quickstart, you already have a running app with a Tasks list in the sidebar. This walkthrough explains what the starter generated and how to change it. If you customized it in Make it yours, those changes stay in place.

What the starter includes. kumiko new app scaffolds a demo tasks
feature: entity, CRUD handlers, list + edit screens, sidebar nav, and seeded
rows. You declare the shape once; Kumiko derives the table, API, access rules,
and admin UI.
At the end you understand:
- which files the scaffold created and what each does
- how the
tasksfeature wires entity → handlers → screens → nav - how to add a field and see it in the UI after
bun dev - how to run the tests the scaffold ships with
Prerequisite: a scaffolded app with bun dev working. Follow the
Quickstart first if you do not have one yet.
Step 1, Scaffold (or use your Quickstart app)
Section titled “Step 1, Scaffold (or use your Quickstart app)”Use the same installer as the Quickstart:
curl -fsSL https://kumiko.rocks/install.sh | bash -s -- my-app --yescd my-appbun installIf Bun is already installed, the CLI can scaffold the app without the installer script. It has no feature picker, so the feature list differs (see below):
bunx @cosmicdrift/kumiko-cli@latest new app my-appThe workspace includes src/features/tasks/feature.ts (full demo feature),
src/seed.ts (demo rows for bun dev), bin/dev.ts, src/client.tsx,
.env.example, and docker-compose.yml.
src/run-config.ts lists the features the app mounts and always ends with
tasksFeature. With --yes, the installer puts the recommended features from
the feature picker there. kumiko new app has no picker and mounts secrets,
auth-foundation and sessions. In both cases config, user, tenant and
auth-email-password are left out of the list, because composeFeatures
mounts them automatically when bin/main.ts passes auth: { admin: { … } }.
Step 2, Configure env + boot validation
Section titled “Step 2, Configure env + boot validation”cp .env.example .envWrite two random local keys into .env. This is the same command as in the
Quickstart.
It replaces the placeholders in the file and prints nothing:
JWT_SECRET="$(openssl rand -base64 32)"MASTER_KEY="$(openssl rand -base64 32)"
sed -i.bak \ -e "s|^JWT_SECRET=.*$|JWT_SECRET=${JWT_SECRET}|" \ -e "s|^KUMIKO_SECRETS_MASTER_KEY_V1=.*$|KUMIKO_SECRETS_MASTER_KEY_V1=${MASTER_KEY}|" \ .env
rm .env.bakunset JWT_SECRET MASTER_KEYThen validate without a database:
bun run boot[runProdApp] boot validation OK (<n> features, <n> registry entries)This validates the mounted feature graph and environment schema without opening
Postgres or Redis. Expect a line containing boot validation OK.
Step 3, See it in the browser
Section titled “Step 3, See it in the browser”docker compose up -dbun devThe welcome banner prints the URL (default http://localhost:4173) and admin
login. Sign in as admin@my-app.local / changeme, open Tasks, demo
rows are already there:

What just happened
Section titled “What just happened”| File | What it does |
|---|---|
src/run-config.ts | Which features your app mounts (APP_FEATURES). |
src/features/tasks/feature.ts | Entity + handlers + screens + nav for the demo. |
src/seed.ts | Inserts demo tasks on first bun dev (idempotent). |
bin/dev.ts | Dev server + welcome banner + seeds: [seedDemoTasks]. |
bin/main.ts | Production bootstrap (bun run start). |
Step 4, Customize the starter feature
Section titled “Step 4, Customize the starter feature”Open src/features/tasks/feature.ts. The starter already declares:
taskEntity, fields (title,status,priority,isUrgent)- CRUD handlers,
defineEntity*Handlerfor create/update/delete/list/detail listScreen+editScreen, generated admin UI definitionsr.nav(...), sidebar entries pointing at those screens
Add a notes text field to see schema-driven UI updates:
// In taskEntity.fields:notes: createTextField({ personal: false, reason: "is_business_data" }),Every text field states whether it holds personal data. Kumiko uses that
stance for the GDPR data export and for erasing a user’s data, so a text field
without it stops boot. personal: false with a reason says the notes are
business data. A field that holds data about a person declares the subject
instead, for example personal: "self", find: "fuzzy". The starter’s title
and status fields use the same form; see
Crypto-shredding for all options.
Add "notes" to listScreen.columns and editScreen.layout.sections[0].fields.
Then give it a label in src/features/tasks/i18n.ts. The file has two objects,
one for the server and one for the browser, and the key goes into both:
tasksTranslationKeys:"tasks:entity:task:field:notes": { de: "Notizen", en: "Notes" }tasksTranslations:"tasks:entity:task:field:notes": "Notizen"underdeand"Notes"underen
Save and let bun dev restart. The development server creates local tables for
this workflow. A production app needs a checked-in migration generated with
bunx kumiko-schema generate <name> before deployment.
Re-run boot to confirm composition still validates:
bun run bootStep 5, Test it
Section titled “Step 5, Test it”The scaffold already wrote two tests. src/__tests__/run-config.test.ts runs
boot validation over the mounted features. src/__tests__/tasks.integration.test.ts
starts a real test stack against Postgres and checks that a task stays inside
its tenant:
describe("tasks", () => { test("a created task is listed for its own tenant only", async () => { const owner = await seedTenant(stack); const other = await seedTenant(stack);
await owner.api.writeOk(TASK_CREATE, { title: "Write tests", status: "todo", priority: 1 });
const ownerTasks = await owner.api.queryOk<TaskList>(TASK_LIST, {}); expect(ownerTasks.rows.map((row) => row.title)).toEqual(["Write tests"]); const otherTasks = await other.api.queryOk<TaskList>(TASK_LIST, {}); expect(otherTasks.rows).toEqual([]); });});The test file is generated from
packages/testing/src/scaffold.ts.
Run the unit tests, then the integration tests with Postgres running:
bun run testdocker compose up -dbun run test:integrationIf you added notes in Step 4, extend the TASK_CREATE payload with a
notes value and assert it in the list. The
testing standard explains the rest of the setup.
Step 6, Add another feature
Section titled “Step 6, Add another feature”The starter’s tasks feature is yours to edit or delete. For a second domain
feature, use the CLI (scaffolds a bare entity, add screens when you need UI):
bunx @cosmicdrift/kumiko-cli add feature notesThis creates src/features/notes/ and auto-mounts it in src/run-config.ts.
Add navigation and translations before opening its screen, then run bun run boot.
Next steps
Section titled “Next steps”- Deploy: Docker, build the production image. Read the production notes in Requirements first. The scaffold is set up for local development only.
- Mount bundled features (
audit,delivery,tier-engine, …) insrc/run-config.ts. - What you get, full capability tour.
- Concepts, why Kumiko works the way it does.
- Patterns, complete
r.*API reference.
If something does not match this doc, open an issue.