4.2 KiB
TDS API
Base URL on the LAN:
http://127.0.0.1:8080
Health
GET /health
GET /ready
Observation Ingestion
POST /v1/observations
x-tds-api-key: change-me
content-type: application/json
Example:
{
"timestamp": "2026-07-21T20:10:00Z",
"source": "synthetic",
"sensor_id": "front-door",
"zone_id": "entrance",
"protocol": "ble",
"identity_hint": "demo-ble-tag-001",
"rssi": -48,
"known": false,
"metadata": { "generator": "tds.synthetic.v1" },
"raw_payload": { "addr": "demo-ble-tag-001" }
}
Dashboard Reads
GET /v1/dashboard/summary
GET /v1/observations?source=synthetic&limit=50
GET /v1/events?zone_id=entrance&type=unknown_device_nearby&limit=50
GET /v1/sensors
GET /v1/zones
GET /v1/collectors
GET /v1/integrations
GET /v1/rules
GET /v1/state?source=unifi&kind=client&limit=50
Responses use:
{
"items": [],
"count": 0
}
where practical. Dashboard summary returns counts and recent events.
Current State
Collector payloads can include metadata.state_key and
metadata.state_fingerprint. TDS uses those fields to maintain a current-state
row and skip duplicate observation inserts when a scheduled poll returns an
unchanged snapshot.
GET /v1/state
GET /v1/state?source=openwrt
GET /v1/state?source=unifi&kind=device
GET /v1/state?source=kismet
GET /v1/state?source=intercept
Current state is the preferred dashboard source for latest device/router/AP status. Observation history remains append-only and is reserved for first-seen or changed snapshots.
See docs/capture-map.md for the canonical source ownership matrix. Dashboard
views should prefer source-scoped current-state reads such as:
GET /v1/state?source=openwrt
GET /v1/state?source=unifi
GET /v1/state?source=kismet
GET /v1/state?source=intercept
MQTT
Generated events publish to:
rf/events
Event payloads match the JSON returned by GET /v1/events.
OpenWrt Collector
OpenWrt is ingested through the existing observation endpoint. The collector uses authenticated ubus JSON-RPC for safe surfaces only:
system boardluci-rpc getDHCPLeasesluci-rpc getHostHintsnetwork.interface dumpiwinfo devicesiwinfo assoclist
It intentionally avoids broad wireless config dumps because those can expose
wireless credentials. See docs/openwrt.md for the read-only ACL setup.
cd /opt/tds/TDS
.venv/bin/python tools/openwrt_collect.py
Use --dry-run to inspect normalized observations without submitting them.
UniFi
UniFi Alarm Manager webhooks are accepted at:
GET /v1/webhooks/unifi/alarm
POST /v1/webhooks/unifi/alarm
x-tds-webhook-token: replace-with-long-random-token
GET query parameters or POST JSON payloads create unifi_alarm events. The
UniFi poller submits client and device state through POST /v1/observations
with source unifi and current-state de-dup metadata.
UniFi webhook events include normalized fields under payload.normalized while
preserving the original UniFi body under payload.raw.
cd /opt/tds/TDS
.venv/bin/python tools/unifi_collect.py --dry-run
See docs/unifi.md for UDM SE API key and Alarm Manager setup.
Kismet Passive RF
Kismet passive RF observations are submitted through POST /v1/observations
with source kismet.
Initial normalized kinds:
devicefor Wi-Fi and Bluetooth devicesaircraftfor ADS-B devicesdatasourcefor Kismet capture source healthserver_statusfor Kismet server health
cd /opt/tds/TDS
.venv/bin/python tools/kismet_collect.py --dry-run
Intercept RF Platform
Intercept RF platform observations are submitted through POST /v1/observations
with source intercept.
Initial normalized kinds:
server_statusfor Intercept process/database/counter healthsdr_devicefor RTL-SDR, HackRF, and other detected SDR devices
cd /opt/tds/TDS
.venv/bin/python tools/intercept_collect.py --dry-run
Next Adapter Phase
After backend task 10, add real source adapters in this order unless priorities change:
- OpenWrt infrastructure state
- UniFi infrastructure and Protect context
- Kismet passive RF observations
- Intercept RF observations
- AntiHunter RF observations