Database Schema
MeshDash uses two SQLite databases. Both use WAL (Write-Ahead Logging) mode for better concurrency and thread-local connections with a 30-second timeout. Understanding the schema helps you query data directly or write plugins that extend storage.
meshtastic_data.db
The main database containing all mesh network data, users, and monitoring rules. Path set by DB_PATH in config.
packets
Every decoded packet received or sent. The primary packet log.
| Column | Type | Description |
|---|---|---|
id | INTEGER PK | Auto-increment row ID |
event_id | TEXT UNIQUE | Unique event ID (pkt_TIMESTAMP_COUNTER for received, tx_TIMESTAMP for sent) |
timestamp | REAL | Unix timestamp (float) when packet was processed |
rx_time | INTEGER | Radio's rxTime field (may differ slightly from timestamp) |
from_id | TEXT | Sender node ID in !xxxxxxxx hex format |
to_id | TEXT | Recipient: !xxxxxxxx or ^all |
channel | INTEGER | Channel index (0–7) |
packet_type | TEXT | Classified type: Message, Position, Telemetry, Node Info, Ack, Traceroute, etc. |
rx_snr | REAL | Signal-to-noise ratio in dB (null if not physically received) |
rx_rssi | INTEGER | Received signal strength in dBm |
hop_limit | INTEGER | Remaining hop limit when received |
hop_start | INTEGER | Original hop limit set by sender. Hops consumed = hop_start − hop_limit |
want_ack | BOOLEAN | Whether the sender requested an ACK |
decoded | TEXT | JSON-encoded decoded payload object |
raw | TEXT | JSON-encoded raw packet object from SDK |
source | TEXT | Source detection result: RF, MQTT, UNKNOWN, or LOCAL |
source_confidence | REAL | Detection confidence score 0.0–1.0 |
nodes
One row per node ever seen. Updated on every packet received from that node and on background sync. The authoritative node database.
| Column | Type | Description |
|---|---|---|
node_id | TEXT PK | Hex node ID: !aabbccdd |
node_num | INTEGER UNIQUE | 32-bit numeric node number |
long_name | TEXT | Node's configured long name |
short_name | TEXT | Node's 4-character short name |
macaddr | TEXT | MAC address from user info |
hw_model | TEXT | Hardware model string e.g. TLORA_V2_1_1P6 |
firmware_version | TEXT | Firmware version string |
role | TEXT | Node role: CLIENT, ROUTER, ROUTER_CLIENT, etc. |
is_local | BOOLEAN | TRUE for the local MeshDash radio node |
last_heard | INTEGER | Unix timestamp of most recent packet from this node |
battery_level | INTEGER | Battery % (0–100) |
voltage | REAL | Battery voltage in V |
channel_utilization | REAL | Channel utilisation % |
air_util_tx | REAL | Air utilisation TX % |
snr | REAL | Most recent SNR reading for this node |
rssi | INTEGER | Most recent RSSI reading |
latitude | REAL | Most recent latitude (decimal degrees) |
longitude | REAL | Most recent longitude |
altitude | INTEGER | Altitude in metres |
position_time | INTEGER | Timestamp of most recent position update |
user_info | TEXT | JSON: {"longName":"...", "shortName":"...", "macaddr":"..."} |
position_info | TEXT | JSON: full position protobuf fields |
device_metrics_info | TEXT | JSON: full deviceMetrics protobuf |
environment_metrics_info | TEXT | JSON: environmentMetrics (temp, humidity, pressure) |
module_config_info | TEXT | JSON: moduleConfig if received |
channel_info | TEXT | JSON: channel settings if received |
updated_at | DATETIME | SQLite timestamp of last row update |
messages
Text messages only — extracted from packets for easy conversation history.
| Column | Type | Description |
|---|---|---|
id | INTEGER PK | Auto-increment |
packet_event_id | TEXT UNIQUE | Links back to packets.event_id |
mesh_packet_id | INTEGER | Meshtastic packet ID used for ACK matching |
from_id | TEXT | Sender node ID |
to_id | TEXT | Recipient node ID or ^all |
channel | INTEGER | Channel index |
text | TEXT | Message text content |
timestamp | REAL | Unix timestamp |
rx_snr | REAL | SNR at reception |
rx_rssi | INTEGER | RSSI at reception |
status | TEXT | SENT, BROADCAST, DELIVERED, or FAILED — updated when ACK/NACK received |
positions
Position history — every position update stored separately for track replay.
| Column | Type | Description |
|---|---|---|
node_id | TEXT | Node that sent this position |
timestamp | REAL | Unix timestamp |
latitude | REAL | Decimal degrees |
longitude | REAL | Decimal degrees |
altitude | INTEGER | Metres |
precision_bits | INTEGER | Position precision bits from firmware |
ground_speed | INTEGER | m/s if available |
sats_in_view | INTEGER | GPS satellite count |
pdop | REAL | Position dilution of precision |
telemetry
Telemetry history — every telemetry update stored for graphing.
| Column | Type | Description |
|---|---|---|
node_id | TEXT | Source node |
timestamp | REAL | Unix timestamp |
battery_level | INTEGER | % |
voltage | REAL | V |
channel_utilization | REAL | % |
air_util_tx | REAL | % |
uptime_seconds | INTEGER | Device uptime |
temperature | REAL | °C (null if no environment sensor) |
relative_humidity | REAL | % RH |
barometric_pressure | REAL | hPa |
gas_resistance | REAL | Ω (BME680 only) |
iaq | REAL | Indoor air quality index (BME680 only) |
neighbors
Neighbor table from NEIGHBOR_INFO_APP packets — the RF topology map.
| Column | Type | Description |
|---|---|---|
node_id | TEXT | Reporting node |
neighbor_id | TEXT | Neighbour node ID |
snr | REAL | SNR between this pair as reported by the node |
last_seen | DATETIME | When this neighbour was last reported |
traceroutes
| Column | Type | Description |
|---|---|---|
from_id | TEXT | Originating node |
to_id | TEXT | Target node |
route_path | TEXT | JSON: {"route_to":[...], "route_back":[...], "snr_towards":[...], "snr_back":[...], "rssi":N, "snr":N, "hops_used":N} |
timestamp | REAL | Unix timestamp |
waypoints
| Column | Type | Description |
|---|---|---|
from_id | TEXT | Node that sent the waypoint |
waypoint_id | INTEGER | Firmware-assigned waypoint ID |
name | TEXT | Waypoint name |
latitude | REAL | Decimal degrees |
longitude | REAL | Decimal degrees |
description | TEXT | Optional description |
timestamp | REAL | Unix timestamp |
hardware_logs
Logs of Admin-type packets and hardware events (GPIO events, admin commands received).
| Column | Type | Description |
|---|---|---|
node_id | TEXT | Source node |
event_type | TEXT | Type: Admin, GPIO, etc. |
details | TEXT | JSON payload or plain string description |
timestamp | REAL | Unix timestamp |
average_metrics_history
Network-wide average SNR and RSSI computed every 5 minutes across all non-local nodes. Used for the overview metrics chart.
| Column | Type | Description |
|---|---|---|
timestamp | REAL UNIQUE | Unix timestamp of this snapshot |
average_snr | REAL | Mean SNR across non-local nodes with valid readings |
average_rssi | REAL | Mean RSSI |
node_count | INTEGER | Number of nodes included in the average |
connection_log
Timestamped connection status events used for the uptime graph in the topbar and diagnostics.
| Column | Type | Description |
|---|---|---|
timestamp | REAL | Unix timestamp |
status | TEXT | Status string e.g. Connected, Reconnecting, Disconnected |
value | REAL | Numeric health value: 0.9 = connected, 0.5 = transitioning, 0.1 = disconnected |
users
| Column | Type | Description |
|---|---|---|
id | INTEGER PK | Auto-increment |
username | TEXT UNIQUE | Login email / username |
hashed_password | TEXT | bcrypt hash — never stored in plain text |
disabled | BOOLEAN | Account disabled flag |
auto_reply_rules
See Auto-Reply API for full field descriptions.
monitor_rules
See Telemetry Monitor API for full field descriptions.
tasks.db
A separate database for scheduled tasks. Path set by TASK_DB_PATH in config. See Tasks API for the full schema.
Database Maintenance
MeshDash automatically prunes old data hourly. Records older than HISTORY_DAYS are deleted from: packets, messages, telemetry, positions, average_metrics_history, and connection_log. Deletion happens in batches of 5,000 rows to avoid holding long locks.
Every 24 hours, PRAGMA optimize is run to update query planner statistics. After pruning, PRAGMA wal_checkpoint(TRUNCATE) keeps the WAL file from growing unboundedly.
sqlite3 meshtastic_data.db are safe while the service is running — WAL mode allows concurrent readers. Always use read-only mode (sqlite3 -readonly) or stop the service before any writes to avoid conflicts.