Building a Karsa app
When a task grows past organizing data into building software — a tool, a dashboard, an automation, a small service — Karsa is where it gets built. Not in a scratch directory. Not against someone else's cloud. Inside the project, on the Karsa SDK.
This is the single most important idea for an agent working in Karsa: the app you build runs caged inside the project, and it talks to infrastructure only through the Karsa SDK. Read this whole page before you scaffold anything.
The shape of a Karsa app
A Karsa app is a per-project program that the daemon runs as a subprocess and surfaces in the UI (today via an iframe). It is caged: it gets capability through a gatekept SDK surface, not raw access to the machine. That cage is a feature — it means an app can be powerful without being dangerous, and it means "expand to the cloud later" is a config change, not a rewrite.
You build the app in the project tree (under karsa/proj_*/), versioned in git like everything else. Its data lives in the project's sqldb/. Its files live in the project. Nothing escapes the cage by default.
The SDK is the whole interface to infrastructure
Everything an app needs from the outside world comes through @karsa/* SDK packages. You do not open sockets, spin up servers, or sign up for external accounts. You call the SDK:
@karsa/sqldb— relational storage. This is the sole storage chokepoint: it is what makes a local app able to "expand" to the cloud unchanged. Tabular data, app state, anything you'd reach for Postgres for — it goes here.@karsa/docdb— document storage (markdown-truth documents with a derived index). Built on top of@karsa/sqldb, not its own database. Use it for notes, specs, content — anything document-shaped.@karsa/blob— large binary objects: uploads, images, generated artifacts.@karsa/queue— background work and message passing between parts of your app.- Plus the rest of the gatekept surface as you need it: schedule, agents, http, secrets.
Because these are the only doors, the daemon can enforce the cage, swap a local backend for a cloud one, and keep the app portable. An app written to the SDK is an app that survives "let's deploy this for real."
How to build, step by step
@karsa/sqldb. Stay in the project. Scaffold the app in the project tree. Do not create it in /tmp, your home directory, or a sibling folder outside Karsa. @karsa/docdb. Pick the right storage. Tabular / relational → @karsa/sqldb. Document-shaped → @karsa/docdb. Files → @karsa/blob. Background jobs → @karsa/queue. Don't introduce a second database. @karsa/blob. Use the SDK for every external need. Need persistence, files, jobs, scheduling? There's an SDK door for it. If you think there isn't, check again before reaching outside — that instinct is usually wrong (see Anti-patterns). @karsa/queue. Keep it caged. Don't try to break out of the cage for "convenience." The cage is what lets the app be trusted and later expanded. karsa/. Version it. It's all plain files under karsa/, in git. Commit as you go.
Local now, cloud later — for free
The reason the SDK matters so much: a Karsa app written against @karsa/sqldb runs locally today and can expand to the cloud without a rewrite, because the storage chokepoint is the seam Karsa controls. The moment you go around the SDK — embed a raw SQLite file, call an external API directly, hardcode a connection string — you break that seam, and "expand" becomes "rebuild."
So: build inside, build on the SDK, and the path to production is already laid.
Where to go next
Read Anti-patterns next — it names the exact wrong turns (building outside Karsa, reaching for Supabase or another external service) and why each one quietly costs you the cage and the expand path.