feat: handle didcomm msg keylist-update-response from mediator - #566
Conversation
| ).makeMessage(); | ||
|
|
||
| await ctx.run(new Send({ message: keyListUpdateMessage })); | ||
| // [ ] handle response https://github.com/hyperledger-identus/sdk-ts/issues/391 |
There was a problem hiding this comment.
I would try doing the following.
If updateMediator is activated, we will send the KeyListUpdateMessage and then wait for the reply.
If we detect an error in the task, the createPeerDID function should fail.
The way the task has been wired for this purpose is strange.
I propose to remove
.register(ProtocolIds.MediationKeysUpdateResponse, MediationKeysUpdateResponse)We don't want this task to trigger automatically when this message arrives, instead I will propose to run this task after
await ctx.run(new Send({ message: keyListUpdateMessage }));The task will not resolve until the response for that keyListUpdate is received, if the response is successful, then we resolve, or else, we throw an exception with the correct error message
Proper testing needs to be implemented as part of this task....
| @@ -0,0 +1,96 @@ | |||
| import { vi, describe, expect, test, beforeEach, afterEach } from 'vitest'; | |||
There was a problem hiding this comment.
This is not proving much, after the changes we requested i think additional tests should be implemented, specially inside the createPeerDID function
|
Yeah this would be a really nice to have. |
4fadbad to
54bb564
Compare
|
@elribonazo quick architectural question before pushing the revisions — Send already returns the response message. Leaning toward using its return value (then running MediationKeysUpdateResponse explicitly) to keep the change minimal. Happy to switch to Mercury.sendMessageParseMessage if you'd prefer the lower-level path. |
|
I'm not sure how deep the test needs. I believe @elribonazo is concerned about the life cycle. Like in createPeerDID there is logic to update the DIDs register in the mediator. The first DID created register in the mediator (as a new account). But all the following DIDs that are created, calls that updatekey to add the new DID as a alias of the first one. |
6a82693 to
e4b7b8d
Compare
a920902 to
14456bf
Compare
|
The old unit test is replaced by Branch is rebased on current main. 784/784 SDK tests pass locally. |
| await ctx.run(new Send({ message: keyListUpdateMessage })); | ||
| // [ ] handle response https://github.com/hyperledger-identus/sdk-ts/issues/391 | ||
| let timeoutHandle: ReturnType<typeof setTimeout> | undefined; | ||
| const timeoutPromise = new Promise<never>((_, reject) => { |
There was a problem hiding this comment.
This doesn't look correct, have u manually tested to debug ?¿
Sending a message to a mediator with the updateKeyList message is one task, good!
Next thing is waiting until we receive another message with the mediation response, currently, you are expecting the mediator to reply with that message as response. But the mediator can respond asynchronously, so this code is not fully correct.
I encourage you to test things manually before submitting a PR like this one, testing locally will probably help you understand what i mean
|
I had it wrong — assumed Send returns the response directly, didn't account for the mediator replying in a separate message later. Got the local identus-docker mediator running, and mediator2.trust0.id is also responding. Going to send a real keylist-update through it and watch what comes back. Then I'll change updateKeyListWithDID to wait for the inbound response by thid, with the 60s timeout on that wait instead of on Send. I'll push the new version after testing it against the real mediator. |
Sounds like a plan, you can use my mediator feel free, ping me if u have any issues.
So basically the thread if what connects everything, same with credential issuance for example, or presentation flows |
14456bf to
9c44fbf
Compare
|
Found the root cause — it wasn't about strict vs lenient timeout, it was a missing header. The mediator returns For reference:
Verified against Changes in the redesign:
|
9c44fbf to
c4e07ee
Compare
|
|
@TheSeydiCharyyev will it depend on what the mediator has configured? Can we try the following approach? A) contains the response and we are ready to validate https://didcomm.org/coordinate-mediation/2.0/ Nothing says there if it should be response of the request message or another message linked to the update key list request. I would encourage us to implement both, if we get it in response, we continue, but if we don't, we must have a way to know when the new diddcomm message appears and validate it by its thid, find the request and complete. All this should probably happen inside the agent |
|
Understand A and B ideas, but I am not sure how to do it in the inside of agent. You meant a method on Agent or separate task? |
|
You can do it inside the current task. What I would try to do:
|
… observable The previous flow assumed Send.run would resolve to the mediator's keylist-update-response. In practice it always resolved to undefined: DIDCommConnection.send returned the registered handler's result rather than the parsed message, and the SDK never asked the mediator for an inline reply. Per coordinate-mediation 2.0 the mediator only answers synchronously when the request carries `return_route: "all"`, and Mercury auto-attaches that header for the piuris in ReturnRouteProtocols (packages/wasm/didcomm/src/Wrapper.ts) -- but keylist-update was never on that list. So every keylist-update went out without return_route, the mediator dispatched the response asynchronously, and the client never observed it. Add keylist-update to ReturnRouteProtocols, make DIDCommConnection.send return the inline response, and rewrite updateKeyListWithDID to send the message, race the call against a 60 s timeout, assert the response is a Message with the expected piuri and a thid matching the outgoing id, and validate the body via MediationKeysUpdateResponse (throws on any non-success / non-no_change result). Tests cover success, no_change, client_error, server_error, malformed body, timeout, wrong piuri, wrong thid and a missing response. A guard on ReturnRouteProtocols prevents the same kind of regression that originally introduced this bug (PR hyperledger-identus#85). Closes hyperledger-identus#391 Signed-off-by: Seydi Charyyev <seydi.charyev@gmail.com>
Addresses review feedback on hyperledger-identus#566: the mediator may answer a keylist-update either inline in the same HTTP reply or asynchronously as a separate inbound message, and the SDK should handle both. updateKeyListWithDID now registers a MESSAGE listener before sending. When Send resolves with a matching inline response that response is used directly (path A); otherwise the method waits for the response to arrive on the MESSAGE event (path B), racing against the existing 60 s timeout. The response is matched by piuri and by a thread id equal to either the outgoing message id or the registered peer DID. Tests cover the async success path, a response threaded on the peer DID, an async failure result, and a non-matching thid timing out. Signed-off-by: Seydi Charyyev <seydi.charyev@gmail.com>
c4e07ee to
2848b74
Compare
|
Pushed the dual-path. About "the same thid u sent, or your peerDID" — I made it match either one: the response is accepted if its Path (B) is covered by unit tests — async success, response threaded on the peerDID, failure result, and non-matching thid timeout. I could not test it against a real mediator though: mediator2 answers inline once |
Addresses review feedback on hyperledger-identus#566: the mediator may answer a keylist-update either inline in the same HTTP reply or asynchronously as a separate inbound message, and the SDK should handle both. updateKeyListWithDID now registers a MESSAGE listener before sending. When Send resolves with a matching inline response that response is used directly (path A); otherwise the method waits for the response to arrive on the MESSAGE event (path B), racing against the existing 60 s timeout. The response is matched by piuri and by a thread id equal to either the outgoing message id or the registered peer DID. Tests cover the async success path, a response threaded on the peer DID, an async failure result, and a non-matching thid timing out. Signed-off-by: Seydi Charyyev <seydi.charyev@gmail.com>
2848b74 to
685e862
Compare
|
|
Went through the full conversation here the journey from auto-registered handler → explicit invocation → discovering the missing One question on the dual-path: for path B (async fallback via MESSAGE listener), is there a risk of the listener leaking if the timeout fires but the message arrives right after cleanup? Or does the |
|
On the listener leak — But while rechecking I found a different leak in the @elribonazo could you take a look at my proposal — store the |
|
Good catch on the |



Description
Fixes #391 — the SDK sent
MediationKeysUpdateListto the mediator but never processed thekeylist-update-responsethat the mediator sends back (per the coordinate-mediation 2.0 spec). Per-recipient failures reported by the mediator went unnoticed by the SDK.Changes
ProtocolIds.MediationKeysUpdateResponse+ user-facingProtocolType.DidcommMediationKeysUpdateResponseMediationKeysUpdateResponseunderplugins/internal/didcomm/connection/— iteratesbody.updatedand logs a warning for each entry whereresultis notsuccessorno_change, includingrecipient_did,action, andresultdidcomm/plugin.tsso the existingDIDCommConnection.receive()→RunProtocoldispatch picks it up[ ] handle responseTODO comment inCreatePeerDID.ts— caller signature is unchanged (Promise<void>)Verification
case m: KeylistUpdatebranch returnsm.makeKeylistResponse(updateResponse).toPlaintextMessagewith(recipient_did, action, result)tuples whereresultis one ofsuccess | no_change | server_errortests/agent/didcomm/MediationKeysUpdateResponse.test.ts(all-success,server_error,client_error, mixed results, missingupdatedfield)Alternatives Considered
server_error/client_errorto propagate failure to the caller — would require changingDIDCommConnection.receive(), which currently swallows handler exceptions viacatch { return undefined }. That affects every other handler (MediateGrant,PickupDelivery, etc.) and is out of scope for this bug fix — worth a follow-up discussion.updateKeyListWithDIDdirectly, bypassing the dispatch — would break the handler-registration pattern used consistently across the didcomm plugin.Chose to mirror
MediateGrant/MediateDeny/ProblemReport: handlers perform side effects (log / store / emit) and do not propagate errors up the stack. Same ergonomics for reviewers, zero behavioural change to existing flows.Checklist