← Blog

sigilOS Remote: SRDX, RFB, and the Session Manager Stack

June 22, 2026 · sigil-os · Sigil-Docs
session-manager remote srdx rfb 0.6.0

The sigilOS Session Manager was prototyped early — a Royal TSX-style folder tree of connections, compile-verified but hollow until the 0.6 remote stack arrived. That stack is now taking shape. Four pieces landed today that together form the complete remote rendering pipeline: a delta frame encoder, an ephemeral frame store, the RFB 3.8 protocol handshake, and a VT100 framebuffer text renderer. None of them are independent — they're a pipeline.


The pipeline

LayerComponentWhat it does
Encodesrdx_encode.sgCompares current fb vs shadow in 64×64 tiles → SRDX delta packet
Storesrdx_store.sg8-slot ephemeral frame buffer; session → frame; newest-wins
Protocolrfb_encode.sgRFB 3.8 handshake + FramebufferUpdate packet builder
Textvt100_fb.sgVT100 escape parser → 8×16 cell render into GPU framebuffer

The data flow for a remote session: the compositor encodes dirty tiles with srdx_encode_frame(), pushes the packet to the session's slot in the SRDX store, the RFB sender pops it and wraps it in a FramebufferUpdate, and the VNC client on the other end paints the result. For text-only sessions, the VT100 renderer writes directly into the GPU framebuffer, bypassing the compositor entirely.


SRDX — the delta encoder

core/srdx_encode.sg works in 64×64-pixel tiles. Each frame it compares the current framebuffer against a shadow copy (the last frame sent). Tiles that haven't changed are skipped. Changed tiles are packed into an SRDX packet: a 4-byte magic (0x53524458), a sequence number, a rect count, and then per-rect headers followed by pixel data.

The GPU path (tile-diff via compute shader, pixel copy via DMA) is silicon-pending — the CPU scalar floor using copy_span is what runs on QEMU and Pi 3. The same accel_tier() check that gates every other GPU path here determines which path runs; no special case in the caller.

On the first frame, srdx_encode_init() zeroes the shadow. On the second frame, if nothing changed, n_rects=0 and the packet is empty — no bandwidth spent. Verified on QEMU: nrects1=1 (one dirty tile), nrects2=0 (shadow caught up).


SRDX store — ephemeral frame buffer

srdx_store.sg is an 8-slot session frame buffer at 0x3C0F08, backed by a 256KB frame pool at 0x3E0000 (8 × 32KB per session). Each slot holds one pending encoded frame — if the sender is slow and the encoder produces two frames before the first is consumed, the newer frame replaces the older one. This is the right trade-off for a remote desktop: a slightly stale frame is better than an unbounded queue.

The security model is simple: the slot index is the only handle EL0 ever holds. Cross-session access is structurally impossible — srdx_store_push and srdx_store_pop both check the active flag on the slot before touching it. Frames larger than 32KB are rejected at push time.

The seq counter lets the consumer detect whether a new frame has arrived without copying: if seq > last_seen, pop; otherwise skip. The test string is OPQRASX — open, push, seq-check, pop, ack, re-push, close. MANIFEST is at 301 entries, 0 FAIL.


RFB 3.8 handshake

RFB (Remote Framebuffer Protocol) is the protocol VNC is built on. Without completing the handshake, a real VNC client rejects the connection immediately. rfb_handshake() in rfb_encode.sg implements all three phases of RFC 6143:

  1. ProtocolVersion — server sends "RFB 003.008\n" (12 bytes), client echoes
  2. Security — server sends [1 type][type=1 None] + SecurityResult=0x00000000 (no auth for now)
  3. ServerInit — width/height (u16 big-endian), 16-byte pixel format (32bpp, 24-bit depth, true-colour, r-shift=16 g-shift=8 b-shift=0), name-length + name bytes

All multi-byte fields are big-endian via rfb_pu16 and rfb_ps32. The loopback stubs (rfb_send/rfb_recv) are no-ops for testing — the real Cap<NetConn> token binds at integration when the net stack's net_listen/net_accept syscalls (109/110, reserved today) are ratified and wired. The test covers 18 assertions across every byte of the handshake.


VT100 framebuffer renderer

core/vt100_fb.sg renders VT100 terminal output directly into the GPU framebuffer. The unit is an 8×16 text cell: vt100_fb_render_cell(fb, pitch, cx, cy, char, fg, bg) places one character; vt100_fb_render_row renders a full row from a 24-byte-stride cell buffer (char/fg/bg triples).

Three rendering tiers, in order of quality:

  1. GPU SDF — pending make_font / sdf.sg atlas (the same high-quality path the terminal uses)
  2. Bitmap glyph — 8×16 font8x16.bin layout, activated via vt100_set_font(addr); glyph bits extracted via integer division (cc0 has no bitwise ops)
  3. Background fill — paints the cell in the background color before the font loads

The MSB-first glyph decode is verified with the bit pattern 170 (0b10101010): columns 0, 2 → foreground; columns 1, 3 → background — alternating correctly.


What's next

The Kernel transport lane for SRDX 0.6.0 is now complete. Syscalls 109–112 (net_conn_listen, net_conn_send, net_conn_recv, net_conn_close) are wired, CAP_NET-gated, and QEMU-verified end-to-end: listen → SYN → ACK → ESTAB → send → recv → close all pass. The SRDX store and RFB encoder can now bind a real Cap<NetConn>. The Session Manager shell is already built — the OS session manager and Apps viewer can now stream over the live seam.