Tacky is high-level library for building XMPP clients
  • Tcl 75.9%
  • Rust 9.5%
  • C 9.4%
  • Makefile 1.6%
  • Python 1.5%
  • Other 2%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
pounceandmiss c7bb089874
session: a wake is noticed within 5 s, on every platform
Connection fix: after a sleep the app sat on a dead link for up to 90 s
before reconnecting (macOS), possibly forever on Windows, and a sleep
during a reconnect wait was waited out after the wake.

The old check compared the wall clock with the monotonic one (which
counts sleep on Windows) and only when the next deadline came, often the
keepalive. Now, while a session has a link or waits to dial, its timer
comes at least every 5 s; a look that finds over 15 s gone on the wall
clock means the process slept: a link is probed, a wait redials at once.
mock_stream's advance stops at each deadline on the way.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 02:32:37 -08: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.13: the README's version, figures and rig table, the changelog's section, the DOAP release, the library version answered (LibraryVersion), media.tcl's createPeer header naming tacky-media ABI 1.5 for relay_only (HANDBOOK.md regenerated), ROBOTS.md saying the ejabberd rig has TURN now, and the regenerated docs/rigs.md, for the tree to be tagged v1.2.13, a patch of the 1.2 line built on 1.2.12 (06250dac): the peer IP exposure fixes of the network audit (a55f6239: no direct file offer accepted by itself, the installation setting ice_relay_only on every media backend, tacky-media ABI 1.5; e5250cf2: the ejabberd rig's STUN/TURN and the two relay-only rig cases) and fix/ledger-34 merged (b7e98a97: the call SAS over every content's fingerprint, a refused bookmark put back and never resent); host-visible first in the changelog. Gate: unit on b7e98a97 (this code but the version strings and documents): webrtc lane 8557 total, 8554 passed, 1 failed (test_message_delete under the host's load, 38 of 38 alone three times), 2 skipped; datachannel lane 8557, 8546 passed, 2 failed (test_http_parallel under load average 45 on 32 cores, 5 of 5 alone five times), 9 skipped; Rust 849 passed each; make -k test-core (1085 ok) in both builds and wasm clean; on e5250cf2 (before the ledger fixes) the lanes were 8552/8550/0/2 and 8552/8542/1/9 (test_http_parallel again). check-api (72 modules, 648 methods, 209 events, 199 types), check-headers, check-cbindgen and check-native-neutral clean; make doc clean; audit clean against the advisory database of 2026-11-11 (fd6f398) with only RUSTSEC-2026-0387 allowed; the Windows (MinGW) and Android (arm64) archives built on b7e98a97 (iOS and macOS not built here). Rigs on e5250cf2, three per server for Prosody 13 and ejabberd 26.09 (ejabberd's rig serving STUN and TURN through the whole set), two for MongooseIM 6.10.0 and Openfire 5.1.2, 519 cases each: Prosody 473/3/43, ejabberd 496/1/22, MongooseIM 407/2/110, Openfire 402/3/114; alone Prosody test_message 14/14 twice and test_otr whole, MongooseIM test_otr and test_chatstates 12/12, Openfire test_otr and test_catchup_history 23 passed 3 skipped; ejabberd test_flaky_send ack-lost-resumed passed one of three runs alone, each failure its reconnect through the suite's fault proxy stalling on STARTTLS, as in 1.2.11's rigs. Counted: Prosody 476/0/43, ejabberd 497/0/22, MongooseIM 409/0/110, Openfire 405/0/114. On b7e98a97 test_calls, test_bookmarks and test_bookmarks_autojoin again: Prosody 24 passed 1 skipped (turnRig), ejabberd 25/0/0. The two relay-only cases on the datachannel build (ejabberd rig with TURN, test_calls and test_jinglefile_p2p): 14 passed, 1 failed (p2p-int-relay-only-crosses-the-turn-relay timed out waiting for the file under load average 55), then test_jinglefile_p2p 5 of 5 alone twice. Coverage kept from 1.2.0 (f5483b52: Tcl 99.0 % procs, Rust 99.2 %, C 98.8 % lines), not re-measured. 2026-11-12 05:00:23 +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 Peer IP exposure (the network audit NA-REPORT of 2026-11-12, C4-C7 and H4; since 1.2.13): no direct file offer (XEP-0234) is accepted by itself any more, whoever sent it and whatever attachment_autofetch and its cap say, since the accept stands a transport up that tells the sender this device's public and LAN addresses and dials the sender's server's SOCKS5 proxy (jinglefile HandleSessionInitiate; MayAutoAccept, which only that path read, is gone; the offer's BoB thumbnail still follows the autofetch policy, and an HTTP file's auto-download is unchanged), and the new installation setting ice_relay_only (0 or 1, unset 0; extdisco relayOnly, read at each call and transfer) keeps every peer from learning the address: calls (one-to-one and group-call legs, calls RelayServers) and data channels (jinglefile RelayServers) take only the TURN URLs of the server's XEP-0215 list and pass -relay-only 1 to tacky::media createPeer, which each backend honours (native: tacky_media_peer_config.relay_only, tacky-media ABI 1.5, libdatachannel's RTC_TRANSPORT_POLICY_RELAY and libwebrtc's kRelay, read only from a backend opened at 1.5 and refused by the ext binding below it rather than gather every address; host: createPeer relayOnly; the page: <MediaOpen> relay_only, for iceTransportPolicy 'relay'), and OnMediaEvent drops any local candidate that is no relay's and hides a relay's raddr/rport, in the trickle and in the SDP (extdisco_relay_candidate, extdisco_relay_sdp); with no TURN service a call fails relay-unavailable (calls <Failed>, the peer told <failed-transport/>), a sender's data channel is skipped for its next transport before anything is sent (NoRelayToOffer) and a receiver's is replaced with IBB (StartAnswer, ReplaceWithIbb with its reason), and SOCKS5 opens no listener and offers no direct candidate and dials only a proxy of our own server (S5bDialAll, file OwnService), never the peer's address nor the peer's server's proxy. Schema: the setting in setting's doc, file accept's doc, MediaOpen relay_only and HostCommand relayOnly (HANDBOOK.md regenerated); ROBOTS.md says why. Tests: the jinglefile suites' pumps tap each offer through file accept as a host does (tests/client/helpers.tcl tacky_tap_offers; a test about an offer that waits turns it off), the rig suites accept likewise (p2p accept_offer in transfer, s5b and jet2 a tap listener); new cases, each failing on develop 06250dac: a mutual contact's offer under the cap waits for the tap, a relay-only data pc over the TURN service alone with no host or srflx candidate and the relay's raddr hidden, the sender without TURN going IBB from the start, the receiver without TURN asking for IBB, SOCKS5 relay-only offering and dialling nothing but own proxies, the page's relay-only MediaOpen, a call without TURN failing relay-unavailable as caller and callee, the candidate filter and its helpers; ext/media's create-peer -relay-only gathering nothing, and tacky-media's contract case relay on both implementations. No rig has TURN, so relay-only connecting over a real relay is not proven here. Gate (this tree): webrtc lane 8552 total, 8550 passed, 0 failed, 2 skipped, Rust 849, contract webrtc passed; datachannel lane 8552, 8543 passed, 0 failed, 9 skipped, Rust 849, test_media 52 passed 1 skipped; check-api (72 modules, 648 methods, 209 events, 199 types), check-headers and check-native-neutral clean; rigs Prosody 13 and ejabberd 26.09, two each, test_calls, test_groupcall, test_jinglefile_p2p, test_jinglefile_s5b, test_jinglefile_jet2: Prosody 49 passed, ejabberd 48 passed 1 skipped (test_jinglefile_p2p's helpers moved ahead of their first use and rerun alone, 4 of 4 on each). 2026-11-12 03:21:45 +02:00
platforms Merge remote-tracking branch 'origin/develop' into integ/1.2.11-dev 2026-11-11 22:32:42 +02:00
schema messagestore: a row or archive result that fails is a fault of its own 2026-11-11 23:22:24 -08:00
tacky session: a wake is noticed within 5 s, on every platform 2026-11-13 02:32:37 -08:00
tacky-core Merge remote-tracking branch 'origin/develop' into integ/1.2.11-dev 2026-11-11 22:32:42 +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 Peer IP exposure (the network audit NA-REPORT of 2026-11-12, C4-C7 and H4; since 1.2.13): no direct file offer (XEP-0234) is accepted by itself any more, whoever sent it and whatever attachment_autofetch and its cap say, since the accept stands a transport up that tells the sender this device's public and LAN addresses and dials the sender's server's SOCKS5 proxy (jinglefile HandleSessionInitiate; MayAutoAccept, which only that path read, is gone; the offer's BoB thumbnail still follows the autofetch policy, and an HTTP file's auto-download is unchanged), and the new installation setting ice_relay_only (0 or 1, unset 0; extdisco relayOnly, read at each call and transfer) keeps every peer from learning the address: calls (one-to-one and group-call legs, calls RelayServers) and data channels (jinglefile RelayServers) take only the TURN URLs of the server's XEP-0215 list and pass -relay-only 1 to tacky::media createPeer, which each backend honours (native: tacky_media_peer_config.relay_only, tacky-media ABI 1.5, libdatachannel's RTC_TRANSPORT_POLICY_RELAY and libwebrtc's kRelay, read only from a backend opened at 1.5 and refused by the ext binding below it rather than gather every address; host: createPeer relayOnly; the page: <MediaOpen> relay_only, for iceTransportPolicy 'relay'), and OnMediaEvent drops any local candidate that is no relay's and hides a relay's raddr/rport, in the trickle and in the SDP (extdisco_relay_candidate, extdisco_relay_sdp); with no TURN service a call fails relay-unavailable (calls <Failed>, the peer told <failed-transport/>), a sender's data channel is skipped for its next transport before anything is sent (NoRelayToOffer) and a receiver's is replaced with IBB (StartAnswer, ReplaceWithIbb with its reason), and SOCKS5 opens no listener and offers no direct candidate and dials only a proxy of our own server (S5bDialAll, file OwnService), never the peer's address nor the peer's server's proxy. Schema: the setting in setting's doc, file accept's doc, MediaOpen relay_only and HostCommand relayOnly (HANDBOOK.md regenerated); ROBOTS.md says why. Tests: the jinglefile suites' pumps tap each offer through file accept as a host does (tests/client/helpers.tcl tacky_tap_offers; a test about an offer that waits turns it off), the rig suites accept likewise (p2p accept_offer in transfer, s5b and jet2 a tap listener); new cases, each failing on develop 06250dac: a mutual contact's offer under the cap waits for the tap, a relay-only data pc over the TURN service alone with no host or srflx candidate and the relay's raddr hidden, the sender without TURN going IBB from the start, the receiver without TURN asking for IBB, SOCKS5 relay-only offering and dialling nothing but own proxies, the page's relay-only MediaOpen, a call without TURN failing relay-unavailable as caller and callee, the candidate filter and its helpers; ext/media's create-peer -relay-only gathering nothing, and tacky-media's contract case relay on both implementations. No rig has TURN, so relay-only connecting over a real relay is not proven here. Gate (this tree): webrtc lane 8552 total, 8550 passed, 0 failed, 2 skipped, Rust 849, contract webrtc passed; datachannel lane 8552, 8543 passed, 0 failed, 9 skipped, Rust 849, test_media 52 passed 1 skipped; check-api (72 modules, 648 methods, 209 events, 199 types), check-headers and check-native-neutral clean; rigs Prosody 13 and ejabberd 26.09, two each, test_calls, test_groupcall, test_jinglefile_p2p, test_jinglefile_s5b, test_jinglefile_jet2: Prosody 49 passed, ejabberd 48 passed 1 skipped (test_jinglefile_p2p's helpers moved ahead of their first use and rerun alone, 4 of 4 on each). 2026-11-12 03:21:45 +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: four of session.rs's lookahead tests bind with mut what they never change (alice, and bob in the key-exchange one); rustc warned on each (unused_mut) and the bindings drop it, tests only, nothing else changes. 2026-11-11 15:40:35 +02:00
tests session: a wake is noticed within 5 s, on every platform 2026-11-13 02:32:37 -08: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 tacky-core: hickory 0.26.3 2026-11-11 18:14:46 +02:00
CHANGELOG.md 1.2.13: the README's version, figures and rig table, the changelog's section, the DOAP release, the library version answered (LibraryVersion), media.tcl's createPeer header naming tacky-media ABI 1.5 for relay_only (HANDBOOK.md regenerated), ROBOTS.md saying the ejabberd rig has TURN now, and the regenerated docs/rigs.md, for the tree to be tagged v1.2.13, a patch of the 1.2 line built on 1.2.12 (06250dac): the peer IP exposure fixes of the network audit (a55f6239: no direct file offer accepted by itself, the installation setting ice_relay_only on every media backend, tacky-media ABI 1.5; e5250cf2: the ejabberd rig's STUN/TURN and the two relay-only rig cases) and fix/ledger-34 merged (b7e98a97: the call SAS over every content's fingerprint, a refused bookmark put back and never resent); host-visible first in the changelog. Gate: unit on b7e98a97 (this code but the version strings and documents): webrtc lane 8557 total, 8554 passed, 1 failed (test_message_delete under the host's load, 38 of 38 alone three times), 2 skipped; datachannel lane 8557, 8546 passed, 2 failed (test_http_parallel under load average 45 on 32 cores, 5 of 5 alone five times), 9 skipped; Rust 849 passed each; make -k test-core (1085 ok) in both builds and wasm clean; on e5250cf2 (before the ledger fixes) the lanes were 8552/8550/0/2 and 8552/8542/1/9 (test_http_parallel again). check-api (72 modules, 648 methods, 209 events, 199 types), check-headers, check-cbindgen and check-native-neutral clean; make doc clean; audit clean against the advisory database of 2026-11-11 (fd6f398) with only RUSTSEC-2026-0387 allowed; the Windows (MinGW) and Android (arm64) archives built on b7e98a97 (iOS and macOS not built here). Rigs on e5250cf2, three per server for Prosody 13 and ejabberd 26.09 (ejabberd's rig serving STUN and TURN through the whole set), two for MongooseIM 6.10.0 and Openfire 5.1.2, 519 cases each: Prosody 473/3/43, ejabberd 496/1/22, MongooseIM 407/2/110, Openfire 402/3/114; alone Prosody test_message 14/14 twice and test_otr whole, MongooseIM test_otr and test_chatstates 12/12, Openfire test_otr and test_catchup_history 23 passed 3 skipped; ejabberd test_flaky_send ack-lost-resumed passed one of three runs alone, each failure its reconnect through the suite's fault proxy stalling on STARTTLS, as in 1.2.11's rigs. Counted: Prosody 476/0/43, ejabberd 497/0/22, MongooseIM 409/0/110, Openfire 405/0/114. On b7e98a97 test_calls, test_bookmarks and test_bookmarks_autojoin again: Prosody 24 passed 1 skipped (turnRig), ejabberd 25/0/0. The two relay-only cases on the datachannel build (ejabberd rig with TURN, test_calls and test_jinglefile_p2p): 14 passed, 1 failed (p2p-int-relay-only-crosses-the-turn-relay timed out waiting for the file under load average 55), then test_jinglefile_p2p 5 of 5 alone twice. Coverage kept from 1.2.0 (f5483b52: Tcl 99.0 % procs, Rust 99.2 %, C 98.8 % lines), not re-measured. 2026-11-12 05:00:23 +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.13: the README's version, figures and rig table, the changelog's section, the DOAP release, the library version answered (LibraryVersion), media.tcl's createPeer header naming tacky-media ABI 1.5 for relay_only (HANDBOOK.md regenerated), ROBOTS.md saying the ejabberd rig has TURN now, and the regenerated docs/rigs.md, for the tree to be tagged v1.2.13, a patch of the 1.2 line built on 1.2.12 (06250dac): the peer IP exposure fixes of the network audit (a55f6239: no direct file offer accepted by itself, the installation setting ice_relay_only on every media backend, tacky-media ABI 1.5; e5250cf2: the ejabberd rig's STUN/TURN and the two relay-only rig cases) and fix/ledger-34 merged (b7e98a97: the call SAS over every content's fingerprint, a refused bookmark put back and never resent); host-visible first in the changelog. Gate: unit on b7e98a97 (this code but the version strings and documents): webrtc lane 8557 total, 8554 passed, 1 failed (test_message_delete under the host's load, 38 of 38 alone three times), 2 skipped; datachannel lane 8557, 8546 passed, 2 failed (test_http_parallel under load average 45 on 32 cores, 5 of 5 alone five times), 9 skipped; Rust 849 passed each; make -k test-core (1085 ok) in both builds and wasm clean; on e5250cf2 (before the ledger fixes) the lanes were 8552/8550/0/2 and 8552/8542/1/9 (test_http_parallel again). check-api (72 modules, 648 methods, 209 events, 199 types), check-headers, check-cbindgen and check-native-neutral clean; make doc clean; audit clean against the advisory database of 2026-11-11 (fd6f398) with only RUSTSEC-2026-0387 allowed; the Windows (MinGW) and Android (arm64) archives built on b7e98a97 (iOS and macOS not built here). Rigs on e5250cf2, three per server for Prosody 13 and ejabberd 26.09 (ejabberd's rig serving STUN and TURN through the whole set), two for MongooseIM 6.10.0 and Openfire 5.1.2, 519 cases each: Prosody 473/3/43, ejabberd 496/1/22, MongooseIM 407/2/110, Openfire 402/3/114; alone Prosody test_message 14/14 twice and test_otr whole, MongooseIM test_otr and test_chatstates 12/12, Openfire test_otr and test_catchup_history 23 passed 3 skipped; ejabberd test_flaky_send ack-lost-resumed passed one of three runs alone, each failure its reconnect through the suite's fault proxy stalling on STARTTLS, as in 1.2.11's rigs. Counted: Prosody 476/0/43, ejabberd 497/0/22, MongooseIM 409/0/110, Openfire 405/0/114. On b7e98a97 test_calls, test_bookmarks and test_bookmarks_autojoin again: Prosody 24 passed 1 skipped (turnRig), ejabberd 25/0/0. The two relay-only cases on the datachannel build (ejabberd rig with TURN, test_calls and test_jinglefile_p2p): 14 passed, 1 failed (p2p-int-relay-only-crosses-the-turn-relay timed out waiting for the file under load average 55), then test_jinglefile_p2p 5 of 5 alone twice. Coverage kept from 1.2.0 (f5483b52: Tcl 99.0 % procs, Rust 99.2 %, C 98.8 % lines), not re-measured. 2026-11-12 05:00:23 +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 platforms/ios.mk builds a Mac Catalyst slice, maccatalyst, when IOS_SDKS names it, for anotherim-swift's Catalyst build (experimental/catalyst's d68972dc, carried to develop). The default is still the device and the simulator. It is the iOS ABI on the Mac: xcrun's macosx SDK, clang's arm64-apple-ios<IOS_MIN>-macabi target, Rust's aarch64-apple-ios-macabi; LibreSSL through cmake's Darwin with the -macabi target in its flags; the Makefile's ios-lib and tcl.mk's iOS pass map the slice the same way, and the Olm archive 1.2 added (IOS_OLM_A) takes the slice's Rust triple as the others do. The generated Package.swift adds .macCatalyst(.v18). 2026-11-11 22:17:28 +02:00
README.md README coverage from a fresh make coverage-all on v1.2.13 (5efd5be2, 2026-11-12), in place of the 1.2.0 run at f5483b52 that every release since had carried over: Tcl 99.1 % procs (was 99.0; xmpp 99.0 to 99.1), Rust 99.2 % lines and C 98.8 % lines as before, ext/crypto's C 99.4 to 99.5 %; every other cell unchanged. The run's Tcl suites 8557 total, 8545 passed, 0 failed, 12 skipped. Only the coverage cells and the run's date and commit change: the code and test lines are 1.2.13's as the release left them (stats.py counted files on disk in the container, where git cannot see the worktree, so its "everything else" row was put back by hand). 2026-11-12 15:37:01 +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 sm: never ack stanzas the host has not had; keep a <failed h/> across a dial 2026-11-11 23:22:24 -08:00
tcl.mk Merge remote-tracking branch 'origin/develop' into integ/1.2.11-dev 2026-11-11 22:32:42 +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.13. 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,907 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,221 8,185 98.8 % lines
crypto C ext/crypto: the tacky::crypto binding 1,444 790 99.5 % 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,084 2,206 100.0 % procs
media Tcl tacky/media: the call media contract and its backends 1,016 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,955 10,693 --
C++ tacky-media/webrtc: the same C API over libwebrtc 4,982 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 92,063 166,770 99.1 % procs
everything else C, Tcl, Python, sh, make, CMake embed/, examples/, tools/, test runner, build files; not in the total 11,222 5,943 --
total (without everything else) Tcl 97,070 · C/C++ 22,433 · Rust 18,671 138,174 205,031 Tcl 99.1 % 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-12 at commit 5efd5be2.

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.13 (2026-11-12, Prosody and ejabberd three rigs each, MongooseIM and Openfire two rigs each, on the release tree before the call SAS and bookmark fixes, whose suites test_calls and test_bookmarks ran again on the tree itself), the rigs' media library present, ejabberd's rig serving STUN and TURN:

