Quickstart
Install the CLI, sign in, create an app, and deploy it. About five minutes from nothing to a live URL.
From nothing to a deployed app in about five minutes.
1. Install the CLI
npm install -g railcode@latest # or: pnpm add -g railcode@latest
railcode --versionThe CLI owns the build for every template. Your app never declares a bundler or a backend build script.
2. Sign in
railcode loginLogin is browser-based. The CLI starts a localhost callback, prints an authorization link,
and opens your browser. You approve the CLI from the dashboard. The CLI then exchanges a
one-time code for a long-lived, revocable personal API token and saves it to
~/.railcode/config.json.
On a headless or SSH machine, use railcode login --paste (alias --no-browser) and paste
the one-time code that the authorize page shows.
If you do not have an organization yet, finish onboarding in the dashboard and run
railcode login again. Deploying needs an org on file.
3. Create an app
railcode init my-app
cd my-app
npm installThe slug must be a DNS label: ^[a-z0-9][a-z0-9-]{0,62}$.
Four templates are available with --template:
| Template | Frontend | Backend function |
|---|---|---|
hono+vite (default) | Vite + React | Hono |
hono+static | one index.html, no build | Hono |
tanstack | TanStack Start (SPA mode) | server functions + /api/* |
static | static files | none, pure hosting |
Each template with a backend function includes a small platform tour: identity, a todo list on the key/value store, files, and the read-only org surfaces. Read it, then delete it. It is scaffolding, not a style guide.
4. Run it locally
railcode devThis serves the frontend and the backend function. It routes the same paths that production
routes (/api/*, /_serverFn/*). The backend function talks to a local data plane with the
same wire format as production. So "works in railcode dev" means "works deployed."
Storage is a local scratch store (--reset clears it). It never touches live data. Governed
capabilities (SQL, saved queries, LLM, email, connectors, agents) go to your real instance
under a dev token that carries your identity. They hit real providers and real spend.
5. Write a route
The backend function is the only thing that touches the platform. A route looks like this:
import { Hono } from "hono";
import { ctx, db } from "@railcode/sdk";
const app = new Hono();
app.get("/api/me", (c) => c.json({ user: ctx.user }));
app.post("/api/notes", async (c) => {
const { title } = await c.req.json();
const id = crypto.randomUUID();
await db.collection("notes").put(`${ctx.user!.id}:${id}`, {
id,
owner: ctx.user!.id, // store the owner, don't trust the request body
title,
created_at: new Date().toISOString(),
});
return c.json({ id }, 201);
});
export default app;The frontend calls fetch('/api/notes'). It never sees a platform endpoint.
6. Declare what the app may do
Write a manifest.yaml beside railcode.json. On apps v2 run_as: app is mandatory: the
backend function is the app's principal.
run_as: app
llm: true
saved_queries:
- revenue_by_monthDeclare only what the backend function uses, then check it:
railcode manifest validate7. Deploy
railcode deployThis runs the CLI-owned build, uploads the static files and the backend function as one
unit, ratifies the manifest, and prints the live URL,
https://<app>.<org>.<serving-domain>/.
A new app defaults to organization access: every member of your org can open it. Use
railcode deploy --private for a private first deploy.
Then what
- Confirm it works: open the URL, check that
/api/*calls succeed, and read the backend's own record of what happened withrailcode logs app --app <slug>. - Seed some data so the UI is not all empty states:
railcode app kv set notes demo '{"title":"Hello"}'. - Read the app model for the shape of everything else, and limits for what the platform deliberately does not do.
Start from an example
For anything past the scaffolded tour, read
Railcode-HQ/railcode-examples. Every
app there is apps v2 (a static frontend/ plus a server/index.ts backend function), so it
is safe to copy from.
| Example | Read it for |
|---|---|
apps/kanban | The plainest app: a shared store and one asymmetric rule, written where a caller cannot reach it |
apps/chat | An agent loop that streams ndjson to the page, and per-user isolation built from keys plus an owner check |
apps/crm | A large legacy app ported through one module |
agents/pitch-deck | Starting an agent run from a backend function and polling it |
agents/proposals | Why a schedule belongs to the agent, not the app |
Copy one directory as plain files:
mkdir my-app && curl -fsSL \
https://github.com/Railcode-HQ/railcode-examples/archive/refs/heads/main.tar.gz \
| tar -xz --strip-components=3 -C my-app railcode-examples-main/apps/kanbanThen set app in railcode.json to your own slug.