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).
| Field | Value |
|---|
kind | 30078 |
content | JSON object — schema pacto.commons.broadcast.v1 |
d | pacto_commons_broadcast |
client | pacto |
subject | user | squad |
exp | Expiry as a unix seconds string |
t | Each hashtag as a separate tag (lowercase, up to 3 total) |
squad | The 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.
| Rule | Detail |
|---|
| Tags | 1–3 tags per broadcast. Each tag must match ^[a-z0-9_]{1,32}$ after lowercasing and stripping any leading #. |
| TTL | durationHours must be one of: 24, 48, 72, 168, 336, 720 (168 = 7 days, 336 = 14 days, 720 = 30 days). |
| Expiry | expiresAt must be approximately created_at + durationHours × 3600, within ±60 seconds. |
| Squad id | For 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. |
| Expired | Events 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 value | Schema | Use case |
|---|
pacto_commons_broadcast | pacto.commons.broadcast.v1 | Commons public discovery feed |
pacto_dashboard_poll | pacto.dashboard_poll.v1 | Squad/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
| Layer | Location |
|---|
| Types | src/lib/commons/types.ts |
| Feed filter, sort, and deduplication | src/lib/commons/commons-feed.ts |
| Tag normalization and reserved tag enforcement | src/lib/commons/tags.ts |
| Wire publish and fetch (Rust) | src-tauri/src/commons.rs |
| Tauri commands | commons_publish_broadcast, commons_fetch_broadcasts, commons_get_local_active, commons_cancel_broadcast |
| Frontend API | src/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.