server passed failed skipped
Prosody 13 476 0 43
ejabberd 26.09 497 0 22
MongooseIM 6.10.0 409 0 110
Openfire 5.1.2 405 0 114

Nine cases failed in the full runs and are counted from runs alone: Prosody's test_calls accept-active-hangup, test_message edit-react-retract and test_otr, MongooseIM's test_otr and test_chatstates, and Openfire's test_otr and two test_catchup_history cases (all whole alone), and ejabberd's test_flaky_send ack-lost-resumed, which passed one of three runs alone: its reconnect through the suite's fault proxy stalls on STARTTLS, as it did once in 1.2.11. The two relay-only cases run on ejabberd alone (turnRig), and on the datachannel build too. 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.

138,174 lines of code, 205,031 lines of tests (23,936 of them integration suites), 11,263 test cases. Coverage: Tcl 99.1 % procs · Rust 99.2 % lines · C 98.8 % lines (make coverage-all on 2026-11-12 at commit 5efd5be2).

suite family files cases
Tacky unit suites (tests/client, tests/http, tests/tcl) 268 8512
Tacky integration suites (tests/integration, need a server) 87 504
Rust: tacky-core 53 463
Rust: tacky-keyring 6 54
Rust: tacky-crypto 21 134
Rust: tacky-omemo 15 181
Rust: tacky-olm 5 24
Tcl extensions (ext/*) 21 1345
C: tacky-media: libtackymedia, rtc-ma, rtc-mv (ctest programs) 42 42
C++: tacky-media: webrtc (ctest programs) 4 4
total 522 11263

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.