How Nacre works
The mental model. There are two sides: the control plane (runs Nacre), and your agent environment (runs you). They talk to each other through a small set of signed endpoints.
Two planes, one agent
Every Nacre customer gets their own dedicated agent environment. That environment runs your OpenClaw instance and nothing else — fully isolated, no noisy neighbours.
Our control plane (the thing at nacre.sh) is the dashboard, billing, and the orchestrator that spawns, resizes, and retires VMs. It never reads your conversation content — just metadata (uptime, resource usage, OpenClaw version).
The control plane
- Hosting
- Next.js 14 on Vercel — marketing site, dashboard, API routes.
- Database
- Supabase Postgres with row-level security. Secrets encrypted with pgsodium.
- Auth
- Supabase Auth — email/password and Google OAuth.
- Billing
- Stripe Checkout + Customer Portal + webhook-driven lifecycle.
- DNS & edge
- Cloudflare. Each VM gets an A record at user-<id>.nacre.sh.
- Offsite backups
- Cloudflare R2 — a different cloud than your VM provider.
Your agent environment
When you pay, we provision a dedicated agent environment for your account — isolated from all other users. A single cloud-init script installs Docker and brings up four containers:
- caddy:2-alpine
- TLS termination via Let's Encrypt; routes /terminal and /ws to ttyd, everything else to OpenClaw.
- openclaw
- The AI assistant itself — pinned to a stable release tag.
- ttyd
- Web terminal. Only reachable via JWT-gated Caddy route.
- oc-agent
- Our Go binary. Heartbeats, command execution, backups. ~10 MB.
The /data volume holds your OpenClaw state — memory files, skills, conversation history, channel credentials. It is yours. We don't read it.
How the two planes talk
Three signed endpoints, all HTTPS, all on nacre.sh:
POST /api/internal/vm/:id/heartbeat— every 30 seconds, agent pushes CPU / RAM / disk / OpenClaw version.POST /api/internal/vm/:id/ready— once, from cloud-init, when Docker Compose is up.POST /api/internal/vm/:id/update-result— after an OpenClaw upgrade finishes, agent reports success or rollback.
Every request carries a per-VM HS256 JWT (scope: 'agent'). The control plane constant-time compares it against the copy it stored in pgsodium when the VM was provisioned.
The provisioning sequence
- Stripe webhook fires —
checkout.session.completed. We verify the signature and check idempotency against the event log. - Subscription + VM rows are inserted in Postgres, state =
creating. - Provisioning job is enqueued — the Supabase edge function picks it up.
- Secrets are minted — agent JWT, ttyd password — and encrypted with pgsodium.
- Cloud API call — new agent environment provisioned in your region with a rendered cloud-init user-data payload.
- Cloudflare DNS — A record for your subdomain created as soon as we have an IP.
- First heartbeat — agent reports in; the VM row flips to
ready; the welcome email goes out. - You land on the dashboard — usually within a couple of minutes of payment.
What survives a disaster
Nightly, your agent tars /data, encrypts it with a per-customer key (HMAC-SHA256(platform_secret, user_id)), and pushes it to Cloudflare R2 — a different cloud provider than your agent environment runs on. If the primary provider loses your disk, we restore from R2 onto a fresh environment. Full DR runbook onsecurity@nacre.sh.