Skip to content

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 profilesseed, 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 installpnpm 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.envpnpm 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:

Terminal window
NOVA_SANDBOX_RUNTIME=wasm # default. runtime/nova-wasm — nothing to install
NOVA_SANDBOX_RUNTIME=nova # runtime/nova-sandbox: Linux namespaces, cgroups v2, seccomp, Landlock
NOVA_SANDBOX_RUNTIME=docker # the image below

nova-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):

Terminal window
docker/sandbox/build.sh # or build.ps1 on Windows — ~3-6 min first build, ~1.1-1.3GB image

The 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 1

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

PathWhat it isWhere
Vercel + Cloudflare Tunnel (chosen, PLAN.md §8.2)Static web + docs on Vercel, the API on a machine you control, reached through a Cloudflare TunnelVercel + Cloudflare production
Docker Composecaddy (automatic HTTPS) fronting api, web, docs containers, with named volumes for SQLite and the workspacedeploy/docker-compose.yml, deploy/Dockerfile.{api,web,docs}
Fly.ioOne fly.toml per app (api, web, docs)deploy/fly/
A bare VPSA single setup scriptdeploy/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.