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
| Layer | Component | What it does |
|---|---|---|
| Encode | srdx_encode.sg | Compares current fb vs shadow in 64×64 tiles → SRDX delta packet |
| Store | srdx_store.sg | 8-slot ephemeral frame buffer; session → frame; newest-wins |
| Protocol | rfb_encode.sg | RFB 3.8 handshake + FramebufferUpdate packet builder |
| Text | vt100_fb.sg | VT100 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:
- ProtocolVersion — server sends
"RFB 003.008\n"(12 bytes), client echoes - Security — server sends
[1 type][type=1 None]+SecurityResult=0x00000000(no auth for now) - 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:
- GPU SDF — pending
make_font/sdf.sgatlas (the same high-quality path the terminal uses) - Bitmap glyph — 8×16
font8x16.binlayout, activated viavt100_set_font(addr); glyph bits extracted via integer division (cc0 has no bitwise ops) - 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.