Skip to content

fix(core): raise vti-didcomm-js floor to ^0.10.0 for long-framed TSP replies - #254

Merged
stormer78 merged 1 commit into
mainfrom
fix/didcomm-js-0.10-long-tsp-frames
Sep 17, 2026
Merged

stormer78 merged 1 commit into
mainfrom
fix/didcomm-js-0.10-long-tsp-frames

Conversation

@stormer78

Copy link
Copy Markdown
Contributor

A keys/list with no context filter timed out in the management console (tsp: timed out awaiting reply frame), while per-context queries worked. The VTA logged the reply sent successfully — count=50 total=94, 200 OK — but the wallet saw no frame.

Root cause

Through @openvtc/vti-didcomm-js 0.8.x, the inbound demux (_onFrame in mediator-transport.js) classified TSP by text.startsWith("-E") — the qb64 spelling of the short -E count code. Spec Rev 3, which this wallet now speaks, widened that count to cover the ciphertext, so any TSP message past ~12 KB is framed with the six-byte long count code and its text starts --E. "--E…".startsWith("-E") is false, so a large reply was handed to the DIDComm unpacker, thrown out as a poison frame, and — because the throw returns before the ack — never acked, so the mediator redelivered it on every reconnect while the waiter timed out.

keys/list with no context filter returns a full page of key records, crosses the threshold, and was silently lost. Per-context replies (small, short-framed) were unaffected. The empty error (no "last inbound frame declined" suffix) confirmed the frame never reached the TSP layer at all.

Fix

Bump @openvtc/pnm-cores @openvtc/vti-didcomm-js dependency ^0.8.0^0.10.0. 0.10.0 (OpenVTC/vti-didcomm-js#24) moves the classifier into isTspFrameText, matching both -E and --E. No plugin source change is needed — the bug was entirely in the library.

Records the ^0.10.0 floor as a correctness constraint in CLAUDE.md, beside the existing ^0.6.2 persist-before-ack floor.

Test fix from the same release

tests/did.egress-socket.mjss non-vacuity control failed — not from the demux fix, but from an unrelated hardening in 0.9+: the resolver now runs its own net-guard on did:webvh resolution by default, so a bare vtiResolve(did, {}) is refused before it dials. The control relied on the resolver dialling a loopback listener to prove the fixture is reachable. It now relaxes the dependencys policy (allowInsecure + allowPrivate) to keep proving that; the two real guard tests are unchanged and still refuse first with this repos assertResolvableWebvhHost. Verified empirically: no-policy → BlockedEndpointError, 0 dials; permissive → 1 dial.

Verification

…replies

Through 0.8.x the inbound demux in mediator-transport.js classified TSP by
`text.startsWith("-E")` — the qb64 spelling of the short `-E` count code.
Spec Rev 3, which this wallet now speaks, widened that count to cover the
ciphertext, so any TSP message past ~12 KB is framed with the six-byte long
count code and its text starts `--E`. Such a reply was handed to the DIDComm
unpacker, thrown out as a poison frame, and — because the throw returns before
the ack — never acked, so the mediator redelivered it every reconnect while the
waiter timed out.

In practice: `keys/list` with no context filter returns a full page of key
records, crosses the threshold, and was silently lost. The VTA logged the reply
sent (count=50 total=94, 200 OK); the wallet saw no frame and reported
`tsp: timed out awaiting reply frame`. Per-context queries (small, short-framed)
were unaffected. vti-didcomm-js 0.10.0 moves the test into `isTspFrameText`,
matching both `-E` and `--E`.

The same release also runs the resolver's own net-guard on did:webvh resolution
by default (0.9+), so a bare `vtiResolve(did, {})` is now refused before it
dials. That broke the non-vacuity control in tests/did.egress-socket.mjs, which
relied on the resolver dialling a loopback listener; the control now relaxes the
dependency's policy (allowInsecure + allowPrivate) to keep proving the fixture
is reachable. The production path — assertResolvableWebvhHost first, then
`vtiResolve(did, {})` — is unchanged, and the two real guard tests still refuse
first with this repo's message.

Records the ^0.10.0 floor as a correctness constraint in CLAUDE.md, beside the
existing ^0.6.2 persist-before-ack floor.

Signed-off-by: Glenn Gore <glenn.g@affinidi.com>
@stormer78
stormer78 merged commit 8b14776 into main Sep 17, 2026
4 checks passed
@stormer78
stormer78 deleted the fix/didcomm-js-0.10-long-tsp-frames branch September 17, 2026 11:50
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