bridgething
Surfaces ↓

System

client.system

Daemon -> webapp system events and replies. Version replies to VersionRequest with the daemon's BridgeThingMeta. DiagnosticsReply, LogsTailReply, LogsSubscribeReply, and DeviceNickname are replies to their matching requests. LogEntry streams matching lines to a live LogsSubscribe subscription. OtaProgress / OtaError report OTA orchestrator state. DeviceNicknameChanged broadcasts whenever the nickname mutates, including from another surface.

Requests

you ask, the daemon answers; await the tagged result and check .ok

versionRequest(): Promise<TypedRequestResult<BridgeThingMeta, never>>

push form: onVersion (subscribe instead of awaiting)

Marker request: webapp asks the bridge for its BridgeThingMeta. Pairs with BridgeToClientSystemMsg::Version.

const res = await client.system.versionRequest();
if (res.ok) {
  console.log(res.response.bridgethingVersion);
}

diagnosticsGet(): Promise<TypedRequestResult<DiagnosticsReply, never>>

push form: onDiagnosticsReply (subscribe instead of awaiting)

Marker request: webapp asks for a one-shot Diagnostics snapshot (disk/memory usage, uptime, SoC temp, load average, versions). Pairs with BridgeToClientSystemMsg::DiagnosticsReply.

const res = await client.system.diagnosticsGet();
if (res.ok) {
  console.log(res.response.diagnostics);
}

logsTail(LogsTail): Promise<TypedRequestResult<LogsTailReply, never>>

push form: onLogsTailReply (subscribe instead of awaiting)

Pull a one-shot batch of recent daemon log entries over the gateway. Mirrors the client LogsTail; both feed the same LogTap ring.

const res = await client.system.logsTail({ source: 'daemon', levels: 'trace', maxLines: 0 });
if (res.ok) {
  console.log(res.response.entries);
}

logsSubscribe(LogsSubscribe): Promise<TypedRequestResult<LogsSubscribeReply, never>>

push form: onLogsSubscribeReply (subscribe instead of awaiting)

Open a streaming daemon-log subscription over the gateway. The daemon returns an opaque token; the companion releases it via LogsUnsubscribe. Scoped to the gateway peer - auto-released when the peer disconnects.

const res = await client.system.logsSubscribe({ source: 'daemon', levels: 'trace' });
if (res.ok) {
  console.log(res.response.token);
}

deviceGetNickname(): Promise<TypedRequestResult<DeviceNicknameReply, never>>

push form: onDeviceNickname (subscribe instead of awaiting)

Gateway-side read of the device nickname. Returns the current value (or None when unset). Daemon also broadcasts DeviceNicknameChanged to gateway peers on mutation so the companion stays in sync without polling.

const res = await client.system.deviceGetNickname();
if (res.ok) {
  console.log(res.response.nickname);
}

Commands

fire-and-forget; the promise resolves once the daemon has taken the message

logsUnsubscribe(LogsUnsubscribe): Promise<void>

await client.system.logsUnsubscribe({ token: '...' });

reboot(): Promise<void>

await client.system.reboot();

powerOff(): Promise<void>

await client.system.powerOff();

factoryReset(): Promise<void>

await client.system.factoryReset();

Events

the daemon pushes these unprompted; subscribing returns an unsubscribe function

onLogEntry(handler: (LogEntry) => void): () => void

const off = client.system.onLogEntry((entry) => {
  console.log(entry.tsUnixS);
});
// call off() to unsubscribe

onOtaProgress(handler: (OtaProgress) => void): () => void

const off = client.system.onOtaProgress((otaProgress) => {
  console.log(otaProgress.phase);
});
// call off() to unsubscribe

onOtaError(handler: (OtaError) => void): () => void

const off = client.system.onOtaError((otaError) => {
  console.log(otaError.code);
});
// call off() to unsubscribe

onDeviceNicknameChanged(handler: (DeviceNicknameReply) => void): () => void

event broadcast when the nickname changes, including changes made from another surface

const off = client.system.onDeviceNicknameChanged((reply) => {
  console.log(reply.nickname);
});
// call off() to unsubscribe

Types

shapes referenced above, as the SDK types them

One log record. ts_unix_s is unix-epoch seconds. target is the tracing target / unit name; message is the rendered single-line body. Pre-filtered at subscription time so wire-bloating trace events don't reach webapps.

type LogEntry = {
  tsUnixS: number;
  level: LogLevel;
  target: string;
  message: string;
};

Per-phase progress tick. percent is 0-100 within the current phase, not the overall flow. eta_ms is best-effort remaining time for the phase when the orchestrator can compute it.

type OtaProgress = {
  phase: OtaPhase;
  percent: number;
  etaMs?: number;
};
type OtaError = {
  code: OtaErrorCode;
  msg: string;
};

Current device nickname. Reply to DeviceGetNickname and DeviceSetNickname, plus the event payload for DeviceNicknameChanged broadcasts.

type DeviceNicknameReply = {
  nickname?: string;
};

