vm: batch 1, OMEMO, files and Jingle FT on three servers #21

Merged
asxpi merged 71 commits from vm/batch-1 into main 2026-11-09 19:18:53 +00:00
Member

Batch 1 of vms/PLAN.md (OMEMO, files, Jingle FT) on ejabberd, prosody and
anotherim-server: 50 GUI runs, each with a filled EVIDENCE.md, plus the
harness fixes the agents made on the way.

Coverage: tools/interop.py gaps prints 0 for XEP-0384, 0424, 0454, 0447,
0448, 0234 and 0396 on all servers. The urn:tacky specs (jet-omemo:2,
dtls-omemo:0, omemo-chats:0, omemo-trust:0) were scoped to ejabberd in this
batch; their prosody/anotherim-server cells stay open for a later batch.

Bugs found or re-recorded (details in the runs' EVIDENCE.md; issue #9 is
updated after merge):

  • new, libtacky: no aes-256-cbc-pkcs7 for XEP-0448, Kaidan's files show as
    a URL-named card and Download keeps the ciphertext (Kaidan's 32-byte IV
    is off-spec too)
  • new, libtacky: aim's sent picture carries no XEP-0448 (esfs) share; PLAN's
    0448 criterion now requires it, prosody's web run re-judged fail
  • new, libtacky: no omemo-trust:0 publish after verifying a contact key
  • S14 (all servers), S15 (OMEMO 0.3 and the dual 2+0.3 send), P1 (Kaidan),
    M27 reproduced; C38-like undecryptable count rises, not traced
  • candidates now recorded: Dino rejects the JET cipher with ":0" (peer,
    spec contradicts itself); plain Jingle FT to Dino fails only on ejabberd
    (proxy65 announces 127.0.0.2); omemo-chats sync needs Verified own
    devices (harness, withdrawn)

Lacks added with evidence: Gajim 0396/0447/0448, Dino 0448, Kaidan
0234/0396/0424; XEP-0396 is na for anotherim-web.

🤖 Generated with Claude Code

