Skip to content

Binary data (fd 4)

fd 4 carries raw binary payloads that fd 3 metadata rows reference via byteLength. There is no framing on fd 4 itself — the metadata side tells the parser exactly how many bytes to read.

If fd 3 emits these in this order:

{"id":"a","type":"image","format":"png","byteLength":4096}
{"id":"b","type":"image","format":"jpeg","byteLength":12000}

…the parser reads exactly 4 096 bytes off fd 4, associates them with id: "a", then reads 12 000 bytes for id: "b". Both streams are sequenced.

Most use cases stick with the default channel (fd 4, alias "data"). For more elaborate workflows you can declare additional named channels in HYPERTERM_CHANNELS and reference them from metadata:

{"id":"raw-vid","type":"canvas2d","dataChannel":"video","byteLength":65536}

The default channel is "data" (fd 4). Custom channels are not exposed by the default client libraries — they’re for advanced integrations that want to multiplex separate data streams (e.g. video frames vs control payloads).

Writes are blocking. If τ-mux is busy rendering, the script’s next write to fd 4 will block until the read pipe drains. This is intentional — non-blocking I/O would risk silent frame loss.

Each panel’s binary payload becomes an ArrayBuffer in the renderer. For images, the buffer is wrapped in a blob URL and assigned to an <img> element — when the panel is cleared, the URL is revoked and the buffer is released.

Panels of type html or svg — whether the markup arrives inline (meta.data) or as an fd 4 payload — render inside a locked-down <iframe sandbox> (no scripts, no same-origin) carrying a strict Content-Security-Policy (script-src 'none'). This holds on both the native app and the web mirror, so a careless or compromised producer can’t run script in the host page (which on the native app holds the app’s full IPC bridge). Inline styles and data:/blob: images are allowed; <script> and inline event handlers (onerror=…) never execute.

If your panel needs to forward clicks/keys back to your script, set interactive: true in the metadata. That panel renders directly (no iframe) so its DOM events can be forwarded — an explicit, opt-in trust boundary. Only use it for markup you fully control.

There is no hard cap, but practical limits:

  • Per-panel payload — up to a few MiB is fine. Multi-hundred-MiB images will exhaust webview memory.
  • Burst rate — backpressure protects you, but emitting frames faster than the renderer can consume just means your script blocks waiting for the renderer.