Five-minute path
From an empty folder to an app with sign-in, a database and a protected route, in four commands and twelve lines you write.
Status Status is where the work is: Planned (in a milestone, not started), Building (tickets in progress), Preview (usable, may change), then the stability it ships at.
Stability The promise an interface ships with.
- Stable Follows semantic versioning; breaking changes need a new major, 12 months of overlap and a codemod.
- Beta Usable in production; may change before it becomes stable.
- Experimental May change or go away; needs an explicit opt-in in main.yaml.
Four commands and one file take you from an empty folder to a running app where every note belongs to the person who wrote it. You need Node 22.18 or later. No account, no keys, and nothing leaves your machine.
1. Set up the project
npx mainsdk init$ npx mainsdk init
mainsdk init · notes
Found empty folder · npm · Claude Code
Wrote main.yaml project prj_01JC8Q4N7V3X9B2D5F6G8H0JKM
app.ts a fetch handler that says hello
package.json mainsdk as a dependency
.gitignore .mainsdk/
CLAUDE.md MainSDK rules, between markers
agent context .claude/ · .cursor/ · AGENTS.md
.mcp.json mainsdk mcp for Claude Code
Every interface runs on a local driver. No account, no keys.
Next
1 npx mainsdk add auth db sign-in and a database
2 npx mainsdk dev app.ts run it on 127.0.0.1:3000
3 npx mainsdk check see what to fix before you shipinit writes main.yaml for you. You never write it by hand on this path.
2. Add sign-in and a database
npx mainsdk add auth db3. Write the app
Replace app.ts with this. It is the only file you write, and it is 12 lines.
import { auth, models } from "mainsdk"
import { s } from "mainsdk/schema"
const { notes } = models({ notes: s.object({ text: s.string() }) })
export default async function fetch(req: Request) {
const { user } = await auth.require(req)
const mine = notes.for(user)
if (req.method === "POST") return Response.json(await mine.create(await req.json()))
return Response.json(await mine.list())
}4. Run it
npx mainsdk dev app.ts$ npx mainsdk dev app.ts
mainsdk dev · notes · env dev
auth local dev sign-in on
db local .mainsdk/db/app.sqlite
log terminal
No cloud calls. No provider keys. Network observe, loopback only.
Ready http://127.0.0.1:3000 · 0.4s
Sign in as a test user: -H "authorization: Bearer dev_ada"
Logs: mainsdk logs --follow · Stop: Ctrl-C
12:04:31 GET / 401 2ms MAIN-AUTH-0001 not signed in
12:04:40 POST / 200 6ms ada
12:04:42 GET / 200 3ms adaIn a second terminal, ask without signing in. You get 401 with a code and the one thing to do:
curl -i http://127.0.0.1:3000/Sign in as the test user ada, write a note and read it back. The dev_ token works only from this machine and only under mainsdk dev:
curl -X POST http://127.0.0.1:3000/ -H "authorization: Bearer dev_ada" -H "content-type: application/json" -d '{"text":"hello"}'
curl http://127.0.0.1:3000/ -H "authorization: Bearer dev_ada"
curl http://127.0.0.1:3000/ -H "authorization: Bearer dev_bob"ada sees her note. bob sees an empty page, because every query belongs to its owner.
5. Check it
npx mainsdk check$ npx mainsdk check
mainsdk check · notes · env dev · Main 27
config secrets policy network auth data
Passed · 6 gates · 0 findings · 1.2sNext
- Existing app: add MainSDK to a project you already have.
- Thirty-minute path: the longer route, with the three ideas explained.
- Interfaces: everything else the app can use, each with a one-line happy path.
Was this page helpful?