Skip to content

agent: contact request rejection and service requests#1833

Open
epoberezkin wants to merge 14 commits into
rpcfrom
ep/request-rejection
Open

agent: contact request rejection and service requests#1833
epoberezkin wants to merge 14 commits into
rpcfrom
ep/request-rejection

Conversation

@epoberezkin

Copy link
Copy Markdown
Member

No description provided.

@epoberezkin
epoberezkin requested a review from spaced4ndy as a code owner July 21, 2026 18:32
@epoberezkin
epoberezkin changed the base branch from master to rpc July 21, 2026 18:33

@simplex-chat-agent simplex-chat-agent Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adds two agent capabilities on top of the DR-enabled contact address feature: (1) contact-request rejection with an optional reason delivered over the double ratchet (RJCT), and (2) a service RPC round-trip — sendServiceRequest blocks for a single AgentServiceResponse/AgentRejection reply, with sync and async variants on both sides.

I read every touched file in full, followed the message flow through Agent.hs (serviceRequest_, prepareReply, sendReply{Sync,Async}, joinConnSrv', smpConfirmation, smpContactRequest, ICReplyDel, cleanupManager), the store/schema/migration changes, and the protocol encodings. The design is coherent and the tests exercise sync, async, rejection, and cross-outage resilience. Encodings don't collide (AgentMessage A/P/J, AgentMessageType A/P/J vs existing tags), smpConfirmation is always followed by ack, the in-memory serviceRequests TMVar map is keyed by a freshly-generated connId so concurrent requests can't collide, and the orElse (registerDelay) timeout logic is correct. Leftover requester connections and unanswered received requests are cleaned up by getExpiredServiceConns/deleteExpiredServiceRequests. No blocking issues found.

Non-blocking observations for the author:

  1. Request vs response timeout asymmetry. The requester's end-to-end wait is serviceRequestTimeout (30s) while the responder may still reply within serviceResponseTimeout (180s) — prepareReply accepts requests up to 180s old and AM_SRV_RESP delivery is retried for 180s. A reply produced between 30s and 180s arrives after the requester has already timed out and torn down the reply connection, yielding ERR (A_SERVICE ASENoPendingRequest) and a wasted round-trip. If the 30s/180s split is intentional (interactive requester patience vs. service-side lifetime), fine; otherwise the requester wait should be >= the response window. Confirm this is deliberate.

  2. Non-DR contact address silently degrades to a contact invitation. sendServiceRequest'joinConnSrv' CRContactUri: when the target address publishes no ratchet keys (ratchet_ == Nothing), the mkInner/AgentServiceRequest payload is ignored and a plain AgentInvitation is sent. The recipient then sees a normal REQ (not SREQ) and the requester blocks until the 30s timeout. Service RPC only makes sense against a DR address, so consider failing fast with a clear error in that branch rather than emitting a contact invitation and timing out.

  3. Minor duplication. The ICReplyDel handler body (secure snd queue → submitPendingMsgdeleteConnectionAsync' True) is identical to sendReplySync with nm = NRMBackground; could call sendReplySync directly.

  4. Migration rename. M20260712_address_drM20260712_address_dr_rpc (name and column additions) is safe only because the address_dr migration is unreleased; renaming an applied migration would break the migrations table. Assuming this branch supersedes the still-unreleased address-dr work, this is fine.

@epoberezkin epoberezkin changed the title core: contact request rejection and service requests agent: contact request rejection and service requests Jul 22, 2026

@simplex-chat-agent simplex-chat-agent Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the full branch (d5f48de3b56b...ccc1d7f8eca5): all 18 files, following the new agent APIs, the store/migration changes, and the SMP transmission handling.

The change adds two capabilities over contact-address double-ratchet connections:

  • Contact request rejection with a reason (rejectContact/rejectContactAsync), surfaced to the requester as the new RJCT event.
  • Service (RPC) requests (sendServiceRequest/Async), with responses (sendServiceReply/Async) and rejections (rejectServiceRequest/Async), surfaced to the service as the new SREQ event.

The wire changes are consistent: AgentInvitationDRAgentContactRequest (envelope tag 'J''A'), three new AgentMessage variants (A/P/J) with matching AgentMessageTypes, and the new AEvent/AEventTag constructors are threaded through aEventTag. Roundtrip encoding is covered by the new ConnectionRequestTests case, and the async/resilient flows have functional tests. The serviceRequests TMVar map, the timeout handling (serviceRequestTimeout/serviceResponseTimeout), the ICReplyDel deferred-reply command, and the cleanup path (getExpiredServiceConns/deleteExpiredServiceRequests) all hang together correctly, including the orphaned-connection GC after an agent restart.

Concurrency and lifecycle look sound: the TMVar is inserted before the send is enqueued, the blocking caller uses registerDelay+orElse for timeout, and stale/cancelled requests are handled by the Nothing branch in the async JOIN JRServiceReq worker. The serviceResponse connection flag and service_request invitation flag are persisted with sensible migration defaults (created_at defaulting to epoch for pre-existing rows, which never match the service_* GC predicates).

Only one nit found — a typo in a new record field name (conntactReq), suggested inline.

Two non-blocking observations:

  • Migration rename. M20260712_address_dr is renamed to M20260712_address_dr_rpc (folding in the new columns) rather than adding a new migration. This is correct only if 20260712_address_dr has never shipped to any client DB; if it has, deployed installs recorded the old name and would re-run the CREATE TABLE address_ratchet_keys. Given both migrations share the same date and author and this is a stacked branch, it appears to be in-development — worth a quick confirm.
  • Payload size. Service request/response payloads are encrypted into the fixed e2eEncConnInfoLength envelope (~11KB with PQ, ~14.8KB otherwise). Payloads beyond that fail encryption and surface as an error to the caller. Fine as a design bound for RPC, just noting it.

Comment thread src/Simplex/Messaging/Agent/Protocol.hs
epoberezkin and others added 2 commits July 22, 2026 11:40
Co-authored-by: simplex-chat-agent[bot] <287173099+simplex-chat-agent[bot]@users.noreply.github.com>
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