Bridge-side identity announce. Daemon sends one of these to every gateway on connect so the companion knows what daemon it's talking to and can opt out of unsupported surfaces.

type BridgeThingMeta = {
  bridgethingVersion: string;
  libbridgethingVersion: string;
  appName: string;
  nickname?: string; // User-set display name for this device. None when the user hasn't set one yet; consumers fall back to `model_name` / `serial_number`. Set via the gateway-side `system.device.setNickname` surface.
  appVersion: string; // Daemon semver (no leading `v`), e.g. `0.8.4`. Compared directly to the manifest's daemon-component version for OTA hot-swap decisions.
  osName: string;
  osVersion: string;
  osDescription: string;
  btMac: string;
  serialNumber: string;
  fccId: string;
  icId: string;
  modelName: string;
  channel: string; // OTA channel the running image was cut on, e.g. `stable` or `dev`. The companion's poll loop only auto-pushes when its configured channel matches; a mismatch surfaces a "channel switch needs full flash" event rather than swapping channels in-band.
  imageVariant: string; // Image variant the running image was cut as, e.g. `prod` or `dev`. Maps to the yocto image recipe name `bridgething-<variant>-image`, which is what the companion uses to construct the OTA artifact URL `images/<channel>/<image_version>/bridgething-<variant>-image.{swu,zck}`.
  imageVersion: string; // Canonical image version (CalVer, e.g. `2026.05.0`). What the companion compares to the manifest's image-component version.
  imageBuildId: string;
  imageBuildDate: string;
  imageDistro: string;
  imageMachine: string;
  discord: string;
  credits: string;
};

Reply to DiagnosticsGet.

type DiagnosticsReply = {
  diagnostics: Diagnostics;
};

Pull a one-shot batch of recent daemon log entries over the gateway. Mirrors the client LogsTail; both feed the same LogTap ring.

type LogsTail = {
  source: LogSource;
  levels: LogLevel[];
  filter?: string;
  maxLines: number;
};

One-shot reply to a gateway LogsTail.

type LogsTailReply = {
  entries: LogEntry[];
};

Open a streaming daemon-log subscription over the gateway. The daemon returns an opaque token; the companion releases it via LogsUnsubscribe. Scoped to the gateway peer - auto-released when the peer disconnects.

type LogsSubscribe = {
  source: LogSource;
  levels: LogLevel[];
  filter?: string;
};

Reply to a gateway LogsSubscribe: the opaque token to pass back to LogsUnsubscribe.

type LogsSubscribeReply = {
  token: string;
};
type LogsUnsubscribe = {
  token: string;
};
type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error';

Stage of the OTA orchestrator. The phase set is shared between kinds, with non-image kinds emitting a subset. Image: Streaming -> Verifying -> Writing (libswupdate to slot) -> Confirming (try-counter reset) -> Reboot. Daemon and BuiltinWebapp: Streaming -> Verifying -> Writing, where Writing/100 means the piece is validated and staged on the bandaid (not yet live). The atomic rotate and the single systemctl restart happen later, on OtaActivate, which emits the terminal Reboot for the whole batch. Confirming is image-only. InstalledWebapp: Streaming -> Verifying -> Writing/0 while the bundle installs into the writable registry. There is no Writing/100, no Confirming, and no Reboot; the terminal signal is the WebappInstalled event (or an OtaError).

type OtaPhase =
  | 'streaming'
  | 'verifying'
  | 'writing'
  | 'confirming'
  | 'reboot';

Terminal error from the OTA orchestrator. After an OtaError the orchestrator is back to idle and a fresh OtaBegin may be sent.

type OtaErrorCode =
  | 'unknownUpdate' // Companion sent fragments for an `update_id` that was never begun (or was abandoned mid-stream).
  | 'offsetMismatch' // A fragment's offset did not match the daemon's `received`.
  | 'hashMismatch' // Streamed total's sha256 did not match `OtaBegin.transfer.sha256`.
  | 'sizeMismatch' // Streamed total's byte length did not match `OtaBegin.transfer.total_size`.
  | 'cancelled' // `CancelUpdate` arrived during a cancelable phase.
  | 'writeFailed' // libswupdate rejected the .swu (parse / handler / I/O failure).
  | 'confirmFailed' // Slot-flip / try-counter reset failed after a successful write.
  | 'internal' // Anything else (transfer-cache I/O, internal channel close, etc.).;

Daemon health snapshot. load_avg is the unix 1/5/15-minute load. soc_temp_c may be None on builds where the SoC thermal probe is not exposed by the kernel.

type Diagnostics = {
  diskUsedBytes: number;
  diskFreeBytes: number;
  memUsedBytes: number;
  memAvailBytes: number;
  uptimeS: number;
  socTempC?: number;
  loadAvg: [f32 ; 3];
  daemonVersion: string;
  kernelVersion: string;
  bootId: string;
};

What stream of log records a subscription pulls from. Daemon is the bridgething tracing subscriber; System is the journald view; All merges both in arrival order.

type LogSource = 'daemon' | 'system' | 'all';