Files
ukmesh/docs/decryption.md
gadgethd 1d243222ef chore(sync): reconcile with production deploy tree — features, docs, plans gitignored
Bring main up to date with the live UKMesh production tree, reconciled
per-file (tree-wins for deployed features; main-wins where main evolved
past the tree's lineage; hand-merges for mixed files). Verified locally
on the deploy host: backend + frontend typechecks, backend unit suite
(543 pass / 0 fail), frontend suite (147 pass / 0 fail), OpenAPI
contract check, frontend production build, scripts typecheck.

Highlights:
- Deployed features land: tags + recent tags, owner public feed,
  Viterbi path prewarm and prior rebuilds, region participation,
  owner packet-sharing JWT scope, pathing docs, README updates.
- Main-side hardening retained: CI RAM steps + smoke cleanup trap,
  hardened scripts, gitleaks/logging rules, stale-MQTT observer
  protections, alert-receiver queue module, health-status refactor
  with tree retention additions (owner_packet_share_deliveries +
  public_feed_deliveries @30d, node_status_samples @30d — matching
  live TimescaleDB policies).
- Credential-generation client close restored on the tree's ownerAccess
  so the operator revocation tool (owner-auth:revoke) keeps working.
- Migrations renumbered to follow main's 058/059 ledger migrations:
  055_public_feed, 060_region_scope_participation,
  061_packet_share_owner_scope_and_jwt; migration-registry test
  assertions updated to the final numbering.
- Planning/report artifacts removed from the repo; local plans/, *.bak*
  patterns gitignored (deploy tree tidied to match).
2026-10-14 01:48:11 +00:00

3.0 KiB

MeshCore channel decryption

How the UK Mesh site decrypts group-text (packet type 5) traffic live, and how historical traffic was retroactively decrypted.

Key store

  • backend/src/mqtt/channelRegistry.ts is the single source of truth:
    • VALIDATED_CHANNELS — default secrets baked into the image (39 entries incl. Public; recovered 2026-08-06 and validated to decrypt real human-readable group text).
    • buildCombinedKeyStore() — merges baked defaults with MESHCORE_CHANNEL_SECRETS (env, comma-separated name:hex or bare hex; dedupes by secret) for secrets that shouldn't be committed.
    • buildSummary() / identifyChannel() — shared by ingest and offline tools.
  • ⚠️ The repo is public: keys in channelRegistry.ts are public. Keep non-derivable/community keys in the env var only.
  • Reading env at startup only — after a change: docker compose -f docker-compose.yml -f docker-compose.live.yml up -d --no-deps --force-recreate backend.

Decryption format

GRP_TXT payload = [channel hash 1B][MAC 2B][AES-128-ECB ciphertext]. Hashtag channel keys are derivable: sha256("#name")[:16]. The public channel key (8b3387e9c5cdea6ac9e5edbaa115cd72, hash byte 11) is published in MeshCore docs. Other derivation schemes exist in the wild (sha1/md5 variants, direct ASCII passphrases).

Stored decrypted data

  • Live ingest stores decrypted content inline in payload.decrypted (message/sender/timestamp/flags) and _summary = [ChannelName] sender: message.
  • Historical decryption lives in the packet_decryptions side table (migration 035), filled by the backfill tool. The feed joins it with COALESCE(p.payload->>'_summary', pd.summary).
  • ⛔ Never bulk-UPDATE the packets hypertable — TimescaleDB plans a seq-scan over every chunk. INSERT into the side table instead.

Backfill tool

node dist/tools/backfillDecrypt.js (run inside the backend container). Two-phase keyset-paginated scan over packets_hash_idx (~1,250 hashes/s). 2026-08-06 run: 387,161 packets processed, 173,778 decrypted.

Feed integration

  • GET /api/feed/messages?channel=<scope>&limit<=50 — up to 50 unique historical messages per channel (90-day bound, dedup by packet hash).
  • The feed sidebar lists every decrypted channel (MESSAGE_SCOPE_CHANNELS in frontend/src/pages/ukmesh/feedModel.ts); new channels need a frontend rebuild to appear as filters.

Validation gate (Ben, 2026-08-06)

Never deploy a recovered key whose plaintext isn't verified USEFUL — sane epoch timestamps + printable UTF-8 on a time-spread sample. 1-byte hash buckets collide across channels (MAC pass rate can reach ~97% on junk); always validate before .env deploy. Honest decrypt coverage measured at ~43.5% of the 90-day volume (raw hash-bucket sums overcount).

Out-of-band keys

~/ukmesh/meshcore-discord-bot/.env holds NORTHEAST_CHANNEL_SECRET (== #northeast) and THENORF_CHANNEL_SECRET (hash 8A) — the discord bot forwards those channels. Check it before attacking new keys.