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.

Commons broadcasts are public Nostr Kind 30078 events published to trusted relays — not MLS, not Gift Wrap. They carry time-bounded discovery signals from individual users and from public squads, and are visible to anyone subscribed to those relays. The content field holds a JSON object conforming to the pacto.commons.broadcast.v1 schema. Because Kind 30078 is a NIP-33 parameterized replaceable event, the d tag value identifies this as a Commons broadcast and distinguishes it from other Kind 30078 uses (such as dashboard polls).

Nostr event wire format

FieldValue
kind30078
contentJSON object — schema pacto.commons.broadcast.v1
dpacto_commons_broadcast
clientpacto
subjectuser | squad
expExpiry as a unix seconds string
tEach hashtag as a separate tag (lowercase, up to 3 total)
squadThe announcements MLS group id — present only when subject=squad
In v1, all broadcasts from the same author share the same d tag value (pacto_commons_broadcast). Relay-level deduplication per broadcast slot is planned post-v1 (work item CM-10). Until then, the app enforces “one active card per author/subject” via a local cooldown and a feed deduplication step (dedupeCommonsBroadcasts in src/lib/commons/commons-feed.ts).

JSON payload schema

The content field is a JSON object. The schema field inside it identifies the version for forward-compatibility validation.

Squad broadcast

A squad broadcast is published when a public squad wants to appear in the Commons discovery feed. The squad object inside the payload carries the stable announcements MLS group id used for join-request routing.
{
  "schema": "pacto.commons.broadcast.v1",
  "subject": "squad",
  "message": "Open cohort for on-chain gov experiments.",
  "durationHours": 24,
  "expiresAt": 1740000000,
  "tags": ["neo", "coordination"],
  "squad": {
    "id": "<announcements_mls_group_id>",
    "name": "Neo Builders",
    "kind": "squad",
    "iconUrl": "https://..."
  }
}

User broadcast

A user broadcast makes an individual discoverable in the Commons feed for the duration of the TTL. The audience field is derived automatically — it is not chosen by the user. The first broadcast ever from a given npub is classified as new_user; all subsequent broadcasts are active_user.
{
  "schema": "pacto.commons.broadcast.v1",
  "subject": "user",
  "message": "Building wallet tooling; happy to DM.",
  "durationHours": 48,
  "expiresAt": 1740086400,
  "tags": ["neo"],
  "audience": "active_user"
}
audience is new_user | active_user and is present only for subject: "user" payloads. The reserved #new tag is added automatically to the first user broadcast and to new public squad broadcasts; it cannot be self-selected.

Cancellation

To retract an active broadcast before it expires, the author publishes a replacement event with the same d tag and a cancelled: true field in the content JSON:
{
  "schema": "pacto.commons.broadcast.v1",
  "subject": "user",
  "message": "Building wallet tooling; happy to DM.",
  "durationHours": 48,
  "expiresAt": 1740086400,
  "tags": ["neo"],
  "audience": "active_user",
  "cancelled": true
}
Clients keep the newest event per (author npub, subject id) and drop the subject from the feed when that newest event has cancelled: true. This prevents the cancelled broadcast from reappearing after a feed refresh, and the author’s cooldown lifts immediately so they can re-broadcast.
Because v1 uses a flat d tag value, relays will store multiple events from the same author until they age out. Clients must apply newest-event-wins deduplication on ingest and in the feed. Do not rely on relay-side replacement for correctness in v1.

Validation rules

Both publishers and consumers must apply these rules.
RuleDetail
Tags1–3 tags per broadcast. Each tag must match ^[a-z0-9_]{1,32}$ after lowercasing and stripping any leading #.
TTLdurationHours must be one of: 24, 48, 72, 168, 336, 720 (168 = 7 days, 336 = 14 days, 720 = 30 days).
ExpiryexpiresAt must be approximately created_at + durationHours × 3600, within ±60 seconds.
Squad idFor squad broadcasts, the squad.id in the payload must match the squad Nostr tag on the event. Must be the stable announcements MLS group id for that squad.
ExpiredEvents past expiresAt are dropped from the feed and pruned from the local commons_broadcasts SQLite cache.

Dashboard poll note

Kind 30078 is also used for dashboard polls. The two uses are distinguished by their d tag value:
d tag valueSchemaUse case
pacto_commons_broadcastpacto.commons.broadcast.v1Commons public discovery feed
pacto_dashboard_pollpacto.dashboard_poll.v1Squad/network dashboard polls (inside MLS announcements group)
Always check the d tag before parsing content. Dashboard polls travel inside MLS group messages (Kind 30078 as a rumor inside Kind 444); Commons broadcasts are published as plain Kind 30078 events on trusted relays.

TypeScript and Rust implementations

LayerLocation
Typessrc/lib/commons/types.ts
Feed filter, sort, and deduplicationsrc/lib/commons/commons-feed.ts
Tag normalization and reserved tag enforcementsrc/lib/commons/tags.ts
Wire publish and fetch (Rust)src-tauri/src/commons.rs
Tauri commandscommons_publish_broadcast, commons_fetch_broadcasts, commons_get_local_active, commons_cancel_broadcast
Frontend APIsrc/lib/api/commons.ts
Commons broadcasts are published to and read from TRUSTED_RELAYS only — the same curated relay set used for other app-specific public events. No Commons traffic appears on MLS or DM sync paths. To change relay behavior, grep for TRUSTED_RELAYS in src-tauri/src/lib.rs and src-tauri/src/commons.rs.

Build docs developers (and LLMs) love