02 / Install & self-host
Run the compute sidecar
On this page 15
This is the operator page. It is the one you follow to start file work — short Python or Node runs on copies of files people already picked — on a laptop, a single self-hosted server, or a dedicated compute node.
What people see in the app (the File work card, the Workspace tab, quotas, approvals) lives on Secure compute. This page is only how to run the sidecar and switch it on safely.
You need two things. One without the other is never a teaser: Feature Status says Needs setup or off, and chat does not mention file work.
- The sidecar is reachable from PHP (
COMPUTE_URL+COMPUTE_TOKEN). - The product switch is on (
COMPUTE.ENABLED).
The sidecar never talks to users. PHP never talks to Docker. Never publish port 8080.
Release state (4.9+). The sidecar ships in the main repository under
sidecars/synaplan-compute/and starts with the development stack — no profile, no token to generate, no image pin. The productiondeploy/contract carries it as an opt-incomputeprofile (COMPOSE_PROFILES=computeindeploy/.env;prepare.shwrites the token) plus spoken answers that start with the stack. Runner images are built from the repo as local tags (synaplan-compute-python:local,synaplan-compute-node:local); the published GHCR 1.0.0 digest pins remain the production default.
Pick a path#
| Who you are | Path | Isolation |
|---|---|---|
| Developer, or one-box self-host | A — same host as PHP | T1 — hardened Docker, no gVisor |
| Hoster who wants a separate box | B — dedicated compute node | T2 — gVisor runsc |
| Operator of a multi-node cluster | B, on a node that serves no web traffic | T2 |
A cluster must not run T1 on the web nodes that serve people. A 16 GB / 8-core box is the right size for Team/Business concurrency (4 runs × 512–1024 MB). Two tiny VMs are not. Sizing notes: sidecars/synaplan-compute/docs/SIZING.md.
Rules that always hold#
- The runner images come from this repo. Compose builds the Python and Node runtimes as local tags the sidecar is configured to accept (
COMPUTE_ALLOW_LOCAL_IMAGES=1). A sidecar started outside Compose without those local images and variables has nothing to run — Path B intentionally runs outside the stack after building the:localimages and settingCOMPUTE_ALLOW_LOCAL_IMAGES/COMPUTE_IMAGE_*(step 5 below). Feature Status “Available” only means/v1/healthanswered — a missing runtime image still fails the first run. - The token is a secret on real installs. Path B and
deploy/use ≥ 32 random bytes (openssl rand -hex 32), mode600, never committed, never pasted into chat or tickets. PHP holds it asCOMPUTE_TOKEN; the sidecar reads the same value asCOMPUTE_AUTH_TOKEN. The laptop path uses a Compose-managed local token you never have to touch. - Scratch and workspaces stay on local disk. Not NFS, not a network volume. Same reason as Qdrant: mmap and file locks. Compose creates
.compute-data/for the laptop path; the distroless sidecar cannot create those directories itself on other layouts —mkdir -p .compute-data/{scratch,workspaces} && chmod -R 777 .compute-dataon first start. - Egress stays off. Today's sidecar reports
features.egress: falseand creates every container without a network. The UI refusesCOMPUTE.EGRESS_ENABLEDuntil a sidecar release with the egress proxy is connected. - The flag seeds on when a sidecar is wired. When
COMPUTE_URLandCOMPUTE_TOKENare already set at firstapp:seed,COMPUTE.ENABLEDandCOMPUTE.WORKSPACES_ENABLEDare inserted as1. An existing row is never overwritten — and the laptop compose additionally pinsFEATURE_COMPUTE_ENABLED=true, so an older database still offers the feature. - PHP needs the URL and token on every process that talks to the sidecar: backend and worker (and every additional worker role you run). The laptop compose sets all three; other layouts persist them in
.env/deploy/.env.
Path A — laptop or one server (T1)#
This is the "git clone and compose up" path. Same machine as PHP. Good for developing and for a modest self-host. It is hardened Docker (--network none, dropped capabilities, read-only root filesystem, non-root user), not gVisor.
git clone https://github.com/metadist/synaplan.git
cd synaplan
make up
# or: docker compose up -d (same stack, but :5173 stays silent until every image is pulled)
That starts the app, the sidecar, the Python/Node runtimes and spoken answers. Compose wires the local token into backend, worker and sidecar, creates .compute-data/, and pins FEATURE_COMPUTE_ENABLED=true plus workspaces — file work is offered immediately, on a new database and on an older one.
If the app was already running before you pulled this change, rebuild once so the new services exist:
docker compose up -d --build
Then switch it on only if Feature Status still says COMPUTE.ENABLED is off (a deliberate off, or an environment pin). Never publish :8080.
Why the socket mount: the sidecar image runs as distroless nonroot and talks to the host Docker daemon through the mounted socket to start one container per run. PHP itself never sees the socket. Default concurrency is COMPUTE_MAX_CONCURRENT=2; the sidecar refuses to start if the token is shorter than 32 bytes.
Production contract (deploy/)#
deploy/compose.yaml carries file work as an opt-in profile and spoken answers as an always-on service. On a single production host, set COMPOSE_PROFILES=compute in deploy/.env (or compute,office) and redeploy — prepare.sh writes COMPUTE_TOKEN next to the other data, and the stack exports COMPUTE_URL plus the enable flags into backend, worker and scheduler. Keep scratch and workspaces on local disk (deploy/data/compute/). See deploy/README.md.
Path B — a dedicated box (T2)#
Use this when people you do not know will send file work. The sidecar runs on a second machine. Web nodes only make HTTPS to PHP and HTTP to COMPUTE_URL on the private network.
What the box needs#
- Linux, Docker Engine, Docker Compose v2.
- gVisor
runscregistered in/etc/docker/daemon.json.docker infomust list therunscruntime, or the sidecar must not be started withCOMPUTE_TIER=gvisor. - A private address the web nodes can reach. Bind
:8080on that address only. Public:8080stays closed (firewall + bind). - Local disk for scratch and workspaces. A shared filesystem, if you have one, is only for the compose file and shared config — never for scratch or workspaces.
- About 16 GB RAM and 8 cores if you want four concurrent runs at 512–1024 MB.
Layout#
/synaplan-compute/ local SSD
src/ clone of metadist/synaplan (build context)
scratch/ ephemeral run dirs
workspaces/ per-person folders (when you switch that on)
.env token + tier + bind (mode 600)
First install#
- Create the directories on local disk.
chmod 0777scratch and workspaces if the sidecar cannotchownthem (it runs as distrolessnonrootwithoutCAP_CHOWN, logs one line about it, and falls back to world-writable modes). - Install Docker and
runsc. Restart dockerd after registering the runtime. - Clone synaplan to
/synaplan-compute/src(build context for the sidecar and the runner Dockerfiles). - Generate a token into
/synaplan-compute/.env(openssl rand -hex 32→COMPUTE_AUTH_TOKEN), setCOMPUTE_TIER=gvisor,COMPUTE_LISTEN=<private-ip>:8080,COMPUTE_SCRATCH_DIR/COMPUTE_WORKSPACES_DIRto the local directories. - Build the runtime images on this box (
make -C src/sidecars/synaplan-compute images→synaplan-compute-python:localandsynaplan-compute-node:local) and let the sidecar accept those local tags withCOMPUTE_ALLOW_LOCAL_IMAGES=1,COMPUTE_IMAGE_PYTHON=synaplan-compute-python:local,COMPUTE_IMAGE_NODE=synaplan-compute-node:local. The published GHCR digest pins are the default when those variables are absent. - Build and start the sidecar container from
src/sidecars/synaplan-compute(Dockerfile in that directory; mount the Docker socket, scratch and workspaces). Health must report"tier":"gvisor"and"features":{"workspaces":true,"egress":false}:
curl -sf http://<private-ip>:8080/v1/health
The full variable list (COMPUTE_MAX_CONCURRENT, COMPUTE_QUEUE_MAX, COMPUTE_RUN_RETENTION_MIN, COMPUTE_MAX_REQUEST_BYTES, COMPUTE_MAX_FILES, sandbox uid/gid, …) is sidecars/synaplan-compute/env.example; the deployment notes are sidecars/synaplan-compute/docs/DEPLOYMENT.md.
Then point PHP at it and switch it on.
Synaplan Cloud#
web.synaplan.com runs exactly this T2 layout on a dedicated compute node. Its host names, private addresses, NFS paths and roll scripts live in the private synaplan-platform repository (docs/COMPUTE-NODE.md, compute/) and are deliberately not on this public site. Operators of that cluster: follow that runbook; nothing on this page replaces it.
Point PHP at the sidecar#
| Install | COMPUTE_URL |
Where the token lives |
|---|---|---|
| Dev / Path A | http://compute:8080 (set by compose) |
compose-managed local token |
deploy/ + compute profile |
http://compute:8080 (exported when the profile is on) |
written by prepare.sh next to the other data |
| Path B | http://<private-ip>:8080 |
the web nodes' environment, same value as the sidecar's COMPUTE_AUTH_TOKEN |
Restart backend and worker (and every extra worker role) after changing the keys on layouts where you manage them by hand. Then Operate → Feature Status → Secure compute must read Secure compute is running; COMPUTE.ENABLED is off before you flip the flag. That is the honest "sidecar is up, product is still off" state.
Switch it on#
On the laptop path there is nothing to do — compose pins the feature on. On a new database with URL + token present at seed time, skip this as well: Feature Status already reads Secure compute is running.
Otherwise Operate → System configuration → Processing → File work → COMPUTE.ENABLED, or pin FEATURE_COMPUTE_ENABLED=true for an automated deploy and restart.
Workspaces (COMPUTE.WORKSPACES_ENABLED) start on with the standard 4.9 stack — and seed on for new databases that already have URL + token; only websites (COMPUTE.EGRESS_ENABLED) stay off. Either can only go on when the sidecar's health check reports that feature. Leave egress / websites off — the shipped sidecar cannot offer it.
Turning it off#
Switch COMPUTE.ENABLED off, or set COMPUTE_URL=disabled and restart PHP. Runs already in flight finish. New ones are not offered. The sidecar can stay running.
Check it works#
- Operate → Feature Status → Secure compute is Available / Secure compute is running. The probe is
GET /v1/health. GET /api/v1/config/runtimereportsfeatures.computeEnabled: true.- In chat, "Can you run code?" or
/helplists File work. - A small CSV → chart produces a card and a file in Files (source File work).
More product checks: Secure compute.
Troubleshooting#
- Feature Status: Secure compute is not reachable. Sidecar down or URL wrong. Path A:
docker compose logs compute. Path B:docker logson the compute node, thencurlthe private URL from inside a backend container (a host that can ping the box is not enough if the container has no route). - Secure compute is running; COMPUTE.ENABLED is off. Expected until you flip the flag. Not an error.
- Every run fails immediately (
program_errorwith empty logs, ordocker_unavailable). The runtime images are built from the repo as:localtags and accepted viaCOMPUTE_ALLOW_LOCAL_IMAGES=1— a sidecar started without those variables (or without the images on its daemon) has nothing to run. Rebuild withdocker compose up -d --buildon Path A, ormake -C sidecars/synaplan-compute imagesplus theCOMPUTE_IMAGE_*variables on Path B. Feature Status can still say Available — that probe does not check runtime images. - Public
:8080answers. Stop. Bind only the private address and check the host firewall. File work must not be on the internet. - Scratch permission lines at startup. Expected without
CAP_CHOWN. Keep scratch/workspaces on a volume no other local user can reach, orchmod 0777those two directories on the host. - "Website access … not offered … yet." Expected. Leave egress off.
- No Workspace tab.
COMPUTE.WORKSPACES_ENABLEDis off, or health does not reportfeatures.workspaces. - You restarted every web node and Feature Status is still empty. The environment keys are missing, or only some compose services received them. Confirm backend and every worker role interpolate
COMPUTE_URL/COMPUTE_TOKEN.
Related#
- Secure compute — what people see, quotas, approvals, egress honesty
- Feature modules — states,
app:modules:list, gates - Hosting Platform — the multi-node picture this sidecar joins
- Production Deployment — the single-node
deploy/contract - Tools & approvals — where unattended file work pauses
- Desktop: Skills — the client-side counterpart, on the user's own computer
- synaplan
docs/COMPUTE.md— flags, seeds, tiers, audit tables sidecars/synaplan-compute/docs/DEPLOYMENT.md— isolation tiers and the sidecar's own variables