MainSDK
Search⌘K
Docs

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.

Planned for 0.3

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.
Stability levels
Updated 2026-10-28

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

bash
npx mainsdk init
snapshot="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 ship

init writes main.yaml for you. You never write it by hand on this path.

2. Add sign-in and a database

bash
npx mainsdk add auth db

3. Write the app

Replace app.ts with this. It is the only file you write, and it is 12 lines.

ts
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

bash
npx mainsdk dev app.ts
snapshot="dev"
$ 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   ada

In a second terminal, ask without signing in. You get 401 with a code and the one thing to do:

bash
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:

bash
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

bash
npx mainsdk check
snapshot="check-pass"
$ npx mainsdk check
mainsdk check · notes · env dev · Main 27

   config    secrets    policy    network    auth    data

Passed · 6 gates · 0 findings · 1.2s

Next

  • 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?

↑↓ moveEnter openEsc close

Not decided yet