Assurance you can check

Most messaging infrastructure asks you to trust a security claim. Tacenta draws the line precisely: what is machine-checked, what is tested, and what is trusted. Each claim below is stated at the strength of its evidence and no stronger, and the tier it sits in tells you what that evidence is.

Proven

Each claim in this section is machine-checked in Lean. The ledger lists 230 theorems, and 80 of them carry no pinned axiom list, so a change to the axioms they rely on would not, by itself, fail the build. CI rebuilds the model, proofs, committed Rust-to-Lean translation and evidence checks on every push. The translation is attested in public CI; regenerating it from Rust is a separate verification step. Where a proof covers the specification but not yet the Rust, or where a part of the system is tested rather than proved, the entry below says so.

The wire format round-trips
Every message the encoder produces, the decoder reads back intact. Wrong version, unknown type, length mismatch, and trailing bytes are rejected by construction, so a malformed frame cannot be mistaken for a valid one.
Delivery is exactly-once and in order
The delivery state machine never replays, reorders, or loses a message, and an acknowledgement can only move forward. A message counts as delivered to a user precisely when every one of their devices has read past it, and once delivered it stays delivered. The shipped Rust inherits this: it is proven to refine the specification.
The ratchet and the handshake match their specification
Tacenta's own protocol implementation is translated to Lean by Charon and Aeneas. Named operations of the Double Ratchet, sparse post-quantum ratchet, ML-KEM Braid and composed Triple Ratchet have machine-checked functional-agreement theorems against executable models, under their stated hypotheses. The classical Double Ratchet and composed Triple Ratchet receive theorems cover successful Rust results only: they do not prove that every model acceptance is accepted by Rust, or relate Rust errors to model refusals and unchanged state. Other named theorems have different scopes; the source ledger states each direction and covered branch. The same work establishes panic-freedom for named ratchet entry points under stated boundary, room and counter preconditions. The refinement proofs stop at the ratchet boundary. Separately, Session::encrypt, Session::decrypt and the two establishment entry points have panic-freedom theorems, each conditional on a contract record of 15 to 44 named hypotheses and on explicit headroom. Decrypt and the responder entry point also assume a model of the zeroize wrapper that holds derived keys. The ledger in the source gives the size of each record; the hypotheses themselves are the fields of Lean structures in the source. Thirteen primitive contracts have satisfiability witnesses, and no theorem shows that any of the four records can be satisfied as a whole. The core has also not yet linked satisfiability witnesses to every numeric size and counter precondition: by its own rough count there are some 140 bound hypotheses on some 95 theorems, in about 54 shapes. Session and prekey orchestration are otherwise modelled and tested, but not proved end to end.
Bounded session lifecycle observations hold in the model
Three Lean theorems cover a bounded abstract lifecycle: a refusal and restore leave a later honest observation unchanged; a terminal failure stays failed; and a replay has no second acceptance. The model assumes its abstract cryptographic verdicts and a committed snapshot. These are narrow L2 evidence, not a new security property or an end-to-end refinement of session orchestration.
Identity trust holds on first contact
Whatever key a device first registers is the key it stays bound to. A different key cannot displace an existing binding, and only the holder of the currently bound key can rotate it. The trust decision is proven of the shipped Rust; the recovery path around it is proven of the specification.

Tested

Verified by running the code against adversarial inputs, not by proof. Strong evidence, a different kind: a test covers the cases it draws, where a proof covers all of them.

The implementation is compared with the model on named paths
Model-generated vectors run against the Rust implementation and against a reader written from the specification. The reader is project-controlled evidence: its isolation from the code rests on records written by its own author for passes 2 to 7, 12 and 13 and is unrecorded for pass 1, and passes 8 to 11 and later maintenance were done with the implementation in view. Generated sequences compare the two ratchets and the Triple Ratchet step by step, including their persisted bytes, import refusals and counter ceilings. The public ledger does not claim cross-implementation interoperability as evidence. A pinned libsignal comparison is planned at the prekey-bundle boundary, but no such run is recorded; Tacenta's message layer uses its own wire format and KDF labels.
Decoders survive hostile input
Six coverage-guided fuzz targets and property-based decoder tests exercise hostile input. The public CI gate runs the Rust test suite, decoder properties and constant-time assembly checks; fuzzing, timing measurements and the interoperability harness run outside that gate.
Session and prekey operations are differential-tested
A project-controlled reader, which its author declared was written from the specification alone, checks all nine agreed operation families over 27 traces and 41 steps, including prekey lifecycle and replay, establishment, authenticated send and receive, skipped-key recovery and named refusals. Five controls are required to fail. The declaration is kept outside the public repository, and the committed reader has changed since it was delivered. This reached the declared bounded L2 target. Two later changes, the legacy_blocked marker rules and identity-key admission, are not in the corpus, and the decision behind that target has not been reopened for them. The reader compares selected outcomes and stored state, not every possible session lifecycle or a full end-to-end refinement, and the signature, key-agreement and MAC verdicts in its traces are declared facts, not recomputed.
Authentication does not leak who exists
A sign-in with a wrong password and a sign-in for an account that does not exist take the same order of time and return the same error, so timing does not reveal which accounts are real. This is code-level reasoning backed by an empirical timing check, not a constant-time proof.
Each release is exercised against the hosted service
On every release tag, the CLI binary that tag published is downloaded, checked against its checksum, and run through a full encrypted round trip against the live service, over TCP and again over the WebSocket carriage. Then each SDK head built from that tag runs the same conversation against the live service: two users sign up, message both ways and one resumes from saved state. TypeScript runs it over the carriage; the Swift Package on a Mac and the Kotlin bindings on the JVM run it over TCP and again over the carriage. The transcript of each run is kept at tacenta.com/dl/conformance/, one file per version, with the binary's hash and the service document the server answered with. It is a record of what ran, not a proof: the Kotlin head runs on the JVM rather than a device, and a pass says the shipped code did what the tests ask of it that day.

