Surfaces ↓
Webapp SDK
A bridgething webapp is a single-page UI that runs full-screen in the Car Thing's chromium kiosk and reaches the on-device daemon through one typed client: @bridgething/client. This reference is generated from the daemon's wire types.
Start a project
Scaffold a webapp (React + Vite + Tailwind, this client preinstalled), then open it with your coding agent:
bun create bridgething my-app Source and issues on GitHub.
Connect
Construct the client once and reuse it (a useMemo in React). The daemon is the sole peer; you never
open sockets or address anything.
import { BridgethingClient } from '@bridgething/client';
const client = new BridgethingClient(); // auto-connects, auto-reconnects The three call shapes
Every surface method is one of three shapes. Learn these and the whole SDK reads the same way.
Events
the daemon pushes; you subscribe. onXxx returns an unsubscribe function.
const off = client.player.onSnapshot(r => render(r.state));
off(); // stop listening Requests
you ask, the daemon answers. Returns a tagged result; check .ok.
const res = await client.player.stateGet();
if (res.ok) render(res.response.state); Commands
fire-and-forget. Resolves once the daemon has taken the message.
await client.player.skipNext(); Request results
A request never returns its payload directly and never throws. It resolves to a tagged result you narrow on
ok. This is the TypedRequestResult<Response, Error> in every request's signature:
type TypedRequestResult<Response, Error> =
| { ok: true; response: Response }
| { ok: false; kind: 'domain'; error: Error }
| { ok: false; kind: 'protocol'; error: WireError }; ok: the discriminant.truemeans success;responseholds the reply.response: the typed reply payload (theResponsein the method's signature). Only present whenok.kind: on failure,'domain'(an expected, operation-specific failure, e.g. an asset that isn't found) or'protocol'(the request could not be reached or dispatched).error: the failure detail. Your operation's error type whenkindis'domain', aWireErrorwhen'protocol'.
Every request also accepts an optional second argument { timeoutMs }.
The 'protocol' failure carries a WireError: the request was malformed, unsupported, or
the handler threw.
type WireError =
| { type: 'unsupported' }
| { type: 'unimplemented' }
| { type: 'malformed'; data: { reason: string } }
| { type: 'handlerFailed'; data: { reason: string } }; Surfaces
19 surfaces on client.<name>
client.asset Daemon-side asset events.
client.audio Daemon -> webapp audio events: TTS lifecycle notifications and volume/mute changes.
client.bluetooth Daemon -> webapp bluetooth surface: connection status/events, in-flight pairing feedback, and the reply to bluetooth.list.
client.capabilities Daemon -> webapp capabilities surface.
client.config Daemon -> webapp config replies and events.
client.forward client.geo Daemon -> webapp location surface: the Position stream a watch produces, plus replies to geo.watch and geo.getOnce.
client.hardware Daemon -> webapp hardware surface: ambient-light and backlight change events, plus the reply to hardware.stateGet.
client.library Daemon -> webapp replies and events for the library surface.
client.net Daemon -> webapp network surface: replies to fetch and ws.open, WebSocket frame/close/error events, and the Stream* event trio driven by net.stream.open.
client.notifications Daemon -> webapp notification mirror.
client.peer Daemon -> webapp peer surface: a full snapshot of every known peer (paired, iAP2, and companion-gateway state), re-sent whenever any peer changes.
client.phone Daemon -> webapp telephony surface.
client.player Daemon -> webapp player surface.
client.store client.system Daemon -> webapp system events and replies.
client.time Daemon -> webapp wall-clock surface: an initial snapshot at announce, Changed events on tz/locale/clock updates, and the reply to time.get.
client.voice Daemon -> webapp voice/NLU surface: mic state-change events and the reply to voice.stateGet.
client.webapp Daemon -> webapp replies and events for the webapp-management surface.