Engine Protocol (v1)
Wire protocol between an upper-layer service/supervisor and a standalone renderer process. The goal is one transport that behaves the same on Linux and Windows, with a single job in flight per process. Status: Implemented forblockscope serve and blockscope supervise (protocol v1).
Transport
- Newline-delimited JSON over the worker’s standard input and output.
- One UTF-8 JSON object per line, terminated by a single
\n. Readers must accept\r\nbut writers emit\nonly. - No BOM, no pretty printing, no multi-line objects.
stdoutcarries protocol messages only. Logs and diagnostics go tostderr.- Standard input is read as raw bytes and decoded as UTF-8; do not route it through a console code page.
- v1 uses no sockets. stdio is identical across supported platforms and needs no port allocation, firewall rule, or named-pipe path.
Lifecycle
- The worker prints exactly one
helloline after its material set is ready. The supervisor must not send jobs beforehello. - One job at a time. The worker processes lines sequentially; the supervisor owns the queue and the concurrency policy.
- Closing stdin is equivalent to
shutdown. The worker finishes the current job, printsbye, and exits 0.
Messages
hello is printed only after the material set is ready, so the supervisor can
size its pool from suggestedWorkers and suggestedThreads instead of guessing
from the host. Values come from cgroup limits on Linux and
GlobalMemoryStatusEx on Windows; see blockscope capacity --json for the same
data from the command line.
Render request
Paths must be absolute. The worker rejects relative paths with
INVALID_REQUEST, so behaviour does not depend on the worker’s working
directory.
timeoutMs is deliberately absent: the supervisor enforces timeouts by killing
and restarting the worker. The engine cannot reliably interrupt its own
rasterizer mid-tile.
Example: single view
Example: multi-view
Render result
outputspreservesviewsorder, one entry per view.- Output files are
{id}.pngfor a single view and{id}-{yaw}.pngfor multiple views. The engine never overwrites a result from another job. cacheHitistruewhen the worker reused a parsed scene and mesh for the sameinputHashandlod;parseMs,lodMs, andmeshMsare then0.sceneHashdescribes the scene that was rendered. Withlod > 1it is the aggregated scene, not the original projection.lodechoes the effective factor. With"lod": "auto"the worker reports the factor it chose, and the cache key staysauto.timingsare wall-clock aggregates for the whole job.statisticsis advisory; consumers must not depend on fields beyondtriangles.
Errors
Parser errors also pass through their stable
LitematicErrorCode names, such as
INVALID_PALETTE, INVALID_REGION_BOUNDS, and
BLOCK_STATE_DATA_LENGTH_MISMATCH.
retryable is advisory. A retried job must be idempotent: outputs are written
to a fresh directory or overwritten in place, and no state survives a job.
Supervisor
blockscope supervise speaks the same protocol on its own stdio and owns a pool of
blockscope serve children:
- Pool size and threads default to
blockscope capacity;--workers,--threads,--queue, and--cacheoverride them. - Jobs are queued and dispatched to idle workers. The queue is bounded; a full
queue returns
BUSY. - Repeated inputs are routed to the worker that already cached them, so a second render of the same file skips parsing and meshing.
- A worker that exits mid-job produces
INTERNAL_ERRORand is restarted. - A job that exceeds
--timeout(default 60 seconds,0disables) is killed, retried once on a fresh worker, then answered withTIMEOUT. - A worker that exceeds its memory ceiling (85 percent of the available memory
divided by the pool size, or
--memoryin MiB) is killed the same way withMEMORY_LIMIT. - Results may arrive out of order because workers run in parallel; match them by
id. - Killed workers are reaped before replacement, so the pool does not accumulate zombies. A supervisor that dies closes the worker pipes, which makes the workers exit on stdin EOF.
Cross-platform rules
Chinese and other non-ASCII file names are supported because every path crosses
the pipe as UTF-8. Supervisors on Windows must read the pipe as UTF-8, not
through the console code page.
The engine must never depend on a console: no colour codes, no interactive
prompts, no reading from a TTY.
Supervisor contract
- Start
Nworkers, each withTrasterizer threads, such thatN * T <= physical cores. Reference:2 x 6on a 12-thread machine. - Route jobs by
inputHashso a repeated input lands on the worker that already holds its snapshot and mesh cache. A cold worker pays the material load once. - Queue depth is bounded. When full, reject with a busy response rather than growing memory.
- Timeout per job, kill and restart the worker, retry once, then surface
TIMEOUT. - On worker crash, restart it; an in-flight job is retried once.
- Never write protocol data to a worker’s stdin while a job is in flight.
Versioning
protocolVersionis an integer, incremented for breaking changes.- New optional request fields and new response fields are not breaking.
- Removing fields, renaming fields, or changing defaults is breaking.
- A worker that receives an unknown
protocolVersionexits with code 2 and ahello-less stderr message.
Non-goals for v1
- Cancellation of a running job.
- Streaming or progressive output.
- Non-PNG formats.
- In-process job queues or multi-job batching.
- Windows peak RSS accounting.
Overrides
serve and supervise accept --force-limits, which raises the parser, mesh,
and render caps for that process. It prints a warning on stderr and is meant for
offline work on oversized schematics, not for the default service profile.