Builds and live sync
A build is a job
A turn or an import takes minutes, so the workbench does not hold a request open for it. The server answers at once with a job; the build runs in a bounded worker pool on the server; progress goes out over the project's event stream; and the result is fetched when it is done. That gives three properties you can rely on:
- Closing the tab does not stop the build. Reopen the project later — from the same device or another — and the workbench finds the running build, shows its stage, and delivers the reply when it lands.
- One build per project at a time. A second message while one is running is refused (already building), because two edits of the same board at once has no meaning. Other projects are not blocked;
soldermask serve --workers Nsets how many can run at once (each runs the router). - A failure is a message, not a hang. If the model call fails, credentials are missing, or the pipeline throws, the thread gets an error bubble with the reason and the composer comes back.
Scripts can follow a job at /api/jobs/<id> or ask for the old synchronous behaviour with "wait": true; see the API.
Live sync
Two devices looking at the same project share view, camera, selection, net highlight and build quantity. Move the 3D board on the desktop and the phone follows; tap a part on the phone and the desktop selects it. It uses a server-sent event stream per project (/api/events?project=<slug>) and last-write-wins: each device tags what it sends and ignores the echo of its own moves, so two devices cannot fight over the camera. Camera moves are throttled to about sixteen per second on the wire.
The same stream carries build progress and, when another device switches versions, the version id — so the second screen follows there too. The Sync pill is green when the stream is connected.
A stream costs a server thread for its whole life, so their number is capped at a third of the server's workers and each one ends after five minutes, the browser reconnecting by itself. A device that cannot get a stream is not shut out: the Sync pill goes grey, the build still reports through the job poll, and the stream is picked up when a slot frees. That matters because a stream cannot see that its client has gone — a phone that dropped off the wifi leaves one running — and without those limits enough of them would take every thread and the server would stop answering anything at all.
Sync is per project and in memory: it needs the app's own server (it is off in the standalone export) and one server process per data directory.