Self-hosting
Novaterra is designed to be self-hosted by the person who owns the world it creates. Everything you need is checked into this repo.
That is not a limitation of the current release. One install is one person’s node, with a single owner enforced in the database, and that is the shape the whole product is built to. A hosted install would be the same software with the same single owner — a node that happens to run on rented metal, exportable and movable to your own hardware — and never a tenant row in somebody else’s database. There is no hosted offering today.
“Exportable and movable” is a command, not an aspiration: see Moving your node to your own hardware for how to carry a node’s beings, their addresses and their signing keys to another machine.
Node profiles
Not every self-hosted install looks the same underneath. Novaterra recognises four node
profiles — seed, leaf, home and hall — that differ in what they run and what they
delegate away. The default install described on this page is a home node: it runs the full
API, the database, the Docker or WASM sandbox, and every scheduled loop, and it is what
pnpm install → pnpm dev gives you. A seed is the opposite end of the spectrum — a small
device, an ESP32 class, that signs and queues but delegates almost everything else to a paired
home. See Node profiles and Seeds for what each profile
runs, what a Seed delegates, and an honest account of what has and has not been measured on real
hardware.
Local development
See Getting started for the full walkthrough:
pnpm install → .env → pnpm dev. Three processes, one machine: Vite on 5173, Fastify on 4000,
Astro on 4321 for these docs.
The code sandbox
code.execute (see Skills catalogue) is the one skill that
runs code a language model wrote, so it gets real containment. The default is WASM, not Docker,
and has been since 6 September 2026:
NOVA_SANDBOX_RUNTIME=wasm # default. runtime/nova-wasm — nothing to installNOVA_SANDBOX_RUNTIME=nova # runtime/nova-sandbox: Linux namespaces, cgroups v2, seccomp, LandlockNOVA_SANDBOX_RUNTIME=docker # the image belownova-wasm runs the language runtime itself — QuickJS and CPython, compiled to wasm32-wasi —
inside wasmtime, behind a hand-written WASI host that has no filesystem path and no sockets behind
any of its imports. About 22 MB on disk, a 5–60 ms start (87 ms measured end to end through code.execute), identical on Windows and
Linux, and nothing to install. What it gives up is stated rather than hidden: no bash, no
numpy/pandas/matplotlib, no npm, and CPU-bound Python about four times slower. See
Sandboxing.
The Docker image, still there and still verified
Reach for it when a project needs bash or the scientific Python stack, or when you install a
plugin that declares the process capability (docker/sandbox/, docker/README.md):
docker/sandbox/build.sh # or build.ps1 on Windows — ~3-6 min first build, ~1.1-1.3GB imageThe run contract is deliberately explicit and lives in one script so it can never drift between the
shell, the docs, and the API’s own invocation
(ensureSandboxImage/runCode in packages/skills/src/code/sandbox.ts, which builds the image
lazily on first use if it doesn’t exist yet):
--network none no network at all--cap-drop=ALL no elevated Linux capabilities--security-opt=no-new-privileges--read-only the image filesystem is immutable--tmpfs /tmp:rw,noexec,nosuid,nodev,size=64m the only writable path outside the mount--memory 512m --memory-swap 512m hard cap, no swap--cpus 1Whichever tier you choose, if it cannot isolate then code.execute fails closed: it refuses to
run rather than falling back to the host or to a weaker tier. Set NOVA_SANDBOX_ALLOW_LOCAL=1 in
.env to opt in to the unsandboxed fallback instead, and only then does it run — on the host,
flagged sandboxed: false. See
Security & privacy for why this matters.
Production deployment options
The deploy/ directory holds more than one path to production, built out further than PLAN.md’s own
description of “Vercel + Cloudflare” alone:
| Path | What it is | Where |
|---|---|---|
| Vercel + Cloudflare Tunnel (chosen, PLAN.md §8.2) | Static web + docs on Vercel, the API on a machine you control, reached through a Cloudflare Tunnel | Vercel + Cloudflare production |
| Docker Compose | caddy (automatic HTTPS) fronting api, web, docs containers, with named volumes for SQLite and the workspace | deploy/docker-compose.yml, deploy/Dockerfile.{api,web,docs} |
| Fly.io | One fly.toml per app (api, web, docs) | deploy/fly/ |
| A bare VPS | A single setup script | deploy/vps/setup.sh |
Every path shares the same non-negotiables: SESSION_SECRET/ENCRYPTION_KEY must be real random
values in production, WEB_ORIGIN/COOKIE_DOMAIN must match your actual domains exactly (a
mismatch here is the single most common self-hosting failure — see the cross-subdomain cookie
mechanics on the production page), and the API
itself always needs to be a real, long-lived process — never a serverless function — because of
SQLite, WebSockets, the Docker sandbox, file watchers, and Telegram’s long-polling.
What’s next
- Keeping it running — supervision, WAL checkpointing, encrypted backups and a restore drill you can run this afternoon. What turns “a person is watching the terminal” into a property of the software.
- Vercel + Cloudflare production — the chosen launch topology, DNS, TLS, the cross-subdomain cookie mechanics, and the smoke test.
- Local-first inference — the plan to move model calls onto your own hardware, and an honest account of what’s built versus what’s still just a plan.
- Finding other nodes — the directory service: a lookup, not an authority, and how to run without one at all.
- Node profiles and Seeds — the four node profiles, and the small-device Seed profile in detail: what it delegates, its measured wire sizes, and what is still inferred rather than measured.
- Moving your node — carrying a node’s beings, their addresses and their signing keys to another machine.
- Scaling and redundancy — four tiers from a laptop to multi-host, and what actually breaks when you run two API processes.