RPE2E Protocol

RPE2E stands for Repartee End-to-End. Version 1 is the protocol currently implemented in native form by repartee and in companion scripts for WeeChat and irssi-compatible clients such as erssi.

This page describes the protocol as implemented today, not an aspirational design draft.

Scope and goals

RPE2E v1 adds end-to-end encryption for IRC channels and private conversations without changing IRC transport. The server still routes traffic, but compatible clients exchange keys out of band and send encrypted payloads over normal IRC messages.

The design goal is pragmatic interoperability across three environments:

  • native Rust in repartee
  • Python script for WeeChat
  • Perl script for irssi and erssi

Version identifiers

RPE2E v1 currently uses these identifiers:

ItemValue
Protocol stringRPE2E01
Encrypted message prefix+RPE2E01
Handshake CTCP tagRPEE2E
Handshake version fieldv=1

The protocol name is RPE2E. The handshake tag is RPEE2E.

Security model

RPE2E encrypts message bodies end-to-end, but it does not hide IRC metadata. The server can still observe:

  • nicknames and ident@host
  • channel names or PM targets
  • timing and approximate message sizes
  • that a handshake took place

Trust is TOFU-oriented: the first contact can require manual accept, can be auto-accepted, or can be silently ignored depending on channel mode. Fingerprint verification is still required if you want protection against active impersonation.

RPE2E v1 also intentionally does not implement a Signal-style double ratchet. Session keys rotate on handshake and REKEY, not on every message.

Handshake transport

Handshake traffic is carried inside CTCP framing sent via NOTICE.

RPEE2E KEYREQ v=1 c=#x p=<b64u32> e=<b64u32> n=<b64u16> s=<b64u64>
RPEE2E KEYRSP v=1 c=#x p=<b64u32> e=<b64u32> wn=<b64u24> w=<b64u> n=<b64u16> s=<b64u64>
RPEE2E REKEY  v=1 c=#x p=<b64u32> e=<b64u32> wn=<b64u24> w=<b64u> n=<b64u16> s=<b64u64>

Field summary:

  • c: channel or PM pseudochannel context
  • p: long-term Ed25519 identity public key
  • e: ephemeral X25519 public key
  • wn: wrap nonce for AEAD-wrapped session key
  • w: wrapped session key ciphertext
  • n: anti-replay nonce included in the signed payload
  • s: Ed25519 signature

KEYREQ starts the exchange. KEYRSP returns a wrapped session key. REKEY is the unsolicited distribution form used to rotate an outgoing session key to already trusted peers.

Cryptographic construction

RPE2E v1 uses:

  • Ed25519 for long-term identity keys and handshake signatures
  • X25519 for ECDH during key exchange
  • HKDF-SHA256 for wrap-key derivation
  • XChaCha20-Poly1305 for message encryption and wrapped session keys

For REKEY, repartee maps the peer's Ed25519 identity public key to X25519 using the RFC 7748 Appendix A birational map, matching libsodium-compatible clients.

Encrypted message wire format

Encrypted channel or PM traffic is sent as a normal IRC line body:

+RPE2E01 <msgid> <ts> <part>/<total> <nonce_b64>:<ct_b64>

Each chunk is an independent cryptographic unit:

  • msgid: random 8-byte message identifier, hex-encoded
  • ts: unix timestamp in seconds
  • part/total: chunk index with total <= 16
  • nonce_b64: 24-byte XChaCha20 nonce
  • ct_b64: ciphertext plus Poly1305 tag

Receivers decrypt chunks immediately. There is no stateful reassembly buffer in the protocol.

AAD and replay window

The current implementation authenticates this AAD layout:

PROTO
  || be16(channel.len) || channel
  || be16(8)           || msgid
  || be16(8)           || ts_be
  || be16(1)           || part
  || be16(1)           || total

Notes:

  • the sender handle is not included in AAD
  • sender binding is enforced at the keyring lookup layer using (handle_from_IRC_prefix, context)
  • the default timestamp tolerance is 300 seconds
  • fields are length-prefixed to prevent delimiter ambiguity

Context model

For real channels, the context key is the channel itself, for example #repartee.

For private messages, RPE2E uses a pseudochannel:

@<peer_handle>

Example:

@~alice@static.example.org

This keeps session storage consistent across channels and PMs while avoiding nick collisions.

Channel modes and trust states

RPE2E v1 supports three per-context modes:

ModeBehavior
auto-acceptfirst contact is trusted automatically
normalpending request with explicit accept/decline
quietunknown peers are ignored until manually handled

Peer trust state is tracked as pending, trusted, or revoked.

Client scripts and downloads

Official companion scripts are published in the repartee repository:

WeeChat requirements

  • WeeChat with Python scripting enabled
  • Python 3
  • PyNaCl installed, for example pip install pynacl
  • load scripts/weechat/rpe2e.py

irssi and erssi requirements

  • irssi or erssi with Perl scripting enabled
  • libsodium available on the system
  • Perl modules used by the script, including Crypt::NaCl::Sodium, MIME::Base64, JSON::PP, Digest::SHA, FFI::Platypus, and FFI::Platypus::Buffer
  • load scripts/irssi/rpe2e.pl

Current v1 limitations

  • no metadata hiding beyond encrypted message bodies
  • no per-message forward secrecy / double ratchet
  • only compatible clients can read +RPE2E01 payloads
  • export/import is operationally useful, but exported key material is plaintext and must be protected accordingly

Relationship to repartee

repartee is the reference implementation at the moment. The protocol page here follows the current Rust code in src/e2e/ and the matching client scripts in scripts/.