- Tcl 72.1%
- C 11.4%
- Rust 10.8%
- Makefile 1.9%
- Python 1.6%
- Other 2.1%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| docker | ||
| docs | ||
| embed | ||
| examples | ||
| ext | ||
| platforms | ||
| schema | ||
| tacky | ||
| tacky-core | ||
| tacky-crypto | ||
| tacky-keyring | ||
| tacky-media | ||
| tacky-omemo | ||
| tests | ||
| tools | ||
| .gitignore | ||
| .gitmodules | ||
| audit.toml | ||
| coverage.mk | ||
| deps.mk | ||
| fuzz.mk | ||
| LICENSE | ||
| Makefile | ||
| README.md | ||
| release.mk | ||
| ROBOTS.md | ||
| tcl.mk | ||
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.1.18. Licence GPL-3.0.
What you get
dist/libtacky.a: the whole library, Tcl included, behindembed/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. dist/tackyd: runs a Tcl program with no Tcl installed.- Native crates with C ABIs: tacky-core, tacky-crypto, tacky-keyring, tacky-omemo, tacky-media.
The API is described once, in schema/tacky.schema.json. The handbook
(make handbook) and the host bindings are generated from it.
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
- 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
- OTR v3 secret chats with SMP
- Telegram secret chats through a gateway
- Group chats (MUC), bookmarks, archive (MAM) with catch-up
- Replies, reactions, corrections, retractions, mentions
- Polls, stickers, silent and scheduled messages
- 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 | 148 |
| 🟡 WIP | partly implemented or partly tested | 17 |
| 🔴 TODO | not implemented, at most reserved in the API | 22 |
| 🟢 DONE | checked by hand; set by a maintainer | 0 |
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/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,404 | 1,737 | 98.9 % procs |
| C | ext/core: the Tcl binding of tacky-core and the keyring; the SQLCipher crypto provider |
3,901 | 3,435 | 99.3 % lines | |
| Rust | tacky-core: DNS, TCP, TLS (rustls), WebSocket, the XML stream, the C ABI; tacky-keyring: the key vault |
12,546 | 7,527 | 99.3 % lines | |
| crypto | C | ext/crypto: the tacky::crypto binding |
1,369 | 676 | 99.7 % lines |
| Rust | tacky-crypto: hashes, HMAC, HKDF, PBKDF2, AES, X25519 with a C ABI |
2,543 | 2,929 | 99.9 % lines | |
| http | Tcl | tacky/http: HTTP/1.1 client for uploads and downloads |
1,065 | 2,194 | 100.0 % procs |
| media | Tcl | tacky/media: the call media contract and its backends |
1,004 | 1,895 | 96.4 % procs |
| C | ext/media: the binding; tacky-media: the media C API over libdatachannel, with audio (Opus) and video (VP8) engines |
9,723 | 10,340 | -- | |
| C++ | tacky-media/webrtc: the same C API over libwebrtc |
3,723 | 271 | -- | |
| omemo | C | ext/omemo: the tacky::omemo binding |
921 | 398 | 98.6 % lines |
| Rust | tacky-omemo: X3DH, Double Ratchet, XEdDSA; OMEMO 0.3 and OMEMO 2 |
1,983 | 3,943 | 100.0 % lines | |
| xmpp | Tcl | tacky/xmpp: the session, the client modules, the XEP-0114 component, the in-process test driver |
72,383 | 119,604 | 98.8 % procs |
| everything else | C, Tcl, Python, sh, make, CMake | embed/, examples/, tools/, test runner, build files; not in the total | 10,354 | 5,125 | -- |
| total (without everything else) | Tcl 76,856 · C/C++ 19,637 · Rust 17,072 | 113,565 | 154,949 | Tcl 98.7 % procs · Rust 99.5 % lines · C 99.0 % 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-10-31 at commit b297863b.
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 v1.1.10 (2026-10-27), three rigs per server at once:
| server | passed | failed | skipped |
|---|---|---|---|
| Prosody 13 | 207 | 1 | 37 |
| ejabberd 26.09 | 229 | 1 | 15 |
| MongooseIM 6.10.0 | 160 | 0 | 49 |
MongooseIM's row is from v1.1 (2026-10-23); it was not rerun since. Skips are features a server or its rig lacks. The one failure on each server was a disappearing-messages test still expecting the old 60 s floor for a peer's timer; fixed in this release, it passes on both.
Nothing has been checked by hand against public servers or other clients yet. That is why no feature is DONE.
113,565 lines of code, 154,949 lines of tests (11,577 of them integration suites), 9,153 test cases. Coverage: Tcl 98.7 % procs · Rust 99.5 % lines · C 99.0 % lines (make coverage-all on 2026-10-31 at commit b297863b).
| suite family | files | cases |
|---|---|---|
| Tacky unit suites (tests/client, tests/http, tests/tcl) | 227 | 6909 |
| Tacky integration suites (tests/integration, need a server) | 44 | 255 |
| Rust: tacky-core | 53 | 431 |
| Rust: tacky-keyring | 6 | 52 |
| Rust: tacky-crypto | 21 | 134 |
| Rust: tacky-omemo | 15 | 176 |
| Tcl extensions (ext/*) | 19 | 1152 |
| C: tacky-media: libtackymedia, rtc-ma, rtc-mv (ctest programs) | 41 | 41 |
| C++: tacky-media: webrtc (ctest programs) | 3 | 3 |
| total | 429 | 9153 |
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 |
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 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
(make handbook) documents every method, event and Tcl command.