Local development
What railcode dev runs locally, what it forwards to your real instance, and the limits you accept.
railcode dev [--port <n>] [--reset]Run it from the directory that contains railcode.json. It serves the frontend and the
backend function. It routes the same paths that production routes (/api/*,
/_serverFn/*).
tanstack: the Vite preset runs the backend function in an embedded runtime, and the CLI proxies/apiand/_serverFnto Vite.hono+vite/hono+static/ bring-your-own: the CLI bundles the backend function with esbuild (watched), runs it in-process, and serves the same paths. A rebuild re-imports the backend function on the next request.
Either way, the backend function calls the CLI's local data plane over HTTP with the same
wire format as production. So "works in railcode dev" means "works deployed."
Local vs forwarded
| Surface | Under railcode dev |
|---|---|
db, files | Local scratch store on disk. --reset seeds it fresh. Never touches live data |
| SQL, saved queries, LLM, email, connectors, agents | Forwarded to the real instance under a CLI-minted dev token that carries your identity |
secrets | Read from your local environment |
| Cron | Not scheduled. Trigger the route by hand (remember: POST) |
Forwarded calls hit real providers, real data, and real spend, including sending email and starting agent runs. Their authority is bounded by the manifest exactly as in production, so authority failures reproduce locally.
Accepted limits
A single identity, secrets from your local environment, and cron triggered by hand.
Local storage is separate
Local dev KV and files live under ~/.railcode/dev/<instance>/<app>/. Only
railcode dev --reset clears them.
railcode app kv and railcode app files read and write the live app, never the local
emulation. So under railcode dev, seed data through the app's own UI instead.
Validating before you ship
railcode dev # confirm the frontend loads and its /api routes answer
railcode manifest validate # strict local parse of manifest.yamlmanifest validate parses the file with the same strict YAML grammar the server uses. Use
spaces, not tabs. A bare yes / no / on / off / null / number resolves to a
non-string and is rejected where a name is expected. Resource names are only checked
against your org at deploy time.
Then exercise the app end to end at desktop and mobile widths. Treat console errors, failed
/api/* calls, and broken layouts as failures to fix.