UTCP over SBI
UTCP over SBI
Section titled “UTCP over SBI”How Janus does Universal Tool Calling Protocol (UTCP) on the wire – and how it still talks to the rest of the world.
SPEC-244 makes SBI the normative transport for native Janus UTCP. JSON-line and HTTP are interop modes for external or older clients that do not speak SBI. They stay on purpose.
Related: std.utcp API, SBI preamble, tutorial UTCP peer and JPEN.
Three modes (one doctrine)
Section titled “Three modes (one doctrine)”| Mode | Who uses it | How |
|---|---|---|
| SBI (normative) | Janus agents, Libertria peers, new tooling | Length-prefixed binary frames: u32 LE size + SBI\0 preamble + fixed LE header |
| HTTP (interop) | Browsers, curl, foreign stacks | Same port as SBI by default (dual-stack demux), or janusd --http |
| JSON-line (interop) | Legacy scripts / out-of-date UTCP clients | janusd --json – line-delimited JSON ops over TCP |
Rule of thumb: write new Janus code against std.utcp and SBI only. Use
HTTP or JSON when you must integrate something that is not Janus-native.
SBI frame picture
Section titled “SBI frame picture”TCP stream: [0..4) frame_len u32 LE [4..) SBI frame
SBI frame: [0..4) "SBI\0" [4] version [5] arch flags [6..8) cap flags [8..24) schema fingerprint (16 bytes) [24..) kind / tool_id / status / n_caps / …Wire kinds
Section titled “Wire kinds”| Kind | Value | Role |
|---|---|---|
| INVOKE_REQ | 1 | Call a tool |
| INVOKE_RES | 2 | Status (and optional result body) |
| PRESENT | 3 | Add capabilities to the connection session |
| RESET | 4 | Clear session caps and staged payload |
| STAGE | 5 | Upload binary payload for the next invoke |
Schema fingerprint
Section titled “Schema fingerprint”The 16-byte fingerprint is BLAKE3-128 in domain-separated KDF mode
(Zig std.crypto.hash.Blake3, same engine as std.crypto.blake3):
- Domain string:
janus.utcp.wire.schema.v1 - Input: a fixed schema descriptor (kinds, result bodies, stage/CHNK rules)
- Output: first 16 bytes of the digest
Peers that disagree on the fingerprint reject each other’s frames. That is intentional.
Result bodies (SBI)
Section titled “Result bodies (SBI)”Successful lease and discovery invokes may attach a binary body after the
INVOKE_RES header (n_caps == 0):
| Magic | Meaning |
|---|---|
| LRES | Lease register / heartbeat / state – entry id, TTL, live entry count |
| MANL | Binary manual – tool list with known indices and required caps |
Invoke utcp.manual (zero required caps) to fetch a MANL document without
speaking JSON.
STAGE and large payloads
Section titled “STAGE and large payloads”- One STAGE frame carries at most 1024 payload bytes.
- Raw STAGE replaces the connection’s staged buffer.
- CHNK (optional inner framing) writes at an offset into a reassembly buffer up to 16 KiB. Use multi-chunk STAGE for larger tools payloads (e.g. split LEAS or future envelopes).
Session protocol (one TCP connection)
Section titled “Session protocol (one TCP connection)”- PRESENT – accumulate known capabilities
- STAGE (optional) – JPEN, LEAS, or other binary payload
- INVOKE – capability check, then tool dispatch
- RESET – clear caps and stage
Multiple INVOKEs may run on the same connection until RESET or EOF.
janusd dual-stack
Section titled “janusd dual-stack”Default janusd listen is SBI + HTTP on the same host:port:
- Read the first four bytes.
- If they look like an HTTP method (
GET,POST, …), handle HTTP. - Otherwise treat them as the SBI length prefix and run the UTCP peer.
Examples:
# Normative + interop on one portjanusd 127.0.0.1 7735
# Curl JSON manual (interop)curl -sS http://127.0.0.1:7735/utcp
# SBI client: std.utcp.peer from Janus (same port)| Flag | Meaning |
|---|---|
(none) / --sbi | Dual-stack SBI + HTTP |
--http | HTTP only |
--json | JSON-line only (interop) |
What not to do
Section titled “What not to do”- Do not invent a new text protocol for Janus↔Janus tool RPC.
- Do not treat feature-file JSON-line BDD as the normative Libertaria wire.
- Do not remove HTTP or
--jsonwithout an explicit product decision – they exist so the out-of-date world can still talk to janusd.
Prove it
Section titled “Prove it”From the compiler repo (janus/), always use ./scripts/zb:
./scripts/zb test-utcp-sbi-wire./scripts/zb test-utcp-peer-session./scripts/zb test-utcp-manual-sbi./scripts/zb test-utcp-stage-chunk./scripts/zb test-janusd-sbi-dogfood # curl /utcp + SBI clientImplementation tip for this surface: origin/unstable @ 402c62e1 (2026-07-21).