WIP: Verify anotherim-js/libtacky bugs from the GUI VM runs (rc9 / v1.2.3, 20 found) #9

Open
opened 2026-11-08 19:27:05 +00:00 by asxpi · 0 comments
Member

Tested

version
under test anotherim-js (aim) 0.8.0-rc9, develop 142ee094, XML_LOG AppImage; anotherim-web = the browser build of the same commit in Chromium 156
backend libtacky v1.2.3 (c95b3d9d)
peers Gajim 2.6.0, Dino 0.5.1, Kaidan 0.16.0, anotherim-web (each in its own NixOS VM, vms/)
servers Prosody 13.0.7, ejabberd 26.09, anotherim-server master@b9e675dd

Evidence re-recorded 2026-11-09 with provenance (#19): every row links an EVIDENCE.md. Batch 0 (#20): 16 runs (8 fail, 8 pass). Batch 1 (#21): 48 runs (25 fail: 18 libtacky, 7 peer; 23 pass): file sharing, the S14 and C38 rechecks, own-device sync on ejabberd. Batch 2 (#22): 75 runs (43 fail: 41 libtacky/anotherim-js, 2 peer; 31 pass; 1 na): XEP-0045 admin, 0249, 0272/0482, 0353, 0402 across devices, 0425, 0486, 0513, and call/file/own-device-sync rechecks on all three servers; 18 fails are UI gaps ("Not implemented"). All in runs/2026110[90]-*-aim-vs-*. Batch 3 (#23): 84 runs (70 pass, 14 fail), message extras and own-account sync on all three servers; its two new findings are UI gaps ("Not implemented in aim" below). Batch 4 (#26): 94 runs (75 pass, 13 fail, 6 na), profile, presence, roster, blocking on all three servers (runs/20261110-<ID>-*, IDs 113843–160046): new C47, C48, M30, M31; #1 C12 reproduced; the Gajim empty-nick note withdrawn.

2026-11-08: 89 runs with Gajim, Dino, Kaidan (77 pass, 12 fail); 2026-11-09: 39 runs against anotherim-web (37 pass, 2 fail, #11). Every run is agent-judged (verified_by = "ai", confidence = "low"). Numbering continues #1. Upstream status not checked yet.

How to check a bug

  1. Open the run's EVIDENCE.md: verdict, what proves it (file:line with the quoted stanza), expected / seen / spec, the deciding screenshots and the control.
  2. Look at the named deciding screenshot (shot-*.png; aim = anotherim-js).
  3. Check a quote or two at its file:line: client.log = aim's XML console (JSON lines), client-tacky.log aim's own log file (libwebrtc errors, withheld stanzas), client-debug.log its debug log, peer-<client>.log the other client, server.log the server, steps.log every click, check and measurement, rtp.log / web-rtp.log / notify-<vm>.log the NIC, web getStats and Notify captures. Under OMEMO 2 nothing inside the envelope is in any log: judge that from the screenshots and steps.log.
  4. Compare with the ✅ control run (same scenario, other server or peer).
  5. If in doubt, re-run with the explore command at the top of the run's EVIDENCE.md, from the interop repo root, with LIBTACKY=v1.2.3, APPIMAGE= the rc9 XML_LOG AppImage and, for anotherim-web, WEB= (vms/tools/env.zsh). explore records nothing; output in $INTEROP_VM_WORK/out-NAME.
  6. Tick verified by a human under the bug.

Harness and re-run: each EVIDENCE.md links the driver at the run's harness_commit (the same as its scenario.py) and gives the re-run command, so the tables leave both out. † (2026-11-08 rows, S15 only now) = script of the run not kept; the re-run is under the table.

Fixed upstream column: libtacky v1.2.4–v1.2.7 (2026-11-09/10) name these IDs; anotherim-js develop is on v1.2.7 since 86e54dac (0.8.0). The runs used rc9 / v1.2.3, so every fix awaits a recheck on 0.8.0. Fixed items mentioned elsewhere in this issue: "Also fixed", right under the bug table.

# Bug Severity Status Fixed upstream Servers seen Evidence Human-checked
S14 OMEMO 2: aim's retraction goes out in the clear; Kaidan shows the fallback security/privacy open, reproduced batch 0 + 1 1.2.4 95e7007d (bare <retract id/> kept beside by default, retract_hint) all three batch 0, 1 ☐
S15 OMEMO: reactions, receipts, markers in the clear; file metadata whenever a 0.3 envelope goes out security/privacy open; file part re-recorded batch 1 no: sealed only once servers advertise urn:tacky:push:sealed-signals:0 (none yet) all three (client-side) 2026-11-08 †, batch 1 ☐
C35 MUC: no instant-room configuration after creating a room correctness open, reproduced batch 2, batch 3 also for a room anotherim-web made (Prosody unlocks after 5 min, deletes the room when empty) – prosody (ejabberd, anotherim hide it) batch 0, 2 ☐
C36 Calls: a session-accept without <rtcp-mux/> is rejected (Kaidan) correctness open, reproduced batch 2 on all three 1.2.4 ebd5f227, 4b5e279f, 7d76eab7 all three batch 0, 2 ☐
C38 OMEMO 2 with Kaidan: "can't be read" count grows correctness not reproduced by restarts; rose around each XEP-0424 run, unexplained – all three batch 0, 1 ☐
C39 Calls: aim's outgoing video never shown – withdrawn (harness, #16) – – batch 0 –
C40 XEP-0448: aes-256-cbc-pkcs7 shares (Kaidan's) not decrypted; URL-named card, Download keeps the ciphertext correctness open (Kaidan's IV off-spec too) 1.2.5 c5f1e50e all three batch 1 ☐
C41 XEP-0448: aim's sent picture has no esfs source (aesgcm:// + SIMS only) correctness open 1.2.5 9f6ac320: the stored record was a clear rebuild, not the wire; recheck which one our evidence shows all three batch 1 ☐
C42 urn:tacky:omemo-trust:0: a contact key's change is published only for Undecided → Verified correctness refined batch 2 1.2.5 8a83649a ejabberd, prosody (anotherim passes Undecided → Verified) batch 1, 2 ☐
C43 XEP-0482: aim's <accept/>/<reject/>/<left/> withheld in an OMEMO room when a member has no OMEMO device correctness new, batch 2, batch 3 prosody too partly, 1.2.6 ad62fa11: answers to a clear invite go clear; to a sealed one still withheld all three batch 2, 3 ☐
C44 Muji group call: anotherim-web hears nothing from aim (opus arrives, level 0) correctness new, batch 2, side not traced; batch 3 prosody too – all three batch 2, 3 ☐
C45 MUC: membership and ban sent with the occupant's full JID; Prosody refuses them silently correctness new, batch 2 (was a candidate) 1.2.6 606eee7f all three (Prosody refuses, the others apply) batch 2 ☐
C46 Mediated room invite: a second 1:1-looking chat of the room's JID; on Prosody no invitation card correctness new, batch 2 (was a candidate) 1.2.6 3569c3fa, bcad614e, 3bb27017, 8ff28739 (also: ejabberd's room password in the invite body hidden) all three batch 2 ☐
C47 XEP-0054: aim writes its profile only as vCard4 (XEP-0292); Kaidan (vcard-temp only) reads an empty card correctness new, batch 4 – ejabberd, anotherim (Prosody maps vCard4 to vcard-temp and hides it) batch 4 ☐
C48 XEP-0191: the OMEMO device-list fetch that failed while blocked is never retried after the unblock; the next message fails "no devices" correctness new, batch 4 (seen as web, same libtacky) – prosody (ejabberd, anotherim answer the blocked fetch) batch 4 ☐
M26 No desktop notification while aim's window has focus minor open (may be intended) – all three batch 0 ☐
M27 "View XML" shows OMEMO 2 messages with the 0.3 EME namespace minor open, cause in libtacky 1.2.4 94769ce0 anotherim, prosody (client-side) batch 0, 1 ☐
M28 anotherim-web shows no desktop notification minor open (harness Chromium only) – all three batch 0 ☐
M29 XEP-0425: a moderated message shows as "Message retracted", without moderator or reason minor new, batch 2 (was a candidate) library side, 1.2.6 67894a54 (moderated_by, retract_reason); anotherim-js must show them ejabberd, anotherim (prosody: na) batch 2 ☐
M30 XEP-0084: aim keeps a contact's avatar after the contact deletes its metadata node minor new, batch 4 – prosody (ejabberd, anotherim send aim no <delete/>) batch 4 ☐
M31 XEP-0172: after an empty <nick/> anotherim-web's Contact info keeps the old nick minor (anotherim-js UI) new, batch 4 – all three batch 4 ☐
P1 Kaidan: "Checksum does not match" on aim's OMEMO 2 upload peer (Kaidan) proven Kaidan's, report upstream – all three batch 0, 1 ☐
P2 Prosody test server hands out no STUN/TURN server config fixed by #13 – prosody batch 0 ☐
P3 Dino intermittently sends no audio in calls peer (Dino) reproduced batch 2, first time on prosody (2 of 3 runs), report upstream – ejabberd, prosody batch 0, 2 ☐
P4 Dino 0.5.1 rejects aim's JET cipher aes-128-gcm-nopadding:0 peer (Dino; XEP-0396 contradicts itself) recorded batch 1 1.2.5 04e6935d (worked around: Dino's cipher spelling sent, both read) all three batch 1 ☐
P5 Dino 0.5.1 rejects aim's IBB transport-replace after losing the tie-break peer (Dino) recorded batch 1 – ejabberd only (needs P6) batch 1 ☐
P6 ejabberd test server: proxy65 announces 127.0.0.2 server config recorded batch 1, not fixed – ejabberd batch 1 ☐

Also fixed: items mentioned elsewhere in this issue

Item Section Fixed in Commit Status
Hang-up after answering Dino's XEP-0353 call: no session-terminate reaches Dino (230335) Unexplained / candidates libtacky 1.2.7 781b33ce: the terminate is written before the media is closed recheck pending
anotherim-js#95: destroyed-room view not shown in rc9 (225948) #1 rechecks (C34) libtacky 1.2.7 c9421614: cause in libtacky, the room's state stayed joined after <destroy/> recheck pending
anotherim-web answers aim's checksum session-info with unknown-session after a Jingle file transfer (180200) Server issues and peer notes libtacky 1.2.5 39d2ff82: no <checksum/> after the last byte (it raced the receiver's terminate) recheck pending
Dino shows a download button instead of aim's picture, cid: thumbnail unsupported (184517) Server issues and peer notes libtacky 1.2.5 c9dfdb6f: the share also carries the thumbnail as a data: URI (≤16 KiB), sealed when encrypted recheck pending (Dino still fetches the file only on click)
C34 (#1): a destroyed room recreated empty after a restart #1 rechecks libtacky 1.2.2 1aed459c confirmed batch 2, all three servers
C30 (#1): aim's decline doesn't reach the inviter #1 rechecks before rc9 (#1) – confirmed batch 2
P2: Prosody test server hands out no STUN/TURN Server issues and peer notes interop #13 (coturn + mod_turn_external) – confirmed batch 2 (relay candidates in every prosody call run)

Run links: https://dev.another.im/another-im/interop/src/branch/main/runs/<dir>/. A bare run ID (Also lines, lists) is runs/20261109-<ID>-* (batch 2 IDs from 000000: 20261110-), each with an EVIDENCE.md; 2026-11-08 runs are runs/20261108-<ID>-* (no EVIDENCE.md). Harness and runs came in with #10, #11, #14, #16, #17, #20, #21, #22. #1 rechecks (C34, C30), the features anotherim-js lacks, and untraced observations are under the peer sections.

Security / privacy

S14. OMEMO 2: aim's retraction goes out in the clear; Kaidan shows the fallback

  • verified by a human
  • Spec: XEP-0420 §9 (elements other than server-processed ones MUST be inside the <envelope/>); XEP-0424 §4.
  • Expected: in an OMEMO 2 chat aim sends <retract/> (and its fallback body) inside the SCE <content/>.
  • Seen: aim sends <retract id=…/> + "This message was retracted." + <fallback/> outside the encryption in an OMEMO 2 chat, on all three servers (batch 1; on ejabberd the retracted line went out as OMEMO 2 + 0.3). Kaidan keeps the original and shows the fallback as a new message marked Unencrypted. anotherim-web and Gajim (OMEMO 0.3, where the retraction is in the clear too) apply it.
    • Kaidan 0.16 has no XEP-0424 (from 0.17), so showing the fallback is expected of it; the bug is aim's: the retraction leaves the encryption.
  • What to look for: Kaidan's shot-3: "x424 … mango" still there, below it "This message was retracted." Unencrypted; steps.log INFO aim sent: <retract/> in clear True, encrypted False.
  • Fix: send body-less signals (retract, reactions, receipts, displayed markers) inside the envelope in OMEMO 2 chats, as S2 did for chat states and markable.
  • Upstream: not checked. Follow-up to S2 (#1), which left body-less signals in the clear on purpose.
result server peer what it shows run deciding screenshot
❌ ejabberd Kaidan OMEMO 2 chat, clear retract; Kaidan keeps "mango" and adds "This message was retracted." Unencrypted 151803 shot-3
❌ prosody Kaidan the same 154924 shot-3
❌ anotherim Kaidan the same 173523 shot-3
✅ anotherim Gajim OMEMO 0.3 chat, the same clear retract; Gajim applies it, and aim applies Gajim's 131816 shot-3

Also batch 0 131521 (anotherim, the same). First seen 2026-11-08: ❌ Kaidan on anotherim 151843 and prosody 151043; ✅ web on ejabberd 203321, prosody 203621, anotherim 222157; ✅ Gajim on ejabberd 141129, prosody 145644, anotherim 133702.

Key stanzas (batch 1; control 131816)
  • 151803 client.log:239 the line aim retracts goes out encrypted (0.3 and OMEMO 2); :250 its retraction, no <encrypted/>: <retract xmlns='urn:xmpp:message-retract:1' id='1794237639606892'/><body>This message was retracted.</body><fallback xmlns='urn:xmpp:fallback:0' for='urn:xmpp:message-retract:1'/>; peer-kaidan.log:670–:678 Kaidan receives it as is.
  • The same: 154924 client.log:246, 173523 client.log:368; steps.log:37 / :40 / :49 aim sent: <retract/> in clear True, encrypted False. Control 131816: client.log:379 clear retraction under 0.3, peer-gajim.log:3503 Gajim applies it.

S15. OMEMO: reactions, receipts, markers in the clear; file metadata whenever a 0.3 envelope goes out

  • verified by a human
  • Spec: XEP-0420 §9 (OMEMO 2); XEP-0384 §7 (0.3 has no SCE); XEP-0454 §2 (only the aesgcm:// URL, inside the envelope).
  • Expected: in OMEMO 2 chats inside the envelope; in 0.3 chats no plaintext metadata beyond a generic body (S3 in #1).
  • Seen (signals, 2026-11-08, not re-recorded): <reactions xmlns='urn:xmpp:reactions:0'> + <store/>, <received/> and <displayed/> unencrypted in OMEMO 0.3 and OMEMO 2 chats.
  • Seen (file metadata, batch 1): a clear SIMS <media-sharing xmlns='urn:xmpp:sims:1'> (name, media type, size) rides beside the ciphertext whenever a 0.3 envelope goes out: in every 0.3 chat (Gajim, Dino: 6 of 6 runs, all servers) and in OMEMO 2 + 0.3 sends on ejabberd, where vm-kaidan and vm-web still have 0.3 devices. anotherim-web's own file messages leak the same (161932 client.log:253), so it is libtacky's. OMEMO 2 alone: nothing beside the envelope (Kaidan and web on prosody and anotherim). All peers still show the pictures.
  • What to look for: client.log at the cited line: </encrypted> followed by <media-sharing xmlns='urn:xmpp:sims:1'>…<name>paste-….png</name><size>1216</size>; aim's View XML (shot-3) shows the SIMS reference under "Encrypted with OMEMO 0.3" or "OMEMO 2 + 0.3".
  • Fix: signals: as S14. Files: no SIMS outside the envelope whenever a 0.3 envelope is sent; see S3's legacy_file_metadata (default on) in #1.
  • Upstream: not checked; S3 in #1 covers the 0.3 file metadata.
result server peer what it shows run deciding screenshot
❌ ejabberd Gajim OMEMO 0.3 upload, SIMS name/type/size in the clear; both pictures shown 154053 shot-3
❌ ejabberd Kaidan OMEMO 2 + 0.3 at once, SIMS beside both envelopes 155210 shot-3
❌ ejabberd anotherim-web OMEMO 2 + 0.3, SIMS from aim and from web 161932 shot-3
✅ prosody anotherim-web OMEMO 2 only: envelope, fallback body, EME, nothing about the file 174321 shot-2
❌ prosody Kaidan 2026-11-08: OMEMO 2 chat, aim's reaction in the clear 140011 — (log only)
❌ ejabberd Kaidan 2026-11-08: OMEMO 2 chat, <received/> receipts in the clear 131609 — (log only)

Also batch 1, 0.3: Gajim 151334 (anotherim), 155434 (prosody); Dino 151933 (anotherim), 154657 (ejabberd), 160043 (prosody); the P4 fallback uploads (161341 client.log:457, 173144 :270). 2 + 0.3: 162532. ✅ OMEMO 2 only: 151535, 152512, 174225, 174804, 175603. 2026-11-08 (harness †; re-run SERVER=… vms/tools/vmrun.zsh explore vms/examples/xep0444.py s15 <peer>, xep0184.py for receipts): reactions with Gajim 133147, Dino 142139; 0.3 uploads 162721, 160145.

Key stanzas (ejabberd: Gajim 154053, Kaidan 155210, web 161932; control prosody 174321)
  • 154053 client.log:242: <encrypted xmlns='eu.siacs.conversations.axolotl'>…<body>I sent you an OMEMO encrypted message…</body><reference …><media-sharing xmlns='urn:xmpp:sims:1'><file …><media-type>image/png</media-type><name>paste-2026-11-09T15-43-25.png</name><size>1216</size>…
  • 155210 client.log:283 the same beside OMEMO 2 + 0.3; 161932 client.log:240 aim's, :253 web's (<name>image.png</name><size>126984</size>).
  • Control 174321 client.log:252: …</encrypted><body>…</body><encryption xmlns='urn:xmpp:eme:0' namespace='urn:xmpp:omemo:2'/><store xmlns='urn:xmpp:hints'/></message>.
  • 2026-11-08: 140011 client.log:185 <reactions xmlns='urn:xmpp:reactions:0' …><reaction>👍</reaction></reactions> (OMEMO 2 chat, :156); 131609 client.log:167 <received xmlns='urn:xmpp:receipts' …/> (:148).

Correctness

C35. MUC: no instant-room configuration after creating a room

  • verified by a human
  • Spec: XEP-0045 §10.1.2 (the owner submits an empty muc#owner form to unlock an instant room), §10.1.1 (a new room is locked until configured).
  • Expected: on status 201, aim submits <query xmlns='http://jabber.org/protocol/muc#owner'><x xmlns='jabber:x:data' type='submit'/></query> (or the configuration the user chose).
  • Seen: aim joins a new room, gets 201 + 110 and owner, and never sends muc#owner. Prosody keeps the room locked ("Room is locked, denying entry") and every other joiner gets item-not-found: Gajim shows "Failed to Join Group Chat / item-not-found", Dino "Room does not exist". ejabberd and anotherim-server open such rooms anyway: there aim also sends no muc#owner (0 in every control's client.log), so it is the same behaviour, hidden.
    • Batch 2: muc_room_lock_timeout (default 5 minutes) unlocks the room at the next join after it lapses, still without muc#owner (195222 server.log:1116 19:53:50 → :1517 19:59:24; 327–356 s in the XEP-0249 runs). The room is not persistent: Prosody deletes it when its last occupant leaves (002115 server.log:2103–:2106), so aim's autojoin bookmark leads nowhere. web's rooms (same app) are locked too (224019, 225635). Every batch-2 room run on prosody hit it.
  • Fix: answer status 201 with the instant-room submit in libtacky's join path.
  • Upstream: not checked.
result server peer what it shows run deciding screenshot
❌ prosody Gajim no muc#owner; Gajim's join refused, item-not-found on screen 125134 shot-2
❌ prosody Dino batch 2: two joins refused ("Room does not exist"), the third let in 5 min 34 s later by Prosody's timeout 195222 shot-2
❌ prosody Dino batch 2 (XEP-0402): Dino refused; on aim's restart Prosody deletes the empty room, the bookmark leads nowhere 002115 shot-2
❌ prosody anotherim-web batch 3 (XEP-0272): the room web (libtacky) made stays locked too; aim's join on the invite gets no self-presence in 90 s, joins after 360 s (steps.log:64, :84) 013459 shot-2
✅ ejabberd Gajim batch 2: same 201, no muc#owner; ejabberd opens the room, every admin step reaches Gajim 194543 shot-6

Also batch 2 on prosody: 194144 (Gajim, within the lock window only), 201824 (web, 352 s), 204944 (Gajim, 327 s), 210018 (Dino, 346 s), 210753 (web, 352 s; web's room too), 224019 (web's room, 356 s), 225030 and 225635 (autojoins refused). Batch 0: 125811 (Dino), control 142257. First seen 2026-11-08: ❌ on prosody with Gajim 160636, Dino 172222, web 212252; ✅ on ejabberd and anotherim with all three (harness †).

Key stanzas (prosody 125134, 195222, 002115; control 194543)
  • 125134 client.log:193 the room created for aim: <presence from='claude-test-165135@conference.prosody.asxp.io/vm-aim' …><x xmlns='http://jabber.org/protocol/muc#user'><status code='201'/><item affiliation='owner' … role='moderator'/><status code='110'/></x>; no muc#owner in client.log (steps.log:21 "room created (201) True, muc#owner submitted False"); server.log:1195 Room is locked, denying entry; peer-gajim.log:1490 <item-not-found …/>.
  • 195222 steps.log:21 the same; server.log:1291, :1363 Room is locked, denying entry (19:55:48, 19:56:24); server.log:1517 (19:59:24) Dino's third join creates an occupant, peer-dino.log:2342 status 110, participant.
  • 002115 server.log:2103 "claude-test-042116@conference.prosody.asxp.io" empty, destroying, :2106 Deleting claude-test-042116@…; after the restart client.log:428/:429 aim's disco#info of the room, item-not-found at :505, :506.
  • Control 194543 client.log:194 status 201, no muc#owner until the destroy; Gajim in the room.

C36. Calls: a session-accept without <rtcp-mux/> is rejected (Kaidan)

  • verified by a human
  • Spec: XEP-0167 §5 (<rtcp-mux/> is optional); XEP-0338 (BUNDLE grouping optional); RFC 5761.
  • Expected: a call without rtcp-mux negotiated works (separate RTCP), or is refused with a clear reason and a session-terminate.
  • Seen: aim's session-initiate offers <rtcp-mux/> and BUNDLE; Kaidan's session-accept has neither. aim's WebRTC rejects the answer ("Failed to set remote answer sdp: The m= section with mid='0' is invalid. RTCP-MUX is not enabled when it is required", on screen and in client-tacky.log), sends no terminate, and stays "Ringing…"; Kaidan ends the call with failed-transport after ~30 s. Dino's and web's accepts carry <rtcp-mux/> and connect.
    • Batch 2: the same in both calls on all three servers, now with the camera feed and, on prosody, TURN (#13), so ICE is not the cause: anotherim 201136 (the first Kaidan call run there), prosody 232601, ejabberd 000154.
  • Fix: set the RTCPeerConnection's rtcpMuxPolicy to negotiate where available, or answer such an accept with session-terminate <failed-application/> and tell the user.
  • Upstream: not checked.
result server peer what it shows run deciding screenshot
❌ ejabberd Kaidan accept without rtcp-mux rejected in both calls, no RTP; Kaidan's failed-transport 125726 shot-3
❌ anotherim Kaidan batch 2: the same, first Kaidan call run on anotherim-server 201136 shot-3
❌ prosody Kaidan batch 2: the same with TURN in place 232601 shot-3
✅ anotherim Dino batch 2: Dino's accept has rtcp-mux; tones and video both ways 195624 shot-9

Also batch 2 000154 (ejabberd); ✅ web on all three (200337, 233237, 000900); batch 0 control 130455. First seen 2026-11-08: ❌ Kaidan on ejabberd 153643 and prosody 175130; ✅ Dino on ejabberd 205011 (harness †).

Key stanzas (125726; batch 2 201136, 232601, 000154)
  • 125726 client.log:226 aim's audio session-initiate offers BUNDLE and <rtcp-mux/>; :248 Kaidan's session-accept has neither (video: :326); client-tacky.log:217 (video :545): Failed to set remote answer sdp: The m= section with mid='0' is invalid. RTCP-MUX is not enabled when it is required.; client.log:277 only Kaidan terminates, <failed-transport/>; rtp.log:2, :102 no RTP or DTLS.
  • 201136 client.log:384, :465 Kaidan's accepts without rtcp-mux; client-tacky.log:244, :603 the rejection; client.log:413, :520 failed-transport; steps.log:58, :59 stun 2 dtls 0 / stun 0 dtls 0.
  • 232601 client.log:356 offer with <rtcp-mux/>, :385 accept without; client-tacky.log:284, :630; client.log:410, :511 failed-transport.
  • 000154 client.log:541; client-tacky.log:217, :546; client.log:652 <failed-transport/><text>ICE connection could not be established.</text>.

C38. OMEMO 2 with Kaidan: "can't be read" count grows

  • verified by a human
  • Status: not reproduced by restarts on any server; but the count rose around each XEP-0424 run with Kaidan, on all three servers, unexplained. Kept open. Not rechecked in batch 2.
  • Spec: XEP-0384 §4.4, §5.3 (key exchanges).
  • Expected: no new undecryptable messages from a peer whose messages decrypt; an archived message already handled is not counted again.
  • 2026-11-08: the banner "N encrypted messages can't be read on this device" for vm-kaidan grew with sign-ins (2 → 5), by eye; aim re-fetches the same archived key exchange (124708: MAM id 1794140133942834 in four queries).
  • Restarts (batch 0 + 1): omemo_restart.py (a line each way, 3 restarts, pill read each time, also 6 notches up): the count never changed (0 on all three in batch 1; 2 throughout in batch 0, 134000); each sign-in re-fetches the same 3 archived key exchanges without counting them.
  • Around XEP-0424 (batch 1): after each S14 run a pill sits just above Kaidan's "x424 … kiwi" of that run, though the last shot of the S14 run had none: ejabberd 0 after 150810, then 155210 shot-5 "5 … can't be read"; prosody 0 after 153800, then 161442 shot-1 "1 …"; anotherim 0 after 161731, then 4 in 173523 (steps.log:14, :66) after a discarded XEP-0424 attempt. Kaidan's messages in 151803 look like those in the passing 150810 (client.log:205, :213); which stanzas get counted was not traced.
  • Fix: not established. Log the stanza-id and reason of each failed decrypt; dedupe MAM results by stanza-id. Next: omemo_restart.py with the XEP-0424 steps.
  • Upstream: not checked.
result server peer what it shows run deciding screenshot
✅ (not reproduced) ejabberd Kaidan 0 before and after 3 restarts; 3 archived kex messages re-fetched each sign-in 150810 shot-6
✅ (not reproduced) prosody Kaidan 0 at the bottom and 6 notches up, start and 3 restarts 153800 shot-10
✅ (not reproduced) anotherim Kaidan the same 161731 shot-9
❌ (rise) anotherim Kaidan the S14 run: 4 before and after, 0 at the end of 161731 173523 shot-2

Also 150531 (prosody, read only at the bottom; re-recorded as 153800) and batch 0 134000 (2 throughout). First seen 2026-11-08 (Kaidan, harness †): ejabberd 124708 (4), 122920 (3); prosody 123333 (3), 121829; anotherim 122631 (2).

Key evidence (150810, 173523; 2026-11-08 124708)
  • 150810 steps.log:35 … :122 aim undecryptable (n, failed) start: (0, 0) … restart-3: (0, 0); :111 archived kex messages fetched again across sign-ins: [('1794237037887154', 3), ('1794237040310281', 3), ('1794237096097015', 3)].
  • 173523 steps.log:14 before: (4, 4), :66 after: (4, 4).
  • 124708 client.log:132, :141, :239, :250 one archived Kaidan message from MAM queries …-2 to -5: <result xmlns='urn:xmpp:mam:2' id='1794140133942834' …><key kex='true' rid='989476894'>….

C39. Calls: anotherim-js's outgoing video never shown on the other side (withdrawn, harness)

  • Withdrawn 2026-11-09: the harness fed the loopback camera a 4:2:2 format libwebrtc rejects; with YUY2 (#16, f45e5c5) the web shows aim's live test card (133803, shot-10). Batch 2: aim's card shown by Dino and web on all three servers (195624, 231837, 235447, 200337, 233237, 000900).

C40. XEP-0448: aes-256-cbc-pkcs7 shares are not decrypted; a URL-named card, Download keeps the ciphertext

  • verified by a human
  • Spec: XEP-0448 §3.2 (decrypt the esfs source with its cipher, key, IV), §4 Table 1 (aes-128-gcm-nopadding:0, aes-256-gcm-nopadding:0, aes-256-cbc-pkcs7:0; CBC: 256-bit key, 128-bit IV).
  • Expected: aim decrypts Kaidan's share, checks its sha-256 and shows pic-…-kaidan.png inline.
  • Seen: Kaidan 0.16 sends pictures under OMEMO 2 as an XEP-0447 share with one XEP-0448 source, cipher urn:xmpp:ciphers:aes-256-cbc-pkcs7:0 (its only one), plus the ciphertext's URL as body and OOB. aim ignores the CBC source and falls back to the URL: a card named after its last segment (5tguSQyso9), and Download saves the ciphertext without an error. All three servers. aim's record keeps the share's name and hashes; only the cipher is missing.
  • Cause (libtacky v1.2.3): file.tcl esfs_url, L2919–L2931 reads only the two GCM ciphers ("aes-256-cbc-pkcs7 is not read").
  • Kaidan's part: its CBC IV is 32 bytes (§4: 128 bits; 153012 steps.log:117); its first 16 bytes decrypt to the announced sha-256 (155852 steps.log:117–:118). CBC support alone won't open Kaidan 0.16's files unless libtacky tolerates that IV.
  • What to look for: aim's shot-5: a card with a random 10-letter name and "Download"; shot-6: "Save", no picture; shot-7 (View XML): an urn:xmpp:esfs:0 source with the CBC cipher.
  • Fix: support aes-256-cbc-pkcs7:0 (taking a 32-byte IV's first 16 bytes, or reporting it); when a share can't be opened, say so on the card (the share's name, "can't decrypt") instead of offering the ciphertext.
  • Upstream: libtacky not checked. Kaidan's IV: report to Kaidan (invent.kde.org/network/kaidan) once checked against 0.17.
result server peer what it shows run deciding screenshot
❌ prosody Kaidan card "5tguSQyso9", Download keeps the ciphertext; the 32-byte IV fails 153012 shot-5
❌ ejabberd Kaidan card "msj4P9ttjf"; IV[:16] decrypts to Kaidan's sha-256 155852 shot-5
✅ prosody anotherim-web an aes-256-gcm XEP-0448 share: decrypted, hash verified, inline 175603 shot-5

Also 153241 (anotherim, "fjosqIUewW") and the same card in the XEP-0454 runs with Kaidan (155210 "MoKmgqSiig", 151535, 152512; 2026-11-08 163656, 183515; batch 0 131002).

Key evidence (153012, 153241, 155852)
  • 153241 peer-kaidan.log:562 Kaidan's slot filename="fjosqIUewW" size="126992" (PNG 126984 B, padded).
  • 153012 steps.log:112 aim's record: <file-sharing xmlns='urn:xmpp:sfs:0'> … <name>pic-193013-kaidan.png</name> … <encrypted xmlns='urn:xmpp:esfs:0' cipher='urn:xmpp:ciphers:aes-256-cbc-pkcs7:0'>; :103 the card with Download; :117 key 32 iv 32, Invalid IV size (32) for CBC.
  • 155852 steps.log:117–:118 kaidan's announced sha-256 […] = the fetched plaintext's: True (IV[:16]).

C41. XEP-0448: aim's sent picture has no esfs source (aesgcm:// + SIMS only)

  • verified by a human
  • Spec: XEP-0448 §3.1 (the sender's share carries <encrypted xmlns='urn:xmpp:esfs:0' cipher=…> with key, IV and the inner source).
  • Expected: under OMEMO 2 aim's picture carries an XEP-0447 share with an esfs source, as anotherim-web's does, so a client that reads XEP-0448 but not aesgcm:// can open it.
  • Seen: aim (desktop, a pasted picture) sends an aesgcm:// body + SIMS inside OMEMO 2, no esfs source: on prosody and ejabberd no share at all; on anotherim-server an SFS share whose only source is <url-data target='aesgcm://…#key'/> (174804 steps.log:77). web's picture (same build) has an aes-256-gcm esfs source, which aim decrypts and shows. web reads aesgcm, so aim's picture still shows there.
  • Where (libtacky v1.2.3, not traced): the sender code exists (message.tcl EsfsSource/EncryptedShareNodes, L5045–L5093); the desktop paste path evidently doesn't reach it.
  • What to look for: aim's shot-3 (View XML of its picture): aesgcm:// body and SIMS, no urn:xmpp:esfs:0; shot-6 (View XML of web's): an urn:xmpp:esfs:0 source, cipher='urn:xmpp:ciphers:aes-256-gcm-nopadding:0', key and IV.
  • Fix: send pasted/attached pictures through the share path the web build uses.
  • Upstream: not checked.
result server peer what it shows run deciding screenshot
❌ prosody anotherim-web aim's record aesgcm:// + SIMS, no esfs; web's esfs share decrypted (judged fail to match the other servers) 175603 shot-3
❌ anotherim anotherim-web aim's SFS share has only an aesgcm:// url-data source 174804 shot-3

Also 162532 (ejabberd, plus S15's clear SIMS).

Key evidence (174804, 175603)
  • 174804 client.log:392 aim's message, OMEMO 2 only; steps.log:77 its record: <file-sharing xmlns='urn:xmpp:sfs:0' …> … <sources> <url-data … target='aesgcm://…/paste-2026-11-09T17-50-27.png#…'/> </sources>; :127–:130 web's share aes-256-gcm-nopadding:0, key 32 iv 12, sha-256 verified.
  • 175603 client.log:255 aim's picture (record aesgcm:// + SIMS, shot-3); client.log:267, steps.log:127, :130 web's share decrypted and verified.

C42. urn:tacky:omemo-trust:0: a contact key's change is published only for Undecided → Verified

  • verified by a human
  • Spec: urn:tacky:omemo-trust:0 (libtacky docs/protocol/omemo-trust-0.md, docs/features.md): every user trust decision (omemo trust without -blind) is published sealed to the own devices.
  • Expected: aim marks vm-gajim's key Verified (or back to Accepted) → a sealed <publish node='urn:tacky:omemo-trust:0'> → web, signed in as vm-aim with aim's device Verified, applies it.
  • Seen (refined batch 2): a contact-key decision is published only for Undecided → Verified (anotherim 194814: web shows Verified within the second). Accepted → Verified (ejabberd 182653, prosody 002914) and Verified → Accepted (002914) publish nothing; the other device stays stale. Own-device decisions are published (181559 client.log:246, :270, :283); omemo-chats:0 syncs on all three servers.
  • What to look for: steps.log aim set #… from Accepted to Verified, then aim published urn:tacky:omemo-trust:0: 0 / False; web got its event: False; no <publish node='urn:tacky:omemo-trust:0'> after it in client.log / server.log.
  • Fix: publish the trust list on every user decision about a contact's device, whatever its previous state.
  • Upstream: not checked.
result server peer what it shows run deciding screenshot
❌ ejabberd anotherim-web as vm-aim Accepted → Verified on aim, no publish, web keeps an older list 182653 shot-5
❌ prosody anotherim-web as vm-aim batch 2: Accepted → Verified and Verified → Accepted, neither published 002914 shot-3
✅ anotherim anotherim-web as vm-aim batch 2: Undecided → Verified, published and applied by web 194814 shot-4

Also ✅ omemo-chats:0: 181559 (ejabberd), 193707 (anotherim), 235706 (prosody).

Key evidence (182653, 002914; control 194814)
  • 182653 steps.log:134 aim lists vm-gajim@ejabberd.asxp.io's keys [('170', 'Accepted')]; :169 aim set #170 from Accepted to Verified; client.log:25, :75 the only omemo-trust traffic: the sign-in fetch (item of 18:22:08).
  • 002914 steps.log:185 aim sets the key Accepted → Verified; :187 aim published urn:tacky:omemo-trust:0: False; web got its event: False; server.log:1382 the only omemo-trust stanza, the sign-in fetch, none through the restore at 00:38:37 (steps.log:208) to the end (00:39:49).
  • 194814 steps.log:182 aim set #193428444 from Undecided to Verified; client.log:424 <publish node='urn:tacky:omemo-trust:0'><item id='current'> (19:53:50), acked :426; peer-anotherim-web.log:406 web's event; steps.log:203 PASS web lists #193428444 Verified.

C43. XEP-0482: aim's <accept/>/<reject/>/<left/> withheld in an OMEMO room when a member has no OMEMO device

  • verified by a human
  • Spec: XEP-0482 §3.1 (the invitee answers the invitation with <accept/>/<reject/>, <left/> on leaving, to the chat the invite came from).
  • Expected: declining Dino's group-call invitation sends <reject id=…/> to the room, so Dino stops inviting aim; joining and leaving send <accept/> and <left/>. The invitation came in the clear, so a clear answer leaks nothing new.
  • Seen: in Dino's private room (OMEMO on for aim) Dino's <invite xmlns='urn:xmpp:call-invites:0' multi='true'> arrives in the clear. After Decline aim's card reads "Declined", but libtacky finds no OMEMO device for vm-dino and withholds the <reject/> rather than send it in the clear; aim's room shows "Not sent: it could not be encrypted for vm-dino (no device with OMEMO keys)". Dino never learns of it. Joining works (Muji is presence and Jingle), but <accept/> and <left/> are withheld the same way. In web's unencrypted rooms all three go out.
  • What to look for: aim's shot after Decline: card "Group call · Declined" and the "Not sent …" banner; client-tacky.log:21; no urn:xmpp:call-invites:0 stanza from aim in client.log after the click.
  • Fix: send XEP-0482 answers the way the invitation came (in the clear when it was clear), or at least don't mark the card "Declined" while the answer was not sent.
  • Upstream: not checked.
result server peer what it shows run deciding screenshot
❌ anotherim Dino Decline: <reject/> withheld, "Not sent" banner, card "Declined" 210351 shot-4
❌ ejabberd Dino the same 002620 shot-4
❌ prosody Dino batch 3: the same, client-tacky.log:21 <reject/> withheld 014802 shot-3
✅ anotherim anotherim-web web's unencrypted room: aim's <reject/> goes to the room, web gets it 214938 shot-4
✅ ejabberd anotherim-web the same 004557 shot-4

Also the XEP-0272 runs with Dino, which pass on the call itself: 205652 and 001757 (accept and left withheld).

Key evidence (002620, 210351, 205652; control 004557)
  • 002620 client.log:653 Dino's invitation in the clear: <invite xmlns='urn:xmpp:call-invites:0' … multi='true' video='false'><muji xmlns='urn:xmpp:jingle:muji:0' room='b34172aa@conference.ejabberd.asxp.io'/></invite>; steps.log:61 Decline clicked.
  • 002620 client-tacky.log:20 OMEMO claude-test-042621@conference.ejabberd.asxp.io: not sent; no usable device for vm-dino@ejabberd.asxp.io:no_devices; :21 groupcall: <reject/> withheld: no member of claude-test-042621@conference.ejabberd.asxp.io can receive an encrypted call invite, and it is not sent in the clear.
  • 210351 client.log:517 the clear invite; client-tacky.log:21 (21:08:15.313) the same <reject/> withheld.
  • 205652 client-tacky.log:21 groupcall: <accept/> withheld: … (21:01:00), :537 <left/> withheld: … (21:01:44); 001757 client-tacky.log:21, :424 likewise.
  • Control 004557 client.log:569 <message … to='claude-test-044557@conference.ejabberd.asxp.io' type='groupchat'><reject xmlns='urn:xmpp:call-invites:0' id='6c7d2f21bda9a91ad9840a124e521147'/><store xmlns='urn:xmpp:hints'/></message>; peer-anotherim-web.log:334 web gets it. 214007 client.log:509 <accept/>, :613 <left/> in web's room.

C44. Muji group call: anotherim-web hears nothing from aim (opus arrives, level 0)

  • verified by a human
  • Spec: XEP-0272 §3 (media between the call room's participants).
  • Expected: in a group call both sides hear each other, as aim and Dino do (205652, 001757).
  • Seen: web starts a group call (API groupcall.start, no UI), aim joins from its card and opens a Jingle session to web. aim hears web (1.00); web hears nothing (0.00), though aim's opus reaches web's NIC at full rate (403 packets / 8 s) and web's getStats counts it with audioLevel 0, energy 0. Not the autoplay policy (--autoplay-policy=no-user-gesture-required). The pair's 1:1 calls play both ways, and Dino hears aim in a group call. Whether aim sends silence on this leg or web doesn't render the track is not traced; both ends are anotherim-js.
  • What to look for: steps.log web hears 440 Hz: 0.00; web's NIC in rtp opus/111 … 403 packets; web's getStats line with "audioLevel":0,"energy":0.
  • Fix: trace first: compare aim's outbound-rtp audioLevel in this call with the 1:1 call, and web's inbound track's attachment to an audio element in the group-call view.
  • Upstream: not checked.
result server peer what it shows run deciding screenshot
❌ ejabberd anotherim-web web hears 0.00 while aim's opus arrives; getStats level 0 003642 shot-5
❌ anotherim anotherim-web the same 214007 shot-5
❌ prosody anotherim-web batch 3 (XEP-0272, after C35's lock lapsed): aim hears 660 Hz 1.00, web hears 440 Hz 0.00; web-rtp.log:1 93 packets, audioLevel 0 013459 shot-4
✅ ejabberd Dino Dino's group call: tones both ways 001757 shot-4
✅ ejabberd anotherim-web 1:1 call of the same pair: tones both ways 000900 shot-10

Also ✅ 205652 (Dino, anotherim), 200337 (1:1 with web, anotherim).

Key evidence (003642, 214007)
  • 003642 client.log:452 web's invitation, :668 aim's session-initiate to web, :689 web's session-accept; steps.log:57, :58 aim hears 660 Hz: 1.00, web hears 440 Hz: 0.00.
  • 003642 rtp.log:16 web's NIC in rtp opus/111 ssrc bed035e4: 403 packets 64778 bytes in 8s (its own out, :20); :24 {"type":"inbound-rtp","kind":"audio","bytes":135855,"packets":1693,"lost":0,…,"audioLevel":0,"energy":0}, rising at :25.
  • 214007 steps.log:59 aim hears 660 Hz True (1.00); web hears 440 Hz False (0.00); :62, :63 out opus/111 403; … in opus/111 403 on both NICs; web-rtp.log:9 "inbound-rtp","kind":"audio",…"packets":2060,…"audioLevel":0,"energy":0.

C45. MUC: membership and ban sent with the occupant's full JID; Prosody refuses them silently

  • verified by a human
  • Spec: XEP-0045 §9.1 (a ban "MUST be performed based on the occupant's bare JID"), §9.3 (membership, bare JIDs in the examples), §5.2 (affiliations are bare-JID based).
  • Expected: aim's members pane sends <item affiliation='member|outcast' jid='vm-gajim@…'/> with the bare JID.
  • Seen: aim sends the occupant's full JID (jid='vm-dino@prosody.asxp.io/interop') for membership and ban in every batch-2 admin run. Prosody refuses both (jid-malformed, "Bare JID expected, got full JID"): nobody is banned and aim shows no error. ejabberd and anotherim-server apply them (301) to one resource. Nick-based steps (subject, roles, kick, destroy) work.
  • What to look for: client.log the muc#admin <item affiliation='outcast' jid='…/resource'>; on prosody the type='error' answer with <jid-malformed/> right after it; aim's screen unchanged.
  • Fix: strip the resource (or use the occupant's real bare JID) in libtacky's affiliation change; show iq errors from muc#admin to the user.
  • Upstream: not checked.
result server peer what it shows run deciding screenshot
❌ prosody Dino membership and ban by full JID, both refused jid-malformed; Dino never banned, aim silent 195222 shot-5
❌ prosody anotherim-web the same with web's full JID 201824 shot-6
❌ ejabberd Gajim full JID; ejabberd applies the ban (301) 194543 shot-9
❌ anotherim Dino full JID; the server applies it 223151 shot-9

Also 201047 (ejabberd, Dino), 204649 (ejabberd, web), 221811, 225948 (anotherim, Gajim), 224559 (anotherim, web); 194144 never got that far (C35). No ✅ control: the spec is the comparison; the server's error is the proof on prosody.

Key stanzas (195222, 201824, 194543)
  • 195222 client.log:285 <item affiliation='member' jid='vm-dino@prosody.asxp.io/interop'/>; :286 Prosody: <iq … type='error'><error type='modify'><jid-malformed …/><text>Bare JID expected, got full JID</text>; :310 <item affiliation='outcast' jid='vm-dino@prosody.asxp.io/interop'><reason>kiwi ban</reason></item>, refused at :312; steps.log:113 no 301.
  • 201824 client.log:325 / :351 membership and ban with jid='vm-web@prosody.asxp.io/another.im (Js)~…', refused at :327 / :353.
  • 194543 client.log:270 <query xmlns='http://jabber.org/protocol/muc#admin'><item affiliation='member' jid='vm-gajim@ejabberd.asxp.io/gajim.QS09QHG1'/></query>; :294 <item affiliation='outcast' jid='vm-gajim@ejabberd.asxp.io/gajim.QS09QHG1'><reason>kiwi ban</reason></item>; peer-gajim.log:2972 status 301.
  • 223151 client.log:592, :689 the same with vm-dino@anotherim.asxp.io/interop; peer-dino.log:8619 301.

C46. Mediated room invite: a second 1:1-looking chat of the room's JID; on Prosody no invitation card

  • verified by a human
  • Spec: XEP-0045 §7.8.2 (mediated invitation: a <message/> from the room's bare JID carrying <x xmlns='http://jabber.org/protocol/muc#user'><invite from=…/>).
  • Expected: a mediated invitation shows as a room invitation (card with Accept / Decline) and nothing else; accepting joins the room.
  • Seen: anotherim-js (aim and web) also files the invitation as a 1:1 chat with the room's bare JID: two rows with the room's name, the room and a "Last seen recently" chat holding the invite text; after Decline that chat stays, unread (211510). On ejabberd and anotherim-server the card works too. On Prosody there is no card: only a chat request from the room's JID ("Messages from people not in your contacts"), so the invitee must join by address (210753, 224019). Gajim's XEP-0249 invitations show the card on every server. Why Prosody's relay gets no card was not traced (all three servers add jabber:x:conference).
  • What to look for: two rows named after the room in the receiver's list; on prosody the "Messages from people not in your contacts" request instead of a "Room invitations" card.
  • Fix: route a muc#user <invite/> to the invitation card only, not to a chat; accept it on Prosody's relay.
  • Upstream: not checked.
result server peer what it shows run deciding screenshot
❌ ejabberd anotherim-web both sides get a duplicate chat per invitation; aim keeps one after Decline 211510 shot-1
❌ anotherim anotherim-web the same (two "claude-test-032628" rows on web, two "…b" on aim) 232627 shot-1
❌ anotherim Dino Dino's mediated invite leaves aim the same second chat 205652 shot-2
❌ prosody anotherim-web no card on web, only a chat request from the room's JID; joined by address 210753 shot-1
❌ prosody anotherim-web the same on aim's side for web's invitation 224019 shot-2
✅ prosody Gajim Gajim's XEP-0249 invitation: aim's card, Accept joins 204944 shot-4

Also 204649 (shots 8, 9), 224559 (shot 2), 001757 (aim's call card in that chat), 000658 (the room's picture on it).

Key evidence (211510, 210753, 205652)
  • 211510 client.log:338 web's invitation as aim receives it from the room: <invite from='vm-web@ejabberd.asxp.io/another.im (Js)'/>; peer-anotherim-web.log:52 aim's to web; EVIDENCE "Seen": web two "claude-test-011510" rows, aim two "claude-test-011510b" rows and a "claude-test-011510c" chat with an unread badge after Decline.
  • 210753 client.log:287 aim's mediated invite; peer-anotherim-web.log:112 Prosody's relay with an added jabber:x:conference; web's first presence to the room is peer-anotherim-web.log:165, its join by address after 352 s (steps.log:85).
  • 224019 client.log:239 web's invitation reaches aim; aim's only join client.log:296, by address after 356 s (steps.log:83).
  • 205652 client.log:482 Dino's mediated invite from the room's bare JID; shot-2 two "claude-test-005653" rows.

C47. XEP-0054: aim writes its profile only as XEP-0292 vCard4, never vcard-temp; Kaidan reads an empty card

  • verified by a human
  • Spec: XEP-0054 §3.2, §3.3 (the user's vCard is set with <iq type='set'><vCard xmlns='vcard-temp'/> and read by others with a get to the bare JID); XEP-0292 §5 leaves the vcard-temp mirror optional.
  • Expected: a vcard-temp-only client (Kaidan 0.16) reads the full name aim set in Edit profile.
  • Seen: Edit profile › Save publishes only <publish node='urn:xmpp:vcard4'>…<fn><text>aim Mango</text></fn>; aim sends no vcard-temp set in any run. ejabberd and anotherim-server keep the two apart, so Kaidan's vcard-temp get for vm-aim returns <vCard xmlns='vcard-temp'><NICKNAME/></vCard> and Kaidan keeps calling it "vm-aim". Prosody maps vCard4 to vcard-temp, so there Kaidan gets <FN>aim Mango</FN> and the bug is hidden. Gajim and web read vCard4 (their XEP-0054 runs pass on all three). The other direction works everywhere: aim reads Kaidan's vcard-temp FN "kaidan Mango".
  • What to look for: client.log aim's Save: a urn:xmpp:vcard4 publish and no vcard-temp set after it; peer-kaidan.log Kaidan's vcard-temp get of vm-aim answered without FN; Kaidan's shot-2: its chat with "vm-aim", no name.
  • Fix: also write the profile to vcard-temp (or merge into it) when the server doesn't advertise a vCard4↔vcard-temp mapping, as other XEP-0292 clients do.
  • Upstream: not checked.
result server peer what it shows run deciding screenshot
❌ ejabberd Kaidan vCard4 only; Kaidan's vcard-temp get of vm-aim answered <NICKNAME/> 152828 shot-2
❌ anotherim Kaidan the same 152203 shot-2
✅ prosody Kaidan control: Prosody's mapping hands Kaidan <FN>aim Mango</FN>; aim shows "kaidan Mango" 155057 shot-1
✅ ejabberd Gajim control: Gajim reads aim's vCard4 120045 shot-2

Also ✅ (vCard4 readers): Gajim 121041 (anotherim), 120250 (prosody); web 142654 (ejabberd), 140411 (anotherim), 141505 (prosody). On prosody Kaidan's Contact Details drew nothing (155057 steps.log:54, :56), so its display of the name there is unshown; the wire decides.

Key evidence (152828, 152203; control 155057)
  • 152828 client.log:531 aim's <publish node='urn:xmpp:vcard4'>…<fn><text>aim Mango</text></fn>; peer-kaidan.log:1872 Kaidan's <vCard xmlns="vcard-temp"/> get of vm-aim while the name is set, :1880 ejabberd's answer <vCard xmlns="vcard-temp"><NICKNAME/></vCard>; peer-kaidan.log:1849 Kaidan's own set with <FN>kaidan Mango</FN>, client.log:566 aim reads it.
  • 152203 client.log:601 the vCard4 publish, no vcard-temp set after it; peer-kaidan.log:1346 Kaidan's get, :1355 <vCard xmlns="vcard-temp"><NICKNAME/></vCard>; peer-kaidan.log:1321 Kaidan's FN, client.log:630, :637 aim reads it.
  • Control 155057 client.log:411 the same vCard4 publish; peer-kaidan.log:1268 Kaidan reads <FN>aim Mango</FN> from Prosody's mapping.

C48. XEP-0191: after an unblock libtacky never refetches the OMEMO device list that failed while blocked

  • verified by a human
  • Spec: XEP-0191 §3.4 (after an unblock communication resumes); XEP-0384 §5.3.1 (device list from PEP).
  • Expected: once aim unblocks vm-web, web's next message is encrypted to aim's devices and arrives.
  • Seen (prosody): while blocked, web (anotherim-js, the same libtacky) asks vm-aim's urn:xmpp:omemo:2:devices and legacy device list for the first time in its session; Prosody answers both service-unavailable, and web's "while blocked" line is not sent: "no devices on vm-aim@prosody.asxp.io's v2 or legacy device lists". aim unblocks (pushed); a minute later web types "after unblock": no new device-list fetch, the same error, the line never reaches aim. The failed fetch is kept as "no devices" and only an event or a new session would replace it. aim's block and unblock themselves work.
    • ejabberd and anotherim-server don't show it: they answer the blocked contact's PEP get with the device lists (143450 peer-anotherim-web.log:207, 134439 :340), so web encrypts, the server bounces the message service-unavailable (XEP-0191 §3.3), and "after unblock" arrives. Gajim on prosody, with aim's devices fetched before the block, passes (125018).
  • What to look for: web's shot-1: "prf 183457 while blocked" with "no devices on vm-aim@prosody.asxp.io's v2 or legacy device lists" and Retry; in peer-anotherim-web.log no device-list get to vm-aim after :158. The "after unblock" failure has no shot in the run (failed-blocking-web.png stayed in the out dir); steps.log:68 is the proof.
  • Fix: don't cache a failed (error) device-list fetch as an empty list; refetch on the next send to that contact, or at least on an unblock push.
  • Upstream: not checked.
result server peer what it shows run deciding screenshot
❌ prosody anotherim-web device lists service-unavailable while blocked, never refetched; "after unblock" fails "no devices" 143456 shot-1
✅ ejabberd anotherim-web the blocked fetch answered; message bounced while blocked, "after unblock" arrives 143450 shot-2
✅ anotherim anotherim-web the same 134439 shot-2
✅ prosody Gajim the same block and unblock; Gajim's line after the unblock arrives 125018 shot-2

Also ✅ XEP-0191 with Gajim 123324 (ejabberd), 114810 (anotherim); Dino 155048 and Kaidan 155601 (ejabberd only).

Key evidence (143456; controls 143450, 134439)
  • 143456 client.log:325 aim's <block xmlns='urn:xmpp:blocking'><item jid='vm-web@prosody.asxp.io'/>, :343 its <unblock …>, :351 the push.
  • 143456 peer-anotherim-web.log:157, :158 web's gets of vm-aim's urn:xmpp:omemo:2:devices and eu.siacs.conversations.axolotl.devicelist; :160, :161 both <error type='cancel'><service-unavailable …/>; no later get to vm-aim. steps.log:36 web sent nothing while blocked, :62 web types "prf 183457 after unblock" (14:40:12, aim unblocked 14:39:24), :68 FAIL blocking: AssertionError: web: no message to vm-aim@prosody.asxp.io in web.log after 60s.
  • 143450 peer-anotherim-web.log:204, :205 web's gets while blocked, :207, :210 ejabberd answers with the items; client.log:504 "after unblock" arrives. 134439 peer-anotherim-web.log:337, :338 gets, :340, :343 items; :357 the bounce.

Minor / SHOULD

M26. No desktop notification while aim's window has focus

  • verified by a human
  • Spec: none (UX). XEP-0184/0333 runs only.
  • Expected: a decision: notify when the chat isn't open even if the window has focus, or document it. May be intended.
  • Seen: a message to a chat that isn't open (chat closed, or another chat open) raises only the unread badge while aim's window has focus; no Notify call in notify-aim.log. With the window minimized aim does notify (notify-aim.log:5 in both batch-0 runs). Receipts and displayed markers work both ways.
    • Corrected 2026-11-09: Gajim 2.6 does notify while focused, with no chat open (notify-gajim.log:5) and with another chat open (:34), so aim differs from Gajim. Dino doesn't notify while focused (125832).
    • aim's Notify while minimized is not reliable: not in two explore re-runs after the recorded runs (vms/AGENTS.md).
  • Fix: —
  • Upstream: —
result server peer what it shows run deciding screenshot
❌ ejabberd Gajim aim focused, chat closed / Saved messages open: badge only, 0 Notify 140003 shot-9
❌ ejabberd anotherim-web the same with web's lines (also M28) 141044 shot-9

First seen 2026-11-08 (harness †): ❌ Gajim ejabberd 125251, Kaidan ejabberd 131609, Gajim prosody 123644; ✅ aim minimized with Gajim anotherim 123358 and web ejabberd 201534.

Key evidence (ejabberd × Gajim, 140003)
  • 140003 notify-aim.log:5 aim's only Notify, while minimized (14:06:46); steps.log:77 focused with Saved messages open: 0 Notify call(s); :44 chat closed: aim: no notification.
  • notify-gajim.log:34 Gajim, focused with another chat open, notifies (also :5, :63). 141044 steps.log:57, :95 the same for aim; notify-aim.log:5 minimized notifies.

M27. "View XML" shows OMEMO 2 messages with the 0.3 EME namespace

  • verified by a human
  • Spec: XEP-0380 (the EME namespace names the encryption actually used).
  • Expected: the stored record of an OMEMO 2 message says urn:xmpp:omemo:2.
  • Seen: the stored record (aim's View XML) shows <encryption xmlns='urn:xmpp:eme:0' namespace='eu.siacs.conversations.axolotl'/> under the dialog's own heading "Encrypted with OMEMO 2"; on the wire the same message was a single OMEMO 2 envelope with EME urn:xmpp:omemo:2.
  • Cause (libtacky v1.2.3): in tacky/xmpp/modules/message.tcl#L4764 the readable-record branch writes the marker unconditionally, j encryption -ns urn:xmpp:eme:0 -namespace eu.siacs.conversations.axolotl (comment: "the profile is not known here, as nothing was encrypted"), while the wire branch a few lines up (L4755–4756) picks v2 or legacy. chats.tcl:276–277 does it right (v2 when the profiles include it).
  • Fix: write the record's marker from the profile used on the wire (omemo::ns v2|legacy, as the wire branch does).
  • Batch 1: the same in every OMEMO 2-only file run on prosody and anotherim (8 runs); masked on ejabberd, where aim sent OMEMO 2 + 0.3 (155210 steps.log:62).
  • What to look for: aim's View XML (shot-3) heading "Encrypted with OMEMO 2" above namespace='eu.siacs.conversations.axolotl'.
  • Upstream: not checked.
result server peer what it shows run deciding screenshot
❌ anotherim Kaidan wire urn:xmpp:omemo:2; View XML's stored record eu.siacs.conversations.axolotl 131002 shot-3
❌ prosody anotherim-web batch 1: the same for aim's picture to web 174321 shot-3

Also batch 1 (FAIL View XML names the encryption used on the wire: stored eu.siacs.conversations.axolotl, wire ['urn:xmpp:omemo:2']): 151535 steps.log:56, 152512 steps.log:62, 153012 steps.log:62, 153241 steps.log:62, 174225 steps.log:81, 174804 steps.log:81, 175603 steps.log:81. First seen 2026-11-08: anotherim 152430 (View XML seen by hand after the run, not recorded). No ✅ control: the wire of the same message is the comparison.

Key evidence (anotherim-server × Kaidan, 131002)
  • 131002 client.log:365 on the wire: <encrypted xmlns='urn:xmpp:omemo:2'>…<encryption xmlns='urn:xmpp:eme:0' namespace='urn:xmpp:omemo:2'/>; steps.log:52 View XML of the same id: <encryption xmlns='urn:xmpp:eme:0' namespace='eu.siacs.conversations.axolotl'/>; :56 FAIL View XML names the encryption used on the wire.

M28. anotherim-web shows no desktop notification for messages or calls

  • verified by a human
  • Spec: none (UX; Web Notifications API).
  • Expected: Web Notifications when permission is granted and the chat isn't open or the window is hidden, like the desktop build; or document that the web build doesn't notify. Could also be the harness's Chromium app window: check in a real browser first (not done).
  • Seen: notification permission granted over DevTools; web got aim's lines focused with no chat open, focused with a menu open, and minimized: notify-web.log has no Notify call at all. In the same run desktop aim, minimized, did notify (notify-aim.log:5). Incoming calls only show the in-app "Incoming call" sheet (2026-11-08).
  • Fix: —
  • Upstream: —
result server peer what it shows run deciding screenshot
❌ ejabberd anotherim-web no Notify from web in any window state; aim minimized notifies 141044 shot-2
✅ ejabberd Gajim the same steps: Gajim notifies in every state 140003 shot-2

First seen 2026-11-08 (harness †): web on ejabberd 201534, prosody 201743, anotherim 205250; call: only the in-app sheet, ejabberd 220703 (shot-2-web-ringing).

Key evidence (ejabberd × anotherim-web, 141044)
  • 141044 notify-web.log:1–:4 the whole capture: no member=Notify, though web got aim's lines with the chat closed (steps.log:23), focused (:124) and minimized (:168).
  • notify-aim.log:5 the desktop build, minimized, notifies web's line (14:19:03); steps.log:21, :34, :55, :68 receipts and markers both ways.

M29. XEP-0425: a moderated message shows as "Message retracted", without moderator or reason

  • verified by a human
  • Spec: XEP-0425 (the room's moderated retraction names the moderator, by=, and carries the <reason/> for the client to show).
  • Expected: the tombstone says the line was moderated, by whom and why, as Gajim shows: "This message has been moderated by …/vm-gajim (reason: kiwi spam)".
  • Seen: aim gets the room's notice in both forms (:0 <apply-to>, :1 in <retract/>) with by='…/vm-gajim' and "kiwi spam", removes the text, and shows the plain "Message retracted · Show history" row of a self-retraction. The same with web moderating (web shows that row too). libtacky's <Retracted> carries moderated_by and reason (per the EVIDENCE), so the gap looks like anotherim-js's display.
  • What to look for: aim's shot-4 "Message retracted · Show history" vs Gajim's shot-5 "moderated by … (reason: kiwi spam)".
  • Fix: render moderated retractions with the moderator's nick and the reason.
  • Upstream: not checked.
result server peer what it shows run deciding screenshot
❌ ejabberd Gajim Gajim moderates aim's line; aim: plain "Message retracted"; Gajim: by + reason 214417 shot-4
❌ anotherim Gajim the same 233507 shot-4
❌ ejabberd anotherim-web web moderates (API); both show the plain row 230307 shot-4

Also 001141 (anotherim, web). Prosody: na, its MUC has no mod_muc_moderation (214403 peer-gajim.log:3137, no urn:xmpp:message-moderate in the room's disco#info). The control is Gajim's own display in the same run.

Key stanzas (214417, 233507)
  • 214417 peer-gajim.log:5487 <moderate xmlns="urn:xmpp:message-moderate:1" id="1794260806414398"><retract xmlns="urn:xmpp:message-retract:1" />…; client.log:337 <apply-to … id='1794260806414398'><moderated xmlns='urn:xmpp:message-moderate:0' by='claude-test-014417@…/vm-gajim'><retract …/><reason>kiwi spam</reason></moderated></apply-to><retract xmlns='urn:xmpp:message-retract:1' id='1794260806414398'><reason xmlns='urn:xmpp:message-moderate:1'>kiwi spam</reason><moderated … by='…/vm-gajim'/></retract>.
  • 233507 client.log:669 the same notice; steps.log:54 PASS aim no longer shows the papaya line.
  • 230307 peer-anotherim-web.log:260 web's <moderate xmlns='urn:xmpp:message-moderate:1' …><retract …/><reason>kiwi spam</reason></moderate>; client.log:483 aim gets it with by='…/vm-web'.

M30. XEP-0084: aim keeps a contact's avatar after the contact deletes its metadata node

  • verified by a human
  • Spec: XEP-0084 §4.5 names an empty <metadata/> for "no avatar"; XEP-0060 §8.4 (a node delete is notified with <delete/>): a deleted metadata node leaves no avatar to show.
  • Expected: on <event><delete node='urn:xmpp:avatar:metadata'/></event> from a contact aim drops its picture and draws the letter, as it does for Gajim's empty <metadata/> (114916).
  • Seen: Dino 0.5.1 removes its avatar by deleting the metadata node. On prosody aim receives the <delete/> and 11 s later still draws Dino's colour bars in its list. On ejabberd and anotherim-server Dino sends the same owner <delete/> (150119 peer-dino.log:5273, 144950 :7401) but aim's client.log has no event for it, so aim can't know; M30 is judged on prosody only. The runs pass on their scenario (avatars cross both ways); this was outside it. It also kept 150320 (XEP-0392 with Dino, anotherim) from comparing colours.
  • What to look for: 150252 client.log:634 the <delete node='urn:xmpp:avatar:metadata'/> event from vm-dino; aim's shot-3: vm-dino still with the bars.
  • Fix: treat a metadata node <delete/> (and a <purge/> or retract of the current item) as "no avatar".
  • Upstream: not checked.
result server peer what it shows run deciding screenshot
❌ prosody Dino <delete/> of Dino's metadata node received; aim keeps the bars 150252 shot-3
(not judged) ejabberd Dino Dino deletes the node, no event reaches aim; aim keeps the bars 150119 shot-3
(not judged) anotherim Dino the same 144950 shot-3
✅ prosody Gajim Gajim removes with an empty <metadata/> (client.log:576); aim draws the letter 114916 shot-3
Key evidence (150252)
  • 150252 client.log:520 Dino's metadata <info bytes='994' … id='24261029d02cd84f6634bf898c429ef5cb5eac22' type='image/png'/>, :525 aim fetches the data; peer-dino.log:3693 Dino's owner <delete node='urn:xmpp:avatar:metadata'/>, :3703 the event it gets back; client.log:634 (15:06:45) aim's <message from='vm-dino@prosody.asxp.io' type='headline'><event xmlns='http://jabber.org/protocol/pubsub#event'><delete node='urn:xmpp:avatar:metadata'/></event></message>; steps.log:61 shot-3 at 15:06:56.

M31. XEP-0172: after an empty <nick/> anotherim-web's Contact info keeps the old nick (anotherim-js UI)

  • verified by a human
  • Spec: XEP-0172 §4.3 (an empty <nick/>: the user no longer has a nickname).
  • Expected: every place web names vm-aim drops "Aim Kiwi" once aim clears its name.
  • Seen: aim clears its name: an empty <nick xmlns='http://jabber.org/protocol/nick'/> and (ejabberd, anotherim) an empty <vcard xmlns='urn:ietf:params:xml:ns:vcard-4.0'/>; web receives both. web's chat row and header go back to "vm-aim", but its Contact info keeps the title "Aim Kiwi", an "A" letter and "Nickname Aim Kiwi", still 45 s later (anotherim). Sending is right: Gajim drops the nick on all three servers. Recorded as libtacky (no app cause yet); the stale field is anotherim-js's Contact info, so aim would show it the same way.
  • What to look for: web's shot-3: header "vm-aim", the column "Aim Kiwi" / "Nickname Aim Kiwi"; shot-4: row and header "vm-aim".
  • Fix: refresh the Contact info title and Nickname row from the contact's current nick (and vCard4) on the empty-nick event.
  • Upstream: not checked.
result server peer what it shows run deciding screenshot
❌ ejabberd anotherim-web empty nick and vCard4 received; Contact info "Aim Kiwi", row and header "vm-aim" 142149 shot-3
❌ prosody anotherim-web the same after the empty nick 140954 shot-3
❌ anotherim anotherim-web the same, 45 s later 135836 shot-3
✅ prosody Gajim the same empty nick: Gajim shows plain vm-aim 115749 shot-3

Also ✅ Gajim 115550 (ejabberd), 120417 (anotherim).

Key evidence (142149, 140954, 135836)
  • 142149 client.log:576 <nick …>Aim Kiwi</nick>, :675 vCard4 <nickname><text>Aim Kiwi</text></nickname>; :823 the empty <nick xmlns='http://jabber.org/protocol/nick'/>, :935 the empty <vcard xmlns='urn:ietf:params:xml:ns:vcard-4.0'/>; peer-anotherim-web.log:357, :364 web receives both.
  • 140954 client.log:639 the empty nick, peer-anotherim-web.log:273 web gets it; steps.log:74, :85 web's Contact info 10 s later.
  • 135836 client.log:1088, :1194 empty nick and vCard4; peer-anotherim-web.log:565 (14:01:55), :573 (14:01:57), :579 web's own fetch answers the empty nick again; steps.log:85 the Contact info shot at 14:02:41.

To report upstream (peer bugs)

P1. Kaidan 0.16: "Transfer failed: Checksum does not match" on aim's OMEMO 2 upload

  • verified by a human
  • Spec: XEP-0448 (encrypted file sharing: the hash describes the plaintext), XEP-0300.
  • Expected: Kaidan verifies the announced sha-256 against the decrypted file and shows the picture.
  • Seen: Kaidan downloads aim's aesgcm upload and shows "Transfer failed: Checksum does not match", on all three servers (batch 1 too, OMEMO 2 and 2 + 0.3). Proven (131002): the announced sha-256 (WHxhFjkD…) = the decrypted upload's (1216 B) = the hash Kaidan stored; the ciphertext's is cKzooa5Q…. Kaidan compares with other bytes. anotherim-web shows the upload inline.
  • What to look for: Kaidan's shot-2: aim's file card with "Transfer failed: Checksum does not match"; steps.log kaidan's stored hash(es) ['WHxhFjkD…']: equal the plaintext's True, the ciphertext's False.
  • Upstream: report to Kaidan (invent.kde.org/network/kaidan) with the hash check.
result server peer what it shows run deciding screenshot
❌ anotherim Kaidan checksum error; announced = decrypted = Kaidan's stored hash 131002 shot-2
❌ ejabberd Kaidan batch 1: the same (aim sent OMEMO 2 + 0.3) 155210 shot-2
❌ prosody Kaidan batch 1: the same (OMEMO 2 only) 151535 shot-2
✅ prosody anotherim-web batch 1: aim's OMEMO 2 upload shown inline by web 174321 shot-2

Also batch 1 152512 (anotherim, OMEMO 2 only). First seen 2026-11-08 (harness †): ❌ Kaidan on anotherim 152430 (hash checked by hand), ejabberd 163656, prosody 183515; ✅ Gajim (OMEMO 0.3, no hash checked) ejabberd 160145, web ejabberd 214312.

Key stanzas (anotherim-server × Kaidan, 131002)
  • 131002 client.log:354 slot for 1232 B (1216 + GCM tag); steps.log:59 Kaidan's record 'error': 'Transfer failed: Checksum does not match', … 'size': 1216, … 'path': None; :61–:64 plain 1216 B sha256 WHxhFjkD…, PASS aim announced the plaintext's sha-256, kaidan's stored hash(es) ['WHxhFjkD…']: equal the plaintext's True, the ciphertext's False.
  • Batch 1: 155210 steps.log:66, :69–:70; 151535 steps.log:60, :62–:64; 152512 steps.log:66, :68–:70.

Kaidan: Transfer failed: Checksum does not match

P3. Dino 0.5.1 intermittently sends no audio in calls

  • verified by a human
  • Spec: XEP-0167 (Dino accepts the audio content with senders='both' and then sends no audio RTP).
  • Expected: Dino's 660 Hz on aim's speaker in every call, as in the passing runs.
  • Seen: batch 0: 1 of 4 runs on ejabberd (132256, video call). Batch 2: 2 of 3: prosody 231837 (video call; first on prosody), ejabberd 235447 (audio call); anotherim 195624 fine. Always Dino → aim; Dino always heard aim.
  • Cause (132256, again in batch 2): Dino's audio SSRC sends only RTCP, no opus, while its video and fake-mic stream run: Dino doesn't send audio; aim isn't dropping it.
  • Upstream: report to Dino (github.com/dino/dino) with 132256's and 231837's rtp.log; check dino#1487 and open audio issues first.
result server peer what it shows run deciding screenshot
❌ ejabberd Dino video call silent from Dino (0.00), Dino's audio SSRC only RTCP; Dino's video arrives 132256 shot-9
❌ prosody Dino batch 2: the same in the video call, first time on prosody 231837 shot-9
❌ ejabberd Dino batch 2: silent in the audio call; the video call fine 235447 shot-3
✅ anotherim Dino batch 2: tones both ways in both calls 195624 shot-9
✅ ejabberd Dino tones both ways in both calls; Dino sends 400 opus / 8 s 130455 shot-9

Also ✅ batch 0 130351 (prosody), 131143, 131724 (ejabberd). First seen 2026-11-08 (harness †): ❌ ejabberd 204146 (both calls), 152129, 152700; ✅ ejabberd 205011, 205746, anotherim 202351.

Key evidence (132256; batch 2 231837, 235447)
  • 132256 steps.log:85 / :86 aim hears 660 Hz: 0.00 …, dino hears 440 Hz: 0.99 …; rtp.log:164 Dino's NIC out rtp VP8/96 ssrc 3328a7d0: 259 packets …; rtp.log:161 out rtcp rtcp ssrc 290fc134: 2 packets 280 bytes in 8s; peer-dino.log:3645 Payload for audio with opus using ssrc 688898356 (= 0x290fc134); rtp.log:275–:281 Dino's Record Stream uncorked.
  • 231837 steps.log:87 aim hears 0.00 in the video call; rtp.log:11 Dino's SSRC b1a22cd3 RTCP only on aim's NIC, rtp.log:161 the same on Dino's, rtp.log:162 VP8 out; aim sends VP8 1506 and opus 402 (rtp.log:16, :17), Dino receives both (:158, :159).
  • 235447 steps.log:47, :48 aim 0.00, Dino 0.99 in the audio call; steps.log:57 Dino's NIC out rtcp 2; in rtcp 2; in opus/111 400; peer-dino.log:3478 Using FakeMic for outgoing audio as it's default; steps.log:87, :88 the video call 1.00 / 1.00.

P4. Dino 0.5.1 rejects aim's JET cipher urn:xmpp:ciphers:aes-128-gcm-nopadding:0

  • verified by a human
  • Spec: XEP-0396 v0.2.0 contradicts itself: §4 "The 'cipher' attribute MUST be set to 'aes-128-gcm-nopadding:0'" (XEP-0391's URN, what aim sends) vs Example 1 cipher='urn:xmpp:ciphers:aes-128-gcm-nopadding' (what Dino knows).
  • Seen: Dino answers aim's XEP-0396 offer (OMEMO 0.3 key transport) with feature-not-implemented "JET cipher or type unknown"; JET never runs. aim falls back to an OMEMO aesgcm upload, which Dino saves with the right sha-256. All three servers.
  • What to look for: Dino's shot-3: the picture arrived as an ordinary upload; aim's shot-2 has no "Sent directly"; steps.log FAIL it went over Jingle, no upload.
  • Fix (interop, optional): libtacky could also offer/accept the unsuffixed name. aim follows the normative text.
  • Upstream: report to Dino (github.com/dino/dino) and ask the XSF to fix Example 1; check open issues first.
result server peer what it shows run deciding screenshot
❌ anotherim Dino feature-not-implemented "JET cipher or type unknown"; fallback upload saved 161341 shot-3
❌ ejabberd Dino the same 173144 shot-3
✅ ejabberd anotherim-web aim's JET (urn:tacky:jingle:jet-omemo:2, aes-256-gcm) to web: sent directly, no upload 173948 shot-3

Also 163331 (prosody, the same).

Key stanzas (161341, 163331, 173144; control 173948)
  • 161341 client.log:399 aim's offer <security xmlns='urn:xmpp:jingle:jet:0' cipher='urn:xmpp:ciphers:aes-128-gcm-nopadding:0' name='file' type='eu.siacs.conversations.axolotl'>; peer-dino.log:4625–:4629 <feature-not-implemented …/><text …>JET cipher or type unknown.
  • 163331 client.log:260, :265, :313 offer, error, fallback slot; steps.log:53, :55. 173144 client.log:217, :224. Control 173948 client.log:216 cipher='urn:xmpp:ciphers:aes-256-gcm-nopadding:0' … type='urn:tacky:jingle:jet-omemo:2', :267 success.

P5. Dino 0.5.1 rejects aim's IBB transport-replace after losing the tie-break (plain Jingle file transfer)

  • verified by a human
  • Spec: XEP-0166 §7.2.12, §8 (simultaneous transport-replace: the initiator's wins); XEP-0260 §2.4, XEP-0261 (fall back to IBB).
  • Seen (ejabberd only): both SOCKS5 candidates fail (P6), both sides send transport-replace at once (aim to IBB, Dino to SOCKS5 again). aim, the initiator, answers Dino's with <conflict/><tie-break/>; Dino then transport-rejects aim's IBB replace and aim ends with failed-transport. Dino stays at "Downloading 1 kB… (0%)". On prosody and anotherim-server the proxy65 is reachable and the same script passes, so P5 shows only where P6 forces the IBB fallback.
  • What to look for: Dino's shot-3: the stuck "Downloading … (0%)" row; client.log Dino's action='transport-reject' after aim's <tie-break/>.
  • Upstream: report to Dino with 163129's client.log:248–:262; check open issues first.
result server peer what it shows run deciding screenshot
❌ ejabberd Dino candidates fail; tie-break; Dino's transport-reject; failed-transport 163129 shot-3
✅ prosody Dino Dino uses aim's proxy65 candidate; checksum, success; file intact 162820 shot-2
✅ anotherim Dino the same over anotherim-server's public proxy65 160833 shot-3

Plain Jingle files to anotherim-web pass on all three servers over the WebRTC data channel (173433, 180200, 180009), also with OMEMO-sealed DTLS fingerprints (174347).

Key stanzas (163129)
  • 163129 client.log:228 aim's offer with candidates host='10.0.2.15' … type='direct' and host='127.0.0.2' jid='proxy.ejabberd.asxp.io/55238004' port='7777' … type='proxy'; :241, :247 <candidate-error/> both ways; :248, :254 both transport-replaces; :255 aim <tie-break/>; :260 Dino action='transport-reject' (IBB); :262 aim <failed-transport/>.

Gajim 2.6 keeps the old nick after an empty <nick/> (withdrawn, not reproduced)

  • Withdrawn 2026-11-10 (batch 4): not reproduced on any of the three servers. After aim's empty <nick/> Gajim logs the removal and shows plain "vm-aim": ejabberd 115550 peer-gajim.log:6871 Received nickname: vm-aim@ejabberd.asxp.io - nickname removed, :6902 Nickname for vm-aim@ejabberd.asxp.io: None (shot-3); prosody 115749 peer-gajim.log:3986, :4017 (shot-3); anotherim 120417 peer-gajim.log:4634 the event, :4649 "nickname removed", :4680 None (shot-3). The stale nick seen in batch 4 is anotherim-web's Contact info (M31).

Server issues and peer notes

  • P6. ejabberd test server: proxy65 announces 127.0.0.2 (server config, not fixed). Unreachable from the VMs, so SOCKS5 between two NATed VMs falls back to IBB, where P5 ends it. 163129 client.log:228 host='127.0.0.2' … port='7777' … type='proxy', server.log:33754. Controls: prosody's proxy (162820 client.log:261) and anotherim-server's 178.105.240.144:7777 (160833 client.log:403) carry the file. Config: nix/ejabberd.nix#L96 mod_proxy65: { access: local, max_connections: 5, port: 7777 } names no public address; setting its hostname/ip should fix it (not tried).
  • P2. Prosody test server: no STUN/TURN. Fixed by #13 (coturn + mod_turn_external): 130351 client.log:232 extdisco STUN/TURN, :244 relay candidates, tones both ways. Batch 2: every prosody call run used relay candidates (231837, 232601, 233237). Before: service-unavailable (2026-11-08 174618 client.log:191).
  • Prosody 13.0.7 test MUC has no XEP-0425 (mod_muc_moderation not loaded): 214403 peer-gajim.log:3137 (no urn:xmpp:message-moderate in the room's disco#info), verdict na. Loading it would make M29 checkable on all three.
  • Peer lacks added (clients.toml, batch 2): Gajim 2.6 has no XEP-0513 (no urn:xmpp:mentions:0 in nbxmpp 7.4.0 or gajim/); Dino 0.5.1 has no XEP-0425 and no XEP-0513. XEP-0513 therefore ran only aim ↔ web (232223, ejabberd, pass).
  • Dino 0.5.1 doesn't fetch a plain XEP-0447 share by itself (behaviour, not a bug): a round download button instead of aim's picture (its cid: thumbnail unsupported); one click fetches it with the right sha-256. 184517 (peer-dino.log:2504, steps.log:53) and 162123 pass with the click; 160550 is superseded.
  • Dino 0.5.1 on anotherim-server: upstream offers only SCRAM-SHA-256/512(-PLUS) and X-OAUTH2, Dino only PLAIN and SCRAM-SHA-1; worked around on the test server only (vms/README.md Known gaps), upstream unchanged (the user's decision).
  • Dino 0.5: doesn't unlock a room it creates either (not recorded, says nothing about aim). Its replies name its own JID in the XEP-0461 to; aim still shows the quote.
  • anotherim-web (minor, not judged): after a Jingle file transfer web answers aim's checksum session-info with unknown-session, having already terminated (180200 client.log:305, :315); the file had arrived.
  • Dino 0.5.1 shows no line for a kick or a ban in its room (its log has 307 / 301; 201047, 223151). Gajim 2.6 first fails to publish a XEP-0402 bookmark when aim made the node (precondition-not-met, then reconfigures; 232823 peer-gajim.log:23922); bookmark sync still passes.
  • Batch 3 minor (not judged as bugs): aim's Link previews switch reads on for libtacky's default sender, which attaches nothing (the harness flips it to store always); anotherim-web sends its own links without a card (its linkmeta preview threw in the browser, likely CORS, not traced); unmuting a room writes <on-mention/> rather than removing the setting; aim's caps lack urn:xmpp:mds:displayed:0+notify, it still gets the events (own-JID PEP). To OMEMO 0.3 peers (Gajim) libtacky attaches no link preview, and stickers and contact cards go only as their fallback text, as its docs say.
  • Batch 4, Dino 0.5.1 avatars: Dino removes its avatar by deleting the urn:xmpp:avatar:metadata node instead of publishing an empty <metadata/> (XEP-0084 §4.5): 150252 peer-dino.log:3693, 150119 :5273, 144950 :7401 (aim's side is M30). And it ignores aim's empty <metadata/>: it receives it (150119 peer-dino.log:5227, 144950 :7355) and keeps drawing aim's removed gradient. That made 150320 (XEP-0392, the batch's only peer fail) unjudgeable: no letter avatars to compare (shot-3); aim's own colour is right (264.4° for 264.3°).
  • Batch 4, Kaidan 0.16 photo hash: on prosody Kaidan never puts its photo hash in presence (empty <photo/>, 152606 peer-kaidan.log:270); aim gets its picture through Prosody's XEP-0084 mapping and the vCard. On ejabberd and anotherim-server it did advertise a2dfb30a… after setting the picture (152328 client.log:600, 151643 client.log:647), so this is Prosody-only (or timing), not "never".
  • Batch 4, Prosody 13.0.7 avatar mapping: it writes the item id of an empty <metadata/> (a UUID) as the vCard photo hash: 115336 server.log:776 <x xmlns='vcard-temp:x:update'><photo>df9f11bc-c4e6-4b56-a11e-9dc4fdf71744</photo></x> in aim's room presence, Gajim: advertised invalid avatar hash (peer-gajim.log:1873). And it keeps the XEP-0084 avatar it derived from a vCard PHOTO after the PHOTO is removed: aim still draws Kaidan's old picture in 154800 (shot-1). Its vCard4↔vcard-temp mapping is what hides C47 there.
  • Batch 4, XEP-0377 na on all three servers: none advertises urn:xmpp:reporting:1, so aim rightly offers only a plain block: ejabberd 131044 client.log:69, 143949; prosody 125559 client.log:74, 144447; anotherim 115526 client.log:110, 135027.

#1 rechecks (fixed upstream)

  • C34 (#1): fixed in libtacky 1.2.2 (1aed459c), confirmed on all three servers. After a destroy aim republishes the bookmark with autojoin='false' and doesn't recreate the room after a restart: prosody 194144 client.log:289, ejabberd 194543 client.log:311 (steps.log:121), anotherim 221811 client.log:622 (steps.log:124), and every other admin run.
    • anotherim-js#95 (destroyed-room view with "Remove" / "Go to the new room") does not show in rc9: after the destroy the room stays an ordinary "Not joined" room, composer open (shot-11); after the restart the row is gone (shot-13): 225948 steps.log:150 Remove False, Go to the new room False, Join False, shot-11, shot-13. Already commented: anotherim-js#95 (comment).
  • C30 (#1): fixed, confirmed. aim's decline names the inviter and reaches it: 211510 client.log:457 <decline to='vm-web@ejabberd.asxp.io/another.im (Js)'/> (peer-anotherim-web.log:327); 232627 client.log:741; 210455 client.log:294; 204944 client.log:321.
  • C3.1 (#1): fixed (libtacky 1.2.2 7dddfd32), confirmed on all three servers (batch 3). A draft pasted with U+0001 U+0002 is published and reaches the second device, no bgerror: 035239 client.log:685, peer-anotherim-web.log:1142 (shot-7); 034631, 031016.
  • C12 (#1): documented deviation (libtacky bc534bf0, features.md), reproduced batch 4 on all three servers with Dino and Kaidan. After a restart with an avatar set, aim's first presence carries the kept hash <x xmlns='vcard-temp:x:update'><photo>0a69a94cf904a6231fcbb36ef413a2ff8b4a6993</photo></x> 23–233 ms before the result of its own <vCard xmlns='vcard-temp'/> get (XEP-0153 §4.2 MUST NOT); steps.log FAIL C12: no photo hash in aim's presence before its own vCard is read. ejabberd Dino 150544 client.log:701 presence, :760 own vCard result; ejabberd Kaidan 152328 client.log:673 presence, :717 own vCard result; prosody Dino 150721 client.log:549 presence, :571 own vCard result; prosody Kaidan 152606 client.log:593 presence, :620 own vCard result; anotherim Dino 145421 client.log:746 presence, :834 own vCard result; anotherim Kaidan 151643 client.log:710 presence, :798 own vCard result. The vCard avatar exchange itself passes both ways in all six. The Gajim and web XEP-0153 runs (115138, 115336, 122058, 140510, 141359, 141718) predate the restart check and signed in without an avatar, so they don't exercise C12.
  • C8 (#1): not exercisable in rc9. Neither aim's nor web's UI writes XEP-0492's <advanced/>, so there is nothing to keep; the plain mute syncs (042801, 045211, 042110). libtacky 1.2.4 7912074c touches this.

Not implemented in aim (anotherim-js rc9 UI gaps: libtacky can, aim's UI can't; the runs judged them fail, not counted as bugs)

Missing features, like the peers' lacks in clients.toml: libtacky has the call (web used its API in the harness). Filing them as feature requests is open.

  • XEP-0249 sending: Invite… sends only a XEP-0045 mediated invitation; libtacky's muc direct_invite is not in the app's API or UI. Receiving XEP-0249 works (card, Accept, Decline). aim answers a direct invitation's Decline with a mediated <decline/> through the room, which XEP-0249 doesn't define (servers relay it). 9 runs, all three servers: Gajim 204944 client.log:241, 210455 client.log:224, 231404 client.log:535; Dino 210018, 211132, 232148; web 210753, 211510, 232627.
  • XEP-0486 setting a room avatar: no UI; libtacky's muc set_avatar works through the API (web set it in 231007, 224019, 000658). Receiving passes everywhere (status 104, vCard fetch, picture in header and list; Dino too). 9 runs: Gajim 215138, 220119, 234127; Dino + Gajim 222216, 222656, 235930; web 224019, 231007, 000658.
  • XEP-0425 moderating: no moderation action in the message menu; web used message.moderate (230307 peer-anotherim-web.log:260, 001141 :382). The display part is M29.
  • XEP-0272/0482 starting a group call: no UI; web used groupcall.start (214007, 003642, 214938, 004557). Joining and declining from the card work (C43, C44 aside).
  • XEP-0245 /me on receipt (batch 3, all three servers, 9 runs): a received /me nods … is drawn raw in aim's bubble and chat-list preview (and in anotherim-web's); libtacky sets Message.me (api.ts:1205), ui/src never reads it. aim's own /me goes out as typed and Gajim and Dino draw it as an action. Gajim 012515 (shot-4), 014206, 022041; Dino 014614, 015658, 022431; web 013117 (steps.log:34 web shows a raw '/me'), 020052, 022822.
  • urn:tacky:pep-expiry:0 setting a time limit (batch 3, all three servers): aim's status sheet has none, so aim's status carries no <expires/>; receiving works: web's API-set status with <expires/> shows on aim and is dropped at the limit. 045539 (peer-anotherim-web.log:211, client.log:475, shot-3), 041146 (client.log:320), 044109 (client.log:624 aim's status without <expires/>).

Unexplained / candidates (not yet evidence)

  • Hang-up after answering Dino's XEP-0353 call: no session-terminate reaches Dino (untraced). aim's screen ends the call, Dino stays in it 60 s+, on all three servers. Only prosody shows aim's side: 230335 server.log:3969 (23:09:02) is aim's last stanza before the disconnect at :4073 (23:10:42): its stream went quiet ~35 s before the click. On anotherim (203202) and ejabberd (233830) aim's logs end before the click. aim's own calls terminate fine (195624 client.log:435, :556). Maybe a stalled stream, not the hang-up; next: a XEP-0167 run with Dino calling, logs kept to the end.
  • Not judged: web, as caller, sent no session-initiate 37 s after aim's <proceed/> (204353 peer-anotherim-web.log:242; no fake mic in that example). web logged no session-terminate for a video call aim ended (000900). aim re-sent a kick iq 37 s later (223151 client.log:609).
  • aim saw no <idle/> (XEP-0319) from Gajim (no example yet).

Resolved in batch 2: full-JID membership/ban → C45; duplicate chat after a mediated invite → C46; moderated message without moderator → M29. Resolved in batch 1: Dino's JET cipher → P4; plain Jingle files to Dino → P5 + P6; omemo-chats:0 withdrawn (harness), omemo-trust:0 → C42.

Harness issues already fixed in the interop repo

  • Before batch 0: the repo copied into /nix/store per commit (ac41415); guest clock lag after stop/cont (2ff2a33); gnome-keyring aborts (8448d94, 9168a55); screensaver (bbe1727); aim before the keyring (e16143e, 24aca6b); the fake camera unfed, then 4:2:2, now YUY2 (c137b7b, #16 f45e5c5; withdrew C39); a dunst per client start (#16).
  • Batch 0 (#20): pill OCR band sweeps (bc25552, eba181f); notify_calls() reads in the guest (1aaf81a, 5493897); 60 s wait for session-terminate (43c4549); client-tacky.log kept (4638f85); "Answer" fallback (9c3f35a); Gajim popovers and composer (342b1c6).
  • Batch 1 (#21): the pill counted six notches up too (4526b3a); omemo_sync.py verifies own devices (f54a4b7) and needs aim's publish and web's event (a090834); Dino's download button (8d4d2d1, 1be1499; also no false View XML FAILs from sealed PEP publishes); the CBC 32-byte IV retried as 16 (d6cf5b1).
  • Batch 2 (#22): Dino's Reject and aim's Answer from the ringing strip (a717672, b535cd9); Dino's room form (da67334) and Join Channel under a notification (22cf9bf); Gajim's room header after a room picture (4fc6beb); aim's keys column scrolled to a far key (eee9d69) and a key id with one misread digit (daa0f43); the destroyed-room read for anotherim-js#95 (a276241).

Not covered

  • Calls with Gajim (none in 2.x); MUC with Kaidan (none in 0.16); Kaidan calling aim (its start_call isn't scripted, so aim declining a Kaidan call is covered only with Dino and web); group calls with Kaidan.
  • Kaidan → aim retraction: Kaidan 0.16 has no XEP-0424 (S14).
  • XEP-0425 on prosody (server lacks it); XEP-0513 only aim ↔ web on ejabberd (Gajim and Dino lack it); XEP-0482 the caller's retract ("missed") not scripted.
  • XEP-0313 catch-up after a dropped session only on Prosody. XEP-0448 GCM from a third-party client: none on the VM matrix. S15's signals: not re-recorded since 2026-11-08; C38: not rechecked in batch 2.
  • Covered since v6 (batch 2, all three servers): XEP-0045 admin, 0249, 0272/0482, 0353 (incl. answered elsewhere), 0402 across devices, 0486 receiving, omemo-chats:0 / omemo-trust:0, JET-OMEMO 2, DTLS-OMEMO; Kaidan calls on anotherim-server; web calls on prosody (233237).
  • Batch 3: urn:tacky:spans:0 and urn:tacky:live-location:0 are na (libtacky only reads them; no harness client sends them; S6 and M25's live-location part unchecked); urn:tacky:schedule:0 only on anotherim-server (mod_schedule); Dino and Kaidan lack most batch 3 specs (clients.toml).
  • Batch 4: setting Dino's and Kaidan's presence is not scripted (RFC 6121 with them checks aim's presence one way; SKIP presence: kaidan: set_presence not scripted yet); Kaidan's Contact Details don't reliably draw in the VM (155057), so its display of aim's name and picture is judged from the wire; XEP-0191 with Dino and Kaidan only on ejabberd (155048, 155601); XEP-0377 na everywhere (no server offers it).
## Tested | | version | |---|---| | under test | anotherim-js (aim) **0.8.0-rc9**, develop `142ee094`, XML_LOG AppImage; anotherim-web = the browser build of the same commit in Chromium 156 | | backend | libtacky **v1.2.3** (`c95b3d9d`) | | peers | Gajim 2.6.0, Dino 0.5.1, Kaidan 0.16.0, anotherim-web (each in its own NixOS VM, `vms/`) | | servers | Prosody 13.0.7, ejabberd 26.09, anotherim-server `master@b9e675dd` | **Evidence re-recorded 2026-11-09 with provenance (#19): every row links an EVIDENCE.md.** Batch 0 (#20): 16 runs (8 fail, 8 pass). Batch 1 (#21): 48 runs (25 fail: 18 libtacky, 7 peer; 23 pass): file sharing, the S14 and C38 rechecks, own-device sync on ejabberd. **Batch 2 (#22): 75 runs (43 fail: 41 libtacky/anotherim-js, 2 peer; 31 pass; 1 na)**: XEP-0045 admin, 0249, 0272/0482, 0353, 0402 across devices, 0425, 0486, 0513, and call/file/own-device-sync rechecks on all three servers; 18 fails are UI gaps ("Not implemented"). All in `runs/2026110[90]-*-aim-vs-*`. Batch 3 (#23): 84 runs (70 pass, 14 fail), message extras and own-account sync on all three servers; its two new findings are UI gaps ("Not implemented in aim" below). Batch 4 (#26): 94 runs (75 pass, 13 fail, 6 na), profile, presence, roster, blocking on all three servers (`runs/20261110-<ID>-*`, IDs 113843–160046): new C47, C48, M30, M31; #1 C12 reproduced; the Gajim empty-nick note withdrawn. 2026-11-08: 89 runs with Gajim, Dino, Kaidan (77 pass, 12 fail); 2026-11-09: 39 runs against anotherim-web (37 pass, 2 fail, #11). Every run is agent-judged (`verified_by = "ai"`, `confidence = "low"`). Numbering continues #1. Upstream status not checked yet. > **How to check a bug** > 1. Open the run's `EVIDENCE.md`: verdict, what proves it (`file:line` with the quoted stanza), expected / seen / spec, the deciding screenshots and the control. > 2. Look at the named deciding screenshot (`shot-*.png`; `aim` = anotherim-js). > 3. Check a quote or two at its `file:line`: `client.log` = aim's XML console (JSON lines), `client-tacky.log` aim's own log file (libwebrtc errors, withheld stanzas), `client-debug.log` its debug log, `peer-<client>.log` the other client, `server.log` the server, `steps.log` every click, check and measurement, `rtp.log` / `web-rtp.log` / `notify-<vm>.log` the NIC, web getStats and Notify captures. Under OMEMO 2 nothing inside the envelope is in any log: judge that from the screenshots and `steps.log`. > 4. Compare with the ✅ control run (same scenario, other server or peer). > 5. If in doubt, re-run with the `explore` command at the top of the run's EVIDENCE.md, from the interop repo root, with `LIBTACKY=v1.2.3`, `APPIMAGE=` the rc9 XML_LOG AppImage and, for anotherim-web, `WEB=` (`vms/tools/env.zsh`). `explore` records nothing; output in `$INTEROP_VM_WORK/out-NAME`. > 6. Tick `verified by a human` under the bug. > > **Harness and re-run:** each EVIDENCE.md links the driver at the run's `harness_commit` (the same as its `scenario.py`) and gives the re-run command, so the tables leave both out. † (2026-11-08 rows, S15 only now) = script of the run not kept; the re-run is under the table. **Fixed upstream** column: libtacky [v1.2.4–v1.2.7](https://dev.another.im/another-im/libtacky/releases) (2026-11-09/10) name these IDs; anotherim-js develop is on v1.2.7 since [86e54dac](https://dev.another.im/another-im/anotherim-js/commit/86e54dac) (0.8.0). The runs used rc9 / v1.2.3, so every fix awaits a recheck on 0.8.0. Fixed items mentioned elsewhere in this issue: "Also fixed", right under the bug table. | # | Bug | Severity | Status | Fixed upstream | Servers seen | Evidence | Human-checked | |---|-----|----------|--------| --- |--------------|----------|---------------| | S14 | OMEMO 2: aim's retraction goes out in the clear; Kaidan shows the fallback | security/privacy | open, reproduced batch 0 + 1 | 1.2.4 [95e7007d](https://dev.another.im/another-im/libtacky/commit/95e7007d) (bare `<retract id/>` kept beside by default, `retract_hint`) | **all three** | batch 0, 1 | ☐ | | S15 | OMEMO: reactions, receipts, markers in the clear; file metadata whenever a 0.3 envelope goes out | security/privacy | open; file part re-recorded batch 1 | no: sealed only once servers advertise `urn:tacky:push:sealed-signals:0` (none yet) | all three (client-side) | 2026-11-08 †, batch 1 | ☐ | | C35 | MUC: no instant-room configuration after creating a room | correctness | open, **reproduced batch 2**, batch 3 also for a room anotherim-web made (Prosody unlocks after 5 min, deletes the room when empty) | – | prosody (ejabberd, anotherim hide it) | batch 0, 2 | ☐ | | C36 | Calls: a session-accept without `<rtcp-mux/>` is rejected (Kaidan) | correctness | open, **reproduced batch 2 on all three** | 1.2.4 [ebd5f227](https://dev.another.im/another-im/libtacky/commit/ebd5f227), [4b5e279f](https://dev.another.im/another-im/libtacky/commit/4b5e279f), [7d76eab7](https://dev.another.im/another-im/libtacky/commit/7d76eab7) | **all three** | batch 0, 2 | ☐ | | C38 | OMEMO 2 with Kaidan: "can't be read" count grows | correctness | not reproduced by restarts; rose around each XEP-0424 run, unexplained | – | all three | batch 0, 1 | ☐ | | C39 | ~~Calls: aim's outgoing video never shown~~ | – | withdrawn (harness, #16) | – | – | batch 0 | – | | C40 | XEP-0448: aes-256-cbc-pkcs7 shares (Kaidan's) not decrypted; URL-named card, Download keeps the ciphertext | correctness | open (Kaidan's IV off-spec too) | 1.2.5 [c5f1e50e](https://dev.another.im/another-im/libtacky/commit/c5f1e50e) | all three | batch 1 | ☐ | | C41 | XEP-0448: aim's sent picture has no esfs source (aesgcm:// + SIMS only) | correctness | open | 1.2.5 [9f6ac320](https://dev.another.im/another-im/libtacky/commit/9f6ac320): the *stored record* was a clear rebuild, not the wire; recheck which one our evidence shows | all three | batch 1 | ☐ | | C42 | `urn:tacky:omemo-trust:0`: a contact key's change is published only for Undecided → Verified | correctness | **refined batch 2** | 1.2.5 [8a83649a](https://dev.another.im/another-im/libtacky/commit/8a83649a) | ejabberd, prosody (anotherim passes Undecided → Verified) | batch 1, 2 | ☐ | | C43 | XEP-0482: aim's `<accept/>`/`<reject/>`/`<left/>` withheld in an OMEMO room when a member has no OMEMO device | correctness | **new, batch 2**, batch 3 prosody too | partly, 1.2.6 [ad62fa11](https://dev.another.im/another-im/libtacky/commit/ad62fa11): answers to a clear invite go clear; to a sealed one still withheld | **all three** | batch 2, 3 | ☐ | | C44 | Muji group call: anotherim-web hears nothing from aim (opus arrives, level 0) | correctness | **new, batch 2**, side not traced; batch 3 prosody too | – | **all three** | batch 2, 3 | ☐ | | C45 | MUC: membership and ban sent with the occupant's full JID; Prosody refuses them silently | correctness | **new, batch 2** (was a candidate) | 1.2.6 [606eee7f](https://dev.another.im/another-im/libtacky/commit/606eee7f) | **all three** (Prosody refuses, the others apply) | batch 2 | ☐ | | C46 | Mediated room invite: a second 1:1-looking chat of the room's JID; on Prosody no invitation card | correctness | **new, batch 2** (was a candidate) | 1.2.6 [3569c3fa](https://dev.another.im/another-im/libtacky/commit/3569c3fa), [bcad614e](https://dev.another.im/another-im/libtacky/commit/bcad614e), [3bb27017](https://dev.another.im/another-im/libtacky/commit/3bb27017), [8ff28739](https://dev.another.im/another-im/libtacky/commit/8ff28739) (also: ejabberd's room password in the invite body hidden) | **all three** | batch 2 | ☐ | | C47 | XEP-0054: aim writes its profile only as vCard4 (XEP-0292); Kaidan (vcard-temp only) reads an empty card | correctness | **new, batch 4** | – | ejabberd, anotherim (Prosody maps vCard4 to vcard-temp and hides it) | batch 4 | ☐ | | C48 | XEP-0191: the OMEMO device-list fetch that failed while blocked is never retried after the unblock; the next message fails "no devices" | correctness | **new, batch 4** (seen as web, same libtacky) | – | prosody (ejabberd, anotherim answer the blocked fetch) | batch 4 | ☐ | | M26 | No desktop notification while aim's window has focus | minor | open (may be intended) | – | all three | batch 0 | ☐ | | M27 | "View XML" shows OMEMO 2 messages with the 0.3 EME namespace | minor | open, cause in libtacky | 1.2.4 [94769ce0](https://dev.another.im/another-im/libtacky/commit/94769ce0) | anotherim, prosody (client-side) | batch 0, 1 | ☐ | | M28 | anotherim-web shows no desktop notification | minor | open (harness Chromium only) | – | all three | batch 0 | ☐ | | M29 | XEP-0425: a moderated message shows as "Message retracted", without moderator or reason | minor | **new, batch 2** (was a candidate) | library side, 1.2.6 [67894a54](https://dev.another.im/another-im/libtacky/commit/67894a54) (`moderated_by`, `retract_reason`); anotherim-js must show them | ejabberd, anotherim (prosody: na) | batch 2 | ☐ | | M30 | XEP-0084: aim keeps a contact's avatar after the contact deletes its metadata node | minor | **new, batch 4** | – | prosody (ejabberd, anotherim send aim no `<delete/>`) | batch 4 | ☐ | | M31 | XEP-0172: after an empty `<nick/>` anotherim-web's Contact info keeps the old nick | minor (anotherim-js UI) | **new, batch 4** | – | **all three** | batch 4 | ☐ | | P1 | Kaidan: "Checksum does not match" on aim's OMEMO 2 upload | peer (Kaidan) | proven Kaidan's, report upstream | – | all three | batch 0, 1 | ☐ | | P2 | ~~Prosody test server hands out no STUN/TURN~~ | server config | fixed by #13 | – | prosody | batch 0 | ☐ | | P3 | Dino intermittently sends no audio in calls | peer (Dino) | **reproduced batch 2, first time on prosody** (2 of 3 runs), report upstream | – | ejabberd, prosody | batch 0, 2 | ☐ | | P4 | Dino 0.5.1 rejects aim's JET cipher `aes-128-gcm-nopadding:0` | peer (Dino; XEP-0396 contradicts itself) | recorded batch 1 | 1.2.5 [04e6935d](https://dev.another.im/another-im/libtacky/commit/04e6935d) (worked around: Dino's cipher spelling sent, both read) | all three | batch 1 | ☐ | | P5 | Dino 0.5.1 rejects aim's IBB transport-replace after losing the tie-break | peer (Dino) | recorded batch 1 | – | ejabberd only (needs P6) | batch 1 | ☐ | | P6 | ejabberd test server: proxy65 announces 127.0.0.2 | server config | recorded batch 1, not fixed | – | ejabberd | batch 1 | ☐ | ### Also fixed: items mentioned elsewhere in this issue | Item | Section | Fixed in | Commit | Status | |---|---|---|---|---| | Hang-up after answering Dino's XEP-0353 call: no session-terminate reaches Dino ([230335](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-230335-XEP-0353-aim-vs-dino+web-as-aim-prosody/EVIDENCE.md)) | Unexplained / candidates | libtacky 1.2.7 | [781b33ce](https://dev.another.im/another-im/libtacky/commit/781b33ce): the terminate is written before the media is closed | recheck pending | | anotherim-js#95: destroyed-room view not shown in rc9 ([225948](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-225948-XEP-0045-aim-vs-gajim-anotherim-server/EVIDENCE.md)) | #1 rechecks (C34) | libtacky 1.2.7 | [c9421614](https://dev.another.im/another-im/libtacky/commit/c9421614): cause in libtacky, the room's state stayed `joined` after `<destroy/>` | recheck pending | | anotherim-web answers aim's checksum `session-info` with `unknown-session` after a Jingle file transfer ([180200](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-180200-XEP-0234-aim-vs-web-prosody/EVIDENCE.md)) | Server issues and peer notes | libtacky 1.2.5 | [39d2ff82](https://dev.another.im/another-im/libtacky/commit/39d2ff82): no `<checksum/>` after the last byte (it raced the receiver's terminate) | recheck pending | | Dino shows a download button instead of aim's picture, `cid:` thumbnail unsupported ([184517](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-184517-XEP-0447-aim-vs-dino-ejabberd/EVIDENCE.md)) | Server issues and peer notes | libtacky 1.2.5 | [c9dfdb6f](https://dev.another.im/another-im/libtacky/commit/c9dfdb6f): the share also carries the thumbnail as a `data:` URI (≤16 KiB), sealed when encrypted | recheck pending (Dino still fetches the file only on click) | | C34 (#1): a destroyed room recreated empty after a restart | #1 rechecks | libtacky 1.2.2 | [1aed459c](https://dev.another.im/another-im/libtacky/commit/1aed459c) | confirmed batch 2, all three servers | | C30 (#1): aim's decline doesn't reach the inviter | #1 rechecks | before rc9 (#1) | – | confirmed batch 2 | | P2: Prosody test server hands out no STUN/TURN | Server issues and peer notes | interop #13 (coturn + `mod_turn_external`) | – | confirmed batch 2 (relay candidates in every prosody call run) | Run links: `https://dev.another.im/another-im/interop/src/branch/main/runs/<dir>/`. A bare run ID (Also lines, lists) is `runs/20261109-<ID>-*` (batch 2 IDs from 000000: `20261110-`), each with an EVIDENCE.md; 2026-11-08 runs are `runs/20261108-<ID>-*` (no EVIDENCE.md). Harness and runs came in with #10, #11, #14, #16, #17, #20, #21, #22. #1 rechecks (C34, C30), the features anotherim-js lacks, and untraced observations are under the peer sections. ## Security / privacy ### S14. OMEMO 2: aim's retraction goes out in the clear; Kaidan shows the fallback - [ ] verified by a human - **Spec:** XEP-0420 §9 (elements other than server-processed ones MUST be inside the `<envelope/>`); XEP-0424 §4. - **Expected:** in an OMEMO 2 chat aim sends `<retract/>` (and its fallback body) inside the SCE `<content/>`. - **Seen:** aim sends `<retract id=…/>` + "This message was retracted." + `<fallback/>` outside the encryption in an OMEMO 2 chat, on all three servers (batch 1; on ejabberd the retracted line went out as OMEMO 2 + 0.3). Kaidan keeps the original and shows the fallback as a new message marked *Unencrypted*. anotherim-web and Gajim (OMEMO 0.3, where the retraction is in the clear too) apply it. - Kaidan 0.16 has no XEP-0424 (from 0.17), so showing the fallback is expected of it; the bug is aim's: the retraction leaves the encryption. - **What to look for:** Kaidan's shot-3: "x424 … mango" still there, below it "This message was retracted." *Unencrypted*; `steps.log` `INFO aim sent: <retract/> in clear True, encrypted False`. - **Fix:** send body-less signals (retract, reactions, receipts, displayed markers) inside the envelope in OMEMO 2 chats, as S2 did for chat states and markable. - **Upstream:** not checked. Follow-up to S2 (#1), which left body-less signals in the clear on purpose. | result | server | peer | what it shows | run | deciding screenshot | |---|---|---|---|---|---| | ❌ | ejabberd | Kaidan | OMEMO 2 chat, clear retract; Kaidan keeps "mango" and adds "This message was retracted." *Unencrypted* | [151803](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-151803-XEP-0424-aim-vs-kaidan-ejabberd/EVIDENCE.md) | [shot-3](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-151803-XEP-0424-aim-vs-kaidan-ejabberd/shot-3-kaidan-after.png) | | ❌ | prosody | Kaidan | the same | [154924](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-154924-XEP-0424-aim-vs-kaidan-prosody/EVIDENCE.md) | [shot-3](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-154924-XEP-0424-aim-vs-kaidan-prosody/shot-3-kaidan-after.png) | | ❌ | anotherim | Kaidan | the same | [173523](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-173523-XEP-0424-aim-vs-kaidan-anotherim-server/EVIDENCE.md) | [shot-3](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-173523-XEP-0424-aim-vs-kaidan-anotherim-server/shot-3-kaidan-after.png) | | ✅ | anotherim | Gajim | OMEMO 0.3 chat, the same clear retract; Gajim applies it, and aim applies Gajim's | [131816](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-131816-XEP-0424-aim-vs-gajim-anotherim-server/EVIDENCE.md) | [shot-3](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-131816-XEP-0424-aim-vs-gajim-anotherim-server/shot-3-gajim-after.png) | Also batch 0 131521 (anotherim, the same). First seen 2026-11-08: ❌ Kaidan on anotherim 151843 and prosody 151043; ✅ web on ejabberd 203321, prosody 203621, anotherim 222157; ✅ Gajim on ejabberd 141129, prosody 145644, anotherim 133702. <details><summary>Key stanzas (batch 1; control 131816)</summary> - `151803 client.log:239` the line aim retracts goes out encrypted (0.3 and OMEMO 2); `:250` its retraction, no `<encrypted/>`: `<retract xmlns='urn:xmpp:message-retract:1' id='1794237639606892'/><body>This message was retracted.</body><fallback xmlns='urn:xmpp:fallback:0' for='urn:xmpp:message-retract:1'/>`; `peer-kaidan.log:670`–`:678` Kaidan receives it as is. - The same: `154924 client.log:246`, `173523 client.log:368`; `steps.log:37` / `:40` / `:49` `aim sent: <retract/> in clear True, encrypted False`. Control 131816: `client.log:379` clear retraction under 0.3, `peer-gajim.log:3503` Gajim applies it. </details> ### S15. OMEMO: reactions, receipts, markers in the clear; file metadata whenever a 0.3 envelope goes out - [ ] verified by a human - **Spec:** XEP-0420 §9 (OMEMO 2); XEP-0384 §7 (0.3 has no SCE); XEP-0454 §2 (only the aesgcm:// URL, inside the envelope). - **Expected:** in OMEMO 2 chats inside the envelope; in 0.3 chats no plaintext metadata beyond a generic body (S3 in #1). - **Seen (signals, 2026-11-08, not re-recorded):** `<reactions xmlns='urn:xmpp:reactions:0'>` + `<store/>`, `<received/>` and `<displayed/>` unencrypted in OMEMO 0.3 and OMEMO 2 chats. - **Seen (file metadata, batch 1):** a clear SIMS `<media-sharing xmlns='urn:xmpp:sims:1'>` (name, media type, size) rides beside the ciphertext **whenever a 0.3 envelope goes out**: in every 0.3 chat (Gajim, Dino: 6 of 6 runs, all servers) and in OMEMO 2 + 0.3 sends on ejabberd, where vm-kaidan and vm-web still have 0.3 devices. anotherim-web's own file messages leak the same (161932 `client.log:253`), so it is libtacky's. **OMEMO 2 alone: nothing beside the envelope** (Kaidan and web on prosody and anotherim). All peers still show the pictures. - **What to look for:** `client.log` at the cited line: `</encrypted>` followed by `<media-sharing xmlns='urn:xmpp:sims:1'>…<name>paste-….png</name><size>1216</size>`; aim's View XML (shot-3) shows the SIMS reference under "Encrypted with OMEMO 0.3" or "OMEMO 2 + 0.3". - **Fix:** signals: as S14. Files: no SIMS outside the envelope whenever a 0.3 envelope is sent; see S3's `legacy_file_metadata` (default on) in #1. - **Upstream:** not checked; S3 in #1 covers the 0.3 file metadata. | result | server | peer | what it shows | run | deciding screenshot | |---|---|---|---|---|---| | ❌ | ejabberd | Gajim | OMEMO 0.3 upload, SIMS name/type/size in the clear; both pictures shown | [154053](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-154053-XEP-0454-aim-vs-gajim-ejabberd/EVIDENCE.md) | [shot-3](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-154053-XEP-0454-aim-vs-gajim-ejabberd/shot-3-aim-view-xml.png) | | ❌ | ejabberd | Kaidan | OMEMO 2 + 0.3 at once, SIMS beside both envelopes | [155210](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-155210-XEP-0454-aim-vs-kaidan-ejabberd/EVIDENCE.md) | [shot-3](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-155210-XEP-0454-aim-vs-kaidan-ejabberd/shot-3-aim-view-xml.png) | | ❌ | ejabberd | anotherim-web | OMEMO 2 + 0.3, SIMS from aim and from web | [161932](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-161932-XEP-0454-aim-vs-web-ejabberd/EVIDENCE.md) | [shot-3](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-161932-XEP-0454-aim-vs-web-ejabberd/shot-3-aim-view-xml.png) | | ✅ | prosody | anotherim-web | OMEMO 2 only: envelope, fallback body, EME, nothing about the file | [174321](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-174321-XEP-0454-aim-vs-web-prosody/EVIDENCE.md) | [shot-2](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-174321-XEP-0454-aim-vs-web-prosody/shot-2-web-got-pic.png) | | ❌ | prosody | Kaidan | 2026-11-08: OMEMO 2 chat, aim's reaction in the clear | [140011](https://dev.another.im/another-im/interop/src/branch/main/runs/20261108-140011-XEP-0444-kaidan-prosody/run.toml) | — (log only) | | ❌ | ejabberd | Kaidan | 2026-11-08: OMEMO 2 chat, `<received/>` receipts in the clear | [131609](https://dev.another.im/another-im/interop/src/branch/main/runs/20261108-131609-XEP-0184-kaidan-ejabberd/run.toml) | — (log only) | Also batch 1, 0.3: Gajim 151334 (anotherim), 155434 (prosody); Dino 151933 (anotherim), 154657 (ejabberd), 160043 (prosody); the P4 fallback uploads (161341 `client.log:457`, 173144 `:270`). 2 + 0.3: 162532. ✅ OMEMO 2 only: 151535, 152512, 174225, 174804, 175603. 2026-11-08 (harness †; re-run `SERVER=… vms/tools/vmrun.zsh explore vms/examples/xep0444.py s15 <peer>`, xep0184.py for receipts): reactions with Gajim 133147, Dino 142139; 0.3 uploads 162721, 160145. <details><summary>Key stanzas (ejabberd: Gajim 154053, Kaidan 155210, web 161932; control prosody 174321)</summary> - `154053 client.log:242`: `<encrypted xmlns='eu.siacs.conversations.axolotl'>…<body>I sent you an OMEMO encrypted message…</body><reference …><media-sharing xmlns='urn:xmpp:sims:1'><file …><media-type>image/png</media-type><name>paste-2026-11-09T15-43-25.png</name><size>1216</size>…` - `155210 client.log:283` the same beside OMEMO 2 + 0.3; `161932 client.log:240` aim's, `:253` web's (`<name>image.png</name><size>126984</size>`). - Control `174321 client.log:252`: `…</encrypted><body>…</body><encryption xmlns='urn:xmpp:eme:0' namespace='urn:xmpp:omemo:2'/><store xmlns='urn:xmpp:hints'/></message>`. - 2026-11-08: `140011 client.log:185` `<reactions xmlns='urn:xmpp:reactions:0' …><reaction>👍</reaction></reactions>` (OMEMO 2 chat, `:156`); `131609 client.log:167` `<received xmlns='urn:xmpp:receipts' …/>` (`:148`). </details> ## Correctness ### C35. MUC: no instant-room configuration after creating a room - [ ] verified by a human - **Spec:** XEP-0045 §10.1.2 (the owner submits an empty `muc#owner` form to unlock an instant room), §10.1.1 (a new room is locked until configured). - **Expected:** on status 201, aim submits `<query xmlns='http://jabber.org/protocol/muc#owner'><x xmlns='jabber:x:data' type='submit'/></query>` (or the configuration the user chose). - **Seen:** aim joins a new room, gets 201 + 110 and owner, and never sends `muc#owner`. Prosody keeps the room locked ("Room is locked, denying entry") and every other joiner gets `item-not-found`: Gajim shows "Failed to Join Group Chat / item-not-found", Dino "Room does not exist". ejabberd and anotherim-server open such rooms anyway: there aim also sends no `muc#owner` (0 in every control's client.log), so it is the same behaviour, hidden. - **Batch 2:** [`muc_room_lock_timeout`](https://hg.prosody.im/trunk/file/tip/plugins/muc/lock.lib.lua) (default 5 minutes) unlocks the room at the next join after it lapses, still without `muc#owner` (195222 `server.log:1116` 19:53:50 → `:1517` 19:59:24; 327–356 s in the XEP-0249 runs). The room is not persistent: Prosody deletes it when its last occupant leaves (002115 `server.log:2103`–`:2106`), so aim's autojoin bookmark leads nowhere. web's rooms (same app) are locked too (224019, 225635). Every batch-2 room run on prosody hit it. - **Fix:** answer status 201 with the instant-room submit in libtacky's join path. - **Upstream:** not checked. | result | server | peer | what it shows | run | deciding screenshot | |---|---|---|---|---|---| | ❌ | prosody | Gajim | no muc#owner; Gajim's join refused, item-not-found on screen | [125134](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-125134-XEP-0045-aim-vs-gajim-prosody/EVIDENCE.md) | [shot-2](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-125134-XEP-0045-aim-vs-gajim-prosody/shot-2-gajim-join-error.png) | | ❌ | prosody | Dino | batch 2: two joins refused ("Room does not exist"), the third let in 5 min 34 s later by Prosody's timeout | [195222](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-195222-XEP-0045-aim-vs-dino-prosody/EVIDENCE.md) | [shot-2](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-195222-XEP-0045-aim-vs-dino-prosody/shot-2-dino-join-error.png) | | ❌ | prosody | Dino | batch 2 (XEP-0402): Dino refused; on aim's restart Prosody deletes the empty room, the bookmark leads nowhere | [002115](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-002115-XEP-0402-aim-vs-dino-prosody/EVIDENCE.md) | [shot-2](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-002115-XEP-0402-aim-vs-dino-prosody/shot-2-dino-join-error.png) | | ❌ | prosody | anotherim-web | batch 3 (XEP-0272): the room web (libtacky) made stays locked too; aim's join on the invite gets no self-presence in 90 s, joins after 360 s (`steps.log:64`, `:84`) | [013459](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-013459-XEP-0272-aim-vs-web-prosody/EVIDENCE.md) | [shot-2](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-013459-XEP-0272-aim-vs-web-prosody/shot-2-aim-join-refused.png) | | ✅ | ejabberd | Gajim | batch 2: same 201, no muc#owner; ejabberd opens the room, every admin step reaches Gajim | [194543](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-194543-XEP-0045-aim-vs-gajim-ejabberd/EVIDENCE.md) | [shot-6](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-194543-XEP-0045-aim-vs-gajim-ejabberd/shot-6-gajim-roles.png) | Also batch 2 on prosody: 194144 (Gajim, within the lock window only), 201824 (web, 352 s), 204944 (Gajim, 327 s), 210018 (Dino, 346 s), 210753 (web, 352 s; web's room too), 224019 (web's room, 356 s), 225030 and 225635 (autojoins refused). Batch 0: 125811 (Dino), control 142257. First seen 2026-11-08: ❌ on prosody with Gajim 160636, Dino 172222, web 212252; ✅ on ejabberd and anotherim with all three (harness †). <details><summary>Key stanzas (prosody 125134, 195222, 002115; control 194543)</summary> - `125134 client.log:193` the room created for aim: `<presence from='claude-test-165135@conference.prosody.asxp.io/vm-aim' …><x xmlns='http://jabber.org/protocol/muc#user'><status code='201'/><item affiliation='owner' … role='moderator'/><status code='110'/></x>`; no `muc#owner` in `client.log` (`steps.log:21` "room created (201) True, muc#owner submitted False"); `server.log:1195` `Room is locked, denying entry`; `peer-gajim.log:1490` `<item-not-found …/>`. - `195222 steps.log:21` the same; `server.log:1291`, `:1363` `Room is locked, denying entry` (19:55:48, 19:56:24); `server.log:1517` (19:59:24) Dino's third join creates an occupant, `peer-dino.log:2342` status 110, participant. - `002115 server.log:2103` `"claude-test-042116@conference.prosody.asxp.io" empty, destroying`, `:2106` `Deleting claude-test-042116@…`; after the restart `client.log:428`/`:429` aim's disco#info of the room, item-not-found at `:505`, `:506`. - Control `194543 client.log:194` status 201, no `muc#owner` until the destroy; Gajim in the room. </details> ### C36. Calls: a session-accept without `<rtcp-mux/>` is rejected (Kaidan) - [ ] verified by a human - **Spec:** XEP-0167 §5 (`<rtcp-mux/>` is optional); XEP-0338 (BUNDLE grouping optional); RFC 5761. - **Expected:** a call without rtcp-mux negotiated works (separate RTCP), or is refused with a clear reason and a `session-terminate`. - **Seen:** aim's `session-initiate` offers `<rtcp-mux/>` and BUNDLE; Kaidan's `session-accept` has neither. aim's WebRTC rejects the answer ("Failed to set remote answer sdp: The m= section with mid='0' is invalid. RTCP-MUX is not enabled when it is required", on screen and in `client-tacky.log`), sends no terminate, and stays "Ringing…"; Kaidan ends the call with `failed-transport` after ~30 s. Dino's and web's accepts carry `<rtcp-mux/>` and connect. - **Batch 2:** the same in both calls on all three servers, now with the camera feed and, on prosody, TURN (#13), so ICE is not the cause: anotherim 201136 (the first Kaidan call run there), prosody 232601, ejabberd 000154. - **Fix:** set the RTCPeerConnection's `rtcpMuxPolicy` to `negotiate` where available, or answer such an accept with `session-terminate` `<failed-application/>` and tell the user. - **Upstream:** not checked. | result | server | peer | what it shows | run | deciding screenshot | |---|---|---|---|---|---| | ❌ | ejabberd | Kaidan | accept without rtcp-mux rejected in both calls, no RTP; Kaidan's failed-transport | [125726](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-125726-XEP-0167-aim-vs-kaidan-ejabberd/EVIDENCE.md) | [shot-3](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-125726-XEP-0167-aim-vs-kaidan-ejabberd/shot-3-aim-in-audio-call.png) | | ❌ | anotherim | Kaidan | batch 2: the same, first Kaidan call run on anotherim-server | [201136](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-201136-XEP-0167-aim-vs-kaidan-anotherim-server/EVIDENCE.md) | [shot-3](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-201136-XEP-0167-aim-vs-kaidan-anotherim-server/shot-3-aim-in-audio-call.png) | | ❌ | prosody | Kaidan | batch 2: the same with TURN in place | [232601](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-232601-XEP-0167-aim-vs-kaidan-prosody/EVIDENCE.md) | [shot-3](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-232601-XEP-0167-aim-vs-kaidan-prosody/shot-3-aim-in-audio-call.png) | | ✅ | anotherim | Dino | batch 2: Dino's accept has rtcp-mux; tones and video both ways | [195624](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-195624-XEP-0167-aim-vs-dino-anotherim-server/EVIDENCE.md) | [shot-9](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-195624-XEP-0167-aim-vs-dino-anotherim-server/shot-9-aim-in-video-call.png) | Also batch 2 000154 (ejabberd); ✅ web on all three (200337, 233237, 000900); batch 0 control 130455. First seen 2026-11-08: ❌ Kaidan on ejabberd 153643 and prosody 175130; ✅ Dino on ejabberd 205011 (harness †). <details><summary>Key stanzas (125726; batch 2 201136, 232601, 000154)</summary> - `125726 client.log:226` aim's audio `session-initiate` offers BUNDLE and `<rtcp-mux/>`; `:248` Kaidan's `session-accept` has neither (video: `:326`); `client-tacky.log:217` (video `:545`): `Failed to set remote answer sdp: The m= section with mid='0' is invalid. RTCP-MUX is not enabled when it is required.`; `client.log:277` only Kaidan terminates, `<failed-transport/>`; `rtp.log:2`, `:102` no RTP or DTLS. - `201136 client.log:384`, `:465` Kaidan's accepts without rtcp-mux; `client-tacky.log:244`, `:603` the rejection; `client.log:413`, `:520` failed-transport; `steps.log:58`, `:59` `stun 2 dtls 0` / `stun 0 dtls 0`. - `232601 client.log:356` offer with `<rtcp-mux/>`, `:385` accept without; `client-tacky.log:284`, `:630`; `client.log:410`, `:511` failed-transport. - `000154 client.log:541`; `client-tacky.log:217`, `:546`; `client.log:652` `<failed-transport/><text>ICE connection could not be established.</text>`. </details> ### C38. OMEMO 2 with Kaidan: "can't be read" count grows - [ ] verified by a human - **Status: not reproduced by restarts on any server; but the count rose around each XEP-0424 run with Kaidan, on all three servers, unexplained.** Kept open. Not rechecked in batch 2. - **Spec:** XEP-0384 §4.4, §5.3 (key exchanges). - **Expected:** no new undecryptable messages from a peer whose messages decrypt; an archived message already handled is not counted again. - **2026-11-08:** the banner "N encrypted messages can't be read on this device" for vm-kaidan grew with sign-ins (2 → 5), by eye; aim re-fetches the same archived key exchange (124708: MAM id `1794140133942834` in four queries). - **Restarts (batch 0 + 1):** `omemo_restart.py` (a line each way, 3 restarts, pill read each time, also 6 notches up): the count never changed (0 on all three in batch 1; 2 throughout in batch 0, 134000); each sign-in re-fetches the same 3 archived key exchanges without counting them. - **Around XEP-0424 (batch 1):** after each S14 run a pill sits just above Kaidan's "x424 … kiwi" of that run, though the last shot of the S14 run had none: ejabberd 0 after 150810, then 155210 [shot-5](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-155210-XEP-0454-aim-vs-kaidan-ejabberd/shot-5-aim-got-pic.png) "5 … can't be read"; prosody 0 after 153800, then 161442 [shot-1](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-161442-XEP-0447-aim-vs-kaidan-prosody/shot-1-aim-encryption-off.png) "1 …"; anotherim 0 after 161731, then 4 in 173523 (`steps.log:14`, `:66`) after a discarded XEP-0424 attempt. Kaidan's messages in 151803 look like those in the passing 150810 (`client.log:205`, `:213`); which stanzas get counted was not traced. - **Fix:** not established. Log the stanza-id and reason of each failed decrypt; dedupe MAM results by stanza-id. Next: `omemo_restart.py` with the XEP-0424 steps. - **Upstream:** not checked. | result | server | peer | what it shows | run | deciding screenshot | |---|---|---|---|---|---| | ✅ (not reproduced) | ejabberd | Kaidan | 0 before and after 3 restarts; 3 archived kex messages re-fetched each sign-in | [150810](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-150810-XEP-0384-aim-vs-kaidan-ejabberd/EVIDENCE.md) | [shot-6](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-150810-XEP-0384-aim-vs-kaidan-ejabberd/shot-6-aim-count-restart-3.png) | | ✅ (not reproduced) | prosody | Kaidan | 0 at the bottom and 6 notches up, start and 3 restarts | [153800](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-153800-XEP-0384-aim-vs-kaidan-prosody/EVIDENCE.md) | [shot-10](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-153800-XEP-0384-aim-vs-kaidan-prosody/shot-10-aim-count-restart-3-up.png) | | ✅ (not reproduced) | anotherim | Kaidan | the same | [161731](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-161731-XEP-0384-aim-vs-kaidan-anotherim-server/EVIDENCE.md) | [shot-9](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-161731-XEP-0384-aim-vs-kaidan-anotherim-server/shot-9-aim-count-restart-3.png) | | ❌ (rise) | anotherim | Kaidan | the S14 run: 4 before and after, 0 at the end of 161731 | [173523](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-173523-XEP-0424-aim-vs-kaidan-anotherim-server/EVIDENCE.md) | [shot-2](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-173523-XEP-0424-aim-vs-kaidan-anotherim-server/shot-2-aim-retracted.png) | Also 150531 (prosody, read only at the bottom; re-recorded as 153800) and batch 0 134000 (2 throughout). First seen 2026-11-08 (Kaidan, harness †): ejabberd 124708 (4), 122920 (3); prosody 123333 (3), 121829; anotherim 122631 (2). <details><summary>Key evidence (150810, 173523; 2026-11-08 124708)</summary> - `150810 steps.log:35` … `:122` `aim undecryptable (n, failed) start: (0, 0)` … `restart-3: (0, 0)`; `:111` `archived kex messages fetched again across sign-ins: [('1794237037887154', 3), ('1794237040310281', 3), ('1794237096097015', 3)]`. - `173523 steps.log:14` `before: (4, 4)`, `:66` `after: (4, 4)`. - `124708 client.log:132`, `:141`, `:239`, `:250` one archived Kaidan message from MAM queries `…-2` to `-5`: `<result xmlns='urn:xmpp:mam:2' id='1794140133942834' …><key kex='true' rid='989476894'>…`. </details> ### C39. ~~Calls: anotherim-js's outgoing video never shown on the other side~~ (withdrawn, harness) - **Withdrawn 2026-11-09:** the harness fed the loopback camera a 4:2:2 format libwebrtc rejects; with YUY2 (#16, f45e5c5) the web shows aim's live test card ([133803](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-133803-XEP-0167-aim-vs-web-ejabberd/EVIDENCE.md), [shot-10](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-133803-XEP-0167-aim-vs-web-ejabberd/shot-10-web-in-video-call.png)). Batch 2: aim's card shown by Dino and web on all three servers (195624, 231837, 235447, 200337, 233237, 000900). ### C40. XEP-0448: aes-256-cbc-pkcs7 shares are not decrypted; a URL-named card, Download keeps the ciphertext - [ ] verified by a human - **Spec:** XEP-0448 §3.2 (decrypt the esfs source with its cipher, key, IV), §4 Table 1 (`aes-128-gcm-nopadding:0`, `aes-256-gcm-nopadding:0`, `aes-256-cbc-pkcs7:0`; CBC: 256-bit key, 128-bit IV). - **Expected:** aim decrypts Kaidan's share, checks its sha-256 and shows `pic-…-kaidan.png` inline. - **Seen:** Kaidan 0.16 sends pictures under OMEMO 2 as an XEP-0447 share with one XEP-0448 source, cipher `urn:xmpp:ciphers:aes-256-cbc-pkcs7:0` (its only one), plus the ciphertext's URL as body and OOB. aim ignores the CBC source and falls back to the URL: a card named after its last segment (`5tguSQyso9`), and Download saves the **ciphertext** without an error. All three servers. aim's record keeps the share's name and hashes; only the cipher is missing. - **Cause (libtacky v1.2.3):** [`file.tcl` `esfs_url`, L2919–L2931](https://dev.another.im/another-im/libtacky/src/tag/v1.2.3/tacky/xmpp/modules/file.tcl#L2919-L2931) reads only the two GCM ciphers ("aes-256-cbc-pkcs7 is not read"). - **Kaidan's part:** its CBC IV is **32 bytes** (§4: 128 bits; 153012 `steps.log:117`); its first 16 bytes decrypt to the announced sha-256 (155852 `steps.log:117`–`:118`). CBC support alone won't open Kaidan 0.16's files unless libtacky tolerates that IV. - **What to look for:** aim's shot-5: a card with a random 10-letter name and "Download"; shot-6: "Save", no picture; shot-7 (View XML): an `urn:xmpp:esfs:0` source with the CBC cipher. - **Fix:** support `aes-256-cbc-pkcs7:0` (taking a 32-byte IV's first 16 bytes, or reporting it); when a share can't be opened, say so on the card (the share's name, "can't decrypt") instead of offering the ciphertext. - **Upstream:** libtacky not checked. Kaidan's IV: report to Kaidan (invent.kde.org/network/kaidan) once checked against 0.17. | result | server | peer | what it shows | run | deciding screenshot | |---|---|---|---|---|---| | ❌ | prosody | Kaidan | card "5tguSQyso9", Download keeps the ciphertext; the 32-byte IV fails | [153012](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-153012-XEP-0448-aim-vs-kaidan-prosody/EVIDENCE.md) | [shot-5](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-153012-XEP-0448-aim-vs-kaidan-prosody/shot-5-aim-got-pic.png) | | ❌ | ejabberd | Kaidan | card "msj4P9ttjf"; IV[:16] decrypts to Kaidan's sha-256 | [155852](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-155852-XEP-0448-aim-vs-kaidan-ejabberd/EVIDENCE.md) | [shot-5](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-155852-XEP-0448-aim-vs-kaidan-ejabberd/shot-5-aim-got-pic.png) | | ✅ | prosody | anotherim-web | an aes-256-gcm XEP-0448 share: decrypted, hash verified, inline | [175603](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-175603-XEP-0448-aim-vs-web-prosody/EVIDENCE.md) | [shot-5](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-175603-XEP-0448-aim-vs-web-prosody/shot-5-aim-got-pic.png) | Also 153241 (anotherim, "fjosqIUewW") and the same card in the XEP-0454 runs with Kaidan (155210 "MoKmgqSiig", 151535, 152512; 2026-11-08 163656, 183515; batch 0 131002). <details><summary>Key evidence (153012, 153241, 155852)</summary> - `153241 peer-kaidan.log:562` Kaidan's slot `filename="fjosqIUewW" size="126992"` (PNG 126984 B, padded). - `153012 steps.log:112` aim's record: `<file-sharing xmlns='urn:xmpp:sfs:0'> … <name>pic-193013-kaidan.png</name> … <encrypted xmlns='urn:xmpp:esfs:0' cipher='urn:xmpp:ciphers:aes-256-cbc-pkcs7:0'>`; `:103` the card with Download; `:117` `key 32 iv 32`, `Invalid IV size (32) for CBC.` - `155852 steps.log:117`–`:118` `kaidan's announced sha-256 […] = the fetched plaintext's: True` (IV[:16]). </details> ### C41. XEP-0448: aim's sent picture has no esfs source (aesgcm:// + SIMS only) - [ ] verified by a human - **Spec:** XEP-0448 §3.1 (the sender's share carries `<encrypted xmlns='urn:xmpp:esfs:0' cipher=…>` with key, IV and the inner source). - **Expected:** under OMEMO 2 aim's picture carries an XEP-0447 share with an esfs source, as anotherim-web's does, so a client that reads XEP-0448 but not aesgcm:// can open it. - **Seen:** aim (desktop, a pasted picture) sends an aesgcm:// body + SIMS inside OMEMO 2, no esfs source: on prosody and ejabberd no share at all; on anotherim-server an SFS share whose only source is `<url-data target='aesgcm://…#key'/>` (174804 `steps.log:77`). web's picture (same build) has an aes-256-gcm esfs source, which aim decrypts and shows. web reads aesgcm, so aim's picture still shows there. - **Where (libtacky v1.2.3, not traced):** the sender code exists ([`message.tcl` `EsfsSource`/`EncryptedShareNodes`, L5045–L5093](https://dev.another.im/another-im/libtacky/src/tag/v1.2.3/tacky/xmpp/modules/message.tcl#L5045-L5093)); the desktop paste path evidently doesn't reach it. - **What to look for:** aim's shot-3 (View XML of its picture): aesgcm:// body and SIMS, no `urn:xmpp:esfs:0`; shot-6 (View XML of web's): an `urn:xmpp:esfs:0` source, `cipher='urn:xmpp:ciphers:aes-256-gcm-nopadding:0'`, key and IV. - **Fix:** send pasted/attached pictures through the share path the web build uses. - **Upstream:** not checked. | result | server | peer | what it shows | run | deciding screenshot | |---|---|---|---|---|---| | ❌ | prosody | anotherim-web | aim's record aesgcm:// + SIMS, no esfs; web's esfs share decrypted (judged fail to match the other servers) | [175603](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-175603-XEP-0448-aim-vs-web-prosody/EVIDENCE.md) | [shot-3](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-175603-XEP-0448-aim-vs-web-prosody/shot-3-aim-view-xml.png) | | ❌ | anotherim | anotherim-web | aim's SFS share has only an aesgcm:// url-data source | [174804](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-174804-XEP-0448-aim-vs-web-anotherim-server/EVIDENCE.md) | [shot-3](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-174804-XEP-0448-aim-vs-web-anotherim-server/shot-3-aim-view-xml.png) | Also 162532 (ejabberd, plus S15's clear SIMS). <details><summary>Key evidence (174804, 175603)</summary> - `174804 client.log:392` aim's message, OMEMO 2 only; `steps.log:77` its record: `<file-sharing xmlns='urn:xmpp:sfs:0' …> … <sources> <url-data … target='aesgcm://…/paste-2026-11-09T17-50-27.png#…'/> </sources>`; `:127`–`:130` web's share `aes-256-gcm-nopadding:0`, `key 32 iv 12`, sha-256 verified. - `175603 client.log:255` aim's picture (record aesgcm:// + SIMS, shot-3); `client.log:267`, `steps.log:127`, `:130` web's share decrypted and verified. </details> ### C42. `urn:tacky:omemo-trust:0`: a contact key's change is published only for Undecided → Verified - [ ] verified by a human - **Spec:** `urn:tacky:omemo-trust:0` (libtacky `docs/protocol/omemo-trust-0.md`, `docs/features.md`): every user trust decision (`omemo trust` without `-blind`) is published sealed to the own devices. - **Expected:** aim marks vm-gajim's key Verified (or back to Accepted) → a sealed `<publish node='urn:tacky:omemo-trust:0'>` → web, signed in as vm-aim with aim's device Verified, applies it. - **Seen (refined batch 2):** a contact-key decision is published only for **Undecided → Verified** (anotherim 194814: web shows Verified within the second). **Accepted → Verified** (ejabberd 182653, prosody 002914) and **Verified → Accepted** (002914) publish nothing; the other device stays stale. Own-device decisions are published (181559 `client.log:246`, `:270`, `:283`); `omemo-chats:0` syncs on all three servers. - **What to look for:** `steps.log` `aim set #… from Accepted to Verified`, then `aim published urn:tacky:omemo-trust:0: 0` / `False; web got its event: False`; no `<publish node='urn:tacky:omemo-trust:0'>` after it in `client.log` / `server.log`. - **Fix:** publish the trust list on every user decision about a contact's device, whatever its previous state. - **Upstream:** not checked. | result | server | peer | what it shows | run | deciding screenshot | |---|---|---|---|---|---| | ❌ | ejabberd | anotherim-web as vm-aim | Accepted → Verified on aim, no publish, web keeps an older list | [182653](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-182653-urn-tacky-omemo-trust-0-aim-vs-web-as-aim-ejabberd/EVIDENCE.md) | [shot-5](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-182653-urn-tacky-omemo-trust-0-aim-vs-web-as-aim-ejabberd/shot-5-web-gajim-keys.png) | | ❌ | prosody | anotherim-web as vm-aim | batch 2: Accepted → Verified and Verified → Accepted, neither published | [002914](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-002914-urn-tacky-omemo-trust-0-aim-vs-web-as-aim-prosody/EVIDENCE.md) | [shot-3](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-002914-urn-tacky-omemo-trust-0-aim-vs-web-as-aim-prosody/shot-3-aim-verified.png) | | ✅ | anotherim | anotherim-web as vm-aim | batch 2: Undecided → Verified, published and applied by web | [194814](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-194814-urn-tacky-omemo-trust-0-aim-vs-web-as-aim-anotherim-server/EVIDENCE.md) | [shot-4](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-194814-urn-tacky-omemo-trust-0-aim-vs-web-as-aim-anotherim-server/shot-4-web-verified.png) | Also ✅ `omemo-chats:0`: 181559 (ejabberd), 193707 (anotherim), 235706 (prosody). <details><summary>Key evidence (182653, 002914; control 194814)</summary> - `182653 steps.log:134` `aim lists vm-gajim@ejabberd.asxp.io's keys [('170', 'Accepted')]`; `:169` `aim set #170 from Accepted to Verified`; `client.log:25`, `:75` the only omemo-trust traffic: the sign-in fetch (item of 18:22:08). - `002914 steps.log:185` aim sets the key Accepted → Verified; `:187` `aim published urn:tacky:omemo-trust:0: False; web got its event: False`; `server.log:1382` the only omemo-trust stanza, the sign-in fetch, none through the restore at 00:38:37 (`steps.log:208`) to the end (00:39:49). - `194814 steps.log:182` `aim set #193428444 from Undecided to Verified`; `client.log:424` `<publish node='urn:tacky:omemo-trust:0'><item id='current'>` (19:53:50), acked `:426`; `peer-anotherim-web.log:406` web's event; `steps.log:203` `PASS web lists #193428444 Verified`. </details> ### C43. XEP-0482: aim's `<accept/>`/`<reject/>`/`<left/>` withheld in an OMEMO room when a member has no OMEMO device - [ ] verified by a human - **Spec:** XEP-0482 §3.1 (the invitee answers the invitation with `<accept/>`/`<reject/>`, `<left/>` on leaving, to the chat the invite came from). - **Expected:** declining Dino's group-call invitation sends `<reject id=…/>` to the room, so Dino stops inviting aim; joining and leaving send `<accept/>` and `<left/>`. The invitation came in the clear, so a clear answer leaks nothing new. - **Seen:** in Dino's private room (OMEMO on for aim) Dino's `<invite xmlns='urn:xmpp:call-invites:0' multi='true'>` arrives in the clear. After Decline aim's card reads "Declined", but libtacky finds no OMEMO device for vm-dino and withholds the `<reject/>` rather than send it in the clear; aim's room shows "Not sent: it could not be encrypted for vm-dino (no device with OMEMO keys)". Dino never learns of it. Joining works (Muji is presence and Jingle), but `<accept/>` and `<left/>` are withheld the same way. In web's unencrypted rooms all three go out. - **What to look for:** aim's shot after Decline: card "Group call · Declined" and the "Not sent …" banner; `client-tacky.log:21`; no `urn:xmpp:call-invites:0` stanza from aim in `client.log` after the click. - **Fix:** send XEP-0482 answers the way the invitation came (in the clear when it was clear), or at least don't mark the card "Declined" while the answer was not sent. - **Upstream:** not checked. | result | server | peer | what it shows | run | deciding screenshot | |---|---|---|---|---|---| | ❌ | anotherim | Dino | Decline: `<reject/>` withheld, "Not sent" banner, card "Declined" | [210351](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-210351-XEP-0482-aim-vs-dino-anotherim-server/EVIDENCE.md) | [shot-4](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-210351-XEP-0482-aim-vs-dino-anotherim-server/shot-4-aim-declined.png) | | ❌ | ejabberd | Dino | the same | [002620](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-002620-XEP-0482-aim-vs-dino-ejabberd/EVIDENCE.md) | [shot-4](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-002620-XEP-0482-aim-vs-dino-ejabberd/shot-4-aim-failed-call.png) | | ❌ | prosody | Dino | batch 3: the same, `client-tacky.log:21` `<reject/> withheld` | [014802](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-014802-XEP-0482-aim-vs-dino-prosody/EVIDENCE.md) | [shot-3](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-014802-XEP-0482-aim-vs-dino-prosody/shot-3-aim-invite-card.png) | | ✅ | anotherim | anotherim-web | web's unencrypted room: aim's `<reject/>` goes to the room, web gets it | [214938](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-214938-XEP-0482-aim-vs-web-anotherim-server/EVIDENCE.md) | [shot-4](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-214938-XEP-0482-aim-vs-web-anotherim-server/shot-4-aim-declined.png) | | ✅ | ejabberd | anotherim-web | the same | [004557](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-004557-XEP-0482-aim-vs-web-ejabberd/EVIDENCE.md) | [shot-4](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-004557-XEP-0482-aim-vs-web-ejabberd/shot-4-aim-declined.png) | Also the XEP-0272 runs with Dino, which pass on the call itself: 205652 and 001757 (accept and left withheld). <details><summary>Key evidence (002620, 210351, 205652; control 004557)</summary> - `002620 client.log:653` Dino's invitation in the clear: `<invite xmlns='urn:xmpp:call-invites:0' … multi='true' video='false'><muji xmlns='urn:xmpp:jingle:muji:0' room='b34172aa@conference.ejabberd.asxp.io'/></invite>`; `steps.log:61` Decline clicked. - `002620 client-tacky.log:20` `OMEMO claude-test-042621@conference.ejabberd.asxp.io: not sent; no usable device for vm-dino@ejabberd.asxp.io:no_devices`; `:21` `groupcall: <reject/> withheld: no member of claude-test-042621@conference.ejabberd.asxp.io can receive an encrypted call invite, and it is not sent in the clear`. - `210351 client.log:517` the clear invite; `client-tacky.log:21` (21:08:15.313) the same `<reject/> withheld`. - `205652 client-tacky.log:21` `groupcall: <accept/> withheld: …` (21:01:00), `:537` `<left/> withheld: …` (21:01:44); `001757 client-tacky.log:21`, `:424` likewise. - Control `004557 client.log:569` `<message … to='claude-test-044557@conference.ejabberd.asxp.io' type='groupchat'><reject xmlns='urn:xmpp:call-invites:0' id='6c7d2f21bda9a91ad9840a124e521147'/><store xmlns='urn:xmpp:hints'/></message>`; `peer-anotherim-web.log:334` web gets it. `214007 client.log:509` `<accept/>`, `:613` `<left/>` in web's room. </details> ### C44. Muji group call: anotherim-web hears nothing from aim (opus arrives, level 0) - [ ] verified by a human - **Spec:** XEP-0272 §3 (media between the call room's participants). - **Expected:** in a group call both sides hear each other, as aim and Dino do (205652, 001757). - **Seen:** web starts a group call (API `groupcall.start`, no UI), aim joins from its card and opens a Jingle session to web. aim hears web (1.00); web hears nothing (0.00), though aim's opus reaches web's NIC at full rate (403 packets / 8 s) and web's getStats counts it with `audioLevel` 0, `energy` 0. Not the autoplay policy (`--autoplay-policy=no-user-gesture-required`). The pair's 1:1 calls play both ways, and Dino hears aim in a group call. Whether aim sends silence on this leg or web doesn't render the track is **not traced**; both ends are anotherim-js. - **What to look for:** `steps.log` `web hears 440 Hz: 0.00`; web's NIC `in rtp opus/111 … 403 packets`; web's getStats line with `"audioLevel":0,"energy":0`. - **Fix:** trace first: compare aim's outbound-rtp `audioLevel` in this call with the 1:1 call, and web's inbound track's attachment to an audio element in the group-call view. - **Upstream:** not checked. | result | server | peer | what it shows | run | deciding screenshot | |---|---|---|---|---|---| | ❌ | ejabberd | anotherim-web | web hears 0.00 while aim's opus arrives; getStats level 0 | [003642](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-003642-XEP-0272-aim-vs-web-ejabberd/EVIDENCE.md) | [shot-5](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-003642-XEP-0272-aim-vs-web-ejabberd/shot-5-web-in-call.png) | | ❌ | anotherim | anotherim-web | the same | [214007](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-214007-XEP-0272-aim-vs-web-anotherim-server/EVIDENCE.md) | [shot-5](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-214007-XEP-0272-aim-vs-web-anotherim-server/shot-5-web-in-call.png) | | ❌ | prosody | anotherim-web | batch 3 (XEP-0272, after C35's lock lapsed): aim hears 660 Hz 1.00, web hears 440 Hz 0.00; `web-rtp.log:1` 93 packets, `audioLevel` 0 | [013459](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-013459-XEP-0272-aim-vs-web-prosody/EVIDENCE.md) | [shot-4](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-013459-XEP-0272-aim-vs-web-prosody/shot-4-aim-invite-card.png) | | ✅ | ejabberd | Dino | Dino's group call: tones both ways | [001757](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-001757-XEP-0272-aim-vs-dino-ejabberd/EVIDENCE.md) | [shot-4](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-001757-XEP-0272-aim-vs-dino-ejabberd/shot-4-aim-in-call.png) | | ✅ | ejabberd | anotherim-web | 1:1 call of the same pair: tones both ways | [000900](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-000900-XEP-0167-aim-vs-web-ejabberd/EVIDENCE.md) | [shot-10](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-000900-XEP-0167-aim-vs-web-ejabberd/shot-10-web-in-video-call.png) | Also ✅ 205652 (Dino, anotherim), 200337 (1:1 with web, anotherim). <details><summary>Key evidence (003642, 214007)</summary> - `003642 client.log:452` web's invitation, `:668` aim's `session-initiate` to web, `:689` web's `session-accept`; `steps.log:57`, `:58` `aim hears 660 Hz: 1.00`, `web hears 440 Hz: 0.00`. - `003642 rtp.log:16` web's NIC `in rtp opus/111 ssrc bed035e4: 403 packets 64778 bytes in 8s` (its own out, `:20`); `:24` `{"type":"inbound-rtp","kind":"audio","bytes":135855,"packets":1693,"lost":0,…,"audioLevel":0,"energy":0}`, rising at `:25`. - `214007 steps.log:59` `aim hears 660 Hz True (1.00); web hears 440 Hz False (0.00)`; `:62`, `:63` `out opus/111 403; … in opus/111 403` on both NICs; `web-rtp.log:9` `"inbound-rtp","kind":"audio",…"packets":2060,…"audioLevel":0,"energy":0`. </details> ### C45. MUC: membership and ban sent with the occupant's full JID; Prosody refuses them silently - [ ] verified by a human - **Spec:** XEP-0045 §9.1 (a ban "MUST be performed based on the occupant's bare JID"), §9.3 (membership, bare JIDs in the examples), §5.2 (affiliations are bare-JID based). - **Expected:** aim's members pane sends `<item affiliation='member|outcast' jid='vm-gajim@…'/>` with the bare JID. - **Seen:** aim sends the occupant's full JID (`jid='vm-dino@prosody.asxp.io/interop'`) for membership and ban in every batch-2 admin run. **Prosody** refuses both (`jid-malformed`, "Bare JID expected, got full JID"): nobody is banned and **aim shows no error**. ejabberd and anotherim-server apply them (301) to one resource. Nick-based steps (subject, roles, kick, destroy) work. - **What to look for:** `client.log` the `muc#admin` `<item affiliation='outcast' jid='…/resource'>`; on prosody the `type='error'` answer with `<jid-malformed/>` right after it; aim's screen unchanged. - **Fix:** strip the resource (or use the occupant's real bare JID) in libtacky's affiliation change; show iq errors from `muc#admin` to the user. - **Upstream:** not checked. | result | server | peer | what it shows | run | deciding screenshot | |---|---|---|---|---|---| | ❌ | prosody | Dino | membership and ban by full JID, both refused jid-malformed; Dino never banned, aim silent | [195222](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-195222-XEP-0045-aim-vs-dino-prosody/EVIDENCE.md) | [shot-5](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-195222-XEP-0045-aim-vs-dino-prosody/shot-5-dino-rejoined.png) | | ❌ | prosody | anotherim-web | the same with web's full JID | [201824](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-201824-XEP-0045-aim-vs-web-prosody/EVIDENCE.md) | [shot-6](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-201824-XEP-0045-aim-vs-web-prosody/shot-6-web-kicked.png) | | ❌ | ejabberd | Gajim | full JID; ejabberd applies the ban (301) | [194543](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-194543-XEP-0045-aim-vs-gajim-ejabberd/EVIDENCE.md) | [shot-9](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-194543-XEP-0045-aim-vs-gajim-ejabberd/shot-9-gajim-banned.png) | | ❌ | anotherim | Dino | full JID; the server applies it | [223151](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-223151-XEP-0045-aim-vs-dino-anotherim-server/EVIDENCE.md) | [shot-9](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-223151-XEP-0045-aim-vs-dino-anotherim-server/shot-9-dino-banned.png) | Also 201047 (ejabberd, Dino), 204649 (ejabberd, web), 221811, 225948 (anotherim, Gajim), 224559 (anotherim, web); 194144 never got that far (C35). No ✅ control: the spec is the comparison; the server's error is the proof on prosody. <details><summary>Key stanzas (195222, 201824, 194543)</summary> - `195222 client.log:285` `<item affiliation='member' jid='vm-dino@prosody.asxp.io/interop'/>`; `:286` Prosody: `<iq … type='error'><error type='modify'><jid-malformed …/><text>Bare JID expected, got full JID</text>`; `:310` `<item affiliation='outcast' jid='vm-dino@prosody.asxp.io/interop'><reason>kiwi ban</reason></item>`, refused at `:312`; `steps.log:113` no 301. - `201824 client.log:325` / `:351` membership and ban with `jid='vm-web@prosody.asxp.io/another.im (Js)~…'`, refused at `:327` / `:353`. - `194543 client.log:270` `<query xmlns='http://jabber.org/protocol/muc#admin'><item affiliation='member' jid='vm-gajim@ejabberd.asxp.io/gajim.QS09QHG1'/></query>`; `:294` `<item affiliation='outcast' jid='vm-gajim@ejabberd.asxp.io/gajim.QS09QHG1'><reason>kiwi ban</reason></item>`; `peer-gajim.log:2972` status 301. - `223151 client.log:592`, `:689` the same with `vm-dino@anotherim.asxp.io/interop`; `peer-dino.log:8619` 301. </details> ### C46. Mediated room invite: a second 1:1-looking chat of the room's JID; on Prosody no invitation card - [ ] verified by a human - **Spec:** XEP-0045 §7.8.2 (mediated invitation: a `<message/>` from the room's bare JID carrying `<x xmlns='http://jabber.org/protocol/muc#user'><invite from=…/>`). - **Expected:** a mediated invitation shows as a room invitation (card with Accept / Decline) and nothing else; accepting joins the room. - **Seen:** anotherim-js (aim and web) also files the invitation as a 1:1 chat with the room's bare JID: two rows with the room's name, the room and a "Last seen recently" chat holding the invite text; after Decline that chat stays, unread (211510). On ejabberd and anotherim-server the card works too. **On Prosody there is no card**: only a chat request from the room's JID ("Messages from people not in your contacts"), so the invitee must join by address (210753, 224019). Gajim's XEP-0249 invitations show the card on every server. Why Prosody's relay gets no card was not traced (all three servers add `jabber:x:conference`). - **What to look for:** two rows named after the room in the receiver's list; on prosody the "Messages from people not in your contacts" request instead of a "Room invitations" card. - **Fix:** route a muc#user `<invite/>` to the invitation card only, not to a chat; accept it on Prosody's relay. - **Upstream:** not checked. | result | server | peer | what it shows | run | deciding screenshot | |---|---|---|---|---|---| | ❌ | ejabberd | anotherim-web | both sides get a duplicate chat per invitation; aim keeps one after Decline | [211510](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-211510-XEP-0249-aim-vs-web-ejabberd/EVIDENCE.md) | [shot-1](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-211510-XEP-0249-aim-vs-web-ejabberd/shot-1-web-joined.png) | | ❌ | anotherim | anotherim-web | the same (two "claude-test-032628" rows on web, two "…b" on aim) | [232627](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-232627-XEP-0249-aim-vs-web-anotherim-server/EVIDENCE.md) | [shot-1](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-232627-XEP-0249-aim-vs-web-anotherim-server/shot-1-web-joined.png) | | ❌ | anotherim | Dino | Dino's mediated invite leaves aim the same second chat | [205652](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-205652-XEP-0272-aim-vs-dino-anotherim-server/EVIDENCE.md) | [shot-2](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-205652-XEP-0272-aim-vs-dino-anotherim-server/shot-2-aim-in-room.png) | | ❌ | prosody | anotherim-web | no card on web, only a chat request from the room's JID; joined by address | [210753](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-210753-XEP-0249-aim-vs-web-prosody/EVIDENCE.md) | [shot-1](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-210753-XEP-0249-aim-vs-web-prosody/shot-1-web-join-error.png) | | ❌ | prosody | anotherim-web | the same on aim's side for web's invitation | [224019](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-224019-XEP-0486-aim-vs-web-prosody/EVIDENCE.md) | [shot-2](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-224019-XEP-0486-aim-vs-web-prosody/shot-2-aim-join-error.png) | | ✅ | prosody | Gajim | Gajim's XEP-0249 invitation: aim's card, Accept joins | [204944](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-204944-XEP-0249-aim-vs-gajim-prosody/EVIDENCE.md) | [shot-4](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-204944-XEP-0249-aim-vs-gajim-prosody/shot-4-aim-accepted.png) | Also 204649 (shots 8, 9), 224559 (shot 2), 001757 (aim's call card in that chat), 000658 (the room's picture on it). <details><summary>Key evidence (211510, 210753, 205652)</summary> - `211510 client.log:338` web's invitation as aim receives it from the room: `<invite from='vm-web@ejabberd.asxp.io/another.im (Js)'/>`; `peer-anotherim-web.log:52` aim's to web; EVIDENCE "Seen": web two "claude-test-011510" rows, aim two "claude-test-011510b" rows and a "claude-test-011510c" chat with an unread badge after Decline. - `210753 client.log:287` aim's mediated invite; `peer-anotherim-web.log:112` Prosody's relay with an added `jabber:x:conference`; web's first presence to the room is `peer-anotherim-web.log:165`, its join by address after 352 s (`steps.log:85`). - `224019 client.log:239` web's invitation reaches aim; aim's only join `client.log:296`, by address after 356 s (`steps.log:83`). - `205652 client.log:482` Dino's mediated invite from the room's bare JID; shot-2 two "claude-test-005653" rows. </details> ### C47. XEP-0054: aim writes its profile only as XEP-0292 vCard4, never vcard-temp; Kaidan reads an empty card - [ ] verified by a human - **Spec:** XEP-0054 §3.2, §3.3 (the user's vCard is set with `<iq type='set'><vCard xmlns='vcard-temp'/>` and read by others with a get to the bare JID); XEP-0292 §5 leaves the vcard-temp mirror optional. - **Expected:** a vcard-temp-only client (Kaidan 0.16) reads the full name aim set in Edit profile. - **Seen:** Edit profile › Save publishes only `<publish node='urn:xmpp:vcard4'>…<fn><text>aim Mango</text></fn>`; aim sends no vcard-temp `set` in any run. ejabberd and anotherim-server keep the two apart, so Kaidan's vcard-temp get for vm-aim returns `<vCard xmlns='vcard-temp'><NICKNAME/></vCard>` and Kaidan keeps calling it "vm-aim". Prosody maps vCard4 to vcard-temp, so there Kaidan gets `<FN>aim Mango</FN>` and the bug is hidden. Gajim and web read vCard4 (their XEP-0054 runs pass on all three). The other direction works everywhere: aim reads Kaidan's vcard-temp FN "kaidan Mango". - **What to look for:** `client.log` aim's Save: a `urn:xmpp:vcard4` publish and no `vcard-temp` set after it; `peer-kaidan.log` Kaidan's vcard-temp get of vm-aim answered without FN; Kaidan's shot-2: its chat with "vm-aim", no name. - **Fix:** also write the profile to vcard-temp (or merge into it) when the server doesn't advertise a vCard4↔vcard-temp mapping, as other XEP-0292 clients do. - **Upstream:** not checked. | result | server | peer | what it shows | run | deciding screenshot | |---|---|---|---|---|---| | ❌ | ejabberd | Kaidan | vCard4 only; Kaidan's vcard-temp get of vm-aim answered `<NICKNAME/>` | [152828](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-152828-XEP-0054-aim-vs-kaidan-ejabberd/EVIDENCE.md) | [shot-2](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-152828-XEP-0054-aim-vs-kaidan-ejabberd/shot-2-kaidan-info.png) | | ❌ | anotherim | Kaidan | the same | [152203](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-152203-XEP-0054-aim-vs-kaidan-anotherim-server/EVIDENCE.md) | [shot-2](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-152203-XEP-0054-aim-vs-kaidan-anotherim-server/shot-2-kaidan-info.png) | | ✅ | prosody | Kaidan | control: Prosody's mapping hands Kaidan `<FN>aim Mango</FN>`; aim shows "kaidan Mango" | [155057](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-155057-XEP-0054-aim-vs-kaidan-prosody/EVIDENCE.md) | [shot-1](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-155057-XEP-0054-aim-vs-kaidan-prosody/shot-1-aim-info.png) | | ✅ | ejabberd | Gajim | control: Gajim reads aim's vCard4 | [120045](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-120045-XEP-0054-aim-vs-gajim-ejabberd/EVIDENCE.md) | [shot-2](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-120045-XEP-0054-aim-vs-gajim-ejabberd/shot-2-gajim-info.png) | Also ✅ (vCard4 readers): Gajim 121041 (anotherim), 120250 (prosody); web 142654 (ejabberd), 140411 (anotherim), 141505 (prosody). On prosody Kaidan's Contact Details drew nothing (`155057 steps.log:54`, `:56`), so its display of the name there is unshown; the wire decides. <details><summary>Key evidence (152828, 152203; control 155057)</summary> - `152828 client.log:531` aim's `<publish node='urn:xmpp:vcard4'>…<fn><text>aim Mango</text></fn>`; `peer-kaidan.log:1872` Kaidan's `<vCard xmlns="vcard-temp"/>` get of vm-aim while the name is set, `:1880` ejabberd's answer `<vCard xmlns="vcard-temp"><NICKNAME/></vCard>`; `peer-kaidan.log:1849` Kaidan's own set with `<FN>kaidan Mango</FN>`, `client.log:566` aim reads it. - `152203 client.log:601` the vCard4 publish, no vcard-temp set after it; `peer-kaidan.log:1346` Kaidan's get, `:1355` `<vCard xmlns="vcard-temp"><NICKNAME/></vCard>`; `peer-kaidan.log:1321` Kaidan's FN, `client.log:630`, `:637` aim reads it. - Control `155057 client.log:411` the same vCard4 publish; `peer-kaidan.log:1268` Kaidan reads `<FN>aim Mango</FN>` from Prosody's mapping. </details> ### C48. XEP-0191: after an unblock libtacky never refetches the OMEMO device list that failed while blocked - [ ] verified by a human - **Spec:** XEP-0191 §3.4 (after an unblock communication resumes); XEP-0384 §5.3.1 (device list from PEP). - **Expected:** once aim unblocks vm-web, web's next message is encrypted to aim's devices and arrives. - **Seen (prosody):** while blocked, web (anotherim-js, the same libtacky) asks vm-aim's `urn:xmpp:omemo:2:devices` and legacy device list for the first time in its session; Prosody answers both `service-unavailable`, and web's "while blocked" line is not sent: "no devices on vm-aim@prosody.asxp.io's v2 or legacy device lists". aim unblocks (pushed); a minute later web types "after unblock": no new device-list fetch, the same error, the line never reaches aim. The failed fetch is kept as "no devices" and only an event or a new session would replace it. aim's block and unblock themselves work. - **ejabberd and anotherim-server don't show it:** they answer the blocked contact's PEP get with the device lists (`143450 peer-anotherim-web.log:207`, `134439 :340`), so web encrypts, the server bounces the message `service-unavailable` (XEP-0191 §3.3), and "after unblock" arrives. Gajim on prosody, with aim's devices fetched before the block, passes (125018). - **What to look for:** web's shot-1: "prf 183457 while blocked" with "no devices on vm-aim@prosody.asxp.io's v2 or legacy device lists" and Retry; in `peer-anotherim-web.log` no device-list get to vm-aim after `:158`. The "after unblock" failure has no shot in the run (`failed-blocking-web.png` stayed in the out dir); `steps.log:68` is the proof. - **Fix:** don't cache a failed (error) device-list fetch as an empty list; refetch on the next send to that contact, or at least on an unblock push. - **Upstream:** not checked. | result | server | peer | what it shows | run | deciding screenshot | |---|---|---|---|---|---| | ❌ | prosody | anotherim-web | device lists `service-unavailable` while blocked, never refetched; "after unblock" fails "no devices" | [143456](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-143456-XEP-0191-aim-vs-web-prosody/EVIDENCE.md) | [shot-1](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-143456-XEP-0191-aim-vs-web-prosody/shot-1-web-unsent.png) | | ✅ | ejabberd | anotherim-web | the blocked fetch answered; message bounced while blocked, "after unblock" arrives | [143450](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-143450-XEP-0191-aim-vs-web-ejabberd/EVIDENCE.md) | [shot-2](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-143450-XEP-0191-aim-vs-web-ejabberd/shot-2-aim-unblocked.png) | | ✅ | anotherim | anotherim-web | the same | [134439](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-134439-XEP-0191-aim-vs-web-anotherim-server/EVIDENCE.md) | [shot-2](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-134439-XEP-0191-aim-vs-web-anotherim-server/shot-2-aim-unblocked.png) | | ✅ | prosody | Gajim | the same block and unblock; Gajim's line after the unblock arrives | [125018](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-125018-XEP-0191-aim-vs-gajim-prosody/EVIDENCE.md) | [shot-2](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-125018-XEP-0191-aim-vs-gajim-prosody/shot-2-aim-unblocked.png) | Also ✅ XEP-0191 with Gajim 123324 (ejabberd), 114810 (anotherim); Dino 155048 and Kaidan 155601 (ejabberd only). <details><summary>Key evidence (143456; controls 143450, 134439)</summary> - `143456 client.log:325` aim's `<block xmlns='urn:xmpp:blocking'><item jid='vm-web@prosody.asxp.io'/>`, `:343` its `<unblock …>`, `:351` the push. - `143456 peer-anotherim-web.log:157`, `:158` web's gets of vm-aim's `urn:xmpp:omemo:2:devices` and `eu.siacs.conversations.axolotl.devicelist`; `:160`, `:161` both `<error type='cancel'><service-unavailable …/>`; no later get to vm-aim. `steps.log:36` `web sent nothing while blocked`, `:62` web types "prf 183457 after unblock" (14:40:12, aim unblocked 14:39:24), `:68` `FAIL blocking: AssertionError: web: no message to vm-aim@prosody.asxp.io in web.log after 60s`. - `143450 peer-anotherim-web.log:204`, `:205` web's gets while blocked, `:207`, `:210` ejabberd answers with the items; `client.log:504` "after unblock" arrives. `134439 peer-anotherim-web.log:337`, `:338` gets, `:340`, `:343` items; `:357` the bounce. </details> ## Minor / SHOULD ### M26. No desktop notification while aim's window has focus - [ ] verified by a human - **Spec:** none (UX). XEP-0184/0333 runs only. - **Expected:** a decision: notify when the chat isn't open even if the window has focus, or document it. May be intended. - **Seen:** a message to a chat that isn't open (chat closed, or another chat open) raises only the unread badge while aim's window has focus; no Notify call in `notify-aim.log`. With the window minimized aim does notify (`notify-aim.log:5` in both batch-0 runs). Receipts and displayed markers work both ways. - **Corrected 2026-11-09:** Gajim 2.6 *does* notify while focused, with no chat open (`notify-gajim.log:5`) and with another chat open (`:34`), so aim differs from Gajim. Dino doesn't notify while focused (125832). - aim's Notify while minimized is not reliable: not in two explore re-runs after the recorded runs (`vms/AGENTS.md`). - **Fix:** — - **Upstream:** — | result | server | peer | what it shows | run | deciding screenshot | |---|---|---|---|---|---| | ❌ | ejabberd | Gajim | aim focused, chat closed / Saved messages open: badge only, 0 Notify | [140003](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-140003-XEP-0184-aim-vs-gajim-ejabberd/EVIDENCE.md) | [shot-9](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-140003-XEP-0184-aim-vs-gajim-ejabberd/shot-9-aim-focused-got.png) | | ❌ | ejabberd | anotherim-web | the same with web's lines (also M28) | [141044](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-141044-XEP-0184-aim-vs-web-ejabberd/EVIDENCE.md) | [shot-9](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-141044-XEP-0184-aim-vs-web-ejabberd/shot-9-aim-focused-got.png) | First seen 2026-11-08 (harness †): ❌ Gajim ejabberd 125251, Kaidan ejabberd 131609, Gajim prosody 123644; ✅ aim minimized with Gajim anotherim 123358 and web ejabberd 201534. <details><summary>Key evidence (ejabberd × Gajim, 140003)</summary> - `140003 notify-aim.log:5` aim's only Notify, while minimized (14:06:46); `steps.log:77` focused with Saved messages open: `0 Notify call(s)`; `:44` chat closed: `aim: no notification`. - `notify-gajim.log:34` Gajim, focused with another chat open, notifies (also `:5`, `:63`). 141044 `steps.log:57`, `:95` the same for aim; `notify-aim.log:5` minimized notifies. </details> ### M27. "View XML" shows OMEMO 2 messages with the 0.3 EME namespace - [ ] verified by a human - **Spec:** XEP-0380 (the EME `namespace` names the encryption actually used). - **Expected:** the stored record of an OMEMO 2 message says `urn:xmpp:omemo:2`. - **Seen:** the stored record (aim's View XML) shows `<encryption xmlns='urn:xmpp:eme:0' namespace='eu.siacs.conversations.axolotl'/>` under the dialog's own heading "Encrypted with OMEMO 2"; on the wire the same message was a single OMEMO 2 envelope with EME `urn:xmpp:omemo:2`. - **Cause (libtacky v1.2.3):** in [`tacky/xmpp/modules/message.tcl#L4764`](https://dev.another.im/another-im/libtacky/src/tag/v1.2.3/tacky/xmpp/modules/message.tcl#L4764) the readable-record branch writes the marker unconditionally, `j encryption -ns urn:xmpp:eme:0 -namespace eu.siacs.conversations.axolotl` (comment: "the profile is not known here, as nothing was encrypted"), while the wire branch a few lines up (L4755–4756) picks v2 or legacy. [`chats.tcl:276–277`](https://dev.another.im/another-im/libtacky/src/tag/v1.2.3/tacky/xmpp/modules/chats.tcl#L276-L277) does it right (v2 when the profiles include it). - **Fix:** write the record's marker from the profile used on the wire (`omemo::ns v2|legacy`, as the wire branch does). - **Batch 1:** the same in every OMEMO 2-only file run on prosody and anotherim (8 runs); masked on ejabberd, where aim sent OMEMO 2 + 0.3 (155210 `steps.log:62`). - **What to look for:** aim's View XML (shot-3) heading "Encrypted with OMEMO 2" above `namespace='eu.siacs.conversations.axolotl'`. - **Upstream:** not checked. | result | server | peer | what it shows | run | deciding screenshot | |---|---|---|---|---|---| | ❌ | anotherim | Kaidan | wire `urn:xmpp:omemo:2`; View XML's stored record `eu.siacs.conversations.axolotl` | [131002](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-131002-XEP-0363-aim-vs-kaidan-anotherim-server/EVIDENCE.md) | [shot-3](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-131002-XEP-0363-aim-vs-kaidan-anotherim-server/shot-3-aim-view-xml.png) | | ❌ | prosody | anotherim-web | batch 1: the same for aim's picture to web | [174321](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-174321-XEP-0454-aim-vs-web-prosody/EVIDENCE.md) | [shot-3](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-174321-XEP-0454-aim-vs-web-prosody/shot-3-aim-view-xml.png) | Also batch 1 (`FAIL View XML names the encryption used on the wire: stored eu.siacs.conversations.axolotl, wire ['urn:xmpp:omemo:2']`): `151535 steps.log:56`, `152512 steps.log:62`, `153012 steps.log:62`, `153241 steps.log:62`, `174225 steps.log:81`, `174804 steps.log:81`, `175603 steps.log:81`. First seen 2026-11-08: anotherim 152430 (View XML seen by hand after the run, not recorded). No ✅ control: the wire of the same message is the comparison. <details><summary>Key evidence (anotherim-server × Kaidan, 131002)</summary> - `131002 client.log:365` on the wire: `<encrypted xmlns='urn:xmpp:omemo:2'>…<encryption xmlns='urn:xmpp:eme:0' namespace='urn:xmpp:omemo:2'/>`; `steps.log:52` View XML of the same id: `<encryption xmlns='urn:xmpp:eme:0' namespace='eu.siacs.conversations.axolotl'/>`; `:56` `FAIL View XML names the encryption used on the wire`. </details> ### M28. anotherim-web shows no desktop notification for messages or calls - [ ] verified by a human - **Spec:** none (UX; Web Notifications API). - **Expected:** Web Notifications when permission is granted and the chat isn't open or the window is hidden, like the desktop build; or document that the web build doesn't notify. Could also be the harness's Chromium app window: check in a real browser first (not done). - **Seen:** notification permission granted over DevTools; web got aim's lines focused with no chat open, focused with a menu open, and minimized: `notify-web.log` has no Notify call at all. In the same run desktop aim, minimized, did notify (`notify-aim.log:5`). Incoming calls only show the in-app "Incoming call" sheet (2026-11-08). - **Fix:** — - **Upstream:** — | result | server | peer | what it shows | run | deciding screenshot | |---|---|---|---|---|---| | ❌ | ejabberd | anotherim-web | no Notify from web in any window state; aim minimized notifies | [141044](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-141044-XEP-0184-aim-vs-web-ejabberd/EVIDENCE.md) | [shot-2](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-141044-XEP-0184-aim-vs-web-ejabberd/shot-2-web-notified.png) | | ✅ | ejabberd | Gajim | the same steps: Gajim notifies in every state | [140003](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-140003-XEP-0184-aim-vs-gajim-ejabberd/EVIDENCE.md) | [shot-2](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-140003-XEP-0184-aim-vs-gajim-ejabberd/shot-2-gajim-notified.png) | First seen 2026-11-08 (harness †): web on ejabberd 201534, prosody 201743, anotherim 205250; call: only the in-app sheet, ejabberd 220703 (shot-2-web-ringing). <details><summary>Key evidence (ejabberd × anotherim-web, 141044)</summary> - `141044 notify-web.log:1`–`:4` the whole capture: no `member=Notify`, though web got aim's lines with the chat closed (`steps.log:23`), focused (`:124`) and minimized (`:168`). - `notify-aim.log:5` the desktop build, minimized, notifies web's line (14:19:03); `steps.log:21`, `:34`, `:55`, `:68` receipts and markers both ways. </details> ### M29. XEP-0425: a moderated message shows as "Message retracted", without moderator or reason - [ ] verified by a human - **Spec:** XEP-0425 (the room's moderated retraction names the moderator, `by=`, and carries the `<reason/>` for the client to show). - **Expected:** the tombstone says the line was moderated, by whom and why, as Gajim shows: "This message has been moderated by …/vm-gajim (reason: kiwi spam)". - **Seen:** aim gets the room's notice in both forms (`:0` `<apply-to>`, `:1` in `<retract/>`) with `by='…/vm-gajim'` and "kiwi spam", removes the text, and shows the plain "Message retracted · Show history" row of a self-retraction. The same with web moderating (web shows that row too). libtacky's `<Retracted>` carries `moderated_by` and `reason` (per the EVIDENCE), so the gap looks like anotherim-js's display. - **What to look for:** aim's shot-4 "Message retracted · Show history" vs Gajim's shot-5 "moderated by … (reason: kiwi spam)". - **Fix:** render moderated retractions with the moderator's nick and the reason. - **Upstream:** not checked. | result | server | peer | what it shows | run | deciding screenshot | |---|---|---|---|---|---| | ❌ | ejabberd | Gajim | Gajim moderates aim's line; aim: plain "Message retracted"; Gajim: by + reason | [214417](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-214417-XEP-0425-aim-vs-gajim-ejabberd/EVIDENCE.md) | [shot-4](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-214417-XEP-0425-aim-vs-gajim-ejabberd/shot-4-aim-moderated.png) | | ❌ | anotherim | Gajim | the same | [233507](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-233507-XEP-0425-aim-vs-gajim-anotherim-server/EVIDENCE.md) | [shot-4](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-233507-XEP-0425-aim-vs-gajim-anotherim-server/shot-4-aim-moderated.png) | | ❌ | ejabberd | anotherim-web | web moderates (API); both show the plain row | [230307](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-230307-XEP-0425-aim-vs-web-ejabberd/EVIDENCE.md) | [shot-4](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-230307-XEP-0425-aim-vs-web-ejabberd/shot-4-aim-moderated.png) | Also 001141 (anotherim, web). Prosody: na, its MUC has no `mod_muc_moderation` (214403 `peer-gajim.log:3137`, no `urn:xmpp:message-moderate` in the room's disco#info). The control is Gajim's own display in the same run. <details><summary>Key stanzas (214417, 233507)</summary> - `214417 peer-gajim.log:5487` `<moderate xmlns="urn:xmpp:message-moderate:1" id="1794260806414398"><retract xmlns="urn:xmpp:message-retract:1" />…`; `client.log:337` `<apply-to … id='1794260806414398'><moderated xmlns='urn:xmpp:message-moderate:0' by='claude-test-014417@…/vm-gajim'><retract …/><reason>kiwi spam</reason></moderated></apply-to><retract xmlns='urn:xmpp:message-retract:1' id='1794260806414398'><reason xmlns='urn:xmpp:message-moderate:1'>kiwi spam</reason><moderated … by='…/vm-gajim'/></retract>`. - `233507 client.log:669` the same notice; `steps.log:54` `PASS aim no longer shows the papaya line`. - `230307 peer-anotherim-web.log:260` web's `<moderate xmlns='urn:xmpp:message-moderate:1' …><retract …/><reason>kiwi spam</reason></moderate>`; `client.log:483` aim gets it with `by='…/vm-web'`. </details> ### M30. XEP-0084: aim keeps a contact's avatar after the contact deletes its metadata node - [ ] verified by a human - **Spec:** XEP-0084 §4.5 names an empty `<metadata/>` for "no avatar"; XEP-0060 §8.4 (a node delete is notified with `<delete/>`): a deleted metadata node leaves no avatar to show. - **Expected:** on `<event><delete node='urn:xmpp:avatar:metadata'/></event>` from a contact aim drops its picture and draws the letter, as it does for Gajim's empty `<metadata/>` (114916). - **Seen:** Dino 0.5.1 removes its avatar by deleting the metadata node. On prosody aim receives the `<delete/>` and 11 s later still draws Dino's colour bars in its list. On ejabberd and anotherim-server Dino sends the same owner `<delete/>` (`150119 peer-dino.log:5273`, `144950 :7401`) but aim's `client.log` has no event for it, so aim can't know; M30 is judged on prosody only. The runs pass on their scenario (avatars cross both ways); this was outside it. It also kept 150320 (XEP-0392 with Dino, anotherim) from comparing colours. - **What to look for:** `150252 client.log:634` the `<delete node='urn:xmpp:avatar:metadata'/>` event from vm-dino; aim's shot-3: vm-dino still with the bars. - **Fix:** treat a metadata node `<delete/>` (and a `<purge/>` or retract of the current item) as "no avatar". - **Upstream:** not checked. | result | server | peer | what it shows | run | deciding screenshot | |---|---|---|---|---|---| | ❌ | prosody | Dino | `<delete/>` of Dino's metadata node received; aim keeps the bars | [150252](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-150252-XEP-0084-aim-vs-dino-prosody/EVIDENCE.md) | [shot-3](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-150252-XEP-0084-aim-vs-dino-prosody/shot-3-aim-restored.png) | | (not judged) | ejabberd | Dino | Dino deletes the node, no event reaches aim; aim keeps the bars | [150119](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-150119-XEP-0084-aim-vs-dino-ejabberd/EVIDENCE.md) | [shot-3](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-150119-XEP-0084-aim-vs-dino-ejabberd/shot-3-aim-restored.png) | | (not judged) | anotherim | Dino | the same | [144950](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-144950-XEP-0084-aim-vs-dino-anotherim-server/EVIDENCE.md) | [shot-3](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-144950-XEP-0084-aim-vs-dino-anotherim-server/shot-3-aim-restored.png) | | ✅ | prosody | Gajim | Gajim removes with an empty `<metadata/>` (`client.log:576`); aim draws the letter | [114916](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-114916-XEP-0084-aim-vs-gajim-prosody/EVIDENCE.md) | [shot-3](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-114916-XEP-0084-aim-vs-gajim-prosody/shot-3-aim-restored.png) | <details><summary>Key evidence (150252)</summary> - `150252 client.log:520` Dino's metadata `<info bytes='994' … id='24261029d02cd84f6634bf898c429ef5cb5eac22' type='image/png'/>`, `:525` aim fetches the data; `peer-dino.log:3693` Dino's owner `<delete node='urn:xmpp:avatar:metadata'/>`, `:3703` the event it gets back; `client.log:634` (15:06:45) aim's `<message from='vm-dino@prosody.asxp.io' type='headline'><event xmlns='http://jabber.org/protocol/pubsub#event'><delete node='urn:xmpp:avatar:metadata'/></event></message>`; `steps.log:61` shot-3 at 15:06:56. </details> ### M31. XEP-0172: after an empty `<nick/>` anotherim-web's Contact info keeps the old nick (anotherim-js UI) - [ ] verified by a human - **Spec:** XEP-0172 §4.3 (an empty `<nick/>`: the user no longer has a nickname). - **Expected:** every place web names vm-aim drops "Aim Kiwi" once aim clears its name. - **Seen:** aim clears its name: an empty `<nick xmlns='http://jabber.org/protocol/nick'/>` and (ejabberd, anotherim) an empty `<vcard xmlns='urn:ietf:params:xml:ns:vcard-4.0'/>`; web receives both. web's chat row and header go back to "vm-aim", but its Contact info keeps the title "Aim Kiwi", an "A" letter and "Nickname Aim Kiwi", still 45 s later (anotherim). Sending is right: Gajim drops the nick on all three servers. Recorded as libtacky (no `app` cause yet); the stale field is anotherim-js's Contact info, so aim would show it the same way. - **What to look for:** web's shot-3: header "vm-aim", the column "Aim Kiwi" / "Nickname Aim Kiwi"; shot-4: row and header "vm-aim". - **Fix:** refresh the Contact info title and Nickname row from the contact's current nick (and vCard4) on the empty-nick event. - **Upstream:** not checked. | result | server | peer | what it shows | run | deciding screenshot | |---|---|---|---|---|---| | ❌ | ejabberd | anotherim-web | empty nick and vCard4 received; Contact info "Aim Kiwi", row and header "vm-aim" | [142149](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-142149-XEP-0172-aim-vs-web-ejabberd/EVIDENCE.md) | [shot-3](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-142149-XEP-0172-aim-vs-web-ejabberd/shot-3-web-info-unnick.png) | | ❌ | prosody | anotherim-web | the same after the empty nick | [140954](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-140954-XEP-0172-aim-vs-web-prosody/EVIDENCE.md) | [shot-3](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-140954-XEP-0172-aim-vs-web-prosody/shot-3-web-info-unnick.png) | | ❌ | anotherim | anotherim-web | the same, 45 s later | [135836](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-135836-XEP-0172-aim-vs-web-anotherim-server/EVIDENCE.md) | [shot-3](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-135836-XEP-0172-aim-vs-web-anotherim-server/shot-3-web-info-unnick-2.png) | | ✅ | prosody | Gajim | the same empty nick: Gajim shows plain vm-aim | [115749](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-115749-XEP-0172-aim-vs-gajim-prosody/EVIDENCE.md) | [shot-3](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-115749-XEP-0172-aim-vs-gajim-prosody/shot-3-gajim-info-unnick.png) | Also ✅ Gajim 115550 (ejabberd), 120417 (anotherim). <details><summary>Key evidence (142149, 140954, 135836)</summary> - `142149 client.log:576` `<nick …>Aim Kiwi</nick>`, `:675` vCard4 `<nickname><text>Aim Kiwi</text></nickname>`; `:823` the empty `<nick xmlns='http://jabber.org/protocol/nick'/>`, `:935` the empty `<vcard xmlns='urn:ietf:params:xml:ns:vcard-4.0'/>`; `peer-anotherim-web.log:357`, `:364` web receives both. - `140954 client.log:639` the empty nick, `peer-anotherim-web.log:273` web gets it; `steps.log:74`, `:85` web's Contact info 10 s later. - `135836 client.log:1088`, `:1194` empty nick and vCard4; `peer-anotherim-web.log:565` (14:01:55), `:573` (14:01:57), `:579` web's own fetch answers the empty nick again; `steps.log:85` the Contact info shot at 14:02:41. </details> ## To report upstream (peer bugs) ### P1. Kaidan 0.16: "Transfer failed: Checksum does not match" on aim's OMEMO 2 upload - [ ] verified by a human - **Spec:** XEP-0448 (encrypted file sharing: the hash describes the plaintext), XEP-0300. - **Expected:** Kaidan verifies the announced sha-256 against the decrypted file and shows the picture. - **Seen:** Kaidan downloads aim's aesgcm upload and shows "Transfer failed: Checksum does not match", on all three servers (batch 1 too, OMEMO 2 and 2 + 0.3). **Proven (131002):** the announced sha-256 (`WHxhFjkD…`) = the decrypted upload's (1216 B) = the hash Kaidan stored; the ciphertext's is `cKzooa5Q…`. Kaidan compares with other bytes. anotherim-web shows the upload inline. - **What to look for:** Kaidan's shot-2: aim's file card with "Transfer failed: Checksum does not match"; `steps.log` `kaidan's stored hash(es) ['WHxhFjkD…']: equal the plaintext's True, the ciphertext's False`. - **Upstream:** report to Kaidan (invent.kde.org/network/kaidan) with the hash check. | result | server | peer | what it shows | run | deciding screenshot | |---|---|---|---|---|---| | ❌ | anotherim | Kaidan | checksum error; announced = decrypted = Kaidan's stored hash | [131002](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-131002-XEP-0363-aim-vs-kaidan-anotherim-server/EVIDENCE.md) | [shot-2](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-131002-XEP-0363-aim-vs-kaidan-anotherim-server/shot-2-kaidan-got-pic.png) | | ❌ | ejabberd | Kaidan | batch 1: the same (aim sent OMEMO 2 + 0.3) | [155210](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-155210-XEP-0454-aim-vs-kaidan-ejabberd/EVIDENCE.md) | [shot-2](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-155210-XEP-0454-aim-vs-kaidan-ejabberd/shot-2-kaidan-got-pic.png) | | ❌ | prosody | Kaidan | batch 1: the same (OMEMO 2 only) | [151535](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-151535-XEP-0454-aim-vs-kaidan-prosody/EVIDENCE.md) | [shot-2](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-151535-XEP-0454-aim-vs-kaidan-prosody/shot-2-kaidan-got-pic.png) | | ✅ | prosody | anotherim-web | batch 1: aim's OMEMO 2 upload shown inline by web | [174321](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-174321-XEP-0454-aim-vs-web-prosody/EVIDENCE.md) | [shot-2](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-174321-XEP-0454-aim-vs-web-prosody/shot-2-web-got-pic.png) | Also batch 1 152512 (anotherim, OMEMO 2 only). First seen 2026-11-08 (harness †): ❌ Kaidan on anotherim 152430 (hash checked by hand), ejabberd 163656, prosody 183515; ✅ Gajim (OMEMO 0.3, no hash checked) ejabberd 160145, web ejabberd 214312. <details><summary>Key stanzas (anotherim-server × Kaidan, 131002)</summary> - `131002 client.log:354` slot for 1232 B (1216 + GCM tag); `steps.log:59` Kaidan's record `'error': 'Transfer failed: Checksum does not match', … 'size': 1216, … 'path': None`; `:61`–`:64` `plain 1216 B sha256 WHxhFjkD…`, `PASS aim announced the plaintext's sha-256`, `kaidan's stored hash(es) ['WHxhFjkD…']: equal the plaintext's True, the ciphertext's False`. - Batch 1: `155210 steps.log:66`, `:69`–`:70`; `151535 steps.log:60`, `:62`–`:64`; `152512 steps.log:66`, `:68`–`:70`. </details> ![Kaidan: Transfer failed: Checksum does not match](https://dev.another.im/attachments/8a054e27-6047-4514-9943-ae4b42534f6b) ### P3. Dino 0.5.1 intermittently sends no audio in calls - [ ] verified by a human - **Spec:** XEP-0167 (Dino accepts the audio content with `senders='both'` and then sends no audio RTP). - **Expected:** Dino's 660 Hz on aim's speaker in every call, as in the passing runs. - **Seen:** batch 0: 1 of 4 runs on ejabberd (132256, video call). **Batch 2: 2 of 3**: prosody 231837 (video call; first on prosody), ejabberd 235447 (audio call); anotherim 195624 fine. Always Dino → aim; Dino always heard aim. - **Cause (132256, again in batch 2):** Dino's audio SSRC sends only RTCP, no opus, while its video and fake-mic stream run: Dino doesn't send audio; aim isn't dropping it. - **Upstream:** report to Dino (github.com/dino/dino) with 132256's and 231837's `rtp.log`; check [dino#1487](https://github.com/dino/dino/issues/1487) and open audio issues first. | result | server | peer | what it shows | run | deciding screenshot | |---|---|---|---|---|---| | ❌ | ejabberd | Dino | video call silent from Dino (0.00), Dino's audio SSRC only RTCP; Dino's video arrives | [132256](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-132256-XEP-0167-aim-vs-dino-ejabberd/EVIDENCE.md) | [shot-9](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-132256-XEP-0167-aim-vs-dino-ejabberd/shot-9-aim-in-video-call.png) | | ❌ | prosody | Dino | batch 2: the same in the video call, first time on prosody | [231837](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-231837-XEP-0167-aim-vs-dino-prosody/EVIDENCE.md) | [shot-9](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-231837-XEP-0167-aim-vs-dino-prosody/shot-9-aim-in-video-call.png) | | ❌ | ejabberd | Dino | batch 2: silent in the audio call; the video call fine | [235447](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-235447-XEP-0167-aim-vs-dino-ejabberd/EVIDENCE.md) | [shot-3](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-235447-XEP-0167-aim-vs-dino-ejabberd/shot-3-aim-in-audio-call.png) | | ✅ | anotherim | Dino | batch 2: tones both ways in both calls | [195624](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-195624-XEP-0167-aim-vs-dino-anotherim-server/EVIDENCE.md) | [shot-9](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-195624-XEP-0167-aim-vs-dino-anotherim-server/shot-9-aim-in-video-call.png) | | ✅ | ejabberd | Dino | tones both ways in both calls; Dino sends 400 opus / 8 s | [130455](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-130455-XEP-0167-aim-vs-dino-ejabberd/EVIDENCE.md) | [shot-9](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-130455-XEP-0167-aim-vs-dino-ejabberd/shot-9-aim-in-video-call.png) | Also ✅ batch 0 130351 (prosody), 131143, 131724 (ejabberd). First seen 2026-11-08 (harness †): ❌ ejabberd 204146 (both calls), 152129, 152700; ✅ ejabberd 205011, 205746, anotherim 202351. <details><summary>Key evidence (132256; batch 2 231837, 235447)</summary> - `132256 steps.log:85` / `:86` `aim hears 660 Hz: 0.00 …`, `dino hears 440 Hz: 0.99 …`; `rtp.log:164` Dino's NIC `out rtp VP8/96 ssrc 3328a7d0: 259 packets …`; `rtp.log:161` `out rtcp rtcp ssrc 290fc134: 2 packets 280 bytes in 8s`; `peer-dino.log:3645` `Payload for audio with opus using ssrc 688898356` (= 0x290fc134); `rtp.log:275`–`:281` Dino's Record Stream uncorked. - `231837 steps.log:87` aim hears 0.00 in the video call; `rtp.log:11` Dino's SSRC b1a22cd3 RTCP only on aim's NIC, `rtp.log:161` the same on Dino's, `rtp.log:162` VP8 out; aim sends VP8 1506 and opus 402 (`rtp.log:16`, `:17`), Dino receives both (`:158`, `:159`). - `235447 steps.log:47`, `:48` aim 0.00, Dino 0.99 in the audio call; `steps.log:57` Dino's NIC `out rtcp 2; in rtcp 2; in opus/111 400`; `peer-dino.log:3478` `Using FakeMic for outgoing audio as it's default`; `steps.log:87`, `:88` the video call 1.00 / 1.00. </details> ### P4. Dino 0.5.1 rejects aim's JET cipher `urn:xmpp:ciphers:aes-128-gcm-nopadding:0` - [ ] verified by a human - **Spec:** [XEP-0396](https://xmpp.org/extensions/xep-0396.html) v0.2.0 contradicts itself: §4 "The 'cipher' attribute MUST be set to 'aes-128-gcm-nopadding:0'" (XEP-0391's URN, what aim sends) vs Example 1 `cipher='urn:xmpp:ciphers:aes-128-gcm-nopadding'` (what Dino knows). - **Seen:** Dino answers aim's XEP-0396 offer (OMEMO 0.3 key transport) with `feature-not-implemented` "JET cipher or type unknown"; JET never runs. aim falls back to an OMEMO aesgcm upload, which Dino saves with the right sha-256. All three servers. - **What to look for:** Dino's shot-3: the picture arrived as an ordinary upload; aim's shot-2 has no "Sent directly"; `steps.log` `FAIL it went over Jingle, no upload`. - **Fix (interop, optional):** libtacky could also offer/accept the unsuffixed name. aim follows the normative text. - **Upstream:** report to Dino (github.com/dino/dino) and ask the XSF to fix Example 1; check open issues first. | result | server | peer | what it shows | run | deciding screenshot | |---|---|---|---|---|---| | ❌ | anotherim | Dino | feature-not-implemented "JET cipher or type unknown"; fallback upload saved | [161341](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-161341-XEP-0396-aim-vs-dino-anotherim-server/EVIDENCE.md) | [shot-3](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-161341-XEP-0396-aim-vs-dino-anotherim-server/shot-3-dino-got-file.png) | | ❌ | ejabberd | Dino | the same | [173144](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-173144-XEP-0396-aim-vs-dino-ejabberd/EVIDENCE.md) | [shot-3](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-173144-XEP-0396-aim-vs-dino-ejabberd/shot-3-dino-got-file.png) | | ✅ | ejabberd | anotherim-web | aim's JET (`urn:tacky:jingle:jet-omemo:2`, aes-256-gcm) to web: sent directly, no upload | [173948](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-173948-urn-tacky-jingle-jet-omemo-2-aim-vs-web-ejabberd/EVIDENCE.md) | [shot-3](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-173948-urn-tacky-jingle-jet-omemo-2-aim-vs-web-ejabberd/shot-3-web-got-file.png) | Also 163331 (prosody, the same). <details><summary>Key stanzas (161341, 163331, 173144; control 173948)</summary> - `161341 client.log:399` aim's offer `<security xmlns='urn:xmpp:jingle:jet:0' cipher='urn:xmpp:ciphers:aes-128-gcm-nopadding:0' name='file' type='eu.siacs.conversations.axolotl'>`; `peer-dino.log:4625`–`:4629` `<feature-not-implemented …/><text …>JET cipher or type unknown`. - `163331 client.log:260`, `:265`, `:313` offer, error, fallback slot; `steps.log:53`, `:55`. `173144 client.log:217`, `:224`. Control `173948 client.log:216` `cipher='urn:xmpp:ciphers:aes-256-gcm-nopadding:0' … type='urn:tacky:jingle:jet-omemo:2'`, `:267` success. </details> ### P5. Dino 0.5.1 rejects aim's IBB transport-replace after losing the tie-break (plain Jingle file transfer) - [ ] verified by a human - **Spec:** XEP-0166 §7.2.12, §8 (simultaneous transport-replace: the initiator's wins); XEP-0260 §2.4, XEP-0261 (fall back to IBB). - **Seen (ejabberd only):** both SOCKS5 candidates fail (P6), both sides send transport-replace at once (aim to IBB, Dino to SOCKS5 again). aim, the initiator, answers Dino's with `<conflict/><tie-break/>`; Dino then **transport-rejects** aim's IBB replace and aim ends with `failed-transport`. Dino stays at "Downloading 1 kB… (0%)". On prosody and anotherim-server the proxy65 is reachable and the same script passes, so P5 shows only where P6 forces the IBB fallback. - **What to look for:** Dino's shot-3: the stuck "Downloading … (0%)" row; `client.log` Dino's `action='transport-reject'` after aim's `<tie-break/>`. - **Upstream:** report to Dino with 163129's `client.log:248`–`:262`; check open issues first. | result | server | peer | what it shows | run | deciding screenshot | |---|---|---|---|---|---| | ❌ | ejabberd | Dino | candidates fail; tie-break; Dino's transport-reject; failed-transport | [163129](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-163129-XEP-0234-aim-vs-dino-ejabberd/EVIDENCE.md) | [shot-3](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-163129-XEP-0234-aim-vs-dino-ejabberd/shot-3-dino-got-file.png) | | ✅ | prosody | Dino | Dino uses aim's proxy65 candidate; checksum, success; file intact | [162820](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-162820-XEP-0234-aim-vs-dino-prosody/EVIDENCE.md) | [shot-2](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-162820-XEP-0234-aim-vs-dino-prosody/shot-2-aim-sent.png) | | ✅ | anotherim | Dino | the same over anotherim-server's public proxy65 | [160833](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-160833-XEP-0234-aim-vs-dino-anotherim-server/EVIDENCE.md) | [shot-3](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-160833-XEP-0234-aim-vs-dino-anotherim-server/shot-3-dino-got-file.png) | Plain Jingle files to anotherim-web pass on all three servers over the WebRTC data channel (173433, 180200, 180009), also with OMEMO-sealed DTLS fingerprints (174347). <details><summary>Key stanzas (163129)</summary> - `163129 client.log:228` aim's offer with candidates `host='10.0.2.15' … type='direct'` and `host='127.0.0.2' jid='proxy.ejabberd.asxp.io/55238004' port='7777' … type='proxy'`; `:241`, `:247` `<candidate-error/>` both ways; `:248`, `:254` both transport-replaces; `:255` aim `<tie-break/>`; `:260` Dino `action='transport-reject'` (IBB); `:262` aim `<failed-transport/>`. </details> ### ~~Gajim 2.6 keeps the old nick after an empty `<nick/>`~~ (withdrawn, not reproduced) - **Withdrawn 2026-11-10 (batch 4):** not reproduced on any of the three servers. After aim's empty `<nick/>` Gajim logs the removal and shows plain "vm-aim": ejabberd [115550](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-115550-XEP-0172-aim-vs-gajim-ejabberd/EVIDENCE.md) `peer-gajim.log:6871` `Received nickname: vm-aim@ejabberd.asxp.io - nickname removed`, `:6902` `Nickname for vm-aim@ejabberd.asxp.io: None` ([shot-3](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-115550-XEP-0172-aim-vs-gajim-ejabberd/shot-3-gajim-info-unnick.png)); prosody [115749](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-115749-XEP-0172-aim-vs-gajim-prosody/EVIDENCE.md) `peer-gajim.log:3986`, `:4017` ([shot-3](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-115749-XEP-0172-aim-vs-gajim-prosody/shot-3-gajim-info-unnick.png)); anotherim [120417](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-120417-XEP-0172-aim-vs-gajim-anotherim-server/EVIDENCE.md) `peer-gajim.log:4634` the event, `:4649` "nickname removed", `:4680` `None` ([shot-3](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-120417-XEP-0172-aim-vs-gajim-anotherim-server/shot-3-gajim-info-unnick.png)). The stale nick seen in batch 4 is anotherim-web's Contact info (M31). ## Server issues and peer notes - **P6. ejabberd test server: proxy65 announces 127.0.0.2 (server config, not fixed).** Unreachable from the VMs, so SOCKS5 between two NATed VMs falls back to IBB, where P5 ends it. [163129](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-163129-XEP-0234-aim-vs-dino-ejabberd/EVIDENCE.md) `client.log:228` `host='127.0.0.2' … port='7777' … type='proxy'`, `server.log:33754`. Controls: prosody's proxy (162820 `client.log:261`) and anotherim-server's `178.105.240.144:7777` (160833 `client.log:403`) carry the file. Config: [`nix/ejabberd.nix#L96`](https://dev.another.im/another-im/interop/src/branch/main/nix/ejabberd.nix#L96) `mod_proxy65: { access: local, max_connections: 5, port: 7777 }` names no public address; setting its `hostname`/`ip` should fix it (not tried). - **P2. Prosody test server: no STUN/TURN. Fixed by #13** (coturn + `mod_turn_external`): [130351](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-130351-XEP-0167-aim-vs-dino-prosody/EVIDENCE.md) `client.log:232` extdisco STUN/TURN, `:244` relay candidates, tones both ways. Batch 2: every prosody call run used relay candidates (231837, 232601, 233237). Before: `service-unavailable` (2026-11-08 174618 `client.log:191`). - **Prosody 13.0.7 test MUC has no XEP-0425** (`mod_muc_moderation` not loaded): [214403](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-214403-XEP-0425-aim-vs-gajim-prosody/EVIDENCE.md) `peer-gajim.log:3137` (no `urn:xmpp:message-moderate` in the room's disco#info), verdict na. Loading it would make M29 checkable on all three. - **Peer lacks added (clients.toml, batch 2):** Gajim 2.6 has no XEP-0513 (no `urn:xmpp:mentions:0` in nbxmpp 7.4.0 or gajim/); Dino 0.5.1 has no XEP-0425 and no XEP-0513. XEP-0513 therefore ran only aim ↔ web ([232223](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-232223-XEP-0513-aim-vs-web-ejabberd/EVIDENCE.md), ejabberd, pass). - **Dino 0.5.1 doesn't fetch a plain XEP-0447 share by itself (behaviour, not a bug):** a round download button instead of aim's picture (its `cid:` thumbnail unsupported); one click fetches it with the right sha-256. [184517](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-184517-XEP-0447-aim-vs-dino-ejabberd/EVIDENCE.md) (`peer-dino.log:2504`, `steps.log:53`) and 162123 pass with the click; 160550 is superseded. - **Dino 0.5.1 on anotherim-server:** upstream offers only SCRAM-SHA-256/512(-PLUS) and X-OAUTH2, Dino only PLAIN and SCRAM-SHA-1; worked around on the test server only (`vms/README.md` Known gaps), upstream unchanged (the user's decision). - **Dino 0.5:** doesn't unlock a room it creates either (not recorded, says nothing about aim). Its replies name its own JID in the XEP-0461 `to`; aim still shows the quote. - **anotherim-web (minor, not judged):** after a Jingle file transfer web answers aim's checksum `session-info` with `unknown-session`, having already terminated ([180200](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-180200-XEP-0234-aim-vs-web-prosody/EVIDENCE.md) `client.log:305`, `:315`); the file had arrived. - **Dino 0.5.1** shows no line for a kick or a ban in its room (its log has 307 / 301; 201047, 223151). **Gajim 2.6** first fails to publish a XEP-0402 bookmark when aim made the node (`precondition-not-met`, then reconfigures; 232823 `peer-gajim.log:23922`); bookmark sync still passes. - **Batch 3 minor (not judged as bugs):** aim's Link previews switch reads on for libtacky's default `sender`, which attaches nothing (the harness flips it to store `always`); anotherim-web sends its own links without a card (its `linkmeta preview` threw in the browser, likely CORS, not traced); unmuting a room writes `<on-mention/>` rather than removing the setting; aim's caps lack `urn:xmpp:mds:displayed:0+notify`, it still gets the events (own-JID PEP). To OMEMO 0.3 peers (Gajim) libtacky attaches no link preview, and stickers and contact cards go only as their fallback text, as its docs say. - **Batch 4, Dino 0.5.1 avatars:** Dino removes its avatar by deleting the `urn:xmpp:avatar:metadata` node instead of publishing an empty `<metadata/>` (XEP-0084 §4.5): `150252 peer-dino.log:3693`, `150119 :5273`, `144950 :7401` (aim's side is M30). And it ignores aim's empty `<metadata/>`: it receives it (`150119 peer-dino.log:5227`, `144950 :7355`) and keeps drawing aim's removed gradient. That made [150320](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-150320-XEP-0392-aim-vs-dino-anotherim-server/EVIDENCE.md) (XEP-0392, the batch's only peer fail) unjudgeable: no letter avatars to compare ([shot-3](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-150320-XEP-0392-aim-vs-dino-anotherim-server/shot-3-dino-colors.png)); aim's own colour is right (264.4° for 264.3°). - **Batch 4, Kaidan 0.16 photo hash:** on prosody Kaidan never puts its photo hash in presence (empty `<photo/>`, [152606](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-152606-XEP-0153-aim-vs-kaidan-prosody/EVIDENCE.md) `peer-kaidan.log:270`); aim gets its picture through Prosody's XEP-0084 mapping and the vCard. On ejabberd and anotherim-server it did advertise `a2dfb30a…` after setting the picture (152328 `client.log:600`, 151643 `client.log:647`), so this is Prosody-only (or timing), not "never". - **Batch 4, Prosody 13.0.7 avatar mapping:** it writes the item id of an empty `<metadata/>` (a UUID) as the vCard photo hash: [115336](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-115336-XEP-0153-aim-vs-gajim-prosody/EVIDENCE.md) `server.log:776` `<x xmlns='vcard-temp:x:update'><photo>df9f11bc-c4e6-4b56-a11e-9dc4fdf71744</photo></x>` in aim's room presence, Gajim: `advertised invalid avatar hash` (`peer-gajim.log:1873`). And it keeps the XEP-0084 avatar it derived from a vCard PHOTO after the PHOTO is removed: aim still draws Kaidan's old picture in [154800](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-154800-XEP-0392-aim-vs-kaidan-prosody/EVIDENCE.md) (shot-1). Its vCard4↔vcard-temp mapping is what hides C47 there. - **Batch 4, XEP-0377 na on all three servers:** none advertises `urn:xmpp:reporting:1`, so aim rightly offers only a plain block: ejabberd [131044](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-131044-XEP-0377-aim-vs-gajim-ejabberd/EVIDENCE.md) `client.log:69`, 143949; prosody [125559](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-125559-XEP-0377-aim-vs-gajim-prosody/EVIDENCE.md) `client.log:74`, 144447; anotherim [115526](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-115526-XEP-0377-aim-vs-gajim-anotherim-server/EVIDENCE.md) `client.log:110`, 135027. ## #1 rechecks (fixed upstream) - **C34 (#1): fixed in libtacky 1.2.2 (1aed459c), confirmed on all three servers.** After a destroy aim republishes the bookmark with `autojoin='false'` and doesn't recreate the room after a restart: prosody 194144 `client.log:289`, ejabberd 194543 `client.log:311` (`steps.log:121`), anotherim [221811](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-221811-XEP-0045-aim-vs-gajim-anotherim-server/EVIDENCE.md) `client.log:622` (`steps.log:124`), and every other admin run. - **anotherim-js#95 (destroyed-room view with "Remove" / "Go to the new room") does not show in rc9:** after the destroy the room stays an ordinary "Not joined" room, composer open (shot-11); after the restart the row is gone (shot-13): [225948](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-225948-XEP-0045-aim-vs-gajim-anotherim-server/EVIDENCE.md) `steps.log:150` `Remove False, Go to the new room False, Join False`, [shot-11](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-225948-XEP-0045-aim-vs-gajim-anotherim-server/shot-11-aim-destroyed.png), [shot-13](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-225948-XEP-0045-aim-vs-gajim-anotherim-server/shot-13-aim-destroyed-room.png). Already commented: [anotherim-js#95 (comment)](https://dev.another.im/another-im/anotherim-js/issues/95#issuecomment-1324). - **C30 (#1): fixed, confirmed.** aim's decline names the inviter and reaches it: 211510 `client.log:457` `<decline to='vm-web@ejabberd.asxp.io/another.im (Js)'/>` (`peer-anotherim-web.log:327`); 232627 `client.log:741`; 210455 `client.log:294`; 204944 `client.log:321`. - **C3.1 (#1): fixed (libtacky 1.2.2 7dddfd32), confirmed on all three servers (batch 3).** A draft pasted with U+0001 U+0002 is published and reaches the second device, no bgerror: [035239](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-035239-urn-tacky-drafts-0-aim-vs-web-as-aim-anotherim-server/EVIDENCE.md) `client.log:685`, `peer-anotherim-web.log:1142` ([shot-7](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-035239-urn-tacky-drafts-0-aim-vs-web-as-aim-anotherim-server/shot-7-web-c31-composer.png)); [034631](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-034631-urn-tacky-drafts-0-aim-vs-web-as-aim-prosody/EVIDENCE.md), [031016](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-031016-urn-tacky-drafts-0-aim-vs-web-as-aim-ejabberd/EVIDENCE.md). - **C12 (#1): documented deviation (libtacky bc534bf0, features.md), reproduced batch 4 on all three servers with Dino and Kaidan.** After a restart with an avatar set, aim's first presence carries the kept hash `<x xmlns='vcard-temp:x:update'><photo>0a69a94cf904a6231fcbb36ef413a2ff8b4a6993</photo></x>` 23–233 ms before the result of its own `<vCard xmlns='vcard-temp'/>` get (XEP-0153 §4.2 MUST NOT); `steps.log` `FAIL C12: no photo hash in aim's presence before its own vCard is read`. ejabberd Dino [150544](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-150544-XEP-0153-aim-vs-dino-ejabberd/EVIDENCE.md) `client.log:701` presence, `:760` own vCard result; ejabberd Kaidan [152328](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-152328-XEP-0153-aim-vs-kaidan-ejabberd/EVIDENCE.md) `client.log:673` presence, `:717` own vCard result; prosody Dino [150721](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-150721-XEP-0153-aim-vs-dino-prosody/EVIDENCE.md) `client.log:549` presence, `:571` own vCard result; prosody Kaidan [152606](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-152606-XEP-0153-aim-vs-kaidan-prosody/EVIDENCE.md) `client.log:593` presence, `:620` own vCard result; anotherim Dino [145421](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-145421-XEP-0153-aim-vs-dino-anotherim-server/EVIDENCE.md) `client.log:746` presence, `:834` own vCard result; anotherim Kaidan [151643](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-151643-XEP-0153-aim-vs-kaidan-anotherim-server/EVIDENCE.md) `client.log:710` presence, `:798` own vCard result. The vCard avatar exchange itself passes both ways in all six. The Gajim and web XEP-0153 runs (115138, 115336, 122058, 140510, 141359, 141718) predate the restart check and signed in without an avatar, so they don't exercise C12. - **C8 (#1): not exercisable in rc9.** Neither aim's nor web's UI writes XEP-0492's `<advanced/>`, so there is nothing to keep; the plain mute syncs ([042801](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-042801-XEP-0492-aim-vs-web-as-aim-ejabberd/EVIDENCE.md), 045211, 042110). libtacky 1.2.4 7912074c touches this. ## Not implemented in aim (anotherim-js rc9 UI gaps: libtacky can, aim's UI can't; the runs judged them fail, not counted as bugs) Missing features, like the peers' `lacks` in `clients.toml`: libtacky has the call (web used its API in the harness). Filing them as feature requests is open. - **XEP-0249 sending:** Invite… sends only a XEP-0045 mediated invitation; libtacky's `muc direct_invite` is not in the app's API or UI. Receiving XEP-0249 works (card, Accept, Decline). aim answers a direct invitation's Decline with a mediated `<decline/>` through the room, which XEP-0249 doesn't define (servers relay it). 9 runs, all three servers: Gajim 204944 `client.log:241`, 210455 `client.log:224`, [231404](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-231404-XEP-0249-aim-vs-gajim-anotherim-server/EVIDENCE.md) `client.log:535`; Dino 210018, 211132, 232148; web 210753, 211510, 232627. - **XEP-0486 setting a room avatar:** no UI; libtacky's `muc set_avatar` works through the API (web set it in 231007, 224019, 000658). Receiving passes everywhere (status 104, vCard fetch, picture in header and list; Dino too). 9 runs: Gajim [215138](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-215138-XEP-0486-aim-vs-gajim-ejabberd/EVIDENCE.md), 220119, 234127; Dino + Gajim 222216, 222656, 235930; web 224019, 231007, 000658. - **XEP-0425 moderating:** no moderation action in the message menu; web used `message.moderate` ([230307](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-230307-XEP-0425-aim-vs-web-ejabberd/EVIDENCE.md) `peer-anotherim-web.log:260`, [001141](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-001141-XEP-0425-aim-vs-web-anotherim-server/EVIDENCE.md) `:382`). The display part is M29. - **XEP-0272/0482 starting a group call:** no UI; web used `groupcall.start` (214007, 003642, 214938, 004557). Joining and declining from the card work (C43, C44 aside). - **XEP-0245 `/me` on receipt (batch 3, all three servers, 9 runs):** a received `/me nods …` is drawn raw in aim's bubble and chat-list preview (and in anotherim-web's); libtacky sets `Message.me` (api.ts:1205), `ui/src` never reads it. aim's own `/me` goes out as typed and Gajim and Dino draw it as an action. Gajim [012515](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-012515-XEP-0245-aim-vs-gajim-anotherim-server/EVIDENCE.md) ([shot-4](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-012515-XEP-0245-aim-vs-gajim-anotherim-server/shot-4-aim-shows-me.png)), 014206, 022041; Dino [014614](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-014614-XEP-0245-aim-vs-dino-ejabberd/EVIDENCE.md), 015658, 022431; web [013117](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-013117-XEP-0245-aim-vs-web-ejabberd/EVIDENCE.md) (`steps.log:34` web shows a raw '/me'), 020052, 022822. - **urn:tacky:pep-expiry:0 setting a time limit (batch 3, all three servers):** aim's status sheet has none, so aim's status carries no `<expires/>`; receiving works: web's API-set status with `<expires/>` shows on aim and is dropped at the limit. [045539](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-045539-urn-tacky-pep-expiry-0-aim-vs-web-ejabberd/EVIDENCE.md) (`peer-anotherim-web.log:211`, `client.log:475`, [shot-3](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-045539-urn-tacky-pep-expiry-0-aim-vs-web-ejabberd/shot-3-aim-peer-status-gone.png)), [041146](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-041146-urn-tacky-pep-expiry-0-aim-vs-web-prosody/EVIDENCE.md) (`client.log:320`), [044109](https://dev.another.im/another-im/interop/src/branch/main/runs/20261110-044109-urn-tacky-pep-expiry-0-aim-vs-web-anotherim-server/EVIDENCE.md) (`client.log:624` aim's status without `<expires/>`). ## Unexplained / candidates (not yet evidence) - **Hang-up after answering Dino's XEP-0353 call: no session-terminate reaches Dino (untraced).** aim's screen ends the call, Dino stays in it 60 s+, on all three servers. Only prosody shows aim's side: [230335](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-230335-XEP-0353-aim-vs-dino+web-as-aim-prosody/EVIDENCE.md) `server.log:3969` (23:09:02) is aim's last stanza before the disconnect at `:4073` (23:10:42): its stream went quiet ~35 s **before** the click. On anotherim ([203202](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-203202-XEP-0353-aim-vs-dino+web-as-aim-anotherim-server/EVIDENCE.md)) and ejabberd ([233830](https://dev.another.im/another-im/interop/src/branch/main/runs/20261109-233830-XEP-0353-aim-vs-dino+web-as-aim-ejabberd/EVIDENCE.md)) aim's logs end before the click. aim's own calls terminate fine (195624 `client.log:435`, `:556`). Maybe a stalled stream, not the hang-up; next: a XEP-0167 run with Dino calling, logs kept to the end. - Not judged: web, as caller, sent no `session-initiate` 37 s after aim's `<proceed/>` (204353 `peer-anotherim-web.log:242`; no fake mic in that example). web logged no `session-terminate` for a video call aim ended (000900). aim re-sent a kick iq 37 s later (223151 `client.log:609`). - aim saw no `<idle/>` (XEP-0319) from Gajim (no example yet). Resolved in batch 2: full-JID membership/ban → **C45**; duplicate chat after a mediated invite → **C46**; moderated message without moderator → **M29**. Resolved in batch 1: Dino's JET cipher → P4; plain Jingle files to Dino → P5 + P6; `omemo-chats:0` withdrawn (harness), `omemo-trust:0` → C42. ## Harness issues already fixed in the interop repo - Before batch 0: the repo copied into /nix/store per commit (ac41415); guest clock lag after `stop`/`cont` (2ff2a33); gnome-keyring aborts (8448d94, 9168a55); screensaver (bbe1727); aim before the keyring (e16143e, 24aca6b); the fake camera unfed, then 4:2:2, now YUY2 (c137b7b, #16 f45e5c5; withdrew C39); a dunst per client start (#16). - Batch 0 (#20): pill OCR band sweeps (bc25552, eba181f); `notify_calls()` reads in the guest (1aaf81a, 5493897); 60 s wait for `session-terminate` (43c4549); `client-tacky.log` kept (4638f85); "Answer" fallback (9c3f35a); Gajim popovers and composer (342b1c6). - Batch 1 (#21): the pill counted six notches up too (4526b3a); `omemo_sync.py` verifies own devices (f54a4b7) and needs aim's publish and web's event (a090834); Dino's download button (8d4d2d1, 1be1499; also no false View XML FAILs from sealed PEP publishes); the CBC 32-byte IV retried as 16 (d6cf5b1). - Batch 2 (#22): Dino's Reject and aim's Answer from the ringing strip (a717672, b535cd9); Dino's room form (da67334) and Join Channel under a notification (22cf9bf); Gajim's room header after a room picture (4fc6beb); aim's keys column scrolled to a far key (eee9d69) and a key id with one misread digit (daa0f43); the destroyed-room read for anotherim-js#95 (a276241). ## Not covered - Calls with Gajim (none in 2.x); MUC with Kaidan (none in 0.16); Kaidan calling aim (its `start_call` isn't scripted, so aim declining a Kaidan call is covered only with Dino and web); group calls with Kaidan. - Kaidan → aim retraction: Kaidan 0.16 has no XEP-0424 (S14). - XEP-0425 on prosody (server lacks it); XEP-0513 only aim ↔ web on ejabberd (Gajim and Dino lack it); XEP-0482 the caller's retract ("missed") not scripted. - XEP-0313 catch-up after a dropped session only on Prosody. XEP-0448 GCM from a third-party client: none on the VM matrix. S15's signals: not re-recorded since 2026-11-08; C38: not rechecked in batch 2. - Covered since v6 (batch 2, all three servers): XEP-0045 admin, 0249, 0272/0482, 0353 (incl. answered elsewhere), 0402 across devices, 0486 receiving, `omemo-chats:0` / `omemo-trust:0`, JET-OMEMO 2, DTLS-OMEMO; Kaidan calls on anotherim-server; web calls on prosody (233237). - Batch 3: `urn:tacky:spans:0` and `urn:tacky:live-location:0` are `na` (libtacky only reads them; no harness client sends them; S6 and M25's live-location part unchecked); `urn:tacky:schedule:0` only on anotherim-server (mod_schedule); Dino and Kaidan lack most batch 3 specs (`clients.toml`). - Batch 4: setting Dino's and Kaidan's presence is not scripted (RFC 6121 with them checks aim's presence one way; `SKIP presence: kaidan: set_presence not scripted yet`); Kaidan's Contact Details don't reliably draw in the VM (155057), so its display of aim's name and picture is judged from the wire; XEP-0191 with Dino and Kaidan only on ejabberd (155048, 155601); XEP-0377 na everywhere (no server offers it).
asxpi changed title from VM runs 2026-11-08: bugs found with Gajim, Dino and Kaidan to Verify anotherim-js/libtacky bugs from the GUI VM runs (rc9 / v1.2.3, 8 found) 2026-11-08 19:31:27 +00:00
asxpi changed title from Verify anotherim-js/libtacky bugs from the GUI VM runs (rc9 / v1.2.3, 8 found) to Verify anotherim-js/libtacky bugs from the GUI VM runs (rc9 / v1.2.3, 7 found) 2026-11-08 20:02:04 +00:00
asxpi changed title from Verify anotherim-js/libtacky bugs from the GUI VM runs (rc9 / v1.2.3, 7 found) to WIP: Verify anotherim-js/libtacky bugs from the GUI VM runs (rc9 / v1.2.3, 7 found) 2026-11-08 20:32:13 +00:00
asxpi changed title from WIP: Verify anotherim-js/libtacky bugs from the GUI VM runs (rc9 / v1.2.3, 7 found) to WIP: Verify anotherim-js/libtacky bugs from the GUI VM runs (rc9 / v1.2.3, 9 found) 2026-11-08 23:13:58 +00:00
asxpi changed title from WIP: Verify anotherim-js/libtacky bugs from the GUI VM runs (rc9 / v1.2.3, 9 found) to WIP: Verify anotherim-js/libtacky bugs from the GUI VM runs (rc9 / v1.2.3, 11 found) 2026-11-09 19:26:14 +00:00
asxpi changed title from WIP: Verify anotherim-js/libtacky bugs from the GUI VM runs (rc9 / v1.2.3, 11 found) to WIP: Verify anotherim-js/libtacky bugs from the GUI VM runs (rc9 / v1.2.3, 16 found) 2026-11-10 01:09:59 +00:00
asxpi changed title from WIP: Verify anotherim-js/libtacky bugs from the GUI VM runs (rc9 / v1.2.3, 16 found) to WIP: Verify anotherim-js/libtacky bugs from the GUI VM runs (rc9 / v1.2.3, 20 found) 2026-11-10 16:43:20 +00:00
Sign in to join this conversation.
No labels
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
another-im/interop#9
No description provided.