Routing Reference
GSV routing is kernel-level message and syscall routing. It is not only chat/session routing. The Kernel Durable Object is the central router for WebSocket clients, agent processes, package apps, adapter workers, and connected devices.
Routing Surfaces
| Surface | Entry Point | Routed By | Destination |
|---|---|---|---|
| CLI or browser client | WebSocket request frame | syscall name, caller capabilities, optional target | Kernel handler, Process DO, or device driver |
| Agent process | Kernel.recvFrame(pid, frame) | process identity and syscall | Kernel handler or device driver |
| Package app | Kernel.appRequest(...) | app frame, package manifest, entrypoint grants | Kernel handler or device driver |
| Adapter worker | adapter.inbound syscall | linked actor identity and surface route | User init process or routed process |
| Device driver | WebSocket response frame | persisted route id | Original client, process, or app |
All requests use the same frame shape:
{
"type": "req",
"id": "call-id",
"call": "fs.read",
"args": { "path": "/home/alice/context.d/00-role.md" }
}Syscall Routing
The dispatcher first checks args.target. If target is omitted or set to gsv, the syscall is handled natively by the Kernel. If target names a connected device and the syscall is routable, the Kernel forwards it to that device.
Only fs.* and shell.exec support device routing. Other domains such as sys.*, proc.*, pkg.*, repo.*, adapter.*, and notification.* are kernel-internal.
{ "path": "/etc/passwd", "target": "gsv" }{ "input": "git status --short", "cwd": "~/projects/gsv", "target": "laptop" }Before forwarding to a device, the Kernel checks:
- The caller can access the device by ownership, group ACL, or root.
- The device is online.
- The device advertises an
implementscapability matching the syscall. - A live driver WebSocket exists for the device id.
Forwarded calls are stored in the Kernel SQLite routing_table with the call id, syscall, origin, target device, and timeout schedule. When the device responds, the Kernel consumes the route and returns the response to the original origin. If the route expires first, the origin receives a 504 timeout response.
Shell continuations use a second durable session mapping. A routed shell start that returns status: "running" records its sessionId and owning device. Later shell.exec requests with that sessionId route to the same device even when target is omitted. This keeps the model-facing Shell tool small while preventing long-running commands from depending on one in-flight route.
codemode.exec is different: the Kernel exposes it as an agent tool, but the Process DO executes it locally with the Worker Loader instead of routing it through the Kernel dispatcher. The manual codemode.run syscall is public and kernel-forwarded to a Process DO, which uses the same executor. CodeMode's in-block shell(...) and fs.*(...) helpers call back into the Process, which then dispatches normal shell.exec and fs.* request frames through the Kernel. That means nested CodeMode calls still use the same device routing, approval policy for agent tool calls, async response, and shell session behavior as direct model tool calls.
Process Routing
Agent conversations are durable processes, identified by PIDs. The long-lived home process for a user is init:{uid}. Other processes are spawned with proc.spawn and usually receive UUID PIDs.
The Kernel stores process metadata in the processes table: uid, gids, profile, parent PID, cwd, source mounts, activity state, active run/conversation ids, queued count, label, and context files. proc.list is answered directly from this registry.
These syscalls are forwarded to the target Process DO after ownership checks:
proc.send
proc.abort
proc.hil
proc.kill
proc.history
proc.reset
codemode.runWhen no PID is supplied, process syscalls default to the caller's init:{uid} process. Non-root callers cannot access another user's process.
Process Signal Routing
Process DOs emit run signals such as proc.run.stream, proc.run.output, proc.run.tool.finished, proc.run.hil.requested, and proc.run.finished. The Kernel routes those signals using run_routes.
For CLI/browser-originated runs, run_routes maps runId to the originating WebSocket connection. For adapter-originated runs, it maps runId to the adapter, account id, surface kind, surface id, and optional thread id. Routes expire after 30 minutes.
If a run route is missing, the Kernel falls back to broadcasting the signal to connected clients for the owning uid.
Process DOs also emit proc.changed for durable state changes such as messages, context estimates, queue size, and conversation archive/fork events. The Kernel uses proc.run.* and proc.changed payloads to keep proc.list activity state current.
Adapter Routing
Messaging adapters call adapter.inbound through a service identity. The Kernel normalizes the adapter id and account id, then resolves the external actor id through identity_links.
Inbound behavior:
- Linked actor: resolve the local uid and deliver to a process.
- Unlinked DM actor: return a link challenge such as
gsv auth link CODE. - Unlinked non-DM actor: drop the message as
unlinked_actor.
The default delivery target is the user's init:{uid} process. A surface_routes entry can override this for a specific adapter account and surface:
adapter + accountId + surface.kind + surface.id -> pidHuman-in-the-loop replies are routed specially. If the target process has a pending HIL request, a DM reply of approval or denial resumes proc.hil instead of starting a new chat turn.
Package App Routing
Package UI and RPC calls are routed through package identity frames. The Kernel verifies:
- The package is installed and enabled.
- The route base and entrypoint match the installed manifest.
- The entrypoint grants the requested syscall.
- The user identity in the app frame is still valid.
Package app syscalls can use the same device routing path as clients and processes. Async device responses are held in memory as pending app responses until the device reply or timeout arrives.
Device Routing
Devices are persistent records in Kernel SQLite. A driver connection registers a device id, owner uid, owner gid, platform, version, and implements list. The access model is Linux-like:
- Root can use every device.
- The owner uid can use the device.
- Members of granted groups can use the device.
Device routing does not rename syscalls. Agents and clients always see the same syscall names, such as fs.read and shell.exec; target selects whether the initial call runs on gsv or a device. For shell continuations, sessionId selects the previously started shell session.
Failure Behavior
| Failure | Result |
|---|---|
| Missing capability | 403 Permission denied |
| Device access denied | 403 Access denied to device |
| Device offline | 503 Device offline |
| No active device connection | 503 No active connection |
| Device does not implement syscall | 400 Device does not implement |
| Device route timeout | 504 Syscall timed out |
| Unknown or foreign process | Process not found or Permission denied |
Related Stores
| Store | Purpose |
|---|---|
routing_table | In-flight device-routed syscalls. |
shell_sessions | Device ownership and lifecycle for resumable shell sessions. |
run_routes | Routes process run signals back to connections or adapter surfaces. |
processes | Kernel process registry and process ownership. |
devices, device_access | Device catalog and group ACLs. |
identity_links | External adapter actor to local uid mapping. |
surface_routes | Adapter surface to process mapping. |