Pacto uses a small set of Nostr event kinds to carry all messaging traffic — private DMs, MLS group messages, public Commons broadcasts, and dashboard polls. On the wire, private messages are always wrapped inside a NIP-59 Gift Wrap (Kind 1059) so relays see only an opaque outer envelope. MLS group traffic travels as Kind 444 with anDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/covenant-gov/pacto-app/llms.txt
Use this file to discover all available pages before exploring further.
h tag pointing to the wire group. Public community signals (Commons broadcasts and dashboard polls) travel as NIP-33 parameterized replaceable events (Kind 30078), distinguished by their d tag value.
Kind reference
| Kind | Name | Usage in Pacto |
|---|---|---|
1059 | GiftWrap | NIP-59 outer DM envelope. Encrypted to the recipient’s pubkey. Hides sender, recipient, and all metadata from relays. |
443 | MlsWelcome | MLS group invite. Always delivered inside a Kind 1059 Gift Wrap. Handed directly to the MLS engine — not processed as a normal DM rumor. |
444 | MlsGroupMessage | MLS-encrypted group traffic. The h tag carries the wire group id (hex). Membership is verified via db::load_mls_groups before the message is processed. |
30078 | Parameterized Replaceable | Commons broadcasts and dashboard polls. The d tag value disambiguates the use case (see d tag disambiguation below). |
14 | Rumor: Text | Inner DM or MLS text message, present after Gift Wrap unwrap or MLS decryption. |
15 | Rumor: File | Inner DM or MLS file attachment. |
7 | Rumor: Reaction | Inner DM or MLS emoji reaction. |
30078 | Rumor: App Event | Typing indicators (Kind 30078 with a d tag), dashboard polls, and Commons broadcasts — all travel as inner rumors of this kind. |
Conversation identity
How Pacto identifies a conversation depends on whether it is a DM or an MLS group:- DM:
chat_idis the other user’s npub (npub1…). - MLS group:
chat_idis the group_id, a hex string read from thehtag. It does not start withnpub1.
ChatType in src-tauri/src/chat.rs distinguishes DirectMessage from MlsGroup. On the sending side, receiver.starts_with("npub1") implies a DM; any other value is treated as an MLS group id.
Gift Wrap anatomy
All private Pacto messages travel inside a NIP-59 Gift Wrap. The outer event is what the relay stores; the inner rumor is only visible to the intended recipient.| Layer | Kind | Description |
|---|---|---|
| Outer (wire) | 1059 | Encrypted to the recipient’s pubkey. The relay sees only this event — sender, recipient, and content are hidden. |
| Inner (after unwrap) | 443 | MLS Welcome. Passed to the MLS engine; not surfaced as a chat message. |
| Inner (after unwrap) | 14, 15, 7, 30078 | DM rumors: text, file, reaction, and app events (typing, wallet payloads, etc.). |
Relays only ever see the outer Kind 1059 envelope. The sender’s identity, the recipient’s identity, and the full message content are encrypted inside. This is the NIP-59 privacy guarantee that Pacto relies on for all 1:1 and MLS invite traffic.
Receiving flow
- Kind 1059 arrives → unwrap. If the inner kind is
443(MlsWelcome), hand it to the MLS engine and emitmls_invite_received. Otherwise route as a DM rumor viaprocess_rumorwithConversationType::DirectMessage, emitmessage_newwithchat_id= npub. - Kind 444 arrives → read
htag → verify membership viadb::load_mls_groups→ process with MLS engine on a blocking thread →process_rumorwithConversationType::MlsGroup→ emitmls_message_newwithgroup_id.
h tag usage
Kind 444 (MlsGroupMessage) carries an h tag whose value is the wire group id — a hex string that maps to the internal MLS group. Before processing, the backend checks db::load_mls_groups to confirm the receiving account is a current member of that group. Unknown h tag values are silently ignored.
d tag disambiguation
Kind 30078 is reused for multiple Pacto-specific application events. Thed tag value identifies which schema applies:
d tag value | Use case | Content schema |
|---|---|---|
pacto_dashboard_poll | Dashboard poll (create or vote, inside an MLS announcements group) | pacto.dashboard_poll.v1 |
pacto_commons_broadcast | Public Commons feed broadcast | pacto.commons.broadcast.v1 |
d tag before attempting to parse the content field. An unknown d tag value should be ignored rather than rejected.