Files
meshexplorer/ingest/migrations/004_region_handle_and_groups.sql
Alex Vanderpot c5ee493d8c region groups: data-driven generation, DB-sourced (drop TS/SQL sync)
Replace the hand-curated region groups with data-driven ones and make the
region_groups ClickHouse table the single source of truth.

- scripts/generate-region-groups.ts: offline generator — clusters regions by
  cross-region packet co-occurrence (min-share single-linkage) at two levels
  (broad "region" + tight "metro"), names clusters via `claude -p`, reconciles
  codes by member overlap so permalinks stay stable, and emits the region_groups
  seed. Migration 004 reseeded with the resulting 39 groups.
- Groups are DB-sourced: getRegionGroups() (cached) feeds /api/regions and the
  dropdown/labels; filtering resolves a selector in SQL to a region
  (region = 'X') or a group (region IN / hasAny ... SELECT region_code FROM
  region_groups WHERE group_code = ...). No hardcoded membership in TS;
  resolveSelector removed.
- Drop the TS<->SQL parity script (no membership left to sync); regionSql and
  the migration ALIAS are kept in sync by hand.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 17:40:44 -04:00

698 lines
30 KiB
SQL
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-- +goose Up
-- Region system rework: derive an uppercase IATA region code from the base topic and surface
-- it as an in-DB handle shared by the web app and Grafana, seed region groups, rework the
-- neighbor graph to be strictly per-region, and add a scheduled region index.
--
-- The topic->region derivation below is the canonical SQL form. Keep every occurrence here
-- byte-for-byte identical to regionSql('topic') in meshexplorer/src/lib/regions.ts (by hand).
-- Canonical:
-- multiIf(lower(topic) IN ('meshcore','meshcore/salish'), 'SEA', match(splitByChar('/', lower(topic))[2], '^[a-z]{3}$'), upper(splitByChar('/', lower(topic))[2]), '')
-- 1. Read-time `region` ALIAS column on the base table (for direct meshcore_packets queries:
-- Grafana packets panels + the packets streamer). No storage, no backfill.
-- +goose StatementBegin
ALTER TABLE meshcore_packets
ADD COLUMN IF NOT EXISTS region String
ALIAS multiIf(lower(topic) IN ('meshcore','meshcore/salish'), 'SEA', match(splitByChar('/', lower(topic))[2], '^[a-z]{3}$'), upper(splitByChar('/', lower(topic))[2]), '')
COMMENT 'Derived IATA region from base topic (read-time ALIAS, mirrors regionSql in src/lib/regions.ts)';
-- +goose StatementEnd
-- 2. Region groups: named sets of IATA codes. The region_groups table is the single source of
-- truth (the app reads it cached; Grafana reads it directly), regenerated by
-- meshexplorer/scripts/generate-region-groups.ts.
CREATE TABLE IF NOT EXISTS region_groups (
group_code LowCardinality(String) COMMENT 'Group key (slug), e.g. pacific-northwest',
group_name String COMMENT 'Display name, e.g. Pacific Northwest',
region_code LowCardinality(String) COMMENT 'Member IATA region code, e.g. SEA',
updated_at DateTime DEFAULT now()
) ENGINE = ReplacingMergeTree(updated_at)
ORDER BY (group_code, region_code)
COMMENT 'Region-group membership (single source of truth). Regenerated by scripts/generate-region-groups.ts.';
-- Data-driven (generated by meshexplorer/scripts/generate-region-groups.ts):
INSERT INTO region_groups (group_code, group_name, region_code) VALUES
('britain-ireland','Britain & Ireland','BHX'),
('britain-ireland','Britain & Ireland','MAN'),
('britain-ireland','Britain & Ireland','BRS'),
('britain-ireland','Britain & Ireland','LCY'),
('britain-ireland','Britain & Ireland','OXF'),
('britain-ireland','Britain & Ireland','MME'),
('britain-ireland','Britain & Ireland','BOH'),
('britain-ireland','Britain & Ireland','LTN'),
('britain-ireland','Britain & Ireland','CEG'),
('britain-ireland','Britain & Ireland','LON'),
('britain-ireland','Britain & Ireland','CAX'),
('britain-ireland','Britain & Ireland','SOU'),
('britain-ireland','Britain & Ireland','BHD'),
('britain-ireland','Britain & Ireland','UPV'),
('britain-ireland','Britain & Ireland','BSH'),
('britain-ireland','Britain & Ireland','LBA'),
('britain-ireland','Britain & Ireland','CBG'),
('britain-ireland','Britain & Ireland','DSA'),
('britain-ireland','Britain & Ireland','HYC'),
('britain-ireland','Britain & Ireland','LGW'),
('britain-ireland','Britain & Ireland','NQT'),
('britain-ireland','Britain & Ireland','SNN'),
('britain-ireland','Britain & Ireland','DUB'),
('britain-ireland','Britain & Ireland','VLY'),
('pacific-northwest','Pacific Northwest','SEA'),
('pacific-northwest','Pacific Northwest','PDX'),
('pacific-northwest','Pacific Northwest','BLI'),
('pacific-northwest','Pacific Northwest','KLS'),
('pacific-northwest','Pacific Northwest','YVR'),
('pacific-northwest','Pacific Northwest','KEH'),
('pacific-northwest','Pacific Northwest','PAE'),
('pacific-northwest','Pacific Northwest','OLM'),
('pacific-northwest','Pacific Northwest','SLE'),
('pacific-northwest','Pacific Northwest','TTD'),
('pacific-northwest','Pacific Northwest','EUG'),
('pacific-northwest','Pacific Northwest','CVO'),
('pacific-northwest','Pacific Northwest','MMV'),
('pacific-northwest','Pacific Northwest','MFR'),
('pacific-northwest','Pacific Northwest','LMT'),
('pacific-northwest','Pacific Northwest','GTP'),
('germany-poland','Germany & Poland','BWE'),
('germany-poland','Germany & Poland','LEJ'),
('germany-poland','Germany & Poland','HAM'),
('germany-poland','Germany & Poland','WRO'),
('germany-poland','Germany & Poland','ERF'),
('germany-poland','Germany & Poland','POZ'),
('germany-poland','Germany & Poland','HAJ'),
('germany-poland','Germany & Poland','DRS'),
('germany-poland','Germany & Poland','BYU'),
('germany-poland','Germany & Poland','KZG'),
('germany-poland','Germany & Poland','ZNO'),
('germany-poland','Germany & Poland','KSF'),
('germany-poland','Germany & Poland','AOC'),
('germany-poland','Germany & Poland','BFE'),
('southern-appalachia','Southern Appalachia','RDU'),
('southern-appalachia','Southern Appalachia','TYS'),
('southern-appalachia','Southern Appalachia','AVL'),
('southern-appalachia','Southern Appalachia','CHA'),
('southern-appalachia','Southern Appalachia','TRI'),
('southern-appalachia','Southern Appalachia','GSO'),
('southern-appalachia','Southern Appalachia','GSP'),
('southern-appalachia','Southern Appalachia','CLT'),
('southern-appalachia','Southern Appalachia','LEX'),
('netherlands','Netherlands','AMS'),
('netherlands','Netherlands','RTM'),
('netherlands','Netherlands','GRQ'),
('netherlands','Netherlands','ENS'),
('netherlands','Netherlands','UTC'),
('netherlands','Netherlands','LEY'),
('netherlands','Netherlands','AMF'),
('netherlands','Netherlands','LWR'),
('netherlands','Netherlands','QYM'),
('czech-republic','Czech Republic','PRG'),
('czech-republic','Czech Republic','BRQ'),
('czech-republic','Czech Republic','VOD'),
('czech-republic','Czech Republic','PED'),
('czech-republic','Czech Republic','JCL'),
('czech-republic','Czech Republic','ZLN'),
('czech-republic','Czech Republic','HRA'),
('czech-republic','Czech Republic','OSR'),
('northern-california','Northern California','SJC'),
('northern-california','Northern California','OAK'),
('northern-california','Northern California','SFO'),
('northern-california','Northern California','MRY'),
('northern-california','Northern California','PRB'),
('northern-california','Northern California','MCE'),
('northern-california','Northern California','COA'),
('alpine-region','Alpine Region','STR'),
('alpine-region','Alpine Region','GVA'),
('alpine-region','Alpine Region','MUC'),
('alpine-region','Alpine Region','ZRH'),
('alpine-region','Alpine Region','ACH'),
('alpine-region','Alpine Region','FMM'),
('alpine-region','Alpine Region','OBF'),
('southern-france','Southern France','LYS'),
('southern-france','Southern France','EBU'),
('southern-france','Southern France','TLN'),
('southern-france','Southern France','FNI'),
('southern-france','Southern France','MPL'),
('southern-france','Southern France','VAF'),
('southern-france','Southern France','XBK'),
('southern-california','Southern California','SAN'),
('southern-california','Southern California','LAX'),
('southern-california','Southern California','OXR'),
('southern-california','Southern California','SBA'),
('southern-california','Southern California','ONT'),
('southern-california','Southern California','PSP'),
('southern-california','Southern California','SNA'),
('central-europe','Central Europe','BTS'),
('central-europe','Central Europe','BUD'),
('central-europe','Central Europe','VIE'),
('central-europe','Central Europe','ILZ'),
('central-europe','Central Europe','TAT'),
('central-europe','Central Europe','KSC'),
('denmark','Denmark','AAR'),
('denmark','Denmark','KEL'),
('denmark','Denmark','ODE'),
('denmark','Denmark','SGD'),
('denmark','Denmark','QXV'),
('southern-netherlands','Southern Netherlands','EIN'),
('southern-netherlands','Southern Netherlands','UDE'),
('southern-netherlands','Southern Netherlands','QAR'),
('southern-netherlands','Southern Netherlands','GLZ'),
('belgium','Belgium','LGG'),
('belgium','Belgium','MST'),
('belgium','Belgium','BRU'),
('belgium','Belgium','ANR'),
('northern-new-zealand','Northern New Zealand','AKL'),
('northern-new-zealand','Northern New Zealand','HLZ'),
('northern-new-zealand','Northern New Zealand','TRG'),
('northern-new-zealand','Northern New Zealand','WRE'),
('south-florida','South Florida','PPM'),
('south-florida','South Florida','FPR'),
('south-florida','South Florida','FLL'),
('southern-norway','Southern Norway','OSL'),
('southern-norway','Southern Norway','TRF'),
('southern-norway','Southern Norway','RYG'),
('colorado-front-range','Colorado Front Range','DEN'),
('colorado-front-range','Colorado Front Range','FNL'),
('colorado-front-range','Colorado Front Range','COS'),
('southern-idaho','Southern Idaho','IDA'),
('southern-idaho','Southern Idaho','PIH'),
('southern-idaho','Southern Idaho','TWF'),
('mid-atlantic','Mid-Atlantic','IAD'),
('mid-atlantic','Mid-Atlantic','SCE'),
('mid-atlantic','Mid-Atlantic','MDT'),
('puget-sound','Puget Sound','SEA'),
('puget-sound','Puget Sound','BLI'),
('puget-sound','Puget Sound','KLS'),
('puget-sound','Puget Sound','YVR'),
('puget-sound','Puget Sound','KEH'),
('puget-sound','Puget Sound','PAE'),
('puget-sound','Puget Sound','OLM'),
('central-germany','Central Germany','BWE'),
('central-germany','Central Germany','LEJ'),
('central-germany','Central Germany','ERF'),
('central-germany','Central Germany','HAJ'),
('central-germany','Central Germany','ZNO'),
('central-germany','Central Germany','KSF'),
('greater-london','Greater London','OXF'),
('greater-london','Greater London','LTN'),
('greater-london','Greater London','LON'),
('greater-london','Greater London','UPV'),
('greater-london','Greater London','CBG'),
('san-francisco-bay-area','San Francisco Bay Area','SJC'),
('san-francisco-bay-area','San Francisco Bay Area','OAK'),
('san-francisco-bay-area','San Francisco Bay Area','SFO'),
('san-francisco-bay-area','San Francisco Bay Area','MRY'),
('east-midlands','East Midlands','LBA'),
('east-midlands','East Midlands','DSA'),
('east-midlands','East Midlands','NQT'),
('south-coast','South Coast','BOH'),
('south-coast','South Coast','SOU'),
('south-coast','South Coast','BSH'),
('central-california','Central California','PRB'),
('central-california','Central California','MCE'),
('central-california','Central California','COA'),
('zurich-area','Zurich Area','ZRH'),
('zurich-area','Zurich Area','ACH'),
('zurich-area','Zurich Area','FMM'),
('southern-oregon','Southern Oregon','MFR'),
('southern-oregon','Southern Oregon','LMT'),
('southern-oregon','Southern Oregon','GTP'),
('vienna-budapest','ViennaBudapest','BTS'),
('vienna-budapest','ViennaBudapest','BUD'),
('vienna-budapest','ViennaBudapest','VIE'),
('irish-sea','Irish Sea','CAX'),
('irish-sea','Irish Sea','BHD'),
('surrey','Surrey','HYC'),
('surrey','Surrey','LGW'),
('greater-manchester','Greater Manchester','MAN'),
('greater-manchester','Greater Manchester','CEG'),
('ireland','Ireland','SNN'),
('ireland','Ireland','DUB'),
('franconia','Franconia','BYU'),
('franconia','Franconia','KZG'),
('munich','Munich','MUC'),
('munich','Munich','OBF'),
('northern-slovakia','Northern Slovakia','ILZ'),
('northern-slovakia','Northern Slovakia','TAT'),
('brussels-antwerp','BrusselsAntwerp','BRU'),
('brussels-antwerp','BrusselsAntwerp','ANR'),
('meuse-valley','Meuse Valley','LGG'),
('meuse-valley','Meuse Valley','MST');
-- 3. Recreate the 001 views to expose `region` (scalar) / `regions` (array). The derivation is
-- inlined (concrete column) so app + Grafana queries against these views never depend on
-- resolving the base-table ALIAS through nested view layers.
CREATE OR REPLACE VIEW meshcore_adverts AS
SELECT
ingest_timestamp,
origin,
origin_pubkey,
mesh_timestamp,
packet,
path_len,
path,
broker,
topic,
multiIf(lower(topic) IN ('meshcore','meshcore/salish'), 'SEA', match(splitByChar('/', lower(topic))[2], '^[a-z]{3}$'), upper(splitByChar('/', lower(topic))[2]), '') AS region,
hex(substring(payload, 1, 32)) AS public_key,
reinterpretAsUInt32(substring(payload, 33, 4)) AS adv_timestamp,
hex(substring(payload, 37, 64)) AS signature,
reinterpretAsUInt8(substring(payload, 101, 1)) AS appdata_flags,
bitAnd(reinterpretAsUInt8(substring(payload, 101, 1)), 0x01) = 0x01 AS is_chat_node,
bitAnd(reinterpretAsUInt8(substring(payload, 101, 1)), 0x02) = 0x02 AS is_repeater,
bitAnd(reinterpretAsUInt8(substring(payload, 101, 1)), 0x03) = 0x03 AS is_room_server,
bitAnd(reinterpretAsUInt8(substring(payload, 101, 1)), 0x10) = 0x10 AS has_location,
bitAnd(reinterpretAsUInt8(substring(payload, 101, 1)), 0x20) = 0x20 AS has_feature1,
bitAnd(reinterpretAsUInt8(substring(payload, 101, 1)), 0x40) = 0x40 AS has_feature2,
bitAnd(reinterpretAsUInt8(substring(payload, 101, 1)), 0x80) = 0x80 AS has_name,
CASE WHEN bitAnd(reinterpretAsUInt8(substring(payload, 101, 1)), 0x10) = 0x10
THEN reinterpretAsInt32(substring(payload, 102, 4))
ELSE NULL
END AS latitude_i,
CASE WHEN bitAnd(reinterpretAsUInt8(substring(payload, 101, 1)), 0x10) = 0x10
THEN reinterpretAsInt32(substring(payload, 106, 4))
ELSE NULL
END AS longitude_i,
latitude_i * 1e-6 AS latitude,
longitude_i * 1e-6 AS longitude,
substring(
payload,
102
+ multiIf(bitAnd(reinterpretAsUInt8(substring(payload, 101, 1)), 0x10) = 0x10, 8, 0)
) AS node_name,
hex(substring(payload, 1, 1)) AS node_hash,
packet_hash
FROM meshcore_packets
WHERE payload_type = 4;
CREATE OR REPLACE VIEW meshcore_adverts_latest AS
SELECT
public_key,
min(ingest_timestamp) AS first_heard,
max(ingest_timestamp) AS last_seen,
argMax(broker, ingest_timestamp) AS broker,
argMax(topic, ingest_timestamp) AS topic,
argMax(region, ingest_timestamp) AS region,
argMax(origin, ingest_timestamp) AS origin,
argMax(mesh_timestamp, ingest_timestamp) AS mesh_timestamp,
argMax(packet, ingest_timestamp) AS packet,
argMax(path_len, ingest_timestamp) AS path_len,
argMax(path, ingest_timestamp) AS path,
argMax(adv_timestamp, ingest_timestamp) AS adv_timestamp,
argMax(signature, ingest_timestamp) AS signature,
argMax(appdata_flags, ingest_timestamp) AS appdata_flags,
argMax(is_chat_node, ingest_timestamp) AS is_chat_node,
argMax(is_repeater, ingest_timestamp) AS is_repeater,
argMax(is_room_server, ingest_timestamp) AS is_room_server,
argMax(has_location, ingest_timestamp) AS has_location,
argMax(has_feature1, ingest_timestamp) AS has_feature1,
argMax(has_feature2, ingest_timestamp) AS has_feature2,
argMax(has_name, ingest_timestamp) AS has_name,
argMax(latitude_i, ingest_timestamp) AS latitude_i,
argMax(longitude_i, ingest_timestamp) AS longitude_i,
argMax(latitude, ingest_timestamp) AS latitude,
argMax(longitude, ingest_timestamp) AS longitude,
argMax(node_name, ingest_timestamp) AS node_name,
argMax(node_hash, ingest_timestamp) AS node_hash,
argMax(packet_hash, ingest_timestamp) AS packet_hash
FROM meshcore_adverts
GROUP BY public_key
ORDER BY last_seen DESC;
CREATE OR REPLACE VIEW meshcore_public_channel_messages AS
SELECT
max(ingest_timestamp) AS ingest_timestamp,
min(mesh_timestamp) AS mesh_timestamp,
groupArray(origin) AS origins,
any(packet) AS packet,
any(path_len) AS path_len,
hex(substring(payload, 1, 1)) AS channel_hash,
hex(substring(payload, 2, 2)) AS mac,
substring(payload, 4) AS encrypted_message,
count() AS message_count,
groupArray((origin, hex(path))) AS origin_path_array, --deprecated
groupArray((origin, hex(origin_pubkey), hex(path))) AS origin_key_path_array, --deprecated
groupArray((broker, topic)) AS topic_broker_array, --deprecated
groupArray((origin, hex(origin_pubkey), hex(path), broker, topic)) AS origin_path_info,
arrayDistinct(arrayFilter(r -> r != '', groupArray(multiIf(lower(topic) IN ('meshcore','meshcore/salish'), 'SEA', match(splitByChar('/', lower(topic))[2], '^[a-z]{3}$'), upper(splitByChar('/', lower(topic))[2]), '')))) AS regions,
any(packet_hash) AS message_id
FROM meshcore_packets
WHERE payload_type = 5
GROUP BY payload
ORDER BY ingest_timestamp DESC;
-- 4. Rework the global neighbor edge graph: region is derived inline from each source's topic
-- (not the hardcoded broker/topic CTE), and direct edges are now region-scoped so a node in
-- one region is never counted as a neighbor in another (§6a: drop the cross-join over regions).
DROP VIEW IF EXISTS meshcore_all_neighbor_edges;
-- +goose StatementBegin
CREATE MATERIALIZED VIEW IF NOT EXISTS meshcore_all_neighbor_edges
REFRESH EVERY 1 HOUR
ENGINE = MergeTree
ORDER BY (region, source_node, target_node)
AS
WITH
node_details AS (
SELECT
public_key,
node_name,
last_seen,
ifNull(latitude, 0.) AS lat,
ifNull(longitude, 0.) AS lon,
-- (0,0) / missing coords are treated as "no usable location" (avoids edges drawn to null island)
(has_location AND abs(ifNull(latitude, 0.)) > 0.01 AND abs(ifNull(longitude, 0.)) > 0.01) AS loc_ok
FROM meshcore_adverts_latest
),
-- Region-tagged latest adverts (region derived once from the latest topic per node)
adverts_latest_r AS (
SELECT
public_key,
is_repeater,
last_seen,
multiIf(lower(topic) IN ('meshcore','meshcore/salish'), 'SEA', match(splitByChar('/', lower(topic))[2], '^[a-z]{3}$'), upper(splitByChar('/', lower(topic))[2]), '') AS region
FROM meshcore_adverts_latest
),
-- Repeater prefixes per region (exactly one repeater per 2-char prefix), last 2 days
repeater_prefixes AS (
SELECT
region,
substring(public_key, 1, 2) AS prefix,
count() AS node_count,
any(public_key) AS representative_key
FROM adverts_latest_r
WHERE is_repeater = 1 AND last_seen >= now() - INTERVAL 2 DAY AND region != ''
GROUP BY region, prefix
HAVING node_count = 1
),
-- Distinct multi-hop path packets (last 1 day), region derived from the packet topic
path_src AS (
SELECT DISTINCT
payload,
path,
path_len,
multiIf(lower(topic) IN ('meshcore','meshcore/salish'), 'SEA', match(splitByChar('/', lower(topic))[2], '^[a-z]{3}$'), upper(splitByChar('/', lower(topic))[2]), '') AS region
FROM meshcore_packets
WHERE path_len >= 2 AND ingest_timestamp >= now() - INTERVAL 1 DAY
),
path_pairs AS (
SELECT DISTINCT
region,
payload,
-- path is a hex string of 1-byte hop prefixes; take 2 hex chars per hop.
upper(substring(path, 2 * i - 1, 2)) AS source_prefix,
upper(substring(path, 2 * i + 1, 2)) AS target_prefix
FROM path_src
ARRAY JOIN range(1, path_len) AS i
WHERE i < path_len AND region != ''
),
path_neighbors AS (
SELECT region, source_prefix, target_prefix, count() AS packet_count
FROM path_pairs
WHERE source_prefix != target_prefix
GROUP BY region, source_prefix, target_prefix
),
path_connections AS (
SELECT
pn.region AS region,
sm.representative_key AS source_node,
tm.representative_key AS target_node,
pn.packet_count AS packet_count
FROM path_neighbors AS pn
INNER JOIN repeater_prefixes AS sm ON sm.region = pn.region AND sm.prefix = pn.source_prefix
INNER JOIN repeater_prefixes AS tm ON tm.region = pn.region AND tm.prefix = pn.target_prefix
),
-- Direct connections (path_len = 0 adverts), last 7 days, region-scoped per the advert's
-- own topic. No cross-region duplication: an edge belongs only to the region it was heard in.
direct_connections AS (
SELECT DISTINCT
multiIf(lower(topic) IN ('meshcore','meshcore/salish'), 'SEA', match(splitByChar('/', lower(topic))[2], '^[a-z]{3}$'), upper(splitByChar('/', lower(topic))[2]), '') AS region,
hex(origin_pubkey) AS source_node,
public_key AS target_node
FROM meshcore_adverts
WHERE path_len = 0
AND hex(origin_pubkey) != public_key
AND ingest_timestamp >= now() - INTERVAL 7 DAY
),
-- Per-region edges: path edges + direct edges not already covered by a path edge
edges AS (
SELECT region, source_node, target_node, 'path' AS connection_type, packet_count
FROM path_connections
UNION ALL
SELECT d.region, d.source_node, d.target_node, 'direct' AS connection_type, CAST(1 AS UInt64) AS packet_count
FROM direct_connections AS d
WHERE d.region != ''
AND (d.region, d.source_node, d.target_node) NOT IN (SELECT region, source_node, target_node FROM path_connections)
AND (d.region, d.target_node, d.source_node) NOT IN (SELECT region, source_node, target_node FROM path_connections)
)
SELECT
e.region AS region,
e.source_node AS source_node,
e.target_node AS target_node,
e.connection_type AS connection_type,
e.packet_count AS packet_count,
sd.node_name AS source_name,
if(sd.loc_ok, sd.lat, NULL) AS source_latitude,
if(sd.loc_ok, sd.lon, NULL) AS source_longitude,
toUInt8(sd.loc_ok) AS source_has_location,
sd.last_seen AS source_last_seen,
td.node_name AS target_name,
if(td.loc_ok, td.lat, NULL) AS target_latitude,
if(td.loc_ok, td.lon, NULL) AS target_longitude,
toUInt8(td.loc_ok) AS target_has_location,
td.last_seen AS target_last_seen
FROM edges AS e
INNER JOIN node_details AS sd ON e.source_node = sd.public_key
INNER JOIN node_details AS td ON e.target_node = td.public_key
-- Drop geographically implausible adjacencies: across a wide aggregated topic (e.g. a
-- letsmesh meshcore/{IATA} feed covering a whole region) the 1-byte path-hop prefixes
-- collide, stitching together repeaters hundreds of km apart that were never adjacent.
-- A real LoRa hop between two located nodes is within ~150 km.
WHERE NOT (sd.loc_ok AND td.loc_ok AND greatCircleDistance(sd.lon, sd.lat, td.lon, td.lat) > 150000);
-- +goose StatementEnd
-- 5. Scheduled region index: a tiny precomputed table of active regions (last 30 days) so the
-- app's region dropdown and Grafana's $region variable read a small table, not a live scan.
-- +goose StatementBegin
CREATE MATERIALIZED VIEW IF NOT EXISTS meshcore_regions
REFRESH EVERY 1 HOUR
ENGINE = MergeTree
ORDER BY region
AS
WITH adverts_latest_r AS (
SELECT
public_key,
last_seen,
multiIf(lower(topic) IN ('meshcore','meshcore/salish'), 'SEA', match(splitByChar('/', lower(topic))[2], '^[a-z]{3}$'), upper(splitByChar('/', lower(topic))[2]), '') AS region
FROM meshcore_adverts_latest
WHERE last_seen >= now() - INTERVAL 30 DAY
)
SELECT
region,
count() AS node_count,
max(last_seen) AS last_seen
FROM adverts_latest_r
WHERE region != ''
GROUP BY region;
-- +goose StatementEnd
-- +goose Down
DROP VIEW IF EXISTS meshcore_regions;
DROP VIEW IF EXISTS meshcore_all_neighbor_edges;
-- +goose StatementBegin
CREATE MATERIALIZED VIEW IF NOT EXISTS meshcore_all_neighbor_edges
REFRESH EVERY 1 HOUR
ENGINE = MergeTree
ORDER BY (region, source_node, target_node)
AS
WITH
regions_topics AS (
SELECT 'seattle' AS region, 'wss://mqtt-us-v1.letsmesh.net:443' AS broker, arrayJoin(['meshcore/SEA']) AS topic
UNION ALL SELECT 'portland', 'tcp://mqtt.davekeogh.com:1883', 'meshcore/pdx'
UNION ALL SELECT 'boston', 'tcp://mqtt.davekeogh.com:1883', 'meshcore/bos'
),
node_details AS (
SELECT public_key, node_name, latitude, longitude, has_location, last_seen
FROM meshcore_adverts_latest
),
repeater_prefixes AS (
SELECT
rt.region AS region,
substring(a.public_key, 1, 2) AS prefix,
count() AS node_count,
any(a.public_key) AS representative_key
FROM meshcore_adverts_latest AS a
INNER JOIN regions_topics AS rt ON a.broker = rt.broker AND a.topic = rt.topic
WHERE a.is_repeater = 1 AND a.last_seen >= now() - INTERVAL 2 DAY
GROUP BY rt.region, prefix
HAVING node_count = 1
),
path_src AS (
SELECT DISTINCT payload, path, path_len, broker, topic
FROM meshcore_packets
WHERE path_len >= 2 AND ingest_timestamp >= now() - INTERVAL 1 DAY
),
path_pairs AS (
SELECT DISTINCT
rt.region AS region,
payload,
upper(substring(path, 2 * i - 1, 2)) AS source_prefix,
upper(substring(path, 2 * i + 1, 2)) AS target_prefix
FROM path_src AS p
INNER JOIN regions_topics AS rt ON p.broker = rt.broker AND p.topic = rt.topic
ARRAY JOIN range(1, path_len) AS i
WHERE i < path_len
),
path_neighbors AS (
SELECT region, source_prefix, target_prefix, count() AS packet_count
FROM path_pairs
WHERE source_prefix != target_prefix
GROUP BY region, source_prefix, target_prefix
),
path_connections AS (
SELECT
pn.region AS region,
sm.representative_key AS source_node,
tm.representative_key AS target_node,
pn.packet_count AS packet_count
FROM path_neighbors AS pn
INNER JOIN repeater_prefixes AS sm ON sm.region = pn.region AND sm.prefix = pn.source_prefix
INNER JOIN repeater_prefixes AS tm ON tm.region = pn.region AND tm.prefix = pn.target_prefix
),
direct_connections AS (
SELECT DISTINCT hex(origin_pubkey) AS source_node, public_key AS target_node
FROM meshcore_adverts
WHERE path_len = 0
AND hex(origin_pubkey) != public_key
AND ingest_timestamp >= now() - INTERVAL 7 DAY
),
edges AS (
SELECT region, source_node, target_node, 'path' AS connection_type, packet_count
FROM path_connections
UNION ALL
SELECT r.region, d.source_node, d.target_node, 'direct' AS connection_type, CAST(1 AS UInt64) AS packet_count
FROM direct_connections AS d
CROSS JOIN (SELECT DISTINCT region FROM regions_topics) AS r
WHERE (r.region, d.source_node, d.target_node) NOT IN (SELECT region, source_node, target_node FROM path_connections)
AND (r.region, d.target_node, d.source_node) NOT IN (SELECT region, source_node, target_node FROM path_connections)
)
SELECT
e.region AS region,
e.source_node AS source_node,
e.target_node AS target_node,
e.connection_type AS connection_type,
e.packet_count AS packet_count,
sd.node_name AS source_name,
sd.latitude AS source_latitude,
sd.longitude AS source_longitude,
sd.has_location AS source_has_location,
sd.last_seen AS source_last_seen,
td.node_name AS target_name,
td.latitude AS target_latitude,
td.longitude AS target_longitude,
td.has_location AS target_has_location,
td.last_seen AS target_last_seen
FROM edges AS e
INNER JOIN node_details AS sd ON e.source_node = sd.public_key
INNER JOIN node_details AS td ON e.target_node = td.public_key;
-- +goose StatementEnd
CREATE OR REPLACE VIEW meshcore_public_channel_messages AS
SELECT
max(ingest_timestamp) AS ingest_timestamp,
min(mesh_timestamp) AS mesh_timestamp,
groupArray(origin) AS origins,
any(packet) AS packet,
any(path_len) AS path_len,
hex(substring(payload, 1, 1)) AS channel_hash,
hex(substring(payload, 2, 2)) AS mac,
substring(payload, 4) AS encrypted_message,
count() AS message_count,
groupArray((origin, hex(path))) AS origin_path_array, --deprecated
groupArray((origin, hex(origin_pubkey), hex(path))) AS origin_key_path_array, --deprecated
groupArray((broker, topic)) AS topic_broker_array, --deprecated
groupArray((origin, hex(origin_pubkey), hex(path), broker, topic)) AS origin_path_info,
any(packet_hash) AS message_id
FROM meshcore_packets
WHERE payload_type = 5
GROUP BY payload
ORDER BY ingest_timestamp DESC;
CREATE OR REPLACE VIEW meshcore_adverts_latest AS
SELECT
public_key,
min(ingest_timestamp) AS first_heard,
max(ingest_timestamp) AS last_seen,
argMax(broker, ingest_timestamp) AS broker,
argMax(topic, ingest_timestamp) AS topic,
argMax(origin, ingest_timestamp) AS origin,
argMax(mesh_timestamp, ingest_timestamp) AS mesh_timestamp,
argMax(packet, ingest_timestamp) AS packet,
argMax(path_len, ingest_timestamp) AS path_len,
argMax(path, ingest_timestamp) AS path,
argMax(adv_timestamp, ingest_timestamp) AS adv_timestamp,
argMax(signature, ingest_timestamp) AS signature,
argMax(appdata_flags, ingest_timestamp) AS appdata_flags,
argMax(is_chat_node, ingest_timestamp) AS is_chat_node,
argMax(is_repeater, ingest_timestamp) AS is_repeater,
argMax(is_room_server, ingest_timestamp) AS is_room_server,
argMax(has_location, ingest_timestamp) AS has_location,
argMax(has_feature1, ingest_timestamp) AS has_feature1,
argMax(has_feature2, ingest_timestamp) AS has_feature2,
argMax(has_name, ingest_timestamp) AS has_name,
argMax(latitude_i, ingest_timestamp) AS latitude_i,
argMax(longitude_i, ingest_timestamp) AS longitude_i,
argMax(latitude, ingest_timestamp) AS latitude,
argMax(longitude, ingest_timestamp) AS longitude,
argMax(node_name, ingest_timestamp) AS node_name,
argMax(node_hash, ingest_timestamp) AS node_hash,
argMax(packet_hash, ingest_timestamp) AS packet_hash
FROM meshcore_adverts
GROUP BY public_key
ORDER BY last_seen DESC;
CREATE OR REPLACE VIEW meshcore_adverts AS
SELECT
ingest_timestamp,
origin,
origin_pubkey,
mesh_timestamp,
packet,
path_len,
path,
broker,
topic,
hex(substring(payload, 1, 32)) AS public_key,
reinterpretAsUInt32(substring(payload, 33, 4)) AS adv_timestamp,
hex(substring(payload, 37, 64)) AS signature,
reinterpretAsUInt8(substring(payload, 101, 1)) AS appdata_flags,
bitAnd(reinterpretAsUInt8(substring(payload, 101, 1)), 0x01) = 0x01 AS is_chat_node,
bitAnd(reinterpretAsUInt8(substring(payload, 101, 1)), 0x02) = 0x02 AS is_repeater,
bitAnd(reinterpretAsUInt8(substring(payload, 101, 1)), 0x03) = 0x03 AS is_room_server,
bitAnd(reinterpretAsUInt8(substring(payload, 101, 1)), 0x10) = 0x10 AS has_location,
bitAnd(reinterpretAsUInt8(substring(payload, 101, 1)), 0x20) = 0x20 AS has_feature1,
bitAnd(reinterpretAsUInt8(substring(payload, 101, 1)), 0x40) = 0x40 AS has_feature2,
bitAnd(reinterpretAsUInt8(substring(payload, 101, 1)), 0x80) = 0x80 AS has_name,
CASE WHEN bitAnd(reinterpretAsUInt8(substring(payload, 101, 1)), 0x10) = 0x10
THEN reinterpretAsInt32(substring(payload, 102, 4))
ELSE NULL
END AS latitude_i,
CASE WHEN bitAnd(reinterpretAsUInt8(substring(payload, 101, 1)), 0x10) = 0x10
THEN reinterpretAsInt32(substring(payload, 106, 4))
ELSE NULL
END AS longitude_i,
latitude_i * 1e-6 AS latitude,
longitude_i * 1e-6 AS longitude,
substring(
payload,
102
+ multiIf(bitAnd(reinterpretAsUInt8(substring(payload, 101, 1)), 0x10) = 0x10, 8, 0)
) AS node_name,
hex(substring(payload, 1, 1)) AS node_hash,
packet_hash
FROM meshcore_packets
WHERE payload_type = 4;
DROP TABLE IF EXISTS region_groups;
-- +goose StatementBegin
ALTER TABLE meshcore_packets DROP COLUMN IF EXISTS region;
-- +goose StatementEnd