Your whole backend in one binary
The whole backend in one command
Section titled “The whole backend in one command”nimbus devRun it in your project. One process — a single Rust binary — serves storage, TypeScript functions, realtime subscriptions, scheduling, and agent sandboxes, and reloads your functions as you save. No Compose file, no sidecar services, no managed account.
Install it first:
brew install nimbus/tap/nimbusOther platforms ship via the install script and release binaries — see the
install options. The
5-minute quickstart scaffolds an app around
nimbus dev; the self-host quickstart runs the
same binary as a long-lived server with nimbus start.
One binary, your choice of client
Section titled “One binary, your choice of client”# migrate an existing Convex appnimbus dev// convex/messages.ts — server-side functions with reactive queriesimport { query, mutation } from "./_generated/server";import { v } from "convex/values";
export const list = query({ args: {}, handler: async (ctx) => await ctx.db.query("messages").take(50),});
export const send = mutation({ args: { author: v.string(), body: v.string() }, handler: async (ctx, { author, body }) => await ctx.db.insert("messages", { author, body }),});One command in your project root: nimbus dev detects the convex/
directory and repoints the convex dependency at the package inside the
binary. See the Convex guide — or scaffold a fresh
app in the 5-minute quickstart.
# migrate an existing Firebase appnimbus dev// Your Firebase code, unchanged — stock imports, served by Nimbusimport { initializeApp } from "firebase/app";import { addDoc, collection, connectFirestoreEmulator, getFirestore, onSnapshot,} from "firebase/firestore";
const db = getFirestore(initializeApp({ projectId: "demo" }));connectFirestoreEmulator(db, "127.0.0.1", 3210);
const messages = collection(db, "messages");await addDoc(messages, { body: "hello from nimbus" });onSnapshot(messages, (live) => console.log("live size", live.size));Firestore is Firebase’s database — Nimbus speaks its wire protocol and
ships a drop-in firebase package inside the binary. nimbus dev
detects the firebase dependency, checks every Firebase import in
your sources is covered, and rewires the dependency at the drop-in —
your imports stay exactly as they are. See
the Firestore guide.
# migrate an existing Functions projectnimbus dev// functions/src/index.ts — Firebase-style HTTP + Firestore triggersimport { onRequest } from "firebase-functions/v2/https";import { onDocumentCreated } from "firebase-functions/v2/firestore";
export const hello = onRequest(async (req, res) => { res.json({ message: "Hello from Nimbus Cloud Functions!" });});
export const onMessageCreated = onDocumentCreated( "messages/{messageId}", async (event) => { console.log("New message:", event.data?.data()); },);Run nimbus dev in your existing Functions project — firebase.json
marks the root. See
the Cloud Functions guide for the
project layout and deploy flow.
# no migration — use your existing drivernimbus dev// Official MongoDB driver, unchanged — Nimbus speaks the wire protocolimport { MongoClient } from "mongodb";
const client = new MongoClient(process.env.NIMBUS_MONGODB_URL);await client.connect();
const messages = client.db("myapp").collection("messages");await messages.insertOne({ author: "Ada", body: "Hello from Nimbus" });const docs = await messages.find({ author: "Ada" }).toArray();nimbus dev sees the mongodb dependency and writes
NIMBUS_MONGODB_URL — the local endpoint plus generated credentials —
to your app’s .env.local. See
the MongoDB guide for credential setup and
the supported command surface.
# no migration — use your existing AWS SDKnimbus dev// Official AWS SDK v3, unchanged — pointed at Nimbus's endpointimport { DynamoDBClient, PutItemCommand, GetItemCommand } from "@aws-sdk/client-dynamodb";
const client = new DynamoDBClient({ endpoint: process.env.NIMBUS_DYNAMODB_ENDPOINT, region: "us-east-1", credentials: { accessKeyId: process.env.NIMBUS_DYNAMODB_ACCESS_KEY_ID, secretAccessKey: process.env.NIMBUS_DYNAMODB_SECRET_ACCESS_KEY, },});
await client.send(new PutItemCommand({ TableName: "orders", Item: { pk: { S: "order-1" }, total: { N: "42" } },}));const { Item } = await client.send(new GetItemCommand({ TableName: "orders", Key: { pk: { S: "order-1" } },}));nimbus dev sees the @aws-sdk/client-dynamodb dependency and writes
the endpoint and a generated access key to .env.local as
NIMBUS_DYNAMODB_* keys. See
the DynamoDB guide for registered-key
credentials and the supported operation surface.
# native API — nothing to migratenimbus start# the same engine over plain RESTcurl -s -X POST http://localhost:8080/api/tenants \ -H "Authorization: Bearer $NIMBUS_TOKEN" \ -H "Content-Type: application/json" -d '{"id": "demo"}'
curl -s -X POST http://localhost:8080/api/tenants/demo/documents \ -H "Authorization: Bearer $NIMBUS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"table": "messages", "fields": {"text": "hello world"}}'See the native API guide and the self-host quickstart for the token setup.
# isolated compute for agents — same binarynimbus start// @nimbus/nimbus SDK — give an agent its own isolated processimport { Nimbus } from "@nimbus/nimbus";
const nimbus = new Nimbus({ endpoint: "http://localhost:8080", tenantId: "demo", token: process.env.NIMBUS_TOKEN,});
const sandbox = await nimbus.sandboxes.create({ profile: "worker", spec: { owner: { kind: "standalone", displayName: "agent-task" }, backend: "container", root: { kind: "oci_image", source: { kind: "reference", reference: "docker.io/library/node:22-alpine" }, }, process: { argv: ["node", "-e", "setInterval(() => {}, 1000)"] }, },});Sandboxes, services, and sessions live in the same binary as your
data — no separate sandbox vendor. Execution runs on Linux hosts;
nimbus machine hosts them on macOS. Start with the
agent sandbox quickstart.
Every tab is the same engine and the same data — five drop-in protocols, the native API, and the sandbox surface. See Developers for the adapters side by side.
Production is one more verb
Section titled “Production is one more verb”nimbus dev # laptop: watch files, run codegen, reload functionsnimbus deploy # production: package, validate, activate atomicallyYou have already run the first one. Both are the same binary with the
same configuration surface — no Terraform, no Helm chart, no Dockerfile
between them. nimbus deploy packages your functions, previews a diff
with --dry-run, and activates the new generation atomically on whatever
server you point it at.
Deploy to production walks the whole flow.
Built for agents
Section titled “Built for agents”Agents need a backend they can run (one binary, started in one
command), inspect (docs shipped as llms.txt, structured
errors, and a
plain table of what works today), and
get isolated compute from.
That last part is first-class: sandboxes are isolated worlds addressed
by id, services are named workloads other code can depend on, and
sessions are expiring leases for stdio, file, and browser-control
access — all managed through the @nimbus/nimbus SDK against the same
server that holds your data.
Scale with tenants and services
Section titled “Scale with tenants and services”The unit of isolation is also the unit of growth. Every tenant gets its own storage namespace, its own write queue, and its own function budgets — load on one tenant cannot degrade another, and adding load means adding tenants. Heavier compute fans out into sandbox-backed services that Nimbus launches and supervises next to your data, and storage can move onto PostgreSQL, MySQL, or libSQL infrastructure that grows independently. One deployment is one process today — no cluster mode; scaling spells out the limits and the partitioning pattern for growing past one machine. Self-hosted, source-available, and protocol-portable by design: no lock-in.