Broadcast
low level
Example
final myBroadcast = ndk.broadcast.broadcast(
nostrEvent: Nip01Event(
pubKey:
'3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d',
kind: 1,
tags: [],
content: 'Hello, World!',
),
);
final result = await myBroadcast.broadcastDoneFuture;
for (final relayResponse in result) {
print(
"broadcast on ${relayResponse.relayUrl}: ${relayResponse.broadcastSuccessful}");
}
You might encounter a warning about missing nip65 data. You can ignore this warning to use NDK with your specified/default relays.
If you want to use the outbox model, check out the enabling gossip guide (recommended).
Relay authentication (NIP-42)
Some relays only accept an event from a client that authenticated. The auth
parameter says which identity the broadcast may be attributed to, exactly like
on requests:
final myBroadcast = ndk.broadcast.broadcast(
nostrEvent: report,
auth: const AuthPolicy.never(),
);
Without auth, a refused event authenticates as its author when a registered
account matches, and as the currently logged-in account otherwise. The relay
therefore decides when your identity is revealed. Pass auth explicitly
whenever that matters.
The account does not have to be one NDK knows: allow and require take the
Account itself, so an identity you built on the spot works.
If require names an account that cannot sign, no connection can carry the
event. Rather than fall back to the anonymous one, which is what require
rules out, nothing is sent and broadcast itself throws
BroadcastAuthUnavailableException.
Across a restart
A signer is never written to disk, only the policy is, as never,
allow:<pubkey> or require:<pubkey>. That matters for the background retries
described below, not for the broadcast you are awaiting.
-
never()survives a restart whole, because it names nobody. A relay that refuses such an event is a final answer, so it is not retried at all. -
allowandrequireresolve their pubkey through the accounts NDK holds. A retry therefore needs that account to be registered again after a restart. -
An identity no account can sign for parks the delivery in
EventDeliveryStatus.needsActioninstead of going out as somebody else. You see it throughloadPendingDeliveries(), and you resume it by broadcasting the same event again with the account in hand.
When to use
Broadcast should be used when your use case has no broadcasting method.
Signing is done automatically, and you can specify a custom signer just for one broadcast.
By default, the inbox/outbox model is used for broadcasting looking at the event data (e.g. if it's a reply) you can also specify specific relays to broadcast to.
Local-first behavior
NDK keeps delivery state for locally created events in the configured cache backend.
That means:
- an event may be visible locally before every relay accepts it
- pending delivery can survive app restarts when the cache backend is persistent
- if signing is delayed by an external signer, the unsigned event can still stay queued locally
- retries can continue later when relays are reachable again
- replaceable events only keep retrying the newest visible version
With MemCacheManager, this state exists while the process is alive. With a persistent CacheManager, it also survives restarts.
Retry behavior
Background delivery retries are not identical for every event.
Current behavior includes:
- replaceable events retry as latest-state-only delivery
- ephemeral events are not kept for retry
- some control-style events can use faster retry behavior
- relay responses classified as permanent failure stop retry for that relay target
- relay responses classified as transient failure remain retryable
- external signer flows can also be retried in the background before relay delivery continues
Sending once without retry
retryDelivery: false keeps one broadcast out of pending delivery:
ndk.broadcast.broadcast(
nostrEvent: event,
specificRelays: ['wss://relay.example.com'],
retryDelivery: false,
);
No delivery record is written for that event, so nothing retries it whatever the
relays answer, and it never shows up in loadPendingDeliveries(). Only
specific-relay broadcasts are ever enrolled, so a gossip broadcast is already
never retried. broadcastReaction() and broadcastDeletion() take the same
parameter.
To turn background retries off for the whole client instead, use
NdkConfig(pendingDeliveryRetriesEnabled: false). That one keeps persisting
delivery state, it only stops replaying it.
External signer retry behavior
When you broadcast through an external signer such as Nip46EventSigner,
Nip07EventSigner, or Nip55EventSigner, signing and relay delivery are treated
as separate steps.
Current behavior:
- if the signer does not complete immediately, the event can remain queued locally
- once signing succeeds later, NDK can continue relay delivery for that same event
- if the signer depends on network transport, retries are biased toward moments when the signer transport looks reachable again
- if the signer succeeds while some target relays are still offline, delivery to those relays can still continue later
In practice this means apps can treat delayed external signing as part of the same local-first publish flow instead of having to rebuild the publish manually.
Delivery policy
NDK uses an internal DeliveryPolicy for local-first broadcast retry handling.
Current policy selection is automatic:
- ephemeral events use
doNotRetry - deletion events use
highPriorityControl - replaceable and addressable events use
latestStateOnly - other events use
persistentEventual
This policy is currently not configurable through ndk.broadcast.broadcast(),
NdkConfig, or a per-event override.
That means:
- you cannot choose a custom retry/backoff profile for one broadcast
- you cannot inject your own delivery policy classifier
- the practical way to influence policy today is through the event kind you publish
- the one per-broadcast control is
retryDelivery: false, which skips retry entirely
Examples:
kind:5deletions get faster control-style retries- replaceable kinds only keep retrying the newest visible version
- ephemeral kinds are not persisted for background retry
Relay targeting
Broadcast targeting can include more than author outbox relays.
Depending on the event, NDK may target:
- explicit relays
- author write relays
- inbox/read relays needed for replies and reactions
Delivery targets are tracked separately from source provenance.