Skip to content

agent: use double ratchet from the first message in contact addresses, request rejection, RPC - #1831

Open
epoberezkin wants to merge 4 commits into
masterfrom
rpc
Open

agent: use double ratchet from the first message in contact addresses, request rejection, RPC#1831
epoberezkin wants to merge 4 commits into
masterfrom
rpc

Conversation

@epoberezkin

Copy link
Copy Markdown
Member

No description provided.

…ess (#1829)

* rfc: agent support for PRC pattern without duplex connection

* update rfc, plan

* corrections

Co-authored-by: Evgeny <evgeny@poberezkin.com>

* split rfcs, add plan

* update rfc

* update rfc and plan

* update plan

* add double ratchet keys to address

* agent schema

* fix version in test

* change ContactRequest encoding

* fix tests

* update plan

* tests, refactor

* fix compilation

* refactor

* refactor more

* fix

* add export

* fix

* refactor

* refactor

* update schema

* rename

* refactor

* refactor 2

* comment, type

* type

* rename

* refactor more

* move type

* refactor

* remove comment

* diff

* refactor

* comment

* puns

* get correct key

* rename

* add autoincrement

* refactor

* move

* move back

* simplify

* rename

* refactor

* tuple

* comment

* split decryption

* refactor

* rename

* refactor

* move

* remove createSndQueue

* remove comment

* remove JoinInvitationReq

* simplify

* rename

* fix

* type synonim

* getConnData

* simplify

* move

* encoding

* version

* async join with ratchet keys

* fix encoding

* rotate address ratchet keys

* rename, parameter order

* refactor, include ratchet keys into ConnectionRequestUri

* simplify

* remove records

* refactor

* rename, encoding

* clean up

* refactor

* compatible

* addrKeysE2EVersion

* support IKUsePQ for contact addresses

* comments, refactor condition

* typos

---------

Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
@epoberezkin
epoberezkin requested a review from spaced4ndy as a code owner July 20, 2026 09:17
epoberezkin and others added 2 commits July 25, 2026 11:35
* agent: request rejection

* implement RPC requests/responses

* simplify rpc, test, async

* renames

* clean up

* fix

* fix query

* set flag at creation

* move

* command type, schema

* rename

* refactor

* typo

Co-authored-by: simplex-chat-agent[bot] <287173099+simplex-chat-agent[bot]@users.noreply.github.com>

* allow overriding the service request timeout per request

* fixes

* use SSENT event for service replies

* AgentServiceError encoding

* sign service requests

* add bad signature error

* update the plan

* clean up

* refactor

* empty line

Co-authored-by: simplex-chat-agent[bot] <287173099+simplex-chat-agent[bot]@users.noreply.github.com>

* add indices

---------

Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
Co-authored-by: simplex-chat-agent[bot] <287173099+simplex-chat-agent[bot]@users.noreply.github.com>

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

Summary

Two features across the agent: (1) contact addresses can publish double-ratchet keys so the first message is DR-encrypted end-to-end (AddressRatchetKeys, address_ratchet_keys table, AgentContactRequest envelope), and (2) service RPC over contact addresses — sendServiceRequest/Async send a signed-or-unsigned request and block for a single response, with rejectContact/sendServiceReply on the other side. Supporting changes: JoinRequest sum type replacing the JOIN argument list, ContactRequest (URI vs stored DRInvitation), BinaryConnectionRequestUri splitting the binary (link-data) encoding from the URI encoding, StoredPrivateKey for persisting an Ed25519 signing key, EncList, and a schema migration.

I read Protocol.hs, Agent.hs (join/accept/reject/service flows, the JOIN command worker, message-delivery result handling, smpConfirmation/smpContactRequest), Store.hs, AgentStore.hs, the migrations and reference schemas, and the Crypto/Encoding/Client/Ratchet changes, plus the connection-request tests and the RFCs/plans. I did not build.

I found no blocking correctness, concurrency, or security defect. The wire tags are collision-free (AgentMessageType A/P/J, envelope 'A' vs inner-message 'A' live in separate layers), the JoinRequest/NEW/UserContactData encodings round-trip and stay backward-compatible (trailing-optional fields, takeByteString tail), the signature binding to rcAD is sound, and address ratchet keys are carried in the signed user-data (not the fixed link data), so rotation keeps the link ID stable while remaining authenticated by the root key.

Non-blocking observations

  • smpContactRequest records the dedup hash before the request is fully processed (unlessM duplicateRequest $ … getAddressRatchetKeys …, Agent.hs ~3981). duplicateRequest commits addProcessedRatchetKeyHash in its own transaction, so if a later step throws (unknown ratchetKeyId, decrypt error, or a storeInvitation failure) the sender's retry is treated as a duplicate and silently dropped — no REQ/SREQ is ever delivered. This mirrors the existing newRatchetKey/ratchetExists pattern, so it's consistent, but worth confirming it's the intended trade-off for the service-request path where a dropped request is a lost RPC.

  • sendServiceRequestAsync blocks the caller. Both sync and async variants funnel through serviceRequest_, which waits on the response TMVar (or the registerDelay timeout). "Async" only means the send is enqueued as a retriable command, not that the call returns before the response. After a process restart the persisted JRServiceReq JOIN finds no in-memory entry in serviceRequests and becomes a no-op (Nothing -> pure ()), so the request is not resent — the connection is later reaped by getExpiredServiceConns. That's defensible (the caller is gone), but the naming may mislead; a note at sendServiceRequestAsync' would help.

  • Repeated confirmation delivery of a service response (ack lost) re-enters dispatchServiceReply; once serviceRequest_ has removed the map entry this emits a spurious ERR (A_SERVICE ASENoPendingRequest). Cosmetic.

  • serviceRequest_ inserts into the serviceRequests map before the tryError block and cleans up after it (not via bracket). An async exception between insert and cleanup leaks the map entry. Low risk and consistent with surrounding code.

  • Minor: the Postgres migration sets connections.created_at default '1970-01-01 00:00:00' (no zone) while the dumped schema shows +01; harmless since the value isn't read for pre-existing rows. The M20260712 SQLite migration's stray , placement in the reference schema is cosmetic.

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.

1 participant