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

68 lines
3.0 KiB
Markdown

# 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.