Skip to main content

Documentation 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.

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 an 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

KindNameUsage in Pacto
1059GiftWrapNIP-59 outer DM envelope. Encrypted to the recipient’s pubkey. Hides sender, recipient, and all metadata from relays.
443MlsWelcomeMLS group invite. Always delivered inside a Kind 1059 Gift Wrap. Handed directly to the MLS engine — not processed as a normal DM rumor.
444MlsGroupMessageMLS-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.
30078Parameterized ReplaceableCommons broadcasts and dashboard polls. The d tag value disambiguates the use case (see d tag disambiguation below).
14Rumor: TextInner DM or MLS text message, present after Gift Wrap unwrap or MLS decryption.
15Rumor: FileInner DM or MLS file attachment.
7Rumor: ReactionInner DM or MLS emoji reaction.
30078Rumor: App EventTyping 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_id is the other user’s npub (npub1…).
  • MLS group: chat_id is the group_id, a hex string read from the h tag. It does not start with npub1.
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.
LayerKindDescription
Outer (wire)1059Encrypted to the recipient’s pubkey. The relay sees only this event — sender, recipient, and content are hidden.
Inner (after unwrap)443MLS Welcome. Passed to the MLS engine; not surfaced as a chat message.
Inner (after unwrap)14, 15, 7, 30078DM 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

  1. Kind 1059 arrives → unwrap. If the inner kind is 443 (MlsWelcome), hand it to the MLS engine and emit mls_invite_received. Otherwise route as a DM rumor via process_rumor with ConversationType::DirectMessage, emit message_new with chat_id = npub.
  2. Kind 444 arrives → read h tag → verify membership via db::load_mls_groups → process with MLS engine on a blocking thread → process_rumor with ConversationType::MlsGroup → emit mls_message_new with group_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. The d tag value identifies which schema applies:
d tag valueUse caseContent schema
pacto_dashboard_pollDashboard poll (create or vote, inside an MLS announcements group)pacto.dashboard_poll.v1
pacto_commons_broadcastPublic Commons feed broadcastpacto.commons.broadcast.v1
Clients must inspect the d tag before attempting to parse the content field. An unknown d tag value should be ignored rather than rejected.

NIP references

NIPRelevance
NIP-17DM format — rumor kinds (14, 15, 7) used inside Gift Wraps
NIP-44Encrypted payload format used by Gift Wraps
NIP-59Gift Wrap protocol — Kind 1059 outer envelope
NIP-33Parameterized replaceable events — Kind 30078 used for Commons and polls
When debugging subscription filters, grep for TRUSTED_RELAYS, add_relay, and subscription filter construction in src-tauri/src/lib.rs. DM sync uses Gift Wrap filters (pubkey(self).kind(GiftWrap).since…); MLS group sync uses per-group cursors for Kind 444 history.

Build docs developers (and LLMs) love