Skip to content

Releases: richer-richard/huddle

huddle 2.0.1 — SAS post-quantum binding fix

10 Jun 00:24
v2.0.1
29174ef

Choose a tag to compare

Fix release. 2.0.0's F1 SAS post-quantum capability binding was asymmetric — each peer folded only the other peer's ML-KEM key into the SAS transcript, so two post-quantum-capable peers derived different safety numbers and could never complete out-of-band verification (and the verified_peers.pq_capable anchor could never be set). It failed closed — no MITM could forge a match and the channel was never weakened — but SAS verification between two 2.0 peers was effectively impossible.

2.0.1 binds both peers' ML-KEM keys in canonical byte-sorted order, so honest peers derive the same code again; a genuine capability mismatch, or a relay stripping one side's key, still makes the codes diverge (downgrade detection preserved).

  • No wire-format or on-disk change. The relay and SAS against 1.3.x / classical / group peers are byte-for-byte unaffected — only client-side SAS derivation changed, so no relay redeploy is required.
  • Both peers must be ≥2.0.1 to complete a PQ-bound SAS. No field peer had a successful PQ-bound SAS before (a match was impossible), so the fix strictly turns guaranteed failures into successes — nothing to stay backward-compatible with.
  • Regression guard added (both_sides_distinct_eks_agree, modeling the real call sites); full huddle-core suite green (201 lib + 4 + 8 + 4).

All four crates published to crates.io at 2.0.1.

huddle 2.0.0 — forward-secrecy steps, recovery, and richer chat

09 Jun 15:58
v2.0.0

Choose a tag to compare

A major release adding a layer of long-wanted capabilities on top of the 1.3.x hardened base. All wire additions are backward-compatible (new variants/fields are optional — a pre-2.0 peer ignores them, old DB rows still decode); new behaviour is 2.0+-only where both ends must understand it.

Built by a waved multi-agent fleet, then put through an adversarial multi-agent bug scan that surfaced 19 real findings — all fixed before this release. Final gauntlet: clippy 0 errors, lib 200 tests, hybrid_dm 4, proptest 4, integration 8/8 (serial), app_over_server 7/7 (serial).

Security & cryptography

  • Post-quantum downgrade residual closed. A peer's ML-KEM-768 capability is bound into the SAS transcript and a new v4 signed invite, and persisted in verified_peers. Once you've verified or been invited by a peer, a malicious relay can no longer silently force the classical fallback.
  • Forward-only Megolm epoch rotation (rotate on N messages / T hours / membership change), with the rotation state now persisted across restarts — a concrete step toward forward secrecy.
  • Content-layer replay protection — a durable per-(room, sender, session, message-index) seen-set drops wire-level replays even across restart / cross-transport rebroadcast.
  • Master-passphrase change + at-rest rekey — atomic, rollback-safe PRAGMA rekey (derives the new key from the existing salt, so there's no salt-write failure window).
  • Safety-number-change alarm on TOFU drift, plus seed/phrase zeroization throughout.

Recovery

  • BIP39 seed phrase — export your identity as a 24-word checksummed mnemonic and restore it (PeerId, ML-KEM key, DM keys) on a new machine.

Reliability

  • At-least-once relay delivery — the relay keeps a queued mailbox row until the recipient ACKs durable receipt; pre-2.0 clients keep the safe delete-after-deliver path.

Product

  • FTS5 full-text search, disappearing messages (per-room TTL, truly deleted), and reactions / replies / edits / deletes (edit shows an "edited" marker; delete-for-everyone is honestly best-effort).

Engineering

  • Single-sourced workspace version, proptest + cargo-fuzz targets, a Prometheus /metrics endpoint on the relay, and a cargo-deny supply-chain gate.

See docs/ROADMAP-2.0-and-beyond.md for the sequenced heavy work this release set the foundation for (MLS groups, Double Ratchet, hybrid PQ auth, metadata blinding, multi-device, mobile).

Relay operators: redeploy huddle-server to get at-least-once delivery + /metrics (backward-compatible, no rush). Install: `cargo install huddle-gui --force` / `huddle --force` / `huddle-server --force`.

huddle 1.3.4 — security & DoS hardening

09 Jun 07:49
v1.3.4
920494e

Choose a tag to compare

A focused hardening release on top of 1.3.3 (no wire-format change; fully compatible with 1.3.x and pre-1.3 peers), closing 19 issues confirmed by a 73-agent adversarial audit of the whole tree — each finding verified by three independent skeptics, then re-reviewed per file for regressions (10/10 files clean).

Security

  • Critical: reject invite version-downgrade (v2/v3 → v1 strips signature + freshness verification; v isn't signature-bound).
  • Fail-secure DB checks: is_member_banned / is_peer_blocked no longer fail open on a database error (they masked errors as "not banned/blocked"); security COUNT(*) checks now deny on error and log.
  • SAS: sas_match refuses to confirm before the SAS code is derived (no confirming a comparison the user never made).

DoS / robustness

  • Relay client now caps WebSocket frames at 512 KiB (was 64 MiB) and bounds nonce/payload sizes + the pre-auth send backlog.
  • Relay server: per-fingerprint connection cap (16) — kills publish fan-out amplification — plus a global accept semaphore (4096).
  • Bounded memory wherever a peer controls the count: incomplete file transfers (LRU + global byte budget), session reject-list, profile-broadcast / key-request throttle maps, attachment listings, and the TUI/GUI open-room message buffers.
  • Path-traversal guard: an attacker file_id is validated as a 64-char hex digest before it touches the cache path.

Correctness

  • load_or_create_salt refuses to overwrite a present-but-corrupt salt (it used to silently regenerate it and permanently brick the SQLCipher DB).
  • A relay configured without a /p2p/<peer-id> suffix now registers its /p2p-circuit reservation.
  • GUI unread counter is now saturating (was a wrapping += 1).

Adds regression tests for the invite downgrade, salt corruption, and file_id traversal / transfer bounding. Install: `cargo install huddle-gui --force` / `huddle --force` / `huddle-server --force`.

huddle 1.3.3 — hardening follow-up

08 Jun 13:20
v1.3.3
9bc3489

Choose a tag to compare

A small fixes release on top of 1.3.2, from a multi-agent audit of the 1.3.2 changes. No wire-format change — fully compatible with 1.3.x and pre-1.3 peers.

Fixes

  • Closed a dial-amplification regression. 1.3.2 capped the opportunistic host-address dial map, but once the cap was reached it still dialed without recording the attempt — so the per-announcer backoff stopped engaging and a flood of bogus room announcements could be turned into repeated outbound dials to an attacker-chosen address. The dial is now refused when the backoff can't be recorded, so both memory and dials stay bounded.
  • SAS verification can't be starved by one peer. The in-memory SAS-handshake map now has a per-partner sub-cap in addition to the global cap, so a single peer can no longer fill it and block everyone else's verification.
  • Slow SAS comparisons no longer time out mid-handshake. The SAS flow TTL is now anchored to code-visible time (and raised to 15 min), so reading the emoji/decimal codes aloud at a relaxed pace won't drop the handshake.
  • Tighter relay pre-auth bound. The pre-WebSocket phase (peek + accept) now shares one timeout instead of two.
  • Cleanup. Removed a dead branch in the GUI close path left by the 1.3.2 refactor (no behavior change).

All findings came from an adversarial multi-agent review; each fix was independently re-verified before release. Build, clippy, and the full test suite (136 lib + integration) are green.

huddle 1.3.2 — bug-fix & hardening pass

08 Jun 10:42
v1.3.2
4c8c237

Choose a tag to compare

A focused fixes/hardening release on top of 1.3.1 — no wire-format change, fully compatible with 1.3.x and pre-1.3 peers. Findings came from a multi-agent audit of the whole tree (15 of 20 confirmed after adversarial verification) plus the huddle app install fix.

huddle app / install

  • huddle app reliably finds your checkout. It builds the GUI from a source clone; previously it only located one via the build-time CARGO_MANIFEST_DIR, the cwd walk-up, or HUDDLE_SRC — so a crates.io install (cargo install huddle) run outside a checkout failed with "couldn't find the huddle source checkout." It now also searches common clone locations under your home directory.

GUI

  • Quit / Restart actually close the window. Confirming "Quit" used to tear down the connection but leave the window open until a second OS-close click; "Restart" could leave two windows. Both now close immediately.

TUI

  • A failed send no longer eats your message — the composed text is restored instead of silently lost.
  • Corrected stale invite-keybinding hints (it's Shift+I / Alt+I, not Ctrl+I / Ctrl+Shift+I / ^I, which collapse to Tab).

Relay (huddle-server)

  • Pre-auth timeout now covers the earliest connection phase, closing a slowloris hole that sat outside the auth-deadline window.
  • Inbound WebSocket frames capped at 512 KiB (was tungstenite's 64 MiB default, ~256× the post-parse payload guard).

Robustness

  • Guarded a panic in the inbound-message path that a concurrent room-leave could trigger (it would permanently halt message processing).
  • Bounded two in-memory maps (sas_flows, host_addr_dial_attempts) against a malicious-peer / unauthenticated-announcement flood.
  • Debounced the 1.3.1 RequestCiphertext key-request emitter so a stalled hybrid handshake can't drive an un-throttled request↔announce ping-pong.

Docs

  • Dropped the SAS module's false Matrix MSC-2241 cross-client-interop claim (huddle uses a 49-emoji subset + rejection sampling under a huddle-specific HKDF info string — not Matrix wire-compatible), and corrected a few stale comments + MANUAL_TESTING scenario 31.

Verified: clippy clean (no new warnings), huddle-core lib 136/136, hybrid_dm 4/4, integration 7/7 (serial). Published to crates.io at 1.3.2 (all four crates).

huddle 1.3.1 — post-quantum downgrade hardening + DM handshake liveness

08 Jun 06:46
v1.3.1
c225a42

Choose a tag to compare

Security + reliability patch hardening the 1.3.0 hybrid post-quantum DM key agreement. No wire-format change; fully compatible with 1.3.0 and pre-1.3 peers.

Post-quantum downgrade hardening

  • PQ-capability pinning. 1.3.0 stopped a relay from stripping the signed ML-KEM fields, but a relay could still replay a peer's captured pre-1.3 (classical-only) announce to force the quantum-breakable classical key — reopening the harvest-now-decrypt-later gap. 1.3.1 pins a peer's post-quantum capability the first time it's seen (persisted across restarts) and then refuses the classical fallback for that peer.
  • Self-healing classical→hybrid upgrade. A DM that ever ends up keyed classical (rollout timing, or a replay that won an initial race) is upgraded to hybrid the moment the partner's capability is observed — no restart — and the upgrade rotates the outbound Megolm session so every message sent after the upgrade uses a key never exposed classically. (Forward-only: messages already sent during the brief classical window stay exposed.) Upgrades are one-way; a hybrid DM is never downgraded.

Handshake liveness

  • The responder explicitly requests the KEM ciphertext when missing, a bounded background nudge re-prompts a stalled handshake, and a decrypt-miss key-request re-delivers a lost session key — so a single lost announce no longer wedges a DM until restart.

Docs

  • Corrected the threat-model wording: rotation is forward-only, the relay can attempt a replay-downgrade (now mitigated by the pin, with a documented first-contact residual), and a few stale claims/labels.

Reviewed by a multi-agent adversarial pass (design judge panel + two review rounds). Full workspace build/clippy/tests green.

huddle 1.3.0 — post-quantum hybrid DM encryption (X25519 + ML-KEM-768)

07 Jun 10:05
v1.3.0
e090ca7

Choose a tag to compare

huddle 1.3.0 — post-quantum hybrid DM encryption (X25519 + ML-KEM-768)

Direct-message key agreement is now hybrid post-quantum. A DM's wrap key is
derived from both a classical X25519 ECDH secret and a post-quantum
ML-KEM-768 (FIPS 203) encapsulated secret, combined through HKDF-SHA256. The
key is secure as long as either primitive holds — so a future quantum computer
that breaks X25519 no longer threatens recorded DMs. This closes the
"harvest now, decrypt later" gap (an adversary storing ciphertext today to
decrypt once quantum hardware exists). It's the same approach Signal standardized
as PQXDH, scoped to huddle's static DM model.

Highlights

  • No new key to manage, no migration. Each identity's ML-KEM keypair is
    derived deterministically from the existing Ed25519 seed, so every current
    identity gains a post-quantum key for free; nothing changes on disk.
  • Backward compatible. The ML-KEM public key and ciphertext ride in new,
    optional MemberAnnounce fields. A 1.3 peer talking to a pre-1.3 peer falls
    back automatically to the classical X25519 DM key — DMs keep working. A DM goes
    hybrid only when both peers are on 1.3+.
  • Downgrade-resistant. The new fields live inside the signed announce
    envelope, so a malicious relay can't strip them to force a classical downgrade
    without breaking the signature.

Unchanged by design (and honest about it)

  • Message contents are still encrypted with Megolm (already quantum-
    resistant — symmetric AES-256 + HMAC-SHA-256); file bytes with
    ChaCha20-Poly1305.
  • Identities and message authenticity still use Ed25519 signatures
    (classical). Forging a signature needs a quantum computer operating live, not
    "harvest now", and replacing it would break the relay auth, fingerprints, TOFU
    pinning, and connect codes. Post-quantum signatures (ML-DSA / SLH-DSA) are a
    possible future step. See SECURITY.md for the full posture.

Notes

  • The relay (huddle-server) runtime is unchanged — the post-quantum work is
    entirely client-side, so existing relays serve 1.3 clients with no redeploy.
  • A multi-agent adversarial crypto review found no critical/high issues; the full
    workspace test suite (incl. two-node gossipsub DMs over the hybrid path) passes.

huddle 1.2.5 — bigger attachments (50 MiB) + attach-by-path polish

06 Jun 07:43
v1.2.5
28e0504

Choose a tag to compare

  • Larger attachments — the per-file cap goes from 1 MiB to 50 MiB. Chunks are now 128 KiB, keeping a full-size file at ~400 chunks — under the relay's per-recipient mailbox cap, so a big file still reaches an offline recipient. The whole file is held in memory on both ends and the receiver enforces its own cap, so a >1 MiB file only lands if both peers are on 1.2.5+. No relay change required (verified end-to-end against the live relay, including 3-member group fan-out + a multi-MiB round-trip).
  • attach-by-path polish (from the 1.2.4 review). TUI + GUI share one ~-expansion helper (only ~ / ~/… expand); "attach by path" is guarded on being in a room; the GUI modal submits on Enter; empty input gives feedback; the in-app help no longer advertises a bare p that only works inside the picker.

All four crates bumped 1.2.4 → 1.2.5.

huddle 1.2.4 — attach a file by typing a path + a tidier composer

06 Jun 05:43
v1.2.4
67efb9c

Choose a tag to compare

What's new in 1.2.4

  • Attach a file by typing its path — for when the native file dialog is in your way (a headless box, an awkward window manager, or you just know the path):
    • TUI: press p in the file picker (or run "attach a file by path" from the command palette) to type a POSIX path. ~ expands to home; a path that isn't a real file shows an inline error and keeps what you typed (it isn't dropped).
    • GUI: Settings → Privacy → "attach by typing a path" swaps the Attach button's native file dialog for a path-entry box. Off by default; the choice persists.
  • Tidier message composer (GUI). The input row no longer reserves a tall resizable band, so the dead space under the message box is gone.

Docs brought up to date: README key-binding tables + File-attachments section, a new MANUAL_TESTING scenario, and SECURITY notes (stable *.workers.dev clearnet default).

Published to crates.io: huddle, huddle-gui, huddle-server, and huddle-core at 1.2.4.

Install: cargo install huddle --force (TUI) / cargo install huddle-gui --force (GUI), or git pull && huddle app for the macOS /Applications/Huddle.app bundle.

huddle 1.2.3 — message timestamps show real gaps + seconds

04 Jun 23:59
v1.2.3
ceaa2c0

Choose a tag to compare

  • No more "continuous" messages across a quiet gap. The chat used to break only on a new calendar day, so two messages sent minutes (or hours) apart with nobody talking in between ran together as one burst. Now a fresh, timestamped group starts whenever messages are more than ~2 minutes apart, so you can see when time actually passed.
  • Seconds in timestamps. Message times now show HH:MM:SS (UTC), matching the logs (already UTC). The desktop app re-shows the sender+time header after a gap; the terminal UI draws a centered time divider.
  • Fix: attachment cards stay aligned with message bodies (file-card column width now derives from the message prefix width, one source of truth).

Seeing an old version (e.g. v1.1.3)?

That's a stale install — the version is compiled in, not a bug. Update with cargo install huddle-gui --force / cargo install huddle --force, or git pull && huddle app.

All four crates bumped 1.2.2 → 1.2.3.