Streaming protocol v1
The public stream <package> [--frames N] [--fps 30..240] [--reload] [--allow-storage] command
prints one JSON object per line. No game interaction is involved. The native
supervisor owns a duplex pair of anonymous pipes connected to its worker.--stream-worker is an internal, unsupported test interface: invoking it
directly bypasses the supervisor/job/watchdog.
Messages
Worker readiness:
{"type":"ready","protocol":1,"api":"0.1"}
Host snapshot (all fields required, no extra fields):
{"type":"snapshot","sequence":0,"timestamp_ms":100,"width":800,"height":600,"x":20,"y":30,"events":["viewport.changed"]}
Sequence must exactly equal the next frame number. Timestamps are unsigned
session milliseconds, monotonic and at most 604800000 (seven days). Width/height: 1–16384;
coordinates: finite ±100000. Events: at most 16 strings, each ≤256 bytes.
Events are callback input, not executable code. Input line: ≤8192 bytes.
RC11 adds validated rect, circle.filled, ui.slider, and ui.cycle command
kinds. Hosts consume this additive schema only when built with RC11 or later;
protocol envelope remains v1. Slider input uses ui.value:<id>:<0..10000>;
runtime checks IDs/values, consumes the latest valid value once, and expires
unused values at frame end. Widget definitions/ranges are documented in GUI.md
and EXTENSIONS_RU.md. Old commands and lifecycle envelopes are unchanged.
Worker frame:
{"type":"frame","sequence":0,"timestamp_ms":100,"commands":[{"kind":"circle","x":20,"y":30,"radius":10,"color":4294967295}]}
The host accepts only the outstanding sequence/timestamp, and validates the
entire graphics schema before forwarding. Commands: ≤256; frame line: ≤256 KiB.preview draws these frames in the native test window; stream does not open
a window. Public stdout contains ready/frame/reloaded/done
messages, not the private host snapshot messages. Since 0.3.5, a consented
telemetry source additionally emits a source message when its state changes:
{"type":"source","source":{"id":"owned-demo","pid":123,"status":"ready","error":"","snapshot":true,"update":false,"update_status":"idle"}}
The envelope and source fields are exact. ID/PID must match the session grant;
status is waiting/ready/unavailable; error is single-line UTF-8, at most 128 bytes.
snapshot is true exactly in ready state; update additionally requires separate
write consent and provider support. update_status is idle/queued/accepted/rejected/
unknown. No telemetry values are included. This message is informational and
does not acknowledge a snapshot or reset the frame watchdog. No source consent
means no source messages. Existing frame and handshake envelopes are unchanged.
After the configured frame count/time or a user stop, the host sends {"type":"stop"}. The worker
runs on_unload, persists permitted configuration and replies with{"type":"done","report":{...}}. The report has the existing brand/API,
diagnostics, logs and callback timings; its frames array is empty because
frames were already streamed, and frames_sent is the count. Each completed
frame is removed from worker history after emission; it is not retained for
the whole session. Logs keep the latest 64 entries and callback timings the
latest 128; logs_total, callbacks_total, frames_total count all activity.
Reports are bounded even for long sessions.
On failure, the final report has ok:false; a consumer must not assume earlier
frames mean successful unload. The public supervisor adds worker_exit when
a valid worker report exists, waits for process exit, and reports watchdog or
protocol failures itself. Closing the parent terminates the owned worker.
The demonstration host sends host.tick on the second snapshot. Viewport and
coordinates are generated test data, not data from an external application.stream defaults to six frames. --frames supports 1–1000000; --seconds
supports 1–3600 and runs until the duration expires; --live requests indefinite
operation until Ctrl+C, subject to the million-frame/seven-day safety guards.preview defaults to live operation until window close. Session-limit options
are mutually exclusive. --reload reloads once halfway through a fixed frame
run, or at frame 30 in live/timed mode; it is not a runtime package-manager API.
The watchdog now bounds five seconds without validated protocol progress,
not the total session duration. It covers startup, missing frame replies and
stuck teardown. Only ready/frame/reloaded/done validation resets progress; partial
bytes do not. Streaming messages remain bounded per line, while the old 4 MiB
lifetime-output cap applies only to batch run. Consumers must drain stdout;
blocking downstream stdout can still stall the supervisor itself.
The default request cap is 60 FPS. --fps changes stream/preview pacing, while
the client uses its live Target framerate setting. Deadlines include worker
and renderer time; waitable timers wait only for the remainder and pump preview
messages. Late frames do not trigger catch-up bursts. Actual performance is
workload-dependent; see FRAME_TIMING_RU.md. Wire fields/API versions are unchanged.
Ctrl+C/Ctrl+Break request a normal stop in the public streaming supervisor.
Window close requests stop too, allowing a current frame to finish, then unload
and save permitted config. Abrupt process termination still invokes job cleanup
without a graceful save. Worker isolation is not a security sandbox; only
trusted packages are supported.
Manual reload uses the exact host message {"type":"reload"} between snapshots.
Success returns {"type":"reloaded","sequence":1,"fingerprint":"<64 lowercase hex>"}:
sequence is the next frame number, not a new frame. Preview sends this command
on F5 and waits for acknowledgement before another snapshot; held-key repeat
is ignored. Esc closes the own window and requests normal stop.
Old widget presses, queued clicks and display commands are cleared on reload.
The manifest, entry and declared resources are checked again. ID and permissions
must stay unchanged; changing those requires a new session. Normal unload saves
permitted configuration before loading the new module. A failure stops the
session with ok:false; there is no rollback to the old module. A validation
failure before unload does not save pending config. Fingerprints are inspection
hashes, not signatures or protection against concurrent file substitution.