Messaging
Pipoke has two private messaging surfaces: one-to-one direct messages and multi-member group chats. Both store only encrypted content on-chain. The chain holds ciphertext, never 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.
#Encryption
Messages are encrypted with X25519 key exchange. Encryption and decryption happen in the browser, and the private keys stay in the browser. The contracts only ever see and store ciphertext.
The chain stores ciphertext. A direct message is stored as two ciphertext blobs, one encrypted to the recipient and one encrypted to the sender, so each party can decrypt their own copy of the thread. A group message is stored as a single ciphertext, and each member holds an encrypted copy of the group key so they can read it.
Because keys never leave the browser, the protocol owner, an indexer, or anyone reading the chain sees only encrypted bytes. The plaintext exists only on the devices of the people in the conversation.
#Direct messages
Direct messages run on 8 inbox shards. PipokeRouter assigns each pair of wallets to one inbox shard, so a conversation between two wallets always lives on the same shard.
PipokeInboxShard.send_message(recipient, ct_to, ct_from)
ct_to is the message encrypted to the recipient, ct_from is the same message encrypted to the sender. Each send appends a numbered message to the pair's thread. A mark_read method records how far each side has read. You cannot DM yourself, and a recipient who has blocked you rejects the message.
Sending a direct message charges a POKE fee, split three ways like other action fees.
#Group chats
Group chats run on 8 group shards. Each group has an admin, a name, a member list, and a message log of encrypted blobs.
| Method | Who | What it does |
|---|---|---|
create_group(name, my_key_ct) |
Anyone | Creates a group, caller becomes admin. |
add_member(group_id, member, key_ct) |
Admin | Adds a member with their encrypted group key. |
remove_member / ban_member |
Admin | Removes or bans a member. |
leave_group(group_id) |
Member | Leaves a group (the admin cannot leave). |
update_member_key |
Admin | Rotates a member's encrypted group key. |
rename_group / close_group / reopen_group |
Admin | Group management. |
send_group_message(group_id, ct_msg) |
Member | Posts an encrypted message to the group. |
Each member stores their own encrypted copy of the group key (member_key_ct), which is how a member decrypts group traffic without the key ever being in plaintext on-chain. Sending a group message charges a POKE fee, split three ways like other action fees.
#Messaging always needs a wallet signature
Session keys let feed actions go through silently. Messaging is deliberately outside that. Every direct message and every group message requires a wallet signature, even when a session key is active. Messaging is a consequential, private action, so it stays gated behind an explicit wallet approval.