3.0 KiB
Public API v2
All endpoints are public and intentionally sanitized. State responses use Cache-Control: no-store.
Endpoints
GET /healthzreports process liveness and build identity.GET /readyzsucceeds only when checkpoint state is healthy, MQTT is connected and subscribed, the ingest queue is healthy, and no packets have been dropped. Normal RF silence remains ready.GET /api/statereturns the authoritativeStateV2snapshot.GET /api/events?bootId=<boot>&after=<seq>is a same-origintext/event-streamwith 15-second keepalives. It replays a bounded sequence window before switching to live events;Last-Event-IDis honored on native reconnects. An expired cursor, changed boot, or retention-pruned topology receivesresetand must rehydrate from/api/state.
State schema
type StateV2 = {
schemaVersion: 2;
bootId: string;
seq: number;
serverTime: number;
status: {
feed: "connected" | "disconnected";
activity: "active" | "quiet";
lastPacketAt?: number;
dropped: number;
version: string;
gitSha: string;
};
map: { center: [0, 20]; zoom: 1.4 };
nodes: NodeV2[];
routes: RouteV2[];
};
type NodeV2 = {
id: string;
label: string;
role: "repeater" | "companion" | "room_server" | "sensor" | "unknown";
observer: boolean;
lat: number;
lng: number;
lastSeen: number;
};
type RouteV2 = {
id: string;
fromId: string;
toId: string;
packetCount: number;
lastHeard: number;
intensity: 0 | 1 | 2 | 3 | 4;
lastKind: "Advert" | "Trace" | "Text" | "ACK" | "Control" | "Other";
traffic: number;
};
Every fromId and toId references one node in the same snapshot. Endpoint labels and coordinates are not duplicated on routes. lastKind is the single sanitized kind from the newest packet observed on the route. traffic is a bounded activity score measured at lastHeard; clients decay it with a 15-minute half-life. It is not packet history or a per-kind counter. Routes older than 24 hours are omitted.
Event stream
Event names are hello, node, packet, status, and reset. State-changing events carry the increasing sequence as their SSE id; hello deliberately has no SSE ID so a disconnect cannot skip its following replay.
Route packet events use the same normalized identifiers:
type RoutePacketEventV2 = {
seq: number;
id: string;
at: number;
payloadType: "Advert" | "Trace" | "Text" | "ACK" | "Control" | "Other";
mode: "route";
segments: Array<{ routeId: string; fromId: string; toId: string }>;
};
Observer packet events contain one sanitized { id, label, lat, lng } point instead of segments. No event contains message content, public keys, raw paths, packet hashes, credentials, or resolver details.
Compatibility
Schema v2 intentionally replaces the embedded route.from and route.to objects from v1. Clients must reject unknown schemaVersion values. Additive fields may appear within v2; removing or changing an existing field requires another schema version.