[FORK]Tacky is high-level library for building XMPP clients
  • Tcl 75.2%
  • Rust 9.7%
  • C 9.7%
  • Makefile 1.6%
  • Python 1.5%
  • Other 2.2%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-11-10 14:57:06 +02:00
docker The crates' rust-version is 1.91, not 1.88, at the user's word: one floor for libtacky and the app, which declares 1.91 as well, so both repositories say one thing. cargo update at 1.91 moves nothing past what 1.88 already allowed (tacky-core on icu 2.4 and idna_adapter 1.2.2, rcgen 0.14.10 and time 0.3.55 kept); vodozemac 0.11 (1.89) is now within the floor but stays at 0.10, an API change of its own, and ROBOTS.md says so. docker/linux.Dockerfile, platforms/wasm.mk, docs/building.md, audit.toml's reasons and CHANGELOG.md's Rust section say 1.91 and why. Gate in the image (emsdk 6.0.13, Rust 1.99.1): make all, tests/run.tcl -j 16 7163 total, 7153 passed, 0 failed, 10 skipped; test-rust 810 passed, 0 failed, 6 ignored; audit 10 lock files, no advisory; wasm, check-api, check-headers, check-native-neutral, test-lib and fuzz-build clean. 2026-11-02 03:12:48 +02:00
docs 1.2.7: the README's version, figures and rig table, the changelog's section, the DOAP release, the library version answered (LibraryVersion), the regenerated HANDBOOK.md and docs/rigs.md for the tree to be tagged v1.2.7, a patch of the 1.2 line built on 1.2.6 (c0f69597): a destroyed room no longer reads as joined and its state is dropped as on a leave (bookmarks <RoomState> and the chat list's row told; author, avatar, message, omemo, push, groupcall and captcha clean up on muc <Destroyed>), and a call's session-terminate is sent before the media is closed, so a slow audio device cannot hold back the hang-up; the host-visible changes listed first. Gate: unit on a6502528 (the merged tree with its version strings) 8333 total, 0 failed on both media lanes run one after the other (webrtc 8331 passed, 2 skipped; datachannel 8324 passed, 9 skipped); Rust 848 passed each; wasm, test-build, test-sqlite3 and its provider, test-libressl and the datachannel media suites green, api.py check clean; audit clean against the advisory database of 2026-11-09 (d9cb059; RUSTSEC-2026-0119 and -0387 allowed by audit.toml); the Windows (MinGW) and Android (arm64) archives built on a6502528 (iOS and macOS not built here); rigs at c9421614 (1.2.6 with the destroyed room's first fix), three per server: Prosody 13 486 total, 447 passed, 2 failed, 37 skipped; ejabberd 26.09 486, 469 passed, 1 failed, 16 skipped; two per server: MongooseIM 6.10.0 486, 394 passed, 1 failed, 91 skipped; Openfire 5.1.2 486, 390 passed, 0 failed, 96 skipped. Alone: Prosody's test_catchup_history 19 of 19 and test_stickers 3 of 3, ejabberd's test_retraction 8 of 8 (its OMEMO 2 case a flake older than 1.2.6), MongooseIM's test_catchup_history 19 of 19. After the destroyed room's second fix, alone on Prosody and ejabberd: test_bookmarks_autojoin, test_bookmarks, test_groupcall, test_groupcall_video, the eleven test_muc_* suites and test_mucsub, 0 failed; after the hang-up fix, alone on all four servers: test_calls 9 of 9 each, test_groupcall 0 failed. Counted: Prosody 449/0/37, ejabberd 470/0/16, MongooseIM 395/0/91, Openfire 390/0/96. Coverage kept from 1.2.0 (f5483b52: Tcl 99.0 % procs, Rust 99.2 %, C 98.8 % lines), not re-measured. 2026-11-10 03:10:42 +02:00
embed tacky-olm bound into Tcl and wired into every build, for the Matrix plan of 2026-10-25 (the user: "let's start the Matrix plan which involves fork of mod_matrix_gw, olm/megolm rust library include" and "just go"): ext/olm/olm.c is the tackyolm extension, the package tacky::olm 0.1 with the commands the design's section 3.1 froze (account, session, group, inbound, verify, sas) and the keypair ensemble its section 3.2 added for the cross-signing keys, one command per function of tacky_olm.h, base64 as Tcl text and everything else as byte arrays, handles olmaccountN, olmsessionN, olmgroupN, olminboundN, olmsasN and olmkeypairN freed by their free or with the interpreter, results of a length only the ABI knows asked into the bound their input sets and again into the reported size on LENGTH, and errors as {TACKY tacky::olm STATUS OLM NAME} for the ABI's statuses (verify and verify_mac raising CRYPTO with "bad signature" and "bad MAC") and {TACKY tacky::olm 1 ARG} for what the glue refuses (a handle it does not have, a seed of another length, a count or index out of range, a message type other than 0 and 1). deps.mk builds the crate (TACKY_OLM_A) and _build/ext/olm/tackyolm.so (make olm), puts olm in the floor, EXT_NAMES and the load check, runs the crate in test-rust and test-rust-full and the binding in test-olm (in floor-test), and counts both in coverage-rust, coverage-c and coverage-all; the Makefile merges libtackyolm.a and libtacky_olm.a into libtacky.a and into the Android, iOS and Windows archives, whose platform files build the crate per target; embed/extensions.h calls Tackyolm_Init on every backend interpreter; the fault shim answers six tacky_olm_* calls; tools/stats.py has the olm component, its Rust test family and its crate, and README.md's generated tables are written again with them; ROBOTS.md and README.md name the crate and the package, with a decision paragraph on the crate, the 0.10 pin and the panic the fuzzer found. The browser build has no tacky::olm. The SAS header said a spent SAS still gave its public key, which the code never did: the header now says public_key is STATE too. Tested in the container: make olm test-olm (test_olm.tcl 134 checks: every command and refusal, an Olm conversation both ways with a large and an empty message, the fallback key, a room's session shared, exported at 3 and imported, a SAS between two handles and a small-order key, RFC 8032's keypair vector, handles across interpreters, and libolm's accounts, signatures, Olm transcript and Megolm session from tests/client/fixtures/matrix read back through the commands; test_olm_faults.tcl 14 under the fault shim), olm.c at 99.8 % of its lines under gcov (the one line left is the init's failure), docker/in-image.sh all with check-native-neutral, test-lib and test-tackyd-json passing against the archive with the fourth Rust staticlib, test-rust 800 passed and 0 failed over all five crates, and the full unit run with TLS_TEST_SERVER (204 suites, 6261 tests, 6260 passed, 0 failed, 1 skipped); cross-built with platforms/android.mk libtacky-android in the anotherim-js android image (aarch64-linux-android, rustc 1.90) and docker/in-image-windows.sh lib (x86_64-pc-windows-gnu, rustc 1.98), each archive linked into a probe (an Android shared object with --no-undefined, a MinGW executable) calling tacky_create, with Tackyolm_Init and the tacky_olm_* functions in it. 2026-10-25 22:23:07 +02:00
examples Comments, build files and the crates' docs no longer cite documents this tree does not have: TRANSFER.md, HTTP-TCL.md, LOCALSTORE.md, ARCHITECTURE.md (the workspace's §9.1 and §9.2), MEDIA.md, JINGLE-FT.md, LIVENESS.md, NOTIFY.md, CHATLIST.md, THREADS.md, STORAGE.md, CHAOS-FINDINGS.md, ONE-CLIENT.md, ROADMAP-another-tacky.md, docs/DEPLOY.md, docs/STABILIZE.md, docs/discovery.md, docs/storage-lock.md, reports/OMEMO_issues.md and reviews/2026-09-*, nor ROBOTS.md by section numbers it lost long ago (§2.2, §7.1, §8, §10.80 and the rest). Each pointer now names the place that exists (tacky/http/README.md, tacky-keyring/README.md, ROBOTS.md's HTTP, "The store", "The shape" and rule 2 or 6, tacky_media.h's data channel section, session/liveness.tcl, modules/file.tcl, ext/common.h's rule 1, deps.mk's and wasm.mk's own sqlite3 paragraphs) or is dropped where the sentence already says what the document did; generated outputs (dist/HANDBOOK.md, REPORT.md under COV_DIR) and the guides' pattern are left as they are. Not touched, being other branches' files: tacky/xmpp/modules/ (about 110 such citations), ext/core/keyring.c and keyring.h, ext/core/tests/test_tls.tcl, tacky/core/keyring.tcl and storage.tcl, and the schema's doc strings. Only comments changed; check-native-neutral, check-headers, check-api and test-tacky are green. 2026-10-07 20:42:28 +03:00
ext Merge branch 'fix/esfs-cbc' into integ/1.2.5 (F1: XEP-0448 shares sealed with AES-256-CBC open; an unreadable ESFS source never falls back to the OOB URL) 2026-11-09 22:43:35 +02:00
platforms LibreSSL's rebuild removes its CMake tree with the source 2026-11-09 21:47:55 +02:00
schema callinbox: a room's relayed invitation that comes while the main tier is closed is told as notify <Sealed> kind invite, its chat the room's own (room?join), where the full client files it (an Android QA run of 2026-11-10, app 0.8.0 on 1.2.7: anastasik invited to the members-only team@conference.another.im saw the room as a 1:1 chat, "Not available", with no Join, and "Test" written there went to the room as type='chat' and came back "Only occupants are allowed to send messages to the conference"): the call tier read the invitation as any message from a bare JID, kind chat naming the room's bare JID, so the host's notice opened a conversation with the room itself; a relayed one may also come bodyless, and is told all the same. Test: closemain-a-relayed-invite-is-told-as-the-rooms-invite, failing before (kind chat, chat the room's bare JID); the schema's <Sealed> kind and chat, since 1.2.8. 2026-11-10 14:32:12 +02:00
tacky message: a 1:1 chat with a room's own JID comes back as the room's, once per client after the account archive's first catch-up (TidyRoomChats; the Android QA run of 2026-11-10, app 0.8.0 on 1.2.7: team@conference.another.im stood in the chat list as a contact, "Not available", holding the room's invitation, and the three messages written there went to the room as type='chat' and came back not-acceptable). A plain chat with a JID is taken for the room's when it holds nothing but that room's relayed invites to itself, our own sends (each went to the room as a 1:1 message, which a room refuses) and holes, and the JID is a room: one on record (a bookmark, a room known or subscribed to, rows in its room?join chat), or, for one holding the room's invites, one whose disco#info answers with a conference identity. Its invites are then filed in room?join as a relayed one is filed there (our own words, the kept stanza without the password, the state they had: a pending one is the room's request, a Join away), the 1:1 chat is cleared (clearChat, so archived under preserve_history, never deleted) and told removed (chatlist <Remove>), and room?join told again. Anyone else's word in the chat, a JID that answers as no conference, or no answer keeps it. TidyInvites of 1.2.6, which runs once, took only a chat of invites alone whose room was on record, so QA's chat, its room never on record and our refused sends beside the invite, stayed. Tests: muc-tidy-room-chat-comes-back-as-the-rooms, -of-a-known-room and -keeps-what-is-in-doubt, failing before (no such tidy); messagestore onlyRoomChatRows and selfInviteRowsOf, since 1.2.8. 2026-11-10 14:38:15 +02:00
tacky-core TLS: a Unicode domain is verified as its A-label 2026-11-07 11:29:30 +02:00
tacky-crypto SQLCipher 4.20.0: the notes that name the release the provider was read against 2026-11-09 21:47:55 +02:00
tacky-keyring tests: cheap Argon2 for the Tcl suites 2026-11-04 08:49:17 -08:00
tacky-media LibreSSL's DTLS MTU patch: its hunk at 4.4.0's line, still needed there 2026-11-09 21:47:55 +02:00
tacky-olm tacky-olm: vodozemac 0.11.2 for RUSTSEC-2026-0451 2026-11-06 23:58:42 +02:00
tacky-omemo tacky-omemo: a session says what unwrapping a message would cost before any key is derived. Session::lookahead reads the header alone: how many message keys the unwrap would derive ahead of the message and keep for the ones before it (the chain's counters up to its own, and for a ratchet step the rest of the chain it leaves), whether those kept fit MAX_SKIPPED_KEYS (past it the oldest go, which are the keys of the messages still to come in order), whether the message is a ratchet step, and whether a pre-key message is a key exchange with a new base key (accepted anew, the state before the previous one let go); Gap, State and Encoding as the unwrap would say. lookahead_fresh judges a key exchange there is no session for. For a host reading an archive newest first, which decides from it whether to unwrap now, out of order, or to wait for the message's turn. The C ABI (tacky_omemo_session_lookahead, tacky_omemo_prekey_message_lookahead_profile, tacky_omemo_lookahead_t) and the Tcl commands session lookahead and session prekey_lookahead follow, with unit and FFI tests. 2026-11-05 00:25:16 +02:00
tests rigs: the desktop app's way of inviting into a members-only room while the invitee is away (test_muc_invite muc-invite-int-members-only-while-away, the Android QA run of 2026-11-10): the owner grants membership, sends the XEP-0249 direct invitation and the XEP-0045 mediated one while romeo is signed out; back, romeo's room?join holds one pending request and there is no 1:1 chat with the room's JID, accepting it joins, and what romeo writes in the room's chat reaches juliet there as the room's message. The direct one is not asked for: it carries no <body/>, and the rigs, with no offline store, archive nothing of a bodyless message. Green on ejabberd 26.09 and Prosody 13 (7 passed, 1 skipped each). 2026-11-10 14:49:33 +02:00
tools 1.2.1: the README's version, figures and rig table, the changelog's section, the DOAP release (XEP-0222 among its rows, its version 1.0 in tools/xep-versions.tsv), the library version answered (LibraryVersion) and the regenerated docs/rigs.md for the tree to be tagged v1.2.1, a patch of the 1.2 line built on 1.2.0 (1dae0a6c): the forge issues #20-#23, modern XMPP (no priority 0, the avatar cap, profile visibility), the fixes of interop issue 1 (security S1-S13 with group-call invites inside OMEMO 2 and legacy_file_metadata, correctness C1-C20, minor M2-M25), the Matrix double send, MongooseIM's re-archived 1:1 message, bookmark write ordering, replies and backups by wire_id, store move safety, a room mention's notification on a row without an author; the host-visible changes listed first. Gate: unit on the tagged tree (cbad2b09 plus these edits) 8126 total, 0 failed on both media lanes (webrtc 8124 passed, 2 skipped; datachannel 8118 passed, 8 skipped); Rust 848 passed each; wasm, api.py check clean; audit clean against the advisory database of 2026-11-06 (309392b; RUSTSEC-2026-0119 and -0387 allowed by audit.toml); rigs three per server on 19440c25: Prosody 13 454 total, 417 passed, 0 failed, 37 skipped; ejabberd 26.09 454, 438 passed, 1 failed (flaky-ack-lost-resumed, a reconnect STARTTLS stall past the wait, failing the same way alone on 1.2.0), 15 skipped; two per server on a683eb50: MongooseIM 6.10.0 454, 364 passed, 0 failed, 90 skipped; Openfire 5.1.2 454, 357 passed, 2 failed (an avatar case passing alone; the bookmark remove case fixed by cbad2b09, test_bookmarks 5/5 on all four servers after it), 95 skipped; coverage kept from 1.2.0 (f5483b52: Tcl 99.0 % procs, Rust 99.2 %, C 98.8 % lines), not re-measured. 2026-11-08 00:27:22 +02:00
.gitignore The XMPP half of the Tcl tree moves under lib/xmpp and the tacky::xmpp packages, the first step of the rename pass. lib/taco/session becomes lib/xmpp/session with package and namespace tacky::xmpp::session (::tacky::session:: renamed everywhere as one token); lib/taco (taco.tcl, redact.tcl, modules/) becomes lib/xmpp/client (client.tcl, redact.tcl, modules/) with the package tacky::xmpp::client; the package xmpp becomes tacky::xmpp; tests/taco and tests/taco_integration become tests/client and tests/integration, all by git mv. lib/taco/pkgIndex.tcl goes and lib/xmpp/pkgIndex.tcl registers tacky::xmpp and its session, client and component subpackages, since pkgUnknown looks only one level below lib/. The internal names stay: the taco_* snit types, :🌮:, procs, State keys and every package version. Everything that named a moved path or package follows in this commit: tests/run.tcl's suite directories and auto_path, test_session.tcl's auto_path and test_session_scram.tcl's source path, deps.mk's load check list and comments, .gitignore's tmp directory, tools/api/tcl.py's module paths, tools/stats.py's parts and test-to-part map, the schema's per-module tcl paths and doc strings (methods.tcl came out unchanged from make gen-api), README.md, ROBOTS.md (with a decision paragraph naming the old names), docs/, the READMEs of lib/, ext/, examples/ and tests/servers, and comments across lib/ and tests/; notes about what a file was called before 2026-10-07 and the paths of the legacy repositories keep their old names. Checked in the container: make test-tacky (tests/run.tcl 4551 total, 4550 passed, 0 failed, 1 skipped, as before; the load check sees tacky::xmpp, tacky::xmpp::client and tackywire; 22 integration suites load), test-tackykeyring and test-tackycore (the tests/tcl suites), test-tk-example, test-build (24 passed) and check-api. 2026-10-07 12:49:27 +03:00
.gitmodules native/ dissolves: tacky-core, tacky-crypto, tacky-keyring, tacky-media and tacky-ratchet move to the repository root with git mv, the libdatachannel submodule's path following in .gitmodules, so the parts a host builds sit beside ext/, lib/ and embed/ instead of one level down. Every path that named native/ follows: the root Cargo.toml's members, deps.mk, the Makefile, coverage.mk, platforms/*.mk, the Windows image's script and Dockerfile, tools/stats.py and check-core-header.py, test_build.sh, the bindings' and Tcl modules' comments, the crates' own READMEs and the docs, with HANDBOOK.md regenerated from them. check-native-neutral keeps its name and now scans the tacky-*/ directories by glob, pruning the submodule as before; its rule reads that nothing under a tacky-*/ directory knows Tcl. Checked in the container: test-rust, check-headers, check-native-neutral, check-api and test-build (24 of 24) pass. 2026-10-07 09:15:32 +03:00
audit.toml The crates' rust-version is 1.91, not 1.88, at the user's word: one floor for libtacky and the app, which declares 1.91 as well, so both repositories say one thing. cargo update at 1.91 moves nothing past what 1.88 already allowed (tacky-core on icu 2.4 and idna_adapter 1.2.2, rcgen 0.14.10 and time 0.3.55 kept); vodozemac 0.11 (1.89) is now within the floor but stays at 0.10, an API change of its own, and ROBOTS.md says so. docker/linux.Dockerfile, platforms/wasm.mk, docs/building.md, audit.toml's reasons and CHANGELOG.md's Rust section say 1.91 and why. Gate in the image (emsdk 6.0.13, Rust 1.99.1): make all, tests/run.tcl -j 16 7163 total, 7153 passed, 0 failed, 10 skipped; test-rust 810 passed, 0 failed, 6 ignored; audit 10 lock files, no advisory; wasm, check-api, check-headers, check-native-neutral, test-lib and fuzz-build clean. 2026-11-02 03:12:48 +02:00
CHANGELOG.md 1.2.7: the README's version, figures and rig table, the changelog's section, the DOAP release, the library version answered (LibraryVersion), the regenerated HANDBOOK.md and docs/rigs.md for the tree to be tagged v1.2.7, a patch of the 1.2 line built on 1.2.6 (c0f69597): a destroyed room no longer reads as joined and its state is dropped as on a leave (bookmarks <RoomState> and the chat list's row told; author, avatar, message, omemo, push, groupcall and captcha clean up on muc <Destroyed>), and a call's session-terminate is sent before the media is closed, so a slow audio device cannot hold back the hang-up; the host-visible changes listed first. Gate: unit on a6502528 (the merged tree with its version strings) 8333 total, 0 failed on both media lanes run one after the other (webrtc 8331 passed, 2 skipped; datachannel 8324 passed, 9 skipped); Rust 848 passed each; wasm, test-build, test-sqlite3 and its provider, test-libressl and the datachannel media suites green, api.py check clean; audit clean against the advisory database of 2026-11-09 (d9cb059; RUSTSEC-2026-0119 and -0387 allowed by audit.toml); the Windows (MinGW) and Android (arm64) archives built on a6502528 (iOS and macOS not built here); rigs at c9421614 (1.2.6 with the destroyed room's first fix), three per server: Prosody 13 486 total, 447 passed, 2 failed, 37 skipped; ejabberd 26.09 486, 469 passed, 1 failed, 16 skipped; two per server: MongooseIM 6.10.0 486, 394 passed, 1 failed, 91 skipped; Openfire 5.1.2 486, 390 passed, 0 failed, 96 skipped. Alone: Prosody's test_catchup_history 19 of 19 and test_stickers 3 of 3, ejabberd's test_retraction 8 of 8 (its OMEMO 2 case a flake older than 1.2.6), MongooseIM's test_catchup_history 19 of 19. After the destroyed room's second fix, alone on Prosody and ejabberd: test_bookmarks_autojoin, test_bookmarks, test_groupcall, test_groupcall_video, the eleven test_muc_* suites and test_mucsub, 0 failed; after the hang-up fix, alone on all four servers: test_calls 9 of 9 each, test_groupcall 0 failed. Counted: Prosody 449/0/37, ejabberd 470/0/16, MongooseIM 395/0/91, Openfire 390/0/96. Coverage kept from 1.2.0 (f5483b52: Tcl 99.0 % procs, Rust 99.2 %, C 98.8 % lines), not re-measured. 2026-11-10 03:10:42 +02:00
coverage.mk tacky-olm bound into Tcl and wired into every build, for the Matrix plan of 2026-10-25 (the user: "let's start the Matrix plan which involves fork of mod_matrix_gw, olm/megolm rust library include" and "just go"): ext/olm/olm.c is the tackyolm extension, the package tacky::olm 0.1 with the commands the design's section 3.1 froze (account, session, group, inbound, verify, sas) and the keypair ensemble its section 3.2 added for the cross-signing keys, one command per function of tacky_olm.h, base64 as Tcl text and everything else as byte arrays, handles olmaccountN, olmsessionN, olmgroupN, olminboundN, olmsasN and olmkeypairN freed by their free or with the interpreter, results of a length only the ABI knows asked into the bound their input sets and again into the reported size on LENGTH, and errors as {TACKY tacky::olm STATUS OLM NAME} for the ABI's statuses (verify and verify_mac raising CRYPTO with "bad signature" and "bad MAC") and {TACKY tacky::olm 1 ARG} for what the glue refuses (a handle it does not have, a seed of another length, a count or index out of range, a message type other than 0 and 1). deps.mk builds the crate (TACKY_OLM_A) and _build/ext/olm/tackyolm.so (make olm), puts olm in the floor, EXT_NAMES and the load check, runs the crate in test-rust and test-rust-full and the binding in test-olm (in floor-test), and counts both in coverage-rust, coverage-c and coverage-all; the Makefile merges libtackyolm.a and libtacky_olm.a into libtacky.a and into the Android, iOS and Windows archives, whose platform files build the crate per target; embed/extensions.h calls Tackyolm_Init on every backend interpreter; the fault shim answers six tacky_olm_* calls; tools/stats.py has the olm component, its Rust test family and its crate, and README.md's generated tables are written again with them; ROBOTS.md and README.md name the crate and the package, with a decision paragraph on the crate, the 0.10 pin and the panic the fuzzer found. The browser build has no tacky::olm. The SAS header said a spent SAS still gave its public key, which the code never did: the header now says public_key is STATE too. Tested in the container: make olm test-olm (test_olm.tcl 134 checks: every command and refusal, an Olm conversation both ways with a large and an empty message, the fallback key, a room's session shared, exported at 3 and imported, a SAS between two handles and a small-order key, RFC 8032's keypair vector, handles across interpreters, and libolm's accounts, signatures, Olm transcript and Megolm session from tests/client/fixtures/matrix read back through the commands; test_olm_faults.tcl 14 under the fault shim), olm.c at 99.8 % of its lines under gcov (the one line left is the init's failure), docker/in-image.sh all with check-native-neutral, test-lib and test-tackyd-json passing against the archive with the fourth Rust staticlib, test-rust 800 passed and 0 failed over all five crates, and the full unit run with TLS_TEST_SERVER (204 suites, 6261 tests, 6260 passed, 0 failed, 1 skipped); cross-built with platforms/android.mk libtacky-android in the anotherim-js android image (aarch64-linux-android, rustc 1.90) and docker/in-image-windows.sh lib (x86_64-pc-windows-gnu, rustc 1.98), each archive linked into a probe (an Android shared object with --no-undefined, a MinGW executable) calling tacky_create, with Tackyolm_Init and the tacky_olm_* functions in it. 2026-10-25 22:23:07 +02:00
deps.mk Merge branch 'fix/esfs-cbc' into integ/1.2.5 (F1: XEP-0448 shares sealed with AES-256-CBC open; an unreadable ESFS source never falls back to the OOB URL) 2026-11-09 22:43:35 +02:00
fuzz.mk Merge feat/matrix-12 (L2, d07d98fc) into integ/1.2: Matrix end-to-end encryption through a gateway on the device (tacky-olm over vodozemac bound as tacky::olm, tacky::xmpp::matrix and the module matrix: Megolm DMs and rooms, key requests, cross-signing, SAS), files as EncryptedFile, reactions, 1:1 calls over the Matrix VoIP events (WIP). Conflicts kept both sides: the wire table's chat_entry gains matrix beside integ's otr/mtproto-direct booleans (one row; the merge had left two), the mtproto-direct and matrix-e2e rows in docs/protocol/README.md (matrix-e2e since v1.2.0, the release that first ships it), the software_info_public and matrix_e2e_enabled setting hooks and texts, deps.mk's coverage notes (the rustup llvm-tools with olm among the C extensions), test_wire_surface's count (494: 481 + the lane's 13) and CHANGELOG.md's 1.2.0-beta1 section. Gate in the container: make all, tests/run.tcl -j 32 7159 total, 7149 passed, 0 failed, 10 skipped; test-rust 810 passed, 0 failed, 6 ignored; test-olm all ok; api.py check, check-headers, check-native-neutral and wasm clean; integration/test_matrix_gateway.tcl on ejabberd 26.09 with omemo-bot 8 of 8. 2026-11-02 02:53:43 +02:00
HANDBOOK.md 1.2.7: the README's version, figures and rig table, the changelog's section, the DOAP release, the library version answered (LibraryVersion), the regenerated HANDBOOK.md and docs/rigs.md for the tree to be tagged v1.2.7, a patch of the 1.2 line built on 1.2.6 (c0f69597): a destroyed room no longer reads as joined and its state is dropped as on a leave (bookmarks <RoomState> and the chat list's row told; author, avatar, message, omemo, push, groupcall and captcha clean up on muc <Destroyed>), and a call's session-terminate is sent before the media is closed, so a slow audio device cannot hold back the hang-up; the host-visible changes listed first. Gate: unit on a6502528 (the merged tree with its version strings) 8333 total, 0 failed on both media lanes run one after the other (webrtc 8331 passed, 2 skipped; datachannel 8324 passed, 9 skipped); Rust 848 passed each; wasm, test-build, test-sqlite3 and its provider, test-libressl and the datachannel media suites green, api.py check clean; audit clean against the advisory database of 2026-11-09 (d9cb059; RUSTSEC-2026-0119 and -0387 allowed by audit.toml); the Windows (MinGW) and Android (arm64) archives built on a6502528 (iOS and macOS not built here); rigs at c9421614 (1.2.6 with the destroyed room's first fix), three per server: Prosody 13 486 total, 447 passed, 2 failed, 37 skipped; ejabberd 26.09 486, 469 passed, 1 failed, 16 skipped; two per server: MongooseIM 6.10.0 486, 394 passed, 1 failed, 91 skipped; Openfire 5.1.2 486, 390 passed, 0 failed, 96 skipped. Alone: Prosody's test_catchup_history 19 of 19 and test_stickers 3 of 3, ejabberd's test_retraction 8 of 8 (its OMEMO 2 case a flake older than 1.2.6), MongooseIM's test_catchup_history 19 of 19. After the destroyed room's second fix, alone on Prosody and ejabberd: test_bookmarks_autojoin, test_bookmarks, test_groupcall, test_groupcall_video, the eleven test_muc_* suites and test_mucsub, 0 failed; after the hang-up fix, alone on all four servers: test_calls 9 of 9 each, test_groupcall 0 failed. Counted: Prosody 449/0/37, ejabberd 470/0/16, MongooseIM 395/0/91, Openfire 390/0/96. Coverage kept from 1.2.0 (f5483b52: Tcl 99.0 % procs, Rust 99.2 %, C 98.8 % lines), not re-measured. 2026-11-10 03:10:42 +02:00
LICENSE libtacky v0.9.9-alpha3: the whole tree as of this release, in one commit (the user chose to fold the history once, at this tag, so the forge's commit list is readable; from here on every branch lands as one squashed commit). The full history up to this point, 1,000-odd commits with the v0.9.9-alpha1 and v0.9.9-alpha2 tags, is kept unchanged on the branch archive/pre-alpha3 (43b06d4), whose tree is byte-identical to this one. 2026-10-02 02:46:29 +03:00
Makefile Rig: Openfire 5.1.2 (with_openfire.sh), a fourth server for the integration suites 2026-11-07 04:05:29 +02:00
README.md 1.2.7: the README's version, figures and rig table, the changelog's section, the DOAP release, the library version answered (LibraryVersion), the regenerated HANDBOOK.md and docs/rigs.md for the tree to be tagged v1.2.7, a patch of the 1.2 line built on 1.2.6 (c0f69597): a destroyed room no longer reads as joined and its state is dropped as on a leave (bookmarks <RoomState> and the chat list's row told; author, avatar, message, omemo, push, groupcall and captcha clean up on muc <Destroyed>), and a call's session-terminate is sent before the media is closed, so a slow audio device cannot hold back the hang-up; the host-visible changes listed first. Gate: unit on a6502528 (the merged tree with its version strings) 8333 total, 0 failed on both media lanes run one after the other (webrtc 8331 passed, 2 skipped; datachannel 8324 passed, 9 skipped); Rust 848 passed each; wasm, test-build, test-sqlite3 and its provider, test-libressl and the datachannel media suites green, api.py check clean; audit clean against the advisory database of 2026-11-09 (d9cb059; RUSTSEC-2026-0119 and -0387 allowed by audit.toml); the Windows (MinGW) and Android (arm64) archives built on a6502528 (iOS and macOS not built here); rigs at c9421614 (1.2.6 with the destroyed room's first fix), three per server: Prosody 13 486 total, 447 passed, 2 failed, 37 skipped; ejabberd 26.09 486, 469 passed, 1 failed, 16 skipped; two per server: MongooseIM 6.10.0 486, 394 passed, 1 failed, 91 skipped; Openfire 5.1.2 486, 390 passed, 0 failed, 96 skipped. Alone: Prosody's test_catchup_history 19 of 19 and test_stickers 3 of 3, ejabberd's test_retraction 8 of 8 (its OMEMO 2 case a flake older than 1.2.6), MongooseIM's test_catchup_history 19 of 19. After the destroyed room's second fix, alone on Prosody and ejabberd: test_bookmarks_autojoin, test_bookmarks, test_groupcall, test_groupcall_video, the eleven test_muc_* suites and test_mucsub, 0 failed; after the hang-up fix, alone on all four servers: test_calls 9 of 9 each, test_groupcall 0 failed. Counted: Prosody 449/0/37, ejabberd 470/0/16, MongooseIM 395/0/91, Openfire 390/0/96. Coverage kept from 1.2.0 (f5483b52: Tcl 99.0 % procs, Rust 99.2 %, C 98.8 % lines), not re-measured. 2026-11-10 03:10:42 +02:00
release.mk HANDBOOK.md: the handbook is a committed file at the root, linked from the README, as the user asked (2026-11-02: "update README, recount all lines etc, and please make a handbook and link to it in readme"): api.py handbook (tools/api/docs.py's default path) and make handbook write HANDBOOK.md instead of the build output dist/HANDBOOK.md, make docs refreshes it, the release tarballs take it from the root (release.mk), and ROBOTS.md, docs/hacking.md and api.py's notes say so; generated from the schema and docs/, 72 modules, 638 methods, 204 events. The README links it where the API is introduced and in the commands, names the 1.2.0-beta1 additions in its features list from CHANGELOG.md and docs/features.md (caps 2.0, Happy Eyeballs, IAP, XEP-0450, OTR announced, direct MTProto chats, Matrix E2EE through a gateway, room hats, mention notifications, invitation links and spaces, Fluux polls, pubsub, microblogging and stories, WebXDC, meetings and payments as work in progress), recounts the status table from docs/features.md (MOSTLY 171, WIP 27, TODO 9, DONE 0, one NOT SUPPORTED) and its numbers with tools/stats.py --readme (126,337 lines of code, 169,646 of tests, 9,835 cases; coverage as recorded from make coverage-all at 6a6b49de); stats --check, api.py check and doap.py --check clean. 2026-11-02 08:49:54 +02:00
ROBOTS.md ROBOTS.md: the call tier's <Sealed> kind invite for a room's relayed invitation and TidyRoomChats, the 1:1 chat with a room's JID taken back as the room's after the first catch-up (the Android QA run of 2026-11-10), since 1.2.8. 2026-11-10 14:55:38 +02:00
tcl.mk Merge feat/matrix-e2e (with feat/matrix-olm) onto develop 1.1.20 for libtacky 1.2.0-beta1 (the user, 2026-11-02: "Add everything we will need for Matrix except MatrixRTC" and "OLM/MegOLM"): tacky-olm bound as tacky::olm, the protocol package tacky::xmpp::matrix and the chat module matrix come in 274 commits late. Conflicts resolved by hand: RetrySend had moved, so the Matrix resend goes into develop's RetrySend after the private-chat branch; Classify's sealed test moved above the XEP-0466 timer read, so a Matrix event counts as sealed there and is opened before the OMEMO step with the wire node kept for the timer; the module list, the method allow-list, the wire types (ChatEntry.matrix, matrix_device, matrix_member, matrix_sas), the schema (ChatEntry.matrix, Message.encryption matrix, unreadable matrix_key, SasEmoji beside ScheduleSupport, the settings matrix_e2e_enabled and matrix_encrypt_dms), ROBOTS.md, docs/features.md and the README's text take both sides; everything the branch said was "since 1.1.3" never shipped and now says since 1.2.0. The README's stats are left to the release. 2026-11-02 02:17:23 +02:00

libtacky

An XMPP client library in Tcl 9, Rust and C. It holds the protocol, the local store, end-to-end encryption, calls and file transfer. Apps talk to it through one JSON API. Tcl programs and gateways load its packages directly.

Version 1.2.7. Licence GPL-3.0.

What you get

  • dist/libtacky.a: the whole library, Tcl included, behind embed/tacky.h.
  • dist/tackyd-json: the same library over stdin and stdout.
  • A wasm build for a browser worker (make wasm).
  • Builds for Linux, Windows, Android and iOS (platforms/).
  • Tcl packages under tacky::*: session, client, component, HTTP, and the OTR, MTProto and Matrix protocol layers.
  • dist/tackyd: runs a Tcl program with no Tcl installed.
  • Native crates with C ABIs: tacky-core, tacky-crypto, tacky-keyring, tacky-omemo, tacky-olm, tacky-media.

The API is described once, in schema/tacky.schema.json: 72 modules, 638 methods, 204 events, 198 types. HANDBOOK.md documents all of it, with the Tcl reference and the guides under docs/. The handbook (make handbook) and the host bindings are generated from the schema.

Features

  • One SQLCipher store per account, keys in a sealed keyring
  • A message is acknowledged only after it is stored
  • SASL2, Bind 2, FAST, SCRAM with channel binding
  • Stream management with resume, CSI, reconnects
  • Entity Capabilities 2.0, Happy Eyeballs, authentication pipelining
  • TLS through rustls, certificate pins, no plaintext fallback
  • STARTTLS, direct TLS and WebSocket
  • SOCKS5 proxy, Tor and I2P per account
  • OMEMO 0.3 and OMEMO 2 under one identity
  • OMEMO automatic trust management (XEP-0450)
  • OTR v3 secret chats with SMP, announced to the contact's devices
  • Telegram secret chats through a gateway, and MTProto secret chats directly between two XMPP devices (experimental)
  • Matrix end-to-end encryption (Olm, Megolm, SAS verification) through a gateway
  • Group chats (MUC), bookmarks, archive (MAM) with catch-up
  • Room hats, mention notifications, invitation links, spaces
  • Replies, reactions, corrections, retractions, mentions
  • Polls (Fluux's urn:fluux:poll:0), stickers, silent and scheduled messages
  • Publish-subscribe on any service, microblogging, stories
  • WebXDC apps, online meetings, payment requests (work in progress)
  • HTTP upload and Jingle file transfer, encrypted
  • Audio and video calls, group calls (Muji)
  • XEP-0114 components for gateways and bots

docs/features.md has one row per specification with its status. Our own extensions (urn:tacky:*) are specified under docs/protocol/.

status meaning specifications
🔵 MOSTLY implemented and tested, not checked by hand against other software 171
🟡 WIP partly implemented or partly tested 27
🔴 TODO not implemented, at most reserved in the API 9
🟢 DONE checked by hand; set by a maintainer 0
⛔ NOT SUPPORTED deliberately not implemented 1

How it fits together

 app (libtacky.a), tackyd-json, or a browser worker (wasm)
   │  JSON  ["module","method",{args},token] →   ["result"|"error",token,…] ←
   ▼
 embed/tacky.c      one backend thread, one Tcl interpreter
   ▼
 tacky/core/wire    dispatch through the schema's allow-list
   ▼
 tacky/xmpp/client  one module per area (account, roster, message, muc, …)
   ├── tacky/xmpp/session   the XMPP session, in Tcl
   │     └── tacky::core    tacky-core (Rust): DNS, connect, TLS, XML
   ├── tacky/core/storage   SQLCipher store per account
   ├── tacky::core::keyring tacky-keyring (Rust): the key vault
   ├── tacky::omemo         tacky-omemo (Rust): the OMEMO ratchet
   ├── tacky::olm           tacky-olm (Rust, vodozemac): Olm and Megolm
   ├── tacky/http           HTTP/1.1 in Tcl
   └── tacky::media         calls: webrtc | datachannel | host | remote

 tacky/xmpp/component      an XEP-0114 component, no client loaded
  • Rust reads untrusted bytes and holds keys: the stream, TLS, parsers, crypto, OMEMO.
  • C binds each crate to Tcl, and runs media (libwebrtc or libdatachannel).
  • Tcl is everything that means something: the session, the client, the store, HTTP.

Calls use one native media backend per build, chosen by MEDIA_BACKEND. ROBOTS.md has the full layout and the reasons behind it.

component language what it is code tests coverage
core Tcl tacky/core: the wire API dispatch, the per-account store, the keyring layer 2,906 1,814 99.1 % procs
C ext/core: the Tcl binding of tacky-core and the keyring; the SQLCipher crypto provider 4,031 3,552 98.8 % lines
Rust tacky-core: DNS, TCP, TLS (rustls), WebSocket, the XML stream, the C ABI; tacky-keyring: the key vault 13,182 8,159 98.8 % lines
crypto C ext/crypto: the tacky::crypto binding 1,444 790 99.4 % lines
Rust tacky-crypto: hashes, HMAC, HKDF, PBKDF2, AES, X25519 with a C ABI 2,543 2,933 99.9 % lines
http Tcl tacky/http: HTTP/1.1 client for uploads and downloads 1,068 2,199 100.0 % procs
media Tcl tacky/media: the call media contract and its backends 1,009 1,906 97.8 % procs
C ext/media: the binding; tacky-media: the media C API over libdatachannel, with audio (Opus) and video (VP8) engines 9,949 10,651 --
C++ tacky-media/webrtc: the same C API over libwebrtc 4,979 342 --
olm C ext/olm: the tacky::olm binding of Olm and Megolm; Matrix above it is the client's 1,042 375 99.8 % lines
Rust tacky-olm: Olm, Megolm, the SAS and Ed25519 keypairs for Matrix's end-to-end encryption over vodozemac, with a C ABI 827 1,022 100.0 % lines
omemo C ext/omemo: the tacky::omemo binding 979 398 98.6 % lines
Rust tacky-omemo: X3DH, Double Ratchet, XEdDSA; OMEMO 0.3 and OMEMO 2 2,080 4,045 99.8 % lines
xmpp Tcl tacky/xmpp: the session, the client modules, the XEP-0114 component, the in-process test driver 90,086 159,733 99.0 % procs
everything else C, Tcl, Python, sh, make, CMake embed/, examples/, tools/, test runner, build files; not in the total 11,204 5,917 --
total (without everything else) Tcl 95,069 · C/C++ 22,424 · Rust 18,632 136,125 197,919 Tcl 99.0 % procs · Rust 99.2 % lines · C 98.8 % lines

code: lines of code, no blanks, comments or generated files. tests: lines of test code for that part. The total leaves out everything else. coverage: Tcl counts procedures called, Rust and C count lines run, from the make coverage-all run on 2026-11-07 at commit f5483b52.

The media C and C++ have no coverage figure: the coverage run uses the default webrtc build, and libwebrtc is not measured.

Status and tests

The API is stable. A change that breaks a host comes with a new major version.

Unit suites run against in-process peers: a fake server with real SCRAM, TLS and WebSocket, a stream relay, fault shims for the C bindings, and reference vectors for the ratchet. Integration suites (tests/integration) run against a real server in a container, with OMEMO echo bots beside it.

Integration results, measured on 1.2.7 (2026-11-10, Prosody and ejabberd three rigs each, MongooseIM and Openfire two rigs each, at commit c9421614, 1.2.6 with the destroyed room's first fix; then the bookmarks and MUC suites on Prosody and ejabberd at ce29cfc7, and test_calls and test_groupcall on every server at 7c5c13ec), the rigs' media library present:

server passed failed skipped
Prosody 13 449 0 37
ejabberd 26.09 470 0 16
MongooseIM 6.10.0 395 0 91
Openfire 5.1.2 390 0 96

Four cases failed in the full run and are counted from runs alone: Prosody's test_catchup_history (19 of 19 alone) and test_stickers (3 of 3), ejabberd's test_retraction (8 of 8; its OMEMO 2 retraction case is an ejabberd flake older than 1.2.6, about one run of four alone), and MongooseIM's test_catchup_history (19 of 19). test_calls' five new cases pass on all four servers (9 of 9 each). Openfire is built from the release tarball with the OF-3416 backport. What MongooseIM and Openfire lack is marked off them by name in the suites and said in their launchers' headers. Skips are features a server or its rig lacks.

docs/rigs.md has the matrix behind this table: per suite and server, the cases that run and those skipped, with the reason, and the cases no server runs. make stats regenerates it, from a tests/servers/sharded.sh all run with RIG_LOGS=_build/shards.

Nothing has been checked by hand against public servers or other clients yet. That is why no feature is DONE.

136,125 lines of code, 197,919 lines of tests (22,871 of them integration suites), 11,012 test cases. Coverage: Tcl 99.0 % procs · Rust 99.2 % lines · C 98.8 % lines (make coverage-all on 2026-11-07 at commit f5483b52).

suite family files cases
Tacky unit suites (tests/client, tests/http, tests/tcl) 266 8291
Tacky integration suites (tests/integration, need a server) 85 476
Rust: tacky-core 53 462
Rust: tacky-keyring 6 54
Rust: tacky-crypto 21 134
Rust: tacky-omemo 15 181
Rust: tacky-olm 5 24
Tcl extensions (ext/*) 21 1344
C: tacky-media: libtackymedia, rtc-ma, rtc-mv (ctest programs) 42 42
C++: tacky-media: webrtc (ctest programs) 4 4
total 518 11012

make docs regenerates these numbers. Coverage comes from the last make coverage-all; its full report is _build/cov/REPORT.md.

Building

Everything builds in libtacky's container image. The machine needs git and docker or podman.

git clone --recurse-submodules ssh://git@dev.another.im:3022/another-im/libtacky.git
cd libtacky
make all test               # build, run every suite that needs no server
make all test REQUIRE=1     # the gate: a missing prerequisite fails

MEDIA_BACKEND is webrtc (default), datachannel or none.

target what it does
make, make dist build everything; dist/libtacky.a
make tackyd-json, make tackyd the two executables
make test every suite that needs no server
make test-tacky-prosody, -ejabberd, -mongooseim, -openfire integration suites against a server in a container
make coverage-all the coverage run behind the table above
make audit every Cargo.lock against the RustSec advisory database (audit.toml is the allow-list); in make gate
make docs handbook, README numbers, DOAP file, Rust API docs
make handbook HANDBOOK.md, from the schema and docs/
make help every target

More in docs/building.md.

Using it

An app links embed/tacky.h and speaks JSON:

tacky *tacky_create(const char *const *backend_args, tacky_emit_fn emit, void *ud);
void   tacky_send(tacky *t, const char *json, size_t len);   /* ["module","method",{args},token] */
void   tacky_destroy(tacky *t);                              /* replies and events arrive at emit */
void   tacky_destroy_wait(tacky *t, unsigned int max_ms);

A Tcl program loads the packages, from a built checkout or through dist/tackyd:

package require tacky::xmpp::component
set h [tacky::xmpp::component new -domain gateway.example.org -secret $secret \
           -host 127.0.0.1 -port 5347 -command onEvent]
tacky::xmpp::component connect $h

examples/component/echo.tcl is a complete component. The handbook, HANDBOOK.md, documents every method, event and Tcl command.