Assumed

The trusted base. This section grew in August 2026: Tacenta now implements the protocol itself rather than depending on libsignal, so more of the responsibility is ours than it used to be. That is a stronger claim to have to defend, not a weaker one.

The primitives, in full
Tacenta implements the protocol, including the handshake, the ratchets and the encodings, and takes its primitives from established libraries, with the exceptions listed under in-house cryptographic code below. X25519 and Ed25519 come from the dalek crates, ML-KEM-1024 from libcrux, and SHA-256, HMAC and AES from the RustCrypto crates. Their security is assumed, not established here.
In-house cryptographic code
XEdDSA, signing with the same key used for key agreement, is implemented in-house over the Dalek curve and signature crates. Tacenta also contains a hand-written MAC comparison in the Braid that is meant to be constant-time: an assembly check covers that property and no proof does, although the comparison's panic-freedom and agreement with the model are proved; a finite-field erasure codec used by the Braid; the rule that decides which identity keys are accepted; and the composition of AES-CBC with HMAC that seals each message. The primitive libraries supply the underlying curve, hash, MAC, cipher and ML-KEM operations. These areas remain appropriate targets for outside scrutiny.
Development tools
Natuvea engineers use automated and AI-assisted tools for code, specification text, tests and review preparation. The maintainer is accountable for accepting the work, and tool output is project-controlled evidence rather than independent review.
Not yet independently audited
A review of the cryptography and code examined pinned commit d2dc386 and its report was received on 2026-09-12. The report and the reviewer's identity are not public, so this page does not count it as external assurance. It was a review, not an audit, and fixes from it are in the commit history of tacenta-core. No independent audit has issued a report, and the proof-ledger engagement is not underway, so nothing here rests on outside sign-off. The core's own readiness gates for that engagement are recorded as not met. The core records no review of the claims ledger, claim by claim, by a reader who did not write it, and the review comments on several recent normative changes were written by the account that authored them.
Published security advisories
Four security advisories have been published for tacenta-core: GHSA-cgvw-9r5f-xrxp, GHSA-v95x-f6p3-4qxg, GHSA-9hv6-fr6w-9758 and GHSA-r8w8-4rg9-mxhm. All four fixes are in the tagged commit this page cites. Erasure of secret state and the rule for which identity keys are acceptable at all are tested, not proved. That rule is separate from the key-binding rule under Identity trust above.
The proof tools and the compiler
The theorems assume the Lean kernel is sound, that Charon and Aeneas model Rust faithfully, and that the Rust compiler is correct. Some proof paths also trust Lean's compiled evaluation. These load-bearing assumptions, and the checks that constrain them, are written down.

Check it yourself

The source and the proofs are public, under the Apache-2.0 license. tacenta-core is the cryptographic core: its CLAIMS.md states what each proof covers and what it does not, and REPRODUCING.md rebuilds them. Its assurance inventory records the current evidence and its open targets; the obligation inventory names the blocking work before the proof-ledger review. tacenta is the product around it, with its own claims ledger and a recipe to rerun its proofs and checks from a clean checkout. Where this page and those ledgers disagree, the ledgers are right.

Why the line matters

A security claim you cannot inspect is a claim you have to take on trust. The value here is not that every part is proven. It is that the boundary between proven, tested, and trusted is drawn in one place and never blurred, so you can decide for yourself whether it meets your bar. For a team that has to answer for the privacy of its users, that boundary is the product.

Assessing Tacenta for a team

If you are deciding whether this boundary meets your application's requirements, write to info@natuvea.com with what you are building and the evidence your review needs. The reply says what exists today, where to find it, and what does not exist yet.