Light RID Scanner is a fixed-station Remote ID (ASTM F3411 / OpenDroneID) Wi-Fi monitor built for Raspberry Pi and other Linux-based capture nodes. It passively listens for drone Remote ID broadcasts over Wi-Fi, decodes them in real time, and presents the results through a local web dashboard.
It also includes a portable edition for mobile deployments and a node-center viewer that aggregates multiple stations into a single pane of glass.
station_edition/Full fixed-station build. It contains the source runtime, web UI, security/auth controls, systemd/AP maintenance, and Raspberry Pi deployment defaults.portable_edition/WIP (Work in Progress). The portable runtime code has been removed while the product policy is being revised; the directory currently contains only a WIP README.- Root
run.pyCompatibility wrapper forstation_edition/run.py. - Root
rid-models.jsonPublic GitHub Raw model map. Binaries also embed this file and restore it if runtime download fails.
The station edition is meant to be usable from its own directory. Runtime files such as config.json, rid_storage.db, and rid-models.json are resolved relative to the current working directory unless explicit paths are passed. Older history-cache.json / rid_history_cache.json files are treated as legacy migration sources.
- Architecture
- Features
- Getting Started
- Configuration
- Web UI
- NIC Binding & OOBE
- Authentication
- External API
- Notification Center
- Enterprise WeCom Notifications
- Alarm Zones
- Host Metrics
- Model Map Updates
- Import & Export
- Node Center Viewer
- Important Files
- Git & Privacy Rules
- OpenDroneID References
The repository contains three distinct editions, each serving a different deployment scenario:
| Directory | Purpose |
|---|---|
station_edition/ |
Full fixed-station build. Includes the scanner core, web server with embedded UI, authentication and session management, systemd service helpers, AP hotspot mode, and Raspberry Pi deployment defaults. This is the primary edition for permanent installations. |
portable_edition/ |
Minimal mobile build. Reuses the scanner and web core but disables web login, API tokens, SSO links, passkeys, host monitoring, and Enterprise WeCom notifications on startup. Designed for quick field deployments where authentication overhead is undesirable. |
viewer/ |
Node-center aggregator. A standalone web service that connects to multiple station_edition instances, fetches their live data via the external API, and renders a unified dashboard. |
The root run.py is a thin compatibility wrapper that delegates directly to station_edition/run.py. Each edition is self-contained and can be run from its own directory.
Light_RID_Scanner/
├── station_edition/
│ ├── run.py # Station entry point
│ ├── config.example.json # Safe example config (committable)
│ └── light_rid/
│ ├── app.py # Application bootstrap & orchestration
│ ├── common_core.py # Shared utilities and constants
│ ├── scan_core.py # Wi-Fi capture, ODID decoding, DJI Beacon parsing
│ ├── process_core.py # Aircraft state machine, history, tracks
│ ├── hardware_core.py # NIC management, iw control, channel ops
│ ├── auth_core.py # Password/passkey auth, session management
│ ├── network_binding_core.py # NIC role assignment, AP hotspot setup
│ ├── web_server.py # HTTP/WebSocket server, embedded HTML/CSS/JS
│ ├── cli_app.py # CLI/TUI interface
│ ├── runtime.py # Runtime context and chunk loader
│ └── platform_compat.py # Platform-specific compatibility layer
├── portable_edition/
│ ├── pe.py # Portable entry point
│ └── bootstrap.py # Startup overrides (disable auth/notify/etc.)
├── viewer/
│ ├── server.py # HTTP/API/WebSocket routing
│ ├── storage.py # SQLite config, node records, auth state
│ ├── aggregation.py # Live station API fetching & aggregation
│ ├── station_ui.py # Station template loading & DOM patching
│ ├── settings_ui.py # Viewer settings page
│ ├── nodes_ui.py # Node manager page
│ ├── ui_common.py # Shared CSS extraction
│ └── paths.py # Resource path resolution
├── pytools/
│ ├── build_release.py # CI/local release builder
│ └── build_viewer.py # Viewer binary builder
├── run.py # Root compatibility wrapper
├── rid_models.json # RID model prefix map
├── requirements.txt # Python dependencies
└── .github/workflows/ # CI build pipelines
The station runtime uses a chunk-loading architecture: app.py loads and executes the core modules (common_core.py, scan_core.py, process_core.py, hardware_core.py, auth_core.py, network_binding_core.py, web_server.py, cli_app.py) in order, assembling them into a single namespace at runtime. This design simplifies the build process while keeping source modules independently readable.
- Passive Wi-Fi Remote ID capture (ASTM F3411 / OpenDroneID)
- 2.4 GHz and 5 GHz band support with configurable channel dwell times
- RSSI-based hit detection with configurable delta
- DJI newer-firmware RID Beacon parsing (extracts UAS ID and firmware type)
- Pilot position extraction from RID
Systemmessages - Wi-Fi AP scanning with vendor OUI lookup
- Real-time map with Leaflet, showing aircraft markers, base station, and alarm zones
- Aircraft list panel with live detail cards (SN, model, altitude, speed, heading, RSSI)
- AP list panel with vendor identification
- Dark/light theme toggle
- Responsive grid layout adaptable to different screen sizes
- Map auto-center with configurable idle timeout
- Aircraft history with first-seen / last-seen timestamps
- Per-aircraft GPS track storage
- SQLite-backed history storage (
rid_storage.db) surviving service restarts - Automatic one-time migration from legacy
history-cache.json/rid_history_cache.jsonon first startup after upgrade - Configurable aircraft offline detection timeout (default 15 seconds)
- Visual editing of all runtime configuration sections
- Base station position management (manual entry or browser geolocation)
- Multiple WeCom webhook channel management
- Multiple rectangular alarm zone configuration
- Passkey registration and deletion for passwordless browser login
- Managed SSO login link generation (requires password re-verification)
- Raw config tree browser — view, edit, save, delete files within the config root (requires secondary password unlock)
- One-click runtime security repair — creates the
ridservice user, grants capture/hotspot capabilities iwwireless tools installation helper- systemd service registration and update
- Manual app version check by Git commit comparison
- Online model map updates with configurable URL and manual trigger
- Optional remote config update from a configurable URL
- Host load trend cards (CPU, memory, temperature, system load, AP count) with 12h / 24h / 7d windows
- Configurable metrics retention (default 7 days)
- List all wireless interfaces with status
- Inspect
iw dev,iw info,iw linkoutput - Switch interfaces between monitor and managed mode
- Set channel on a specific interface
- Restart individual NICs
- Restart the main scanner process
- Server-side notification center, synchronized across all active browser sessions
- Aircraft online/offline events
- Alarm zone entry/exit events
- Model map update results (success/failure)
- Manual browser notices from the built-in UI
- Single delete or clear-all operations
- Multiple webhook channel support
- Per-channel enable/disable toggle
- Online notification to all enabled channels
- Test notification to all enabled channels
- Configurable send timeout and re-online cooldown
- Backward compatibility with legacy single-key config
- Password-based browser login with scrypt-hashed credentials
- Passkey (WebAuthn) support bootstrapped from initial password login
- SSO login links with server-side check codes
- Configurable session TTL (default 30 minutes)
- IP whitelist for external API access
- Rate limiting on login, SSO creation, token reveal, and API token failures
- Sensitive fields (webhook keys, API tokens) masked in visual settings
- Linux (Raspberry Pi OS recommended; 64-bit OS for
arm64binaries) - Wireless NIC with monitor mode support
- Root privileges for monitor mode and channel switching
iwandhostapdwhen using NIC binding with AP hotspot mode- Python 3.10+
cd station_edition
python3 -m venv .venv
source .venv/bin/activate
pip install -r ../requirements.txt
python run.py --no-tuiThe portable edition is currently WIP and has no runnable source entrypoint. When available, it will automatically disable authentication, notifications, and host monitoring for quick field deployments.
CI produces single-file compiled binaries for Linux. Only the binary is required for a first start — missing config and history files are created automatically.
install -m 0755 light_rid_station-arm64 /opt/light-rid/light_rid_station-arm64Configure systemd to run it directly:
[Unit]
Description=Light RID Scanner
After=network.target
[Service]
ExecStart=/opt/light-rid/light_rid_station-arm64 --config /opt/light-rid/config.json --no-tui
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.targetThe binary resolves runtime files relative to its working directory. If rid_models.json is missing, it downloads the latest version from GitHub Raw; if the network is unavailable, it falls back to the embedded resource shipped inside the binary.
# Station edition
python pytools/build_release.py --edition station --target arm64
python pytools/build_release.py --edition station --target x86_64
# Portable edition
python pytools/build_release.py --edition portable --target x86_64
python pytools/build_release.py --edition portable --target x32The x32 target requires a 32-bit Python runtime; the GitHub Actions workflow handles this in a dedicated 32-bit Docker job.
CI artifact matrix:
| Edition | Architecture | Runner |
|---|---|---|
| station | x86_64 | ubuntu-24.04 |
| station | arm64 | ubuntu-24.04-arm |
| portable | x86_64 | ubuntu-24.04 |
| portable | arm64 | ubuntu-24.04-arm |
| portable | x32 | 32-bit Docker |
The runtime configuration is a single JSON file (config.json) organized into the following sections:
| Section | Purpose |
|---|---|
basic |
Capture and runtime behavior — interface, channel, hop settings, dwell times, RSSI delta, aircraft timeout, debug mode |
notify |
Enterprise WeCom notification channels — per-webhook enable/disable, send timeout, re-online cooldown |
web |
Map and UI configuration — base station coordinates, default zoom, heading reference, DJI lookup URL, alarm zone definitions |
ap |
Wi-Fi AP scan limits and vendor database settings — max AP list size, OUI file source |
auth |
Browser login and session authentication — user/password hashes, session TTL, passkey storage, SSO link list |
api |
External API token authentication — enable flag, token hash, token list with expiry, IP whitelist |
model_update |
Online RID model map updates — enable flag, JSON source URL |
config_update |
Remote runtime config updates — enable flag, JSON source URL, last-check tracking |
app_update |
Upstream Git commit comparison — enable flag, commit API URL |
metrics |
Host load metrics — enable flag, retention days, temperature sensor source |
network_bindings |
NIC role assignments and AP hotspot profile — per-interface roles, SSID, DHCP range |
A complete, commented example is provided at station_edition/config.example.json. It contains safe defaults for all sections and is designed to be copied and customized:
cp station_edition/config.example.json config.jsonThen edit config.json to match your hardware and deployment requirements.
Runtime files are resolved relative to the current working directory unless explicit paths are passed via command-line arguments:
| File | Purpose | Auto-created? |
|---|---|---|
config.json |
Main runtime configuration | Yes (from defaults) |
rid_storage.db |
SQLite history store and retained raw packet data | Yes |
history-cache.json |
Legacy JSON history source, auto-imported once when present | Legacy migration source |
rid_history_cache.json |
Older legacy JSON history source, auto-imported once when present | Legacy migration source |
rid_models.json |
RID model prefix-to-name mapping | Yes (from GitHub or embedded resource) |
oui.txt |
MAC OUI vendor database | Optional (auto-downloaded) |
light_rid_scanner/host_metrics.jsonl |
Host load samples | Yes (system temp dir) |
The landing page presents a full-screen dashboard with four zones:
- Header — live statistics (aircraft count, AP count, uptime), scan status indicator, theme toggle
- Map panel — Leaflet-based map displaying aircraft markers (with heading arrows), base station icon, and enabled alarm zone rectangles. Aircraft markers update in real time via WebSocket push. Clicking a marker opens a detail card with SN, model, altitude, speed, heading, RSSI, and last-seen time.
- Bottom panel — switchable between aircraft list, AP list, and event log views. The aircraft list shows compact cards sorted by last-seen time. The AP list displays detected Wi-Fi access points with vendor names from the OUI database.
- Footer — navigation links to
/settings,/logs,/hardware-assistant, and theme toggle.
The settings page provides visual editors organized as collapsible cards:
- Basic — interface, channel, hop behavior, dwell times, lost timeout, debug toggle
- Notification — WeCom channel list with add/edit/delete/test per channel
- Web — base station name, coordinates (with browser geolocation fill-in), default zoom, DJI lookup URL
- Alarm Zones — multiple rectangular zones with enable/disable per zone
- AP — max list size, vendor database settings
- Auth — enable/disable web login, session TTL, login methods (password / passkey)
- Passkeys — register new passkeys, delete existing ones
- SSO Links — generate managed login links (requires password re-check), delete to invalidate
- API — external API enable/disable, token management with expiry and single-use options
- Model Update — URL field, enable/disable auto-check, manual update button
- Config Update — URL field, enable/disable, manual pull button
- App Update — commit comparison against upstream
- Metrics — enable/disable host monitoring, retention period, trend charts
- Raw Config — password-rechecked config tree browser with inline edit/save/delete
- Security Repair — one-click service user creation, capability grant,
iwinstall, systemd registration - Import/Export — separate flows for settings files and scan data
A dedicated page for wireless NIC operations:
- Lists all detected wireless interfaces with current mode, channel, and state
- Displays raw
iw dev,iw info, andiw linkoutput for inspection - Buttons to switch each interface between monitor and managed mode
- Channel setter with per-interface target selection
- NIC restart button
- Scanner process restart button
Provides multiple log views in a tabbed interface:
- Runtime log — main scanner log output
- Operation log — audit trail of config changes, auth events, updates
- Scan log — raw Wi-Fi capture log
- Diff view — unified diff between runtime and scan logs
- Export — text export for a single view, or ZIP export for all views
- API docs — rendered view of
GET /api/docs
Sensitive fields (webhook keys, API tokens) are masked in the visual display. Leaving a masked field unchanged preserves the stored value.
basic.iface is treated as a fixed binding. The scanner does not auto-rotate across available NICs. If the specified interface is missing at startup, the service remains alive in a degraded state and surfaces a configuration warning through the web UI, rather than silently switching to another adapter.
This design ensures predictable behavior in multi-NIC deployments — the scanner always uses the intended interface, and startup problems are surfaced as explicit configuration issues.
When config.json is missing, broken, or has no bound NIC, the web UI automatically enters the OOBE flow at /oobe. This guided setup collects the minimum required configuration:
- Select wireless NIC — choose from detected interfaces
- Set RID channel — pick the monitoring channel
- Base station coordinates (optional) — set the map center point
- Web login credentials (optional) — set username and password for browser auth
After OOBE completes, the configuration is written to disk and the normal dashboard loads.
NICs can be assigned the ap_web role through the Custom NIC Binding settings. When configured:
hostapdcreates a Wi-Fi hotspot with the specified SSID and password- A built-in DHCP server assigns addresses in the
172.16.0.0/24range - The web UI is exposed at
172.16.0.1:80 - The hotspot runs on a configurable channel (default: 6)
This allows the scanner to serve its web UI over its own Wi-Fi network, useful for field deployments without existing infrastructure.
Each detected NIC can be assigned one of these roles: scan, web, ap_web, disabled, idle, or none. The scan role is synchronized back to basic.iface.
Browser-based authentication uses a normal /login page with session cookies, not HTTP Basic Auth prompts. Configuration:
{
"auth": {
"enabled": true,
"username_hash": "<scrypt(username)>",
"password_hash": "<scrypt(password)>",
"session_ttl_min": 30,
"realm": "Light RID Scanner",
"login_methods": ["password", "passkey"]
}
}Generate scrypt hashes matching the app's format:
python3 - <<'PY'
import base64, hashlib, secrets
def hash_secret(text):
salt = secrets.token_bytes(16)
digest = hashlib.scrypt(
text.encode(), salt=salt,
n=2**14, r=8, p=1, dklen=32
)
return "scrypt$16384$8$1$%s$%s" % (
base64.urlsafe_b64encode(salt).decode().rstrip("="),
base64.urlsafe_b64encode(digest).decode().rstrip("="),
)
print("user:", hash_secret("your_username"))
print("pass:", hash_secret("your_password"))
PYPasskeys (WebAuthn) provide passwordless browser login after an initial bootstrap:
- Log in with username and password
- In
/settings, register a passkey — the browser creates a credential pair - The public key is stored in
auth.passkeys - On subsequent logins, the
/loginpage offers passkey authentication - Passkeys can be deleted from Settings at any time
For trusted external launchers, SSO-style login links use a server-side check code:
/login?check=<server-check-code>
The Settings page generates these links after a fresh username/password verification. The generated URL remains valid until its check code is deleted from the Settings list. Old-style URLs carrying user or password query parameters are tolerated as inert strings — only check is validated.
- Session TTL is configurable via
auth.session_ttl_min(default: 30 minutes) - When a session expires, page API requests return an auth failure
- Built-in pages automatically redirect to
/login - Login, SSO creation, token reveal, and API token failures are rate-limited in memory and written to the operation log
The external API provides machine-readable access to scanner data for integrations, scripts, and the Node Center Viewer.
All three conditions must be met:
- Web login auth is enabled (
auth.enabled: true) - Username and password hashes are configured
- API token is configured (
api.tokenorapi.token_hash)
This design ensures the external API is only exposed when the operator has explicitly set up authentication.
Two request header styles are supported:
# Style 1: Custom header
curl -H "X-API-Token: YOUR_TOKEN" http://0.0.0.0:4600/api/v1/snapshot
# Style 2: Bearer token
curl -H "Authorization: Bearer YOUR_TOKEN" http://0.0.0.0:4600/api/v1/dronesGenerate a token hash using the same scrypt format:
python3 - <<'PY'
import base64, hashlib, secrets
salt = secrets.token_bytes(16)
digest = hashlib.scrypt(b"YOUR_TOKEN_HERE", salt=salt, n=2**14, r=8, p=1, dklen=32)
print("scrypt$16384$8$1$%s$%s" % (
base64.urlsafe_b64encode(salt).decode().rstrip("="),
base64.urlsafe_b64encode(digest).decode().rstrip("="),
))
PYThe API supports multiple tokens, each with optional expiry and single-use flags. If only api.token_hash is stored (without api.token), the external API still works, but the Settings page cannot reveal or copy the plain token.
Recommended workflow:
- Generate a random token locally
- Keep both
api.tokenandapi.token_hashif you want Settings to reveal/copy it later - Use IP whitelist mode when the caller IP range is predictable
- Store the plain token only in your API client, script, or secrets manager
- Never commit real tokens to Git
| Method | Path | Description |
|---|---|---|
| GET | /api/docs |
API metadata, auth hints, and endpoint index |
| GET | /api/health |
Simple health check |
| Method | Path | Description |
|---|---|---|
| GET | /api/v1/snapshot |
Full runtime snapshot for integrations |
| GET | /api/v1/auth/status |
Auth status summary |
| GET | /api/v1/drones |
Current aircraft list |
| GET | /api/v1/drones/{sn} |
Single aircraft detail |
| GET | /api/v1/tracks/{sn} |
Track points for one aircraft |
| GET | /api/v1/aps |
Current AP list |
| GET | /api/v1/logs?type=event|scan|ap&limit=200 |
Event/scan/AP logs |
| Method | Path | Description |
|---|---|---|
| POST | /api/v1/history/clear |
Clear all history |
| POST | /api/v1/history/delete |
Delete one aircraft from history |
| POST | /api/v1/tracks/clear |
Clear all tracks or one aircraft's tracks |
| POST | /api/v1/config/reload |
Reload config from disk |
When api.whitelist_enabled is true, only requests from addresses in api.whitelist pass token auth:
{
"api": {
"whitelist_enabled": true,
"whitelist": [
"127.0.0.1",
"192.168.1.0/24"
]
}
}These endpoints are for the built-in web UI and require a valid browser session (they are not part of the token-protected /api/v1/* namespace):
Notifications
GET /api/notifications?limit=200POST /api/notifications/POST /api/notifications/delete/POST /api/notifications/clear
Settings
GET /api/settings/view/GET /api/settings/runtimeGET /api/settings/metrics?window=12h|24h|7dGET /api/settings/systemd/status/GET /api/settings/api-docsPOST /api/settings/visual/test/POST /api/settings/visual/savePOST /api/settings/raw/unlock/POST /api/settings/raw/savePOST /api/settings/notify/testPOST /api/settings/passkey/start/POST /api/settings/passkey/finish/POST /api/settings/passkey/deletePOST /api/settings/login-link/create/POST /api/settings/login-link/deletePOST /api/settings/models/save/POST /api/settings/models/upsert/POST /api/settings/models/update/GET /api/settings/models/listPOST /api/settings/app-update/checkPOST /api/settings/systemd/register/POST /api/settings/iw/install/POST /api/settings/security/repair
Config
GET /api/config/GET /api/config/file?path=<within-config-root>POST /api/config/file/delete
Logs
GET /api/logs/view?type=runtime|operation|scan|scan_diff|apGET /api/logs/export?type=all|runtime|operation|scan|scan_diff|ap
Hardware
GET /api/hw/status/POST /api/hw/op
Tools
GET /api/tools/export/all/GET /api/tools/export/track?sn=<SN>GET /api/tools/diagnostic.zipPOST /api/tools/import/all/POST /api/tools/import/trackGET /api/tracks/get?sn=<SN>/POST /api/tracks/clearPOST /api/history/delete/POST /api/history/clear
Passkey Login
POST /api/passkey/login/start/POST /api/passkey/login/finish
When api.enabled is false, /api/docs, /api/health, and all /api/v1/* paths are not open on the LAN — they only work from the built-in web pages through the browser session flow. External scripts must wait until the API is explicitly enabled.
- The current API token is shown as a masked password field
- Revealing or copying it requires a fresh username/password check
- That re-check uses the same web auth credentials, but API routes still require the API token when external API mode is enabled
- Generating a managed SSO login link also requires the same fresh username/password check
- Login, SSO login link, token reveal, and external API token failures are rate-limited in memory and written to the operation log.
The notification center is server-side, backed by in-memory storage and synchronized across all active browser sessions via /api/notifications. Unlike browser-local storage, notifications survive page refreshes and are visible from any connected browser.
Sources:
- Aircraft online / offline events
- Alarm zone entry events
- Model map update results (success / failure)
- Manual notices posted by the built-in UI
Notifications can be deleted individually or cleared in bulk from the UI.
The system supports multiple WeCom (企业微信) webhook channels for aircraft online alerts:
{
"notify": {
"enabled": true,
"send_timeout_sec": 8,
"notify_reonline": true,
"reonline_cooldown_sec": 300,
"wecom_webhooks": [
{
"name": "Primary Channel",
"enabled": true,
"key": "YOUR_WEBHOOK_KEY"
},
{
"name": "Backup Channel",
"enabled": false,
"key": "YOUR_BACKUP_KEY"
}
]
}
}Behavior:
- Online notifications are sent to all enabled channels
- Test notifications (from Settings) are sent to all enabled channels
- Re-online notifications are rate-limited by
reonline_cooldown_sec(default: 300 seconds) - The legacy
notify.wecom_webhook_keysingle-key field is still accepted for backward compatibility - Webhook keys are masked in the Settings UI
Multiple rectangular alarm zones can be defined, drawn on the map as red rectangles:
{
"web": {
"alarm_zones": [
{
"name": "North Field",
"enabled": true,
"lat1": 30.000000,
"lon1": 121.000000,
"lat2": 30.010000,
"lon2": 121.010000
}
]
}
}Behavior:
- Enabled zones are rendered as red rectangles on the map
- A drone entering an enabled zone triggers a fullscreen browser warning
- Browser notification is sent if the user has granted notification permission
- Legazy
web.alarm_zonesingle-zone field is still accepted for backward compatibility
When enabled, the scanner samples host metrics approximately once per minute and stores them in the system temp directory under light_rid_scanner/host_metrics.jsonl. The file is created automatically, preserved across service restarts, and pruned according to metrics.retention_days (default: 7 days).
Tracked metrics:
- CPU usage
- Memory usage
- Temperature (auto-detected sensor or manual source)
- System load
- AP count
The Settings page renders these as separate mini trend charts with selectable time windows: 12 hours, 24 hours, or 7 days.
{
"metrics": {
"enabled": true,
"retention_days": 7,
"temperature_source": "auto"
}
}Metrics are disabled by default. Enable them in /settings to start collecting.
The RID model map (rid_models.json) maps RID prefix codes to human-readable drone model names. It can be updated online from a configurable URL.
Default source:
https://raw.githubusercontent.com/luyii-code-1/Light_RID_Scanner/refs/heads/main/rid_models.json
Behavior:
- Automatic checks run once per day when
model_update.enabledistrue - Manual update uses the same URL field in Settings
- The URL must start with
http://orhttps:// - Successful and failed updates are written to the operation log and notification center
- The Settings page also allows direct editing of the model list as a prefix/model table
- Aircraft detail cards showing "N/A" for the model offer options to add a local mapping or open a prefilled GitHub Issue / PR edit page
Binary builds embed a snapshot of rid_models.json as a fallback resource. If the file is missing at runtime and the network is unavailable, the embedded copy is restored automatically.
The system provides separate export/import flows for settings and scan data:
Settings (configuration):
GET /api/settings/export/settings— export current settings JSONPOST /api/settings/import/settings— import and merge settings
Scan data (history + tracks):
GET /api/settings/export/scan-data— export aircraft history and tracksPOST /api/settings/import/scan-data— import scan data
Combined tools:
GET /api/tools/export/all— export all data as ZIPGET /api/tools/export/track?sn=<SN>— export single aircraft track as ZIPPOST /api/tools/import/all— import all data from ZIPPOST /api/tools/import/track— import track data
These helpers are available from the Settings page and via direct API calls from the built-in UI.
viewer/server.py is a standalone web service that aggregates multiple station_edition instances into a unified dashboard. It is designed for operators managing several fixed stations who want a single-pane-of-glass view.
python viewer/server.py --host 0.0.0.0 --port 4700Open http://<center-ip>:4700/.
The viewer stores only its own configuration in viewer/cfg.db:
- Node API root URLs
- Node API tokens
- Optional viewer password and SSO login settings
It does not store remote aircraft, base-station, AP, track, or health data. Every dashboard refresh fetches current data from each configured station API in parallel and renders the aggregate result. This means the viewer is stateless with respect to scan data — stop the viewer, and no scan data is retained.
/ — Dashboard
- Reuses the Station page template from
station_edition/light_rid/web_server.py - Viewer code patches the data/API layer to fetch from remote stations instead of a local scanner
- Station-only controls (scanner start/stop, channel change, etc.) are removed from the DOM
/settings — Viewer Settings
- Viewer host status (uptime, version)
- Default map center position and zoom level
- Password login configuration for the viewer itself
- SSO check login configuration
- EULA acceptance controls
/nodes — Node Manager
- Add, edit, test, and delete station nodes
- Node info cards with live status
- Load charts and scan count displays
- One-click remote SSO URL creation for each node
- Batch restart and batch model-database update across selected nodes
Enter only the API root URL, for example http://192.168.1.10:4600. Paths, query strings, fragments, and user-info are rejected — the viewer appends /api/v1 paths itself and validates by making real API calls before saving.
The viewer sends the configured token as both X-API-Token and Authorization: Bearer <token> headers.
| Module | Responsibility |
|---|---|
viewer/server.py |
HTTP routing, API proxying, WebSocket |
viewer/storage.py |
SQLite database for config, nodes, auth/session state |
viewer/aggregation.py |
Parallel station API fetching and data aggregation |
viewer/station_ui.py |
Station HTML template loading and viewer DOM patching |
viewer/settings_ui.py |
Viewer-specific settings page (Station-styled) |
viewer/nodes_ui.py |
Node manager page (Station-styled) |
viewer/ui_common.py |
Shared CSS extraction from Station templates |
viewer/paths.py |
Resource path resolution |
python pytools/build_viewer.py --target x86_64CI builds viewer binaries through .github/workflows/build-viewer.yml for Linux x86_64, Linux x32, Linux arm64, Windows windows-x86_64, and Windows windows-x32.
| File | Description | Commit? |
|---|---|---|
run.py |
Root compatibility wrapper for station edition | Yes |
station_edition/run.py |
Station edition entry point | Yes |
station_edition/light_rid/ |
All scanner, parser, server, auth, and UI modules | Yes |
portable_edition/pe.py |
Portable edition entry point | Yes |
rid_models.json |
RID model prefix-to-name mapping | Yes |
rid_build_info.json |
Local build marker (commit + build number) | Yes |
station_edition/config.example.json |
Safe example configuration | Yes |
config.json |
Real runtime configuration | Never |
rid_storage.db |
Runtime SQLite history store | Never |
history-cache.json |
Legacy JSON history cache kept only for one-time upgrade import | Never |
rid_history_cache.json |
Older legacy JSON history cache kept only for one-time upgrade import | Never |
config.json.rollback |
Automatic rollback copy for recovery | Never |
oui.txt |
MAC OUI vendor database (auto-downloaded) | Never |
light_rid_scanner/host_metrics.jsonl |
Host metrics samples (system temp dir) | Never |
viewer/cfg.db |
Viewer node and auth configuration | Never |
The build version shown in the UI follows the format commit:<short-sha>#<build-number>, read from rid_build_info.json. The current release line is v2.0, but the UI uses the commit-based label for traceability of local builds.
Before pushing to GitHub, verify:
-
config.jsonis not staged - No real webhook keys are in any tracked file
- No real API tokens are in any tracked file
- No runtime-generated history or cache files are staged
- Only
station_edition/config.example.jsoncontains example config values
- Open Drone ID Core C Library — official reference implementation
- OpenDroneID Specs Repository — specification drafts and documentation
For the final, authoritative ASTM Remote ID standard text, obtain ASTM F3411 directly from ASTM International.