Pipoke
Pipoke

DMs and Group Chats

Pipoke direct messages and group messages are end-to-end encrypted. The ciphertext lives on chain in an inbox shard or a group shard. The keys live in your wallet. The platform never sees the plaintext.

Pipoke runs on Octra Devnet today. Any fee, price, or limit referred to here is a contract setting chosen for testing. Every one is owner-settable, and mainnet values will be different. These docs describe how the mechanics work, not what the numbers are.

#DMs: the crypto

Pipoke DMs use NaCl box (X25519 + XSalsa20-Poly1305). Each participant has an X25519 box keypair derived deterministically from their wallet signature, published once via set_followers_only_pk.

For each DM, the app encrypts the plaintext twice:

Ciphertext Encrypted to Read by
ct_to recipient's box pubkey the recipient
ct_from your own box pubkey you (so your outbox is readable)

Both ciphertexts ship in the same on-chain tx. The shard stores both.

#DM method surface

Method Signature Purpose
send_message send_message(recipient, ct_to, ct_from) Standard DM.
send_message_ext send_message_ext(recipient, ct_to, ct_from, reply_to, expires_at_epoch) DM with optional reply_to and per-message expiry.
forward_message forward_message(recipient, ct_to, ct_from, forward_of_origin) Forward another DM, with attribution to the original.
mark_read mark_read(pair_key, msg_idx) Mark a thread read on your side.
set_dm_allowlist_required toggle Only allow DMs from wallets on your allowlist.
set_dm_allowlist_member (addr, on) Add or remove a wallet from your allowlist.
set_dm_default_expiry (epochs) Default expiry for DMs you send.

DMs always require a wallet signature. The session key cannot derive your X25519 secret, so DM sends are explicitly outside the session-key scope.

#16 inbox shards: pair routing

The inbox is sharded across 16 shards. Any two wallets always land on the same shard because the shard index is derived from the pair, not from the sender alone:

pair_inbox_id_of_call(my_addr, their_addr) -> shard_id  (0..15)

The function is symmetric in (my_addr, their_addr): order does not matter. No matter who sends first, the conversation lives in one place.

The on-chain message record carries:

Field What it is
pair_key Symmetric hash of the two wallets.
msg_idx Per-pair sequential index.
sender, recipient Wallets.
ct_to, ct_from Sealed ciphertexts.
reply_to Index of the message being replied to, if any.
forward_of_origin Pointer to the original message, if a forward.
expires_at Epoch after which the message is considered expired.
epoch Send epoch.
tx_hash The on-chain tx.

#Groups: the crypto

A group uses a symmetric key (NaCl secretbox) shared across N members. The group shard stores the ciphertext of each message. The symmetric key is distributed to members through sealed wraps: one wrap per member, each sealed to that member's box pubkey.

Method Signature Effect
create_group create_group(name, my_key_ct, principal) Creates the group. The creator's own copy of the group key is my_key_ct.
add_member add_member(group_id, member_addr, member_key_ct, principal) Adds a member. member_key_ct is the group key sealed to that member's box pubkey.
remove_member remove_member(group_id, member_addr, principal) Removes a member from the on-chain roster.
leave_group leave_group(group_id, principal) You leave.
rename_group rename_group(group_id, new_name, principal) Rename.
close_group close_group(group_id, principal) Close.
reopen_group reopen_group(group_id, principal) Reopen.
send_group_message send_group_message(group_id, text_ct, attachment_ct, principal) Send a group message.

There are 16 group shards. A group is assigned to one at create-time.

#Group rekey (roadmap)

When a member leaves or is removed, the previous symmetric key is technically still readable to them (they have it in their browser). Group rekey rotates to a new symmetric key sealed only to the remaining members. Pipoke V1 does not yet implement automatic group rekey; it is on the roadmap.

#Why on chain

Permanent record (encrypted). Your conversations are not at a service provider's mercy. The bytes are on chain.

No metadata leak about message content. The chain holds ciphertext only. The platform sees who-sent-what-when and who-is-in-a-group-with-whom, but not what they said.

#See also