Skip to content

Feat/tak dm receipts - #1283

Merged
rinchen merged 8 commits into
Colorado-Mesh:mainfrom
RobertMTalbot:feat/tak-dm-receipts
Oct 17, 2026
Merged

rinchen merged 8 commits into
Colorado-Mesh:mainfrom
RobertMTalbot:feat/tak-dm-receipts

Conversation

@RobertMTalbot

@RobertMTalbot RobertMTalbot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features
    • TAK companion direct messages now show a delivery receipt after acknowledgement or a “Not confirmed” note if automatic retries receive no acknowledgement.
    • Messages that could not be sent receive a status note. Eligible notes are delivered when the recipient reconnects.
    • MeshCore “Not confirmed” notes include a reminder to send a flood advert.

@coderabbitai

coderabbitai Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository: Colorado-Mesh/mesh-client/.coderabbit.yaml
  • Review profile: QUIET
  • Plan: Advanced
  • Run ID: c7faf97e-e802-4e4d-9652-fbe37d34e0d9
📥 Commits

Reviewing files that changed from the base of the PR and between c0568bc and 8e56ec0.

⛔ Files ignored due to path filters: 1
  • src/renderer/locales/en/translation.json is excluded by !src/renderer/locales/**
📒 Files selected for processing: 23
  • README.md
  • src/main/index.ipc-security.test.ts
  • src/main/ipc/tak-handlers.test.ts
  • src/main/ipc/tak-handlers.ts
  • src/main/tak-server-manager.test.ts
  • src/main/tak-server-manager.ts
  • src/main/tak/geochat-cot.test.ts
  • src/main/tak/geochat-cot.ts
  • src/preload/index.ts
  • src/renderer/hooks/useTakInboundChat.ts
  • src/renderer/lib/autoResend/autoResendController.test.ts
  • src/renderer/lib/autoResend/autoResendController.ts
  • src/renderer/lib/devElectronApiStub.ts
  • src/renderer/lib/tak/takDmStatus.test.ts
  • src/renderer/lib/tak/takDmStatus.ts
  • src/renderer/lib/tak/takInboundChat.test.ts
  • src/renderer/lib/tak/takInboundChat.ts
  • src/renderer/lib/tak/takMeshSend.test.ts
  • src/renderer/lib/tak/takMeshSend.ts
  • src/renderer/vitest.electronApiMock.ts
  • src/shared/electron-api.types.ts
  • src/shared/tak-types.ts
  • vitest.config.mts

Included review availability: This review used your included allowance. Your plan provides up to 2 included reviews per hour; 1 remain after this review.

📝 Walkthrough

TAK direct messages now use references to associate radio send outcomes with the originating TAK client. The client reports delivered, unconfirmed, or not-sent status. The TAK server returns a delivery receipt or an explanatory note when applicable.

Changes: TAK direct-message delivery status

Layer / File(s) Summary
Status contract and API
src/shared/tak-types.ts, src/main/tak/geochat-cot.ts, src/shared/electron-api.types.ts, src/preload/index.ts
Shared types define message references and three outcomes. TAK helpers validate status updates and build receipt CoT events. The preload API forwards status updates to the main process.
Server reference and outcome handling
src/main/tak-server-manager.ts, src/main/ipc/tak-handlers.ts, src/main/index.ipc-security.test.ts, src/main/ipc/tak-handlers.test.ts, src/main/tak-server-manager.test.ts
The server records referenced direct messages and handles reported outcomes. It sends receipts for eligible delivered messages and notes for other outcomes, including the MeshCore advert hint. Tests cover validation, connected and disconnected clients, and expired or unknown references.
Renderer send and outcome tracking
src/renderer/lib/autoResend/*, src/renderer/lib/tak/*, src/renderer/hooks/useTakInboundChat.ts, src/renderer/lib/devElectronApiStub.ts, src/renderer/vitest.electronApiMock.ts, vitest.config.mts, README.md
Radio sends retain their original message reference across automatic resends. The renderer reports delivered or unconfirmed when resend tracking ends, and reports not-sent when a referenced send cannot start or fails. Tests, mocks, test routing, and documentation cover the status flow.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant TakServerManager
  participant useTakInboundChat
  participant sendTakTextOnMesh
  participant AutoResendController
  participant takDmStatus
  participant takHandlers
  TakServerManager->>useTakInboundChat: Emit direct-chat target with dmRef
  useTakInboundChat->>sendTakTextOnMesh: Send to companion destination
  sendTakTextOnMesh->>AutoResendController: Track outbound message
  AutoResendController->>takDmStatus: Emit acked or gave-up outcome
  takDmStatus->>takHandlers: Invoke tak:dmStatus with outcome
  takHandlers->>TakServerManager: Forward validated status
  TakServerManager->>TakServerManager: Send receipt or outcome note
Loading

Suggested reviewers: rinchen

Merge Risk: ⚪ Minimal · up to 8e56e

No actionable delivery-status issue remains from this review; the PR is ready for normal merge checks.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 72.50% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 80 functions across 52 files. (1 skipped:… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Title check Passed The title identifies the main change: TAK direct-message receipts. It is concise and clearly related to the changeset.
Full details: Docstring Coverage

Explanation

Docstring coverage is 72.50% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 80 functions across 52 files. (1 skipped: 1 unsupported.)

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@RobertMTalbot
RobertMTalbot marked this pull request as draft October 16, 2026 18:55
@RobertMTalbot
RobertMTalbot force-pushed the feat/tak-dm-receipts branch 2 times, most recently from 6f9c4de to 1f73153 Compare October 16, 2026 21:18
RobertMTalbot and others added 3 commits October 16, 2026 15:59
Each radio's "Messaging in ATAK" section gains a "Direct messages" switch.
With it on:

- Companions this radio can message (a MeshCore companion whose key it
  holds, any Meshtastic node) get a chat endpoint on their marker, so ATAK
  offers to message them. Repeaters, room servers and sensors do not.
- A message to a companion's contact goes out from this radio through the
  Chat send path as a direct message, prefixed [CALLSIGN], and shows in Chat.
- A DM heard from a companion goes to the ATAK device that last wrote to it,
  or to every connected device when none has; it is held (with channel
  messages, 50 max, 24 h) while the device it is for is away. Retried copies
  are relayed once. Command replies from repeaters and room servers are not
  relayed.
- A companion with no marker on the map gets a contact at 0, 0 for a day
  after the last message, under its marker's uid, so a later marker replaces
  it.
- A message ATAK cannot deliver (DMs off for the radio, or not a node this
  radio can reach) gets a note in its conversation saying why.
- DMs and chat endpoints are never sent to the remote TAK server.

New IPC: tak:pushDirectMessage and tak:setDirectMessaging. TakInboundChat
now carries a target (channel or direct) in place of channelKey.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EHFPfW7vZXpRdR3tBBTX3r
A direct message sent from ATAK now tells the device that wrote it how it
went, once mesh-client's automatic resends settle:

- Acknowledged by the companion: a b-t-f-d delivery receipt naming the
  device's message id, so ATAK marks it delivered.
- Still unacknowledged after the last resend (4 tries): a "Not confirmed"
  note in the conversation, quoting the message. For MeshCore it adds that
  a flood advert from each radio usually fixes a DM that keeps failing.
- Never sent (no radio could take it): a "Not sent to the mesh" note.

A note for a device that is away waits with the held messages; a receipt
is dropped. Main waits up to 30 minutes for an outcome.

The resend controller reports how each send's lineage ends (acked or
gave up), keyed by the first send's store id through re-keys and resends,
via subscribeAutoResendOutcomes. takDmStatus.ts watches DMs sent from TAK
and reports their outcome over the new tak:dmStatus IPC, keyed by a dmRef
main attaches to each inbound DM.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EHFPfW7vZXpRdR3tBBTX3r
RobertMTalbot and others added 2 commits October 16, 2026 17:20
The radio TAK uses per protocol is recorded in an effect, so for a moment
after a switch to another radio of the same protocol it can still name the
old one, whose transport may still be open. findTakSendTargets now drops a
direct target whose recorded radio is not the protocol's current identity.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EHFPfW7vZXpRdR3tBBTX3r
Failed direct sends reach the originating client: valid for this PR on its own, and handled in the next PR of the stack, Colorado-Mesh#1283 (delivery receipts). There, main attaches a dmRef to each direct message. When relayTakChatToMesh fails to send, or finds no radio that can send, it reports not-sent over the new tak:dmStatus IPC. TakServerManager.reportDmOutcome then writes "mesh-client: Not sent to the mesh: …" into that conversation, on the device that wrote, holding it if that device is away. The same channel carries "delivered" and "Not confirmed", so I've kept it in Colorado-Mesh#1283 rather than add a separate IPC here.
Stale protocol radio: valid, and fixed in this PR. findTakSendTargets now drops a direct target whose recorded radio isn't the protocol's current identity (getIdentityIdForProtocol, the resolver App uses). A test covers the switch window.
RobertMTalbot added a commit to RobertMTalbot/mesh-client that referenced this pull request Oct 16, 2026
Failed direct sends reach the originating client: valid for this PR on its own, and handled in the next PR of the stack, Colorado-Mesh#1283 (delivery receipts). There, main attaches a dmRef to each direct message. When relayTakChatToMesh fails to send, or finds no radio that can send, it reports not-sent over the new tak:dmStatus IPC. TakServerManager.reportDmOutcome then writes "mesh-client: Not sent to the mesh: …" into that conversation, on the device that wrote, holding it if that device is away. The same channel carries "delivered" and "Not confirmed", so I've kept it in Colorado-Mesh#1283 rather than add a separate IPC here.
Stale protocol radio: valid, and fixed in this PR. findTakSendTargets now drops a direct target whose recorded radio isn't the protocol's current identity (getIdentityIdForProtocol, the resolver App uses). A test covers the switch window.
@rinchen
rinchen marked this pull request as ready for review October 16, 2026 23:34
@rinchen

rinchen commented Oct 17, 2026

Copy link
Copy Markdown
Member

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor
⚠️ Action not completed

Review rate limited.


Your included review limit is currently reached under our Fair Usage Limits Policy. This review may still proceed through usage-based billing if eligible. Your next included review will be available in 12 minutes.

@rinchen

rinchen commented Oct 17, 2026

Copy link
Copy Markdown
Member

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.

@rinchen
rinchen merged commit c78d221 into Colorado-Mesh:main Oct 17, 2026
23 checks passed
RobertMTalbot added a commit to RobertMTalbot/mesh-client that referenced this pull request Oct 17, 2026
RobertMTalbot added a commit to RobertMTalbot/mesh-client that referenced this pull request Oct 17, 2026
rinchen added a commit that referenced this pull request Oct 17, 2026
Fifth and last of the TAK messaging PRs. **Builds on #1283** (which
builds on #1282 and #1280), so until those merge this PR also shows
their commits; review the top commit only.

## What it does

Reticulum joins MeshCore and Meshtastic for direct messages with ATAK.
It has its own **Reticulum messaging in ATAK** section in the TAK panel,
holding only a **Direct messages** switch (off by default), since
Reticulum has no channels to serve. With it on:

- **Peers with a position.** An LXMF peer whose delivery destination is
known under its own node id gets a chat endpoint on its `RN-` marker, so
ATAK can message it.
- A peer whose LXMF address belongs to another destination (a telephony
or bare identity hash that remaps) stays a plain marker, because its
replies would arrive from a different contact.
  - RMAP interfaces stay plain markers.
- **Peers with no position.** These appear at 0, 0 only once mesh-client
has a conversation with them: a DM sent or received, before or since TAK
was running. So ATAK's contact list doesn't fill with every peer heard.
- The renderer sends main up to 64 such peers, most recent first, over
the new `tak:setDirectPeers` IPC.
- Main announces their contacts along with the channel contacts, and
removes a contact when its peer drops off the list.
- **ATAK → Reticulum.** A message goes out as an LXMF DM through the
Chat send path, with Chat's automatic resends. Its delivery proof sets
the message to acked, which gives the device PR 4's receipt; with no
proof after the resends, the device gets the "Not confirmed" note.
- **Reticulum → ATAK.** `ingestReticulumLxmfPayload` hands each newly
stored inbound LXMF message on to ATAK. Status updates, our own sends
and reactions are left out. As with the other radios, a reply goes to
the device that last wrote, or to every device when none has.
- Nothing about direct messages goes to the remote TAK server.

## Changes

- **`shared`.** `TAK_DM_PROTOCOLS` gains `reticulum`, plus
`TakDirectPeer` and `TAK_DIRECT_PEERS_MAX`.
- **`main`.**
  - `parseTakDirectPeers` validates the peer list.
- `TakServerManager.setDirectPeers` stores conversation peers. They are
messageable, announced at 0, 0 while they have no marker, and added or
removed when the Reticulum switch changes.
  - New `tak:setDirectPeers` handler, limited to 60 a minute.
- **`renderer`.**
  - `canTakDirectMessage` handles Reticulum.
- `listTakConversationPeers` builds the peer list from the message
store, named from the node store.
- `useTakInboundChat` records the Reticulum radio and keeps main told
which peers have conversations. It only builds the list while TAK is up
and the switch is on.
  - The Reticulum ingest relays new DMs.
- `TakDirectMessagesSection` and its wiring in `TakServerPanel` and App;
strings, settings search and the README.

## Checks

- 2,539 tests pass across TAK, ingest, Reticulum send, auto-resend,
settings search and the send hook.
- ESLint and Prettier are clean on the changed files, and both
typechecks pass.
- `check:i18n`, `check:ipc-contract`, `check:console-log` and
`check:log-injection` pass.
- The main bundle grows 667,331 → 669,365 bytes (+2,034), about 18.8 KB
under the 672 KiB budget.

## Still to try on a device

- Start an LXMF conversation in mesh-client with a peer that has no
position, then carry it on from ATAK. The peer appears at 0, 0, the
message arrives, the reply lands on the device that wrote, and ATAK
marks it delivered.
- A peer with a position can be messaged from its marker.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01EHFPfW7vZXpRdR3tBBTX3r


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Reticulum LXMF conversation peers with known delivery addresses can be
contacted through TAK direct messaging. Configure Reticulum direct
messaging from TAK settings.
* Positionless Reticulum peers appear in TAK at coordinates 0, 0 after a
conversation is established.
* **Improvements**
* TAK provides delivery acknowledgements and notices for unconfirmed or
unsent direct messages, including resend failures. MeshCore failure
notices include a flood-advert hint.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Joey Stanford <nv0n@coloradomesh.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants