Agent reference: MECP (Mesh Emergency Communication Protocol)
Deep subsystem reference for AI assistants. Open when a task touches MECP compose, alerts, audit log, ALERT_APP ingest, or cross-protocol RF rebroadcast. Hard rules live in AGENTS.md.
Wire format
MECP/<severity>/<codes> [freetext]
- Severity:
0MAYDAY,1URGENT,2SAFETY,3ROUTINE - Codes: letter + two digits (
M01,M16medical supply drop, …); drillD01/D02setisDrill(suppresses alerts). Labels come from vendored language packs underlanguages/ - Max 200 UTF-8 bytes (
MAX_MESSAGE_BYTES) - Vendored engine:
src/renderer/lib/mecp/engine/from xiang-dev-1/MECP (GPLv3) - Language packs:
src/renderer/lib/mecp/languages/(CC BY 4.0) - Overview / video: mecp.radio
- App wrappers:
mecpMessages.ts(MECP_REGEX,tryParseMecp),mecpAlert.ts,mecpRebroadcast.ts
Receive path
- Messages land in
messageStoreas normal chat text (Meshtastic RF/MQTT, MeshCore, Reticulum). - Meshtastic
ALERT_APP(port 11) is decoded likeTEXT_MESSAGE_APPinMeshtasticProtocol/ MQTT. useMecpAlertWatcher(mounted once fromApp.tsx):- Seeds a dedup set at mount (no alert/audit on hydration)
- New inbound MECP → durable audit append (
mecp:appendReceived) - Alerts: sev 0
'mecpSiren'(~5s six-cycle siren, length-matched to URGENT) + emergency toast; sev 1 US EAS-style 853+960 Hz'mecpEas'(~5s) + toast (ignore mutes; always including focused chat); sev 2'mecpSafety'(short–long dit–dah × 6, 1175 Hz square, ~4.4s) / 3'mecp'repeated rising triple when unmuted; drills never alert - Focused Chat still alerts via
ChatPanel→triggerMecpAlert(deduped with the watcher) - Optional RF rebroadcast (§ below)
- Upserts eligible MECP into the Incident Command store — see Incident Command. Live: peer reports plus B03/R01/B02 control updates, excluding history, store-and-forward, tapback and invalid severity. Seed (mount snapshot or DB rows): records older than
INCIDENT_SEED_MAX_AGE_MS(24h) are dropped before any handling, and new reports honor resolved tombstones. Own traffic is skipped except own B01/B03 beacon control (not via S&F/tapback), so Resolve can send B03. Records stored by the bulk message-store writers (wasMessageBulkLoaded, tracked per stored record object) always take the silent seed path even when hydration lands after mount; a DB row never demotes a live record already in the store - Chat channel/DM chips and the overflow
ChatChannelSwitcher(rows + trigger) show a static severity-coloredShieldAlert(MecpUnreadIcon) for the most severe unread MECP in that view (computeUnreadMecpSeverityByView, same watermark as unread counts). The Incident tab badge counts open MAYDAY/URGENT only, drills included (incidentTabBadgeCount, S9); quit confirmation still uses non-drillopenMaydayUrgentCount
Default tone shapes and timings: notification-sounds.md — Default MECP tone shapes.
Durable audit log
- File:
mecp-received.log(+.1size rotate) under ElectronuserData— not sessionmesh-client.log - IPC:
mecp:appendReceived,mecp:exportReceivedLog(Save dialog) - Included in support bundles
- App → MECP section: Export MECP log (Save dialog) alongside RF bridge settings and a link to upstream docs
Send path
- App → MECP → Show MECP button in Chat (default off) gates the Chat MECP compose control
- When enabled: Chat MECP button →
MecpComposeModal(defaults: ROUTINE + Drill category, no codes selected) → encode →sendEmergencyText(emergencySend.ts) → livehandleSendChunk/useSendMessage(follows open DM/channel) - Emergency outbox: when offline / MQTT-only MeshCore, or when the live send throws, the report is queued in the chat outbox with
priority: 'emergency'— no 24h drain cutoff, no 5-attempt stop, soft cap of 20 rows (overflow blocks the oldest, never deletes). See emcomm.md — WS2 - Attach GPS uses the app share-location waterfall (
resolveShareLocation), not rawnavigator.geolocationalone - Meshtastic outbound uses normal text (
TEXT_MESSAGE_APP), not ALERT_APP
RF rebroadcast (default off)
- App → MECP section: rules
{ enabled, bidirectional, endpointA, endpointB }(Meshtastic/MeshCore channel indices 0–7) - One-way A→B by default; Bidirectional toggle enables B→A
- Trigger: new inbound MECP with
receivedViarf/both(not mqtt-only); skip own/history/drill - Loop guard: payload+dest fingerprint TTL
- After each successful bridge send: short follow-up notice
MECP from <sender> via <Meshtastic|MeshCore> (<channel name>)(not a wire MECP; channel name, not index) - Implementation:
mecpRebroadcast.ts+sendMecpRebroadcast.ts
Incident Command
Inbound MECP feeds the always-visible Incident tab (persistent incidentStore, cross-protocol merge, R01 ACK / B02 beacon Confirm, Resolve, map markers). Ops alerts, exports, SAR map tools, and incident track retention are also EMCOMM workstreams. See emcomm.md.
Out of scope (follow-ups)
- RetAlert (
!RETALERT!…) - SQLite
mecpParsedcolumn (Incident Command uses a Zustand persist store — see emcomm.md) - MeshCore Rooms bubble styling
- Send via
ALERT_APPportnum - Reticulum DM bridge endpoints
Sound choices and volume are configurable per severity in App → Notifications. Original sounds remain defaults; MAYDAY/URGENT retain mute bypass and a 10% volume floor. See notification-sounds.md.