← Blog
0.7.0 · BROWSER · KERNEL

sigilOS Browser 0.7.0: IPC Ring + Cap-Isolated Renderer Spawn + Design RFC

June 22, 2026 · sigil-kernel · sigil-apps · Sigil-Docs
browser security kernel ipc capability 0.7.0

The 0.7.0 browser sprint opens on two fronts. core/browser_ipc.sg (sigil-kernel ceedcb6) wires the multi-process IPC channel that connects the browser, renderer, GPU, and net processes — the fleet ABI every browser component builds to. core/renderer_spawn.sg isolates each tab into a cap-stripped process (only CAP_IPC; all other caps removed). And docs/BROWSER_RFC.md (sigil-apps 5211c20) lays out the complete browser design: tab model, session model, cap inspector, private mode, Smart Folder integration, address bar, extension manager, and build gate sequence.


1. browser_ipc.sg — the multi-process ring (sigil-kernel ceedcb6)

The IPC ring is a 16-slot shared-memory bus connecting 4 process types. Each slot is 64 bytes: a typed message header with inline args, plus a shared payload page for larger data. The ring is non-blocking and dst-routed.

Process topology

8 message types

Message typesrc → dstWhat it carries
IPC_T_NAVIGATEBrowser → RendererURL, tab_id, cap profile
IPC_T_LOAD_COMMITRenderer → BrowserCommitted URL, status
IPC_T_PAINT_LAYERRenderer → GPULayer descriptors, dirty rect
IPC_T_PAINT_DONEGPU → BrowserFrame complete, seq number
IPC_T_INPUT_EVTBrowser → RendererKey/mouse event, tab_id
IPC_T_NET_REQRenderer → NetURL, method, headers
IPC_T_NET_RESPNet → RendererStatus, headers, body offset
IPC_T_CLOSE_TABBrowser → Renderertab_id, teardown signal

Fleet ABI surface

bipc_send(type, src, dst, tab, a0, a1, a2) → slot
bipc_recv(dst) → slot    (non-blocking, dst-matched)
bipc_ack(slot)
BIPC_MAGIC = 0x49504300

PASS: IPC init=1 msg=1 rnd=1 (init, single-message round-trip, multi-destination routing).

Why shared-memory ring (not syscall per message)

A syscall for each IPC message would add ~1–2µs per frame (at 60fps: 60µs/s overhead, plus context switch cost). The ring eliminates that: sender writes to a slot, receiver polls. Zero-copy for small messages (≤48B inline args). Large payloads use the shared payload page — one slot carries the offset + length, the receiver reads directly. No copy, no syscall after init.


2. renderer_spawn.sg — cap isolation per tab (sigil-kernel ceedcb6)

The security promise: a compromised renderer can't escape the tab. renderer_spawn.sg enforces this at the capability layer.

rend_spawn(tab_id) creates a renderer slot (table of 8) with a capability profile that has only CAP_IPC set — all other capabilities are explicitly cleared. The renderer can send and receive IPC messages. It cannot: access the filesystem, open network connections, write to the display directly, or spawn additional processes. All real capabilities are brokered through the Net process (for network) and GPU process (for display).

rend_spawn(tab_id)
Allocates renderer slot. Clears all caps. Grants only CAP_IPC. Dispatches IPC_T_NAVIGATE with the initial URL.
rend_navigate(tab_id, url)
Sends IPC_T_NAVIGATE to the renderer. Does NOT give the renderer a URL pointer — the URL is a message argument, not a memory reference.
rend_kill(tab_id)
Tears down renderer slot. Clears CAP_IPC. Slot available for reuse.
rend_crash(tab_id)
Called on renderer fault. Strips all caps from the faulting slot. Notifies Browser via IPC_T_CLOSE_TAB. Renderer cannot reach any resource after fault.

Why cap-stripping (not just sandboxing)

In a traditional OS, a "sandboxed" process still runs in the same address space class as other processes — a kernel exploit can escape. In sigilOS, the renderer spawned by rend_spawn has only CAP_IPC in its capability register. Even a kernel-level exploit can only reach what CAP_IPC permits — the rest of the capability space is structurally absent. The OS doesn't decide what the renderer "should" do at runtime; it decided at spawn time, and the decision is the cap set.


3. BROWSER_RFC.md — the design contract (sigil-apps 5211c20)

The RFC gates the chrome UI build. Brief summary of each section:

Tab model

Each tab is a separate EL0 process with its own cap profile, history stack, and display-layer connection. No shared state between tabs — structural isolation. Tab strip mirrors the column-browser: leftmost=oldest, + at right opens new.

Session model

A browser tab and an SRDX remote session are the same object at the session layer. BrowserSession { id, origin, mode, cap_profile, private }. Opening srdx://sigil-pi:5900 appears in both the browser tab strip and the Session Manager's REMOTE DISPLAY folder. One unified session layer.

Cap inspector UX

Every tab has a live cap display (address-bar lock icon). HELD caps shown with accent chip. NOT HELD caps shown grayed — explicitly visible, not hidden. Per-cap Revoke button. Cap changes fire session_cap_update to the renderer immediately.

Private mode (structural)

Not a cookie-clear. A separate EL0 context with persistence caps removed ([fs:rw] for cache/history absent). On close: synchronous kernel heap-zero before slot reuse. [net] is still HELD — private mode is local isolation, not network opacity. Address bar shows [private] badge.

Smart Folder integration

Browser history and bookmarks are tagged items in the FS substrate. @web @recent surfaces all recent web pages. @bookmark is just fs_tag. History, files, and SRDX sessions are one unified search space.

Extension manager

Extensions run in the [unprotected zone] — always shown, never hidden. Cap grant sheet before any code runs. [zone trust badge] in extension manager UI. Honest UX, not pretending everything is sandboxed.

Build gate sequence


4. Why multi-process matters for sigilOS

Chrome introduced multi-process in 2008 to contain tab crashes. sigilOS takes that idea to its logical end: the renderer process doesn't just restart on crash — it was never allowed to hold any resource other than IPC. When it crashes, rend_crash() strips its one remaining cap (CAP_IPC) and the Browser gets a clean close notification. There is no "what did this renderer have access to?" question — the answer is always "only IPC, and now not even that."

The cap inspector makes this visible to users in real time. Not "this page has no permissions" (Chrome's weakest claim) but a live table of exactly what capabilities this session holds and what it does not. NOT HELD rows are as important as HELD rows — they are the security guarantee made visible.