Local Studio ships a complete, signed, versioned mobile bridge that is running on every desktop install and is currently consumed by nothing. The mobile client that was supposed to consume it (0xSero/litter, the KittyLitter phone app) instead SSHes in and guesses filesystem paths.
This issue asks for the small piece that would let the contract actually be depended on. It is not a request to change the protocol.
What exists today
shared/agent/litter-bridge.ts — 700 lines, Effect Schema, LITTER_BRIDGE_PROTOCOL_VERSION = 1, onExcessProperty: "error" throughout.
services/agent-runtime/src/litter-bridge-gateway.ts — 3,292 lines. Ed25519 per-request signatures over a domain-separated length-prefixed preimage, canonical-JSON body hashing, 60s request lifetime, 30s skew, replay store, opaque device-bound cursors.
services/agent-runtime/src/litter-bridge-mutation-ledger.ts — 571 lines, SQLite idempotency ledger with lease/dispatch/settle.
test/litter-bridge-gateway.test.ts — 26 tests, including cross-language golden vectors for canonical JSON and the signature preimage.
Verified live on a stock install just now:
$ curl -s -XPOST -H 'content-type: application/json' \
-H "x-local-studio-litter-bridge-secret: $SECRET" \
http://127.0.0.1:8081/api/litter-bridge/v1 -d '{}'
{"type":"error","protocolVersion":1,"requestId":"fde0628f-…",
"error":{"code":"unsupported_version","message":"Gateway request is invalid","retriable":false,…}}
What the consumer actually does
litter's shared Rust layer reads litter-bridge.json — and uses exactly one field from it. From shared/rust-bridge/codex-mobile-client/src/local_studio.rs:
metadata="${agent_dir%/pi-agent}/litter-bridge.json"
pid=$(sed -n 's/.*"pid"[[:space:]]*:[[:space:]]*\([0-9][0-9]*\).*/\1/p' "$metadata" 2>/dev/null | head -n 1)
if [ -n "$pid" ] && [ -x "/proc/$pid/exe" ]; then
program=$(readlink -f "/proc/$pid/exe" 2>/dev/null || true)
url, secret, secretHeader, controllerId, and protocolVersion are all ignored. And because the branch is /proc-gated it never executes on macOS, where the fallback is a hardcoded guess at the Electron bundle layout:
"$app/Contents/Resources/app/frontend/.next/standalone/frontend/node_modules/@earendil-works/pi-coding-agent/dist/cli.js"
That path encodes Next.js standalone output and npm hoisting and the @earendil-works/pi-coding-agent package layout. Any packaging change silently breaks phone access to Local Studio, with no version negotiation and no error that points here.
Requests
1. A documented, machine-readable runtime descriptor.
litter-bridge.json is already the natural discovery point and is already written 0600 with a protocolVersion. Please extend it with the runtime facts the consumer is currently guessing, so path discovery becomes a read instead of a search:
This is information Local Studio already knows for certain at launch and every consumer currently reconstructs by guesswork. alleycat has an independent copy of the same guessing in crates/alleycat/src/local_studio.rs::bundled_pi_runtime().
2. docs/litter-bridge.md. There is currently no prose anywhere: no README section, no ADR, and alleycat appears zero times in this repository. The entire user-facing documentation of the feature is two sentences of UI copy in profile-settings.tsx. Please document the discovery file, the two auth layers, the five served request types, and — importantly — which contract types are defined but not served, because that is not discoverable without reading the router. Today that set is controller_action_request, session_transfer, and the capabilities manifest; the gateway 400s all three.
3. Clarify the intended status of session_transfer.
LitterBridgeSessionAuthoritySchema = ["local-studio", "litter"] plus SessionTransferEnvelope / TransferCursor / TransferAck / ContentHashes describe bidirectional session handoff — start a session in Local Studio, resume it on the phone, and back. This is the single most valuable thing in the contract and it is schema-only. If it is planned, downstream should build toward it; if it is abandoned, it should be deleted so nobody plans around it.
4. Consider publishing the conformance vectors.
A non-TS client must byte-exactly reimplement canonicalLitterBridgeJson and litterBridgeSignaturePreimage to compute bodyHash. The vectors in test/litter-bridge-gateway.test.ts:483-511 are exactly the right fixtures; emitting them as a JSON file under shared/agent/ would let Rust and Swift clients test against them directly. alleycat's crates/local-studio-proto already carries a hand-ported golden vector that will drift silently.
Context
Related work: 0xSero/litter#186. alleycat's Rust half of this protocol (crates/local-studio-proto, ~2,450 lines + 40 passing tests) is currently dead code, so both ends of a working, tested, signed protocol are sitting unused on either side of a gap that is presently bridged by a shell script.
Local Studio ships a complete, signed, versioned mobile bridge that is running on every desktop install and is currently consumed by nothing. The mobile client that was supposed to consume it (
0xSero/litter, the KittyLitter phone app) instead SSHes in and guesses filesystem paths.This issue asks for the small piece that would let the contract actually be depended on. It is not a request to change the protocol.
What exists today
shared/agent/litter-bridge.ts— 700 lines, Effect Schema,LITTER_BRIDGE_PROTOCOL_VERSION = 1,onExcessProperty: "error"throughout.services/agent-runtime/src/litter-bridge-gateway.ts— 3,292 lines. Ed25519 per-request signatures over a domain-separated length-prefixed preimage, canonical-JSON body hashing, 60s request lifetime, 30s skew, replay store, opaque device-bound cursors.services/agent-runtime/src/litter-bridge-mutation-ledger.ts— 571 lines, SQLite idempotency ledger with lease/dispatch/settle.test/litter-bridge-gateway.test.ts— 26 tests, including cross-language golden vectors for canonical JSON and the signature preimage.Verified live on a stock install just now:
What the consumer actually does
litter's shared Rust layer readslitter-bridge.json— and uses exactly one field from it. Fromshared/rust-bridge/codex-mobile-client/src/local_studio.rs:url,secret,secretHeader,controllerId, andprotocolVersionare all ignored. And because the branch is/proc-gated it never executes on macOS, where the fallback is a hardcoded guess at the Electron bundle layout:That path encodes Next.js standalone output and npm hoisting and the
@earendil-works/pi-coding-agentpackage layout. Any packaging change silently breaks phone access to Local Studio, with no version negotiation and no error that points here.Requests
1. A documented, machine-readable runtime descriptor.
litter-bridge.jsonis already the natural discovery point and is already written 0600 with aprotocolVersion. Please extend it with the runtime facts the consumer is currently guessing, so path discovery becomes a read instead of a search:{ "protocolVersion": 1, "url": "http://127.0.0.1:8081/api/litter-bridge/v1", "secretHeader": "x-local-studio-litter-bridge-secret", "secret": "…", "controllerId": "…", "pid": 8389, "issuedAt": "2026-07-22T23:59:23.166Z", // proposed additions "piAgentDir": "/Users/me/.local-studio/pi-agent", "piRuntime": { "program": "/Applications/Local Studio.app/Contents/MacOS/Local Studio", "args": ["/Applications/Local Studio.app/Contents/Resources/.../dist/cli.js"], "env": { "ELECTRON_RUN_AS_NODE": "1" } } }This is information Local Studio already knows for certain at launch and every consumer currently reconstructs by guesswork.
alleycathas an independent copy of the same guessing incrates/alleycat/src/local_studio.rs::bundled_pi_runtime().2.
docs/litter-bridge.md. There is currently no prose anywhere: no README section, no ADR, andalleycatappears zero times in this repository. The entire user-facing documentation of the feature is two sentences of UI copy inprofile-settings.tsx. Please document the discovery file, the two auth layers, the five served request types, and — importantly — which contract types are defined but not served, because that is not discoverable without reading the router. Today that set iscontroller_action_request,session_transfer, and thecapabilitiesmanifest; the gateway 400s all three.3. Clarify the intended status of
session_transfer.LitterBridgeSessionAuthoritySchema = ["local-studio", "litter"]plusSessionTransferEnvelope/TransferCursor/TransferAck/ContentHashesdescribe bidirectional session handoff — start a session in Local Studio, resume it on the phone, and back. This is the single most valuable thing in the contract and it is schema-only. If it is planned, downstream should build toward it; if it is abandoned, it should be deleted so nobody plans around it.4. Consider publishing the conformance vectors.
A non-TS client must byte-exactly reimplement
canonicalLitterBridgeJsonandlitterBridgeSignaturePreimageto computebodyHash. The vectors intest/litter-bridge-gateway.test.ts:483-511are exactly the right fixtures; emitting them as a JSON file undershared/agent/would let Rust and Swift clients test against them directly.alleycat'scrates/local-studio-protoalready carries a hand-ported golden vector that will drift silently.Context
Related work:
0xSero/litter#186.alleycat's Rust half of this protocol (crates/local-studio-proto, ~2,450 lines + 40 passing tests) is currently dead code, so both ends of a working, tested, signed protocol are sitting unused on either side of a gap that is presently bridged by a shell script.