Batch 1 of vms/PLAN.md (OMEMO, files, Jingle FT) on ejabberd, prosody and anotherim-server: 50 GUI runs, each with a filled EVIDENCE.md, plus the harness fixes the agents made on the way. Coverage: `tools/interop.py gaps` prints 0 for XEP-0384, 0424, 0454, 0447, 0448, 0234 and 0396 on all servers. The urn:tacky specs (jet-omemo:2, dtls-omemo:0, omemo-chats:0, omemo-trust:0) were scoped to ejabberd in this batch; their prosody/anotherim-server cells stay open for a later batch. Bugs found or re-recorded (details in the runs' EVIDENCE.md; issue #9 is updated after merge): - new, libtacky: no aes-256-cbc-pkcs7 for XEP-0448, Kaidan's files show as a URL-named card and Download keeps the ciphertext (Kaidan's 32-byte IV is off-spec too) - new, libtacky: aim's sent picture carries no XEP-0448 (esfs) share; PLAN's 0448 criterion now requires it, prosody's web run re-judged fail - new, libtacky: no omemo-trust:0 publish after verifying a contact key - S14 (all servers), S15 (OMEMO 0.3 and the dual 2+0.3 send), P1 (Kaidan), M27 reproduced; C38-like undecryptable count rises, not traced - candidates now recorded: Dino rejects the JET cipher with ":0" (peer, spec contradicts itself); plain Jingle FT to Dino fails only on ejabberd (proxy65 announces 127.0.0.2); omemo-chats sync needs Verified own devices (harness, withdrawn) Lacks added with evidence: Gajim 0396/0447/0448, Dino 0448, Kaidan 0234/0396/0424; XEP-0396 is na for anotherim-web. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Batch 1 records xep0363.py as XEP-0454, XEP-0447 and XEP-0448. Under
OMEMO 2 the peer's SFS/ESFS metadata never shows on the wire, and aim
showed Kaidan's file as a card with a random name (#9 M27) rather than
inline. A new last step, aim-got-record, notes how aim shows the
peer's file, clicks Download if offered, and reads aim's View XML of
it (url-data, esfs cipher, hash algos, name). view_xml() now also
takes a screen position for a received bubble.

Assisted-by: Claude:opus-5-5
The batch 1 coverage rule asks for a run or a lacks entry per pair.
None of these peer versions has the feature, so the matrix shows them
as not supported instead of gaps:

- Gajim 2.6.0: nbxmpp 7.4.0 names urn:xmpp:sfs:0, gajim never uses
  it; neither has esfs:0 or jingle:jet:0 (XEP-0447, 0448, 0396).
- Dino 0.5.1: no urn:xmpp:esfs:0 in libxmpp-vala or its OMEMO plugin
  (XEP-0448).
- Kaidan 0.16.0: XEP-0424 only from 0.17; its QXmpp 1.17 has no
  Jingle file transfer or JET (XEP-0234, 0396).

Found by grepping the installed binaries and sources in the store.

Assisted-by: Claude:opus-5-5
Batch 1 covers urn:tacky:jingle:jet-omemo:2, dtls-omemo:0, omemo-chats:0
and omemo-trust:0, which only libtacky speaks: Gajim 2.6.0, Dino 0.5.1
and Kaidan 0.16.0 carry none of these strings in their binaries. Mark
them lacking so gaps lists only the anotherim-web cells.

Assisted-by: Claude:opus-5-5
Gajim's chat with aim on anotherim was still unencrypted from an
earlier plain run, so the XEP-0454 run's Gajim leg went out as a plain
OOB upload. Under ENC=omemo the example now switches both chats on
before sending instead of trusting each client's default.

Assisted-by: Claude:opus-5-5
aim's "N encrypted messages can't be read" pill sits with its old
messages: the run's own lines push it above the pane, and on prosody
the restart counts read 0 while the start's pill (6) was just out of
view. pills() now counts at the bottom and again six wheel notches up
(new scroll() helper), with a shot of each.

Assisted-by: Claude:opus-5-5
C38 recheck with omemo_restart.py: OMEMO 2 lines both ways decrypted
and shown on both screens; after three restarts no new "can't be read"
pill under the new messages, and the same three archived key-exchange
messages were re-fetched on every sign-in, as on anotherim-server.
The start's pill (6, older messages) scrolled out of view, so its count
after the restarts was not read; re-recorded with the scroll fix.

Assisted-by: Claude:opus-5-5
Batch 1 recheck of #9 C38 on ejabberd with omemo_restart.py: OMEMO on
both sides, a line each way, then aim restarted three times. aim's
"can't be read" pill never appeared (0 after every sign-in) although
the same three archived kex messages came back from MAM each time.
Kaidan's OMEMO 2 replies carry kex keys only for vm-aim's other
devices; aim sends to Kaidan under both 0.3 and OMEMO 2.

Assisted-by: Claude:opus-5-5
OMEMO 0.3 media sharing works both ways: aim's aesgcm upload is
decrypted and shown inline by Gajim, its announced sha-256 matches the
decrypted upload, and Gajim's encrypted picture shows inline in aim.

It fails on metadata: beside the OMEMO envelope aim's message carries a
clear SIMS <media-sharing/> with the file's name, size and media type
(client.log:374), #9 S15 / #1 S3. Cause libtacky.

Assisted-by: Claude:opus-5-5
Batch 1 recheck of #9 S14 on ejabberd: in the OMEMO 2 chat with Kaidan
aim retracts its encrypted message and sends the <retract/> with the
fallback body "This message was retracted." in the clear. Kaidan 0.16,
which has no XEP-0424, shows it as a new Unencrypted line beside the
untouched original. S14 is now seen on all three servers. Kaidan's own
leg is skipped: it sends no retraction (clients.toml lacks).

Assisted-by: Claude:opus-5-5
aim's OMEMO 2 picture to Kaidan: one upload slot for the encrypted
size, and nothing beside the envelope but the fallback body and EME.
Kaidan shows "Checksum does not match" although its stored hash is
the decrypted upload's, which aim announced: #9 P1 (Kaidan's), now on
prosody too. Kaidan's own file comes as XEP-0448 ESFS with
aes-256-cbc-pkcs7, not aesgcm://. View XML again names the 0.3 EME
namespace for an OMEMO 2 message (#9 M27).

Assisted-by: Claude:opus-5-5
OMEMO 0.3 media sharing works both ways: Dino decrypts aim's aesgcm
upload, shows it inline and keeps it with the right sha-256; Dino's
encrypted picture shows inline in aim.

It fails on metadata, as with Gajim: a clear SIMS <media-sharing/> with
the file's name, size and type rides beside aim's OMEMO envelope
(client.log:399), #9 S15 / #1 S3. Cause libtacky.

Assisted-by: Claude:opus-5-5
Kaidan's file to aim is an XEP-0448 share whose ESFS source is
aes-256-cbc-pkcs7, and aim shows it as a card named after the URL. To
tell what the source really holds, interop-aesgcm now also fetches a
plain https source (XEP-0447) or decrypts an ESFS one (GCM or
CBC-PKCS7, key and IV from the share), and xep0363's aim-got-record
compares the result with the peer's announced sha-256
(shared_sha256()).

Assisted-by: Claude:opus-5-5
Gajim 2.6 stages a pasted picture on a "Send Files" page whose white
on blue label OCR misses, and Return does nothing there, so the
XEP-0454 run on ejabberd timed out waiting for Gajim's upload. Click
it from the Cancel label beside it.

Assisted-by: Claude:opus-5-5
aim's OMEMO 2 aesgcm upload leaks nothing outside the envelope, but
Kaidan 0.16 shows "Checksum does not match" and keeps no file. The hash
aim announced equals the decrypted upload's and the one Kaidan stored,
so the check is Kaidan's (#9 P1, cause peer). View XML again names the
0.3 EME namespace for an OMEMO 2 message (#9 M27).

Assisted-by: Claude:opus-5-5
Kaidan's picture to aim is an XEP-0448 share whose only source is ESFS
with aes-256-cbc-pkcs7. aim has no CBC: it shows a card named after
the upload URL ("5tguSQyso9") instead of the picture, and its
Download keeps the ciphertext without an error. Kaidan's IV is 32
bytes where XEP-0448 gives 128 bits; its first 16 bytes decrypt the
blob to the announced sha-256 (checked on the host). aim's own file
to Kaidan is aesgcm:// + SIMS, not ESFS, and hits #9 P1.

Assisted-by: Claude:opus-5-5
Kaidan 0.16 sends aes-256-cbc-pkcs7 shares with a 32-byte IV (XEP-0448
gives 128 bits); its first 16 bytes decrypt them to the announced
hash. The tool now says so ("iv-truncated") and hashes the plaintext,
so a run shows whether the peer's file is intact, not only that its
IV is off.

Assisted-by: Claude:opus-5-5
Gajim had a second, 693 px high window named Gajim mapped when it came
to send its picture in xep0363 on ejabberd; click_in took the first
match, so the composer click at -32 landed on a received picture in
the chat and opened it in Chromium, which covered the Send Files page.

Assisted-by: Claude:opus-5-5
Kaidan 0.16 shares its picture as XEP-0448 inside OMEMO 2 with an
esfs source using urn:xmpp:ciphers:aes-256-cbc-pkcs7:0 (upload padded
to the AES block, random name). aim's stored record has the share, but
aim does not decrypt the CBC source: it shows a download card named
after the URL ("fjosqIUewW") and never the picture. XEP-0448 §4 lists
the CBC cipher beside the GCM ones; libtacky handles only GCM. New
bug, cause libtacky. aim's own leg is #9 P1 and M27 again.

Assisted-by: Claude:opus-5-5
OMEMO 0.3 media sharing works both ways on ejabberd: Gajim decrypts
aim's aesgcm upload, shows it inline and keeps it with the sha-256 aim
announced; Gajim's encrypted picture shows inline in aim.

It fails on metadata as on anotherim-server: a clear SIMS
<media-sharing/> with the file's name, size and type rides beside aim's
OMEMO envelope (client.log:242), #9 S15 / #1 S3. Cause libtacky.

Assisted-by: Claude:opus-5-5
C38 re-recorded with the scroll-up count: OMEMO 2 lines both ways
decrypted on both screens, and no "can't be read" pill at the bottom
or six notches up, at the start and after each of three restarts,
while the same three archived key exchanges were re-read on every
sign-in. C38 not reproduced on prosody, as on anotherim-server.

Assisted-by: Claude:opus-5-5
OMEMO 0.3 media sharing works both ways on ejabberd: Dino decrypts
aim's aesgcm upload, shows it inline and keeps it with the sha-256 aim
announced; Dino's encrypted picture shows inline in aim.

It fails on metadata: a clear SIMS <media-sharing/> with the file's
name, size and type rides beside aim's OMEMO envelope
(client.log:225), #9 S15 / #1 S3. Cause libtacky.

Assisted-by: Claude:opus-5-5
In an unencrypted chat each side shares a picture with an SFS
<file-sharing/> (file metadata, sha-256, url-data source) and the URL
body marked as its fallback. Kaidan downloads aim's with the right
sha-256; aim shows Kaidan's inline, keeps its name, and the fetched
source matches Kaidan's sha-256.

The FAIL in the steps is the example mistaking aim's sealed
omemo-chats PEP item for the message; fixed in the next vms: commit.

Assisted-by: Claude:opus-5-5
S14 recheck: aim's retraction of an OMEMO 2 line goes out in clear,
<retract/> plus the fallback body, with no envelope. Kaidan 0.16 has
no XEP-0424, keeps the line and shows the fallback as a new
unencrypted one. Kaidan's own leg is skipped (it can't retract until
0.17).

Assisted-by: Claude:opus-5-5
aim sends its picture to Kaidan under OMEMO 2 and 0.3 at once, with a
clear SIMS <media-sharing/> naming the file beside both envelopes
(#9 S15, libtacky). Kaidan rejects the correct upload with "Checksum
does not match" although its stored hash equals the decrypted bytes'
(#9 P1, Kaidan). Kaidan's own picture is an XEP-0448 CBC share that aim
shows as a random-named card. aim's chat also gained "5 encrypted
messages can't be read" since the XEP-0384 run an hour before
(C38-like, not traced).

Assisted-by: Claude:opus-5-5
Dino 0.5.1 does not fetch aim's plain XEP-0447 share by itself: it
shows a round download button (and cannot load aim's cid: thumbnail).
download_file() clicks it under the sender's header line, and xep0363
uses it when the receiver has not saved the file after 60 s.

In a plain chat the View XML check took aim's sealed omemo-chats PEP
publish (<encrypted xmlns='urn:xmpp:pte:0'>) for the file message and
failed; it now looks only at aim's messages to the peer.

Checked with an explore run against Dino on anotherim (ENC=plain): the
click downloads the file with the sent sha-256.

Assisted-by: Claude:opus-5-5
The OMEMO 0.3 picture transfer works both ways: each side shows the
other's picture inline, Gajim kept aim's file with the announced
plaintext hash, and Gajim's aesgcm upload decrypts in aim's VM. It
fails on the privacy check: beside the envelope aim sends a clear SIMS
reference with the file's name, type and size (#9 S15).

Assisted-by: Claude:opus-5-5
An aesgcm:// share (XEP-0454, Gajim and Dino) carries no hash, and
the check printed "announced [] = the fetched plaintext's: False".

Assisted-by: Claude:opus-5-5
OMEMO 0.3 pictures go both ways: each side shows the other's inline,
Dino kept aim's file with the announced plaintext hash, and Dino's
aesgcm upload decrypts in aim's VM. Fails on the privacy check: a
clear SIMS reference with name, type and size rides beside the
envelope (#9 S15).

Assisted-by: Claude:opus-5-5
Kaidan's picture arrives under OMEMO 2 as an XEP-0448 share whose ESFS
source is aes-256-cbc-pkcs7. aim does not decrypt it: it shows a card
named after the URL ("msj4P9ttjf") and Download saves the ciphertext
without an error, as on prosody. Decrypted by the harness with the
share's key (16 of Kaidan's 32 IV bytes) the bytes match Kaidan's
announced sha-256. Cause libtacky; Kaidan's 32-byte CBC IV is off-spec.

Assisted-by: Claude:opus-5-5
In an unencrypted chat aim's SFS share reaches Dino, which shows a
download button (it cannot load aim's cid: thumbnail and does not fetch
plain shares by itself); one click and Dino keeps the file with the
sent sha-256. Dino's share comes as a sourceless <file-sharing/> plus a
second message with <attach-to/> and its <sources/>; aim joins them and
shows the picture inline.

Assisted-by: Claude:opus-5-5
With encryption off aim sends Dino a complete XEP-0447 share (inline
disposition, metadata with sha-256, https url-data source, sfs
fallback, OOB and SIMS too). Dino lists it as a file to download and
does not fetch it, though it fetched aim's OMEMO uploads by itself.
Dino's own share (sourceless, sources attached in a second message)
shows inline in aim. Cause: peer (Dino); not checked whether a click
downloads it.

Assisted-by: Claude:opus-5-5
With "Send files directly" on and no encryption, aim offers its picture
over Jingle file transfer with a direct and a proxy SOCKS5 candidate.
The two NATed guests meet at anotherim-server's proxy65, which unlike
ejabberd's announces a public address; aim activates the proxy, sends
its checksum, and Dino ends the session with success and keeps the
file with the sent sha-256. No upload fallback.

Assisted-by: Claude:opus-5-5
In the prosody XEP-0447 run OCR missed the newest "vm-aim" header, and
the lowest one it read belonged to an older message at the top of the
pane: the click hit nothing and Dino never fetched aim's share. Only a
header in the lower part of the pane counts now; without one the
button above the composer (364,732) is clicked.

Assisted-by: Claude:opus-5-5
aim offers its picture over Jingle with JET-OMEMO, cipher
urn:xmpp:ciphers:aes-128-gcm-nopadding:0 as XEP-0396 §4 demands. Dino
0.5.1 answers feature-not-implemented "JET cipher or type unknown": it
only knows the cipher without ":0", as in the XEP's own Example 1. aim
falls back to an OMEMO upload, which Dino shows. The spec contradicts
itself; cause recorded as peer (Dino), the #9 candidate now has
evidence.

Assisted-by: Claude:opus-5-5
With encryption off aim and Kaidan exchange XEP-0447 shares (inline
disposition, metadata with sha-256, https url-data, sfs fallback).
Kaidan saves aim's bytes and shows the picture; aim shows Kaidan's
picture inline under its own name, its source's hash matching the
announced one.

Assisted-by: Claude:opus-5-5
With OMEMO off on both sides each sends a picture as a plain SFS share
(metadata, sha-256, https url-data, fallback body). Kaidan kept aim's
file with no error and shows it; aim shows Kaidan's inline under its
own name, and the source matches the announced hash.

Assisted-by: Claude:opus-5-5
aim and anotherim-web exchange encrypted pictures, both shown inline
with the right hashes; web's message carries an aesgcm body and an
XEP-0448 aes-256-gcm share inside OMEMO 2. Both sides send OMEMO 2 and
0.3 at once with a clear SIMS <media-sharing/> naming the file beside
the envelopes (client.log:240, :253), #9 S15. Cause libtacky.

Assisted-by: Claude:opus-5-5
aim and web keep a received file in their own store, so saved_file()
raises NotImplementedError for them and the send step ended as a SKIP
with peer aim:web or web. For them the receiver's own session-terminate
with <success/> now decides; batch 1 runs XEP-0234, 0396 and the
urn:tacky:jingle specs against anotherim-web this way.

Assisted-by: Claude:opus-5-5
Plain SFS shares both ways. Dino 0.5.1 cannot render aim's cid:
thumbnail and does not fetch a plain share by itself; after a click it
kept the file with aim's sha-256 and shows it. Dino announces its
file first and attaches the https source in a second message; aim
shows it inline.

Assisted-by: Claude:opus-5-5
OMEMO 2 both ways with Kaidan, then aim restarted three times (the #9
C38 recheck). The "can't be read" count stays 0 after every sign-in;
each sign-in re-reads the same three archived key-exchange messages
from MAM without counting them. C38 not reproduced here, as in
batch 0.

Assisted-by: Claude:opus-5-5
anotherim-web's picture carries an XEP-0448 share (aes-256-gcm ESFS
source, sha-256) inside OMEMO 2; aim decrypts it, the hash matches and
the picture shows inline. But every file message, aim's and web's,
also carries a clear SIMS <media-sharing/> naming the file beside the
OMEMO 2 + 0.3 envelopes (#9 S15), and aim's pasted picture goes out as
aesgcm + SIMS without an XEP-0448 share. Cause libtacky.

Assisted-by: Claude:opus-5-5
With "Send files directly" on and no encryption, aim offers the
picture over Jingle; Dino accepts and takes aim's proxy65 candidate
on prosody, aim activates it, sends its checksum, and Dino ends with
success and the same sha-256 on disk. No upload fallback. On ejabberd
the same ends failed-transport.

Assisted-by: Claude:opus-5-5
aim offers the picture over Jingle with JET-OMEMO (OMEMO 0.3 key
transport, cipher urn:xmpp:ciphers:aes-128-gcm-nopadding:0). Dino 0.5.1
answers feature-not-implemented "JET cipher or type unknown": it knows
the cipher only without ":0", as in XEP-0396's example, while the
XEP's text and XEP-0391 use ":0". aim falls back to an OMEMO upload,
which Dino saves intact. Records the #9 candidate.

Assisted-by: Claude:opus-5-5
aim offers the picture correctly over SOCKS5 (direct 10.0.2.15 and
ejabberd's proxy65, which announces 127.0.0.2); both candidates fail.
Both sides then send transport-replace at once, aim wins the tie-break
as initiator, and Dino rejects aim's IBB replace, so aim ends the
session failed-transport and falls back to an upload Dino does not
fetch. Cause: peer (Dino after the tie-break); proxy65's address is a
server config issue. Records the #9 candidate from xep0234.py.

Assisted-by: Claude:opus-5-5
The prosody run of the same script passes over proxy65; it shows that
the ejabberd fail starts with proxy65 announcing 127.0.0.2.

Assisted-by: Claude:opus-5-5
Tesseract dropped the menu line of aim's "Delete" (for everyone), so
the lone word "Delete" of "Delete locally" was clicked: the XEP-0424
run with Kaidan on anotherim got a "Delete the message locally?"
dialog and sent nothing. _retract_aim now skips a Delete followed by
"locally" on its line and, with only that one read, clicks 37 px
above it. Checked in a REPL against Kaidan: the retraction went out.

xep0424 waits 90 s for aim's stanza (its XML log lags ~30 s) and logs
aim's undecryptable count before and after: it rose from 0 to 3 in
the Kaidan chat around that failed run (#9 C38).

Assisted-by: Claude:opus-5-5
aim offers the picture over Jingle with JET-OMEMO (OMEMO 0.3 key
transport, cipher urn:xmpp:ciphers:aes-128-gcm-nopadding:0). Dino 0.5.1
answers feature-not-implemented "JET cipher or type unknown", as on
prosody: it knows the cipher only without ":0". aim falls back to an
OMEMO upload, which Dino saves intact. Cause: peer.

Assisted-by: Claude:opus-5-5
With "Send files directly" on and no encryption aim offers the picture
over Jingle with the WebRTC data channel transport; web accepts,
confirms with <received/> and ends the session with success; no upload.
web answers aim's checksum with unknown-session after its terminate
(minor, already noted in AGENTS.md).

Assisted-by: Claude:opus-5-5
S14 again: in an OMEMO 2 chat aim sends its retraction and the
fallback body outside the envelope (client.log:368); Kaidan 0.16, which
has no XEP-0424, shows the fallback as a new unencrypted line and keeps
the original. Kaidan's own leg is skipped (it only deletes locally).

aim's "can't be read" pill stood at 4 before and after; it was 0 at
the end of the XEP-0384 run an hour earlier, so #9 C38 rose again in
between (a discarded attempt and a REPL session), cause not traced.

Assisted-by: Claude:opus-5-5
What the prosody runs taught: Jingle FT to Dino works through
prosody's proxy65, Kaidan's ESFS shares are CBC with a 32-byte IV that
aim cannot open, aim's clear SIMS beside OMEMO 0.3, Dino needing a
click on a plain SFS share, the "can't be read" pill scrolling out of
view, and that agents share the session scratchpad. README lists
scroll(), shared_sha256() and view_xml((x, y)).

Assisted-by: Claude:opus-5-5
Jingle over anotherim's public proxy65, the XEP-0396 cipher spelling,
Dino's unfetched plain shares and download_file, Kaidan's CBC-only
XEP-0448 shares, the "Delete locally" OCR trap in aim's menu, and a
summary of the batch's verdicts on this server.

Assisted-by: Claude:opus-5-5
Assisted-by: Claude:opus-5-5
With OMEMO on and "Send files directly", aim offers the picture with a
JET <security type='urn:tacky:jingle:jet-omemo:2'> whose AES-256-GCM key
travels in an OMEMO 2 envelope; the data channel's DTLS fingerprint is
sealed the same way. web accepts, confirms <received/> and ends with
success; no upload. Run against anotherim-web (vm-web) so it fills the
matrix cell; the brief's aim:web pair records as aim vs aim.

Assisted-by: Claude:opus-5-5
A direct file transfer over the WebRTC data channel with OMEMO on: aim's
offer and web's accept each carry their DTLS fingerprint sealed in an
OMEMO 2 envelope (the dlts-srtp-verification element); the transfer
completes with <received/> and success, no fingerprint in clear.

Assisted-by: Claude:opus-5-5
In an OMEMO 2 chat aim's aesgcm upload goes out with nothing about the
file outside the envelope and shows inline in web; web's picture (aesgcm
body plus an aes-256-gcm XEP-0448 share) shows inline in aim, its hash
verified. View XML again shows the 0.3 EME namespace (#9 M27).

Assisted-by: Claude:opus-5-5
anotherim-js and anotherim-web both advertise libtacky's own
urn:tacky:jingle:jet-omemo:2 and pick it for a direct file under OMEMO
(ejabberd runs 20261109-173948 and -174347), so XEP-0396 never runs
between them; it is what libtacky sends to Dino and other peers.
Mark the web cell na instead of leaving a gap no run can fill.

Assisted-by: Claude:opus-5-5
OMEMO 2 pictures both ways, inline on both screens, hashes right.
Both file messages carry nothing beside the envelope but the fallback
body and EME; web's share inside is aesgcm plus an aes-256-gcm ESFS
source. On ejabberd the same pair added a 0.3 envelope and a clear
SIMS (S15). View XML again names the 0.3 namespace (M27).

Assisted-by: Claude:opus-5-5
Receiving works: web's OMEMO 2 picture carries an aes-256-gcm esfs
share, which aim decrypts, verifies and shows inline. Sending does
not: aim's pasted picture has an SFS share whose only source is an
aesgcm:// URL, no esfs source, so it is XEP-0454 in an SFS wrapper;
web reads it anyway. Same as on ejabberd. View XML's EME namespace is
wrong again (#9 M27).

Assisted-by: Claude:opus-5-5
With OMEMO off on both sides each sends a picture as a plain SFS share
(metadata, sha-256, https url-data, fallback body); each shows the
other's inline, and web's source matches its announced hash.

Assisted-by: Claude:opus-5-5
libtacky reads a synced urn:tacky:omemo-chats:0 or omemo-trust:0 list
only when it is signed by a device of the account the user verified
(HANDBOOK; a device trusted blindly or merely accepted could have been
added by the server). The example accepted the second device only, so
web as vm-aim received aim's sealed list (ejabberd, batch 1) and
rightly ignored it: the "both open, cause not traced" note was the
harness. Set each other device Verified first.

Assisted-by: Claude:opus-5-5
In an unencrypted chat aim and web each send a picture as an SFS share
with a url-data source; both show inline and web's announced sha-256
matches the fetched bytes.

Assisted-by: Claude:opus-5-5
web's picture comes as an XEP-0448 share with an aes-256-gcm ESFS
source inside OMEMO 2; aim decrypts it, the hash matches and it shows
inline. aim's own pasted picture uses aesgcm plus SIMS rather than an
XEP-0448 share, all inside the envelope. View XML again names the
0.3 namespace (M27).

Assisted-by: Claude:opus-5-5
With "Send files directly" on and no encryption, aim sends its picture
to web over Jingle with the WebRTC data channel transport; web accepts,
shows it and ends the session with success. aim's checksum arrives
after web's terminate (unknown-session), as on ejabberd.

Assisted-by: Claude:opus-5-5
With "Send files directly" on and no encryption aim offers the picture
over the WebRTC data channel; web accepts, confirms <received/> and
ends with success, and shows the picture. No upload. aim's checksum
session-info arrives after the terminate (unknown-session).

Assisted-by: Claude:opus-5-5
omemo_sync.py on ejabberd failed to verify the account's second other
device: its trust button sat at y 661, the menu opened shifted up over
the button (Verified at 668), and _trust_aim only looked under the
button. The third device's trust word was cut off at the bottom edge,
so omemo_keys listed it with no trust word and it was never verified.

Scroll the keys column up when the row to set is low or the last row
has no trust word, and take the menu item nearest the button that was
not on screen before the click (another row's label is excluded).

Assisted-by: Claude:opus-5-5
The three servers judged the same behaviour differently: aim decrypts
and shows anotherim-web's XEP-0448 (aes-256-gcm esfs) picture, but its
own pasted picture goes out as aesgcm:// + SIMS inside OMEMO 2 with no
urn:xmpp:esfs:0 source. ejabberd (d0d66e9) and anotherim-server
(4145136) failed on that; prosody's run 20261109-175603 passed on
receiving alone.

XEP-0448 is a sender and a receiver requirement (§3.1, §3.2), so the
coordinator settled on fail: change the prosody run's verdict to fail
with cause libtacky ("aim sends no XEP-0448 share"), and make PLAN.md's
XEP-0448 pass criterion require aim's sent picture to carry an esfs
share too, so later runs are judged alike.

Assisted-by: Claude:opus-5-5
anotherim-web signed in as vm-aim (a second device of aim's account).
After both devices set the account's other devices Verified, aim
turning OMEMO off for vm-gajim publishes a sealed omemo-chats item
(client.log:296), web gets the event and shows the chat unencrypted;
turning it back on is applied too. The earlier attempts, with the own
devices only Accepted or the verification step failing in the harness,
left web encrypted and were not recorded.

Assisted-by: Claude:opus-5-5
anotherim-web signed in as vm-aim, all own devices Verified on both
sides. aim marks vm-gajim's key Verified, but publishes no
urn:tacky:omemo-trust:0 item: its log after the change has no publish
to the node and web gets no event. Own-device decisions are published
(run 181559), contact-key decisions are not. web's "Verified", which
made the example's check pass, comes from an older list fetched at
sign-in; after the example reverts aim to Accepted the two devices
disagree. Two keys-column shots of the run copied in as shot-5/6.
Cause libtacky.

Assisted-by: Claude:opus-5-5
ENC=plain. aim's pasted picture carries an XEP-0447 file-sharing
element (file metadata, sha-256, url-data https source) beside OOB and
SIMS; web shows it inline. web's picture comes with the same share,
aim shows it inline and the fetched source matches web's sha-256.
Same as the prosody and anotherim-server runs of this pair.

Assisted-by: Claude:opus-5-5
Re-recorded with the current xep0363.py: the earlier ejabberd fail
(160550, cause peer) ran before download_file clicked Dino's download
button. aim's plain picture carries an XEP-0447 share; Dino fetches no
plain SFS share by itself and can't load the cid: thumbnail, but after
the click it saves the file with aim's sha-256 and shows it. Dino's
two-part share (metadata, then the source by message-attaching) shows
inline in aim. Same as prosody (162123).

Assisted-by: Claude:opus-5-5
vms: omemo_sync needs aim's publish, notes batch 1 on ejabberd
All checks were successful
/ check (pull_request) Successful in 11m4s
a090834f12
The omemo-trust run 182653 passed its "web lists the key Verified"
check although aim published nothing: web held Verified from an older
list. Note web's state before aim's change and require aim's publish
and web's event when it was already Verified. Count aim's publish
with wait_count: its XML log reaches the host late, so count()
printed 0 for a publish that client.log holds.

AGENTS.md: own-device sync findings, the late aim log, the keys
column scrolling, and batch 1 results on ejabberd.

Assisted-by: Claude:opus-5-5
asxpi merged commit ee43bc1fd5 into main 2026-11-09 19:18:53 +00:00
Sign in to join this conversation.
No reviewers
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!21
No description provided.