Model: which half of XMPP runs where
This example rebases the fhe-messenger triage circuit on the XMPP data model (RFC 6120 message stanzas, RFC 6122 bare JIDs):
- JID (
localpart@domain), roster (roster.txt: jid → maildrop), and the<message>stanza (from/to/subject/body) live CLIENT-side as plain structured data. The sender's stanza goes tostanza.login the sender's home dir; watch reads the subject back from it. - The recipient's SERVER-side profile = their maildrop directory: cc/pk/mk (public), ct_rules + ct_one (their encrypted rule box), ct_msgrep (encrypted subject hash of each incoming stanza, sender-encrypted under the recipient's pk), ct_match (server output).
- The FHE-relevant field is ONLY the subject watchword: triage matches it against the rule box via the ct×ct soft-equality circuit. The message BODY is out of scope (body privacy = classic E2E/Autocrypt territory, and body-plaintext never enters the modelled server anyway).
- Roster lookups, presence, SASL authentication, and transport stay classical — in any real adaptation these cannot run inside the FHE job.
Privacy model and protocol decomposition
- Each USER keygens a CKKS keypair on the client machine.
pk.bin/cc.bin/mk.binform a public roster entry;sk.binnever leaves the client home (0600, Rust + kernel guards both refuse runs that would ship it). - A SENDER encrypts the subject representation under the RECIPIENT's public key (pk-only encryption; the recipient's secret key is the only thing that can ever see plaintext — the server holds no share of it).
- The SERVER (one Fog job / maildrop turn = one recorded triage circuit) receives ciphertexts only, runs the triage circuit CTC-only, and returns ONLY the encrypted match vector it probed. No plaintext is ever constructed, in recording or in replay (hollow recording for Fog).
What this example does / does NOT do
- DOES: XMPP-shaped message flow (JID/roster/stanza), encrypted subject triage ct×ct (soft-equality polynomial), pk-based recipient maildrop, secret-key hygiene (never on the server), local sim gate + a Fog-compatible 2^16 hollow gate, record/replay differential.
- Does NOT: fan-out routing on encrypted recipient ids (needs soft-eq over an encrypted roster + rotations; sizing in the escalation list below), presence/push (no event loop on batch Fog), mailbox state at rest (needs persistent jobs + named encrypted handles — the Fog capability wishlist in niobium-fhe-workflows), encrypted bodies, MUC, SASL/TLS.
"Adapt XMPP/DeltaChat?" — this example demonstrates what "based on XMPP" can mean on today's Fog: the data model + sender/recipient key discipline transfers cleanly; adaptation of XMPP SERVER LOGIC (routing decisions, roster fan-out, archive) is a from-scratch FHE circuit, not a port, and per-message latency on this worker generation is tens of seconds, not milliseconds. DeltaChat (IMAP-based) inherits the same constraints plus its own gate.
Circuit: encrypted triage (this check-in)
Packing (one ciphertext, SIMD slots; live region = slots 0..R-1):
ct_msgrep: the message topic hash, replicated by the CLIENT into every slot of the live region (client-visible public packing geometry; the value is private).ct_rules: the recipient's R private keyword hashes in slots 0..R-1, encrypted by the recipient themselves and parked server-side (the "rule box" of the mailbox).ct_one: every slot = 1.0 (client-encrypted public constant — never build plaintexts inside the recorded trace, and every server multiply stays CTC, matching both recorder invariants).- R = RULE_CAP from
pack(default 4, max 8).
Hashing (client side, pure Rust, no FHE): lowercase ASCII token → FNV-1a 64-bit → low 48 bits → affinely into [-0.45, +0.45]. Stays strictly inside the [-1, 1] band of the soft-equality polynomial with margin.
Soft equality: d = msgrep - rule; sc = (1 - d²)^8, evaluated as
repeated squaring: d2 = d·d; u = one - d2; u2 = u·u; u4 = u2·u2;
u8 = u4·u4. All four multiplies are ct×ct.
- Match (d = 0): (1)⁸ = 1.
- Clean miss (|d| ≥ 0.5): (1 - 0.25)⁸ ≈ 0.10.
- Ambiguity edge: the score crosses MATCH_TAU = 0.5 at |d| ≈ 0.288
((1 - 0.288²)⁸ ≈ 0.50). So
packenforces a DEMO GUARD: minimum |Δhash| between the topic and every non-matching rule must be ≥ MIN_SEP (0.30, just past the 0.288 ambiguity edge) or it exits with a loud error. With 2^48 hash values randomly placed in a 0.9-wide interval a close pair is likely, so expect to edit the demo strings if one collides — never weaken the guard.
Depth audit (fresh ciphertexts, FLEXIBLEAUTO adjustment): d2 (1), u·u (2), u4 (3), u8 (4). Circuit depth 4 of MULT_DEPTH = 6 budget; SCALE 50, FIRST 60 (logQ = 60 + 6·50 = 360 bits; comfortable at N = 2^16, demo posture HEStd_NotSet like the golden and llm-fog examples).
Trace budget: 4 CTC mults + 2 subtractions, no rotations, no bootstrap.
Measured 4,255 FHETCH instructions. At the measured Fog calibration
(~2,700 instr/s worker wall, llm job 97a7f536) this is ~1.6 s of pure
worker compute; the verified Fog round trip (job 0b2cdb44) ran
10 s worker wall for 138.5 MB upload and 4.2 MB encrypted match output —
dominated by transport/upload, not compute.
Keygen cost: N = 2^16, depth 6 — a few seconds, a few hundred MB RAM per user on this box. Fine for demo scale (2-4 users).
Parameter selection (trailing)
- CKKS, FLEXIBLEAUTO, MULT_DEPTH 6, SCALING_MOD_SIZE 50, FIRST_MOD_SIZE 60, HEStd_NotSet.
- CKKS rather than BFV/BGV because the Fog runs CKKS only; a faithful local twin keeps CKKS end-to-end (fog-rag-design precedent).
- No rotation keys: purely slotwise circuit, mk only. Worker-side key material stays ~10 MB instead of the ~hundreds of MB a rotation-key pack would cost.
What a real server-side FHE messenger would need from here
Ordered by distance:
- Fan-out routing on encrypted recipient ids — same soft-eq polynomial, roster hashed into a second ciphertext, one rotation to sweep; adds rk.bin to the worker upload (~linear in distinct rotation angles).
- Encrypted unread counts + PIR-style mailbox retrieval — rotations; still in worker-generation reach with small state.
- Stateful mailbox: persistent/resumable jobs + a named encrypted-handle store (Fog wishlist item — per-call uploads over ~100 MB are the viability cliff, so anything mailbox-shaped needs this first).
- Delivery receipts, presence, typing — event-loop semantics, out of scope for batch Fog today. 1-2 buildable on this worker generation; 3-4 gate on the Fog roadmap.
Threat model
- Honest-but-curious server; the Fog worker never sees
sk.bin, never handles plaintext, and returns only the one ciphertext it probed. - ENVELOPE METADATA is not protected (who sent to whom, when, how many bytes) — this is a CKKS compute-privacy demo, not a mixnet; XMPP's TCP session/authentication stays classical in any real adaptation.
- All server multiplies are ct×ct with public structure encrypted — the strongest privacy story, and it keeps the recorded trace free of plaintext-dependent branching.