Gives senders of event-graph broadcast messages evidence about whether peers accepted them, and defines the sender-side rebroadcast/recreate policy that prevents messages written while offline from being silently lost to DAG rotation. Covers the reply wire format, delivery-state exposure to applications, and darkirc's outbound tracking, rebroadcast, and recreate flow as the reference implementation.
A node that receives an EventPut SHALL send the sender an
EventPutStatus reply on the same channel describing the outcome:
Has { inserted: true } when the event was newly accepted into the
node's DAGHas { inserted: false } when the node already has the event,
including when the event is only found in a retained older rotation
slot — the receiver SHALL check its retained slots before nacking
TooOld, so a peer still holding a rotated-away message answers
Has rather than TooOldNack { reason: TooOld } when the event predates the node's current
rotation window and is not present in any retained slotNack { reason: NotSynced } when the node is still performing its
initial DAG sync and skipped the eventNack { reason: Invalid } when the event fails structural or
proof validationNack { reason: Busy } when an internal condition prevented
processingReplies apply to relayed events equally: the receiver cannot and does not distinguish originator from relay.
EventPut for an event it does not have,
which passes all validationHas { inserted: true }EventPut whose event is already in its
DAGHas { inserted: false }EventPut whose event timestamp precedes
its current genesisNack { reason: TooOld } only if the event is not
present in any of its retained rotation slotsEventPut for an event it holds in a
retained older rotation slot (for example after the sender
disconnected and the DAG rotated)Has { inserted: false } rather than
Nack { reason: TooOld }EventPutNack { reason: NotSynced }EventPut that trips existing
strike/flood/parent-depth policingEventPutStatus payloads SHALL carry only the event id and a coarse
outcome. Nack reasons SHALL NOT encode fine-grained validation detail.
All enum variants SHALL use explicit u8 discriminants so the wire
format is stable. Decoding an EventPutStatus with an unknown variant
or malformed body SHALL be rejected without panicking, and the receiving
node SHALL NOT strike or ban the sender solely for an undecodable
status.
EventPutStatus carrying a variant it does
not knowEventPutStatus is serializedThe event graph SHALL expose a subscription that republishes every
inbound EventPutStatus unfiltered, together with the peer channel it
arrived on. The event graph itself SHALL NOT track outbound deliveries
or make retry decisions; that policy belongs to applications.
When darkirc publishes a chat message event, it SHALL first persist an outbound record containing the event id, the plaintext message fields needed to rebuild it, a state, an attempt counter, and a link to any superseding replacement event. Records SHALL survive restart. Records SHALL be closed (removable) once a positive outcome is observed or the attempt cap is reached.
For an outbound record with no positive outcome, darkirc SHALL wait
until it is DAG-synced and has at least a configured minimum number of
peer connections, then rebroadcast the original event unchanged. It
SHALL retry with backoff, bounded by a configured maximum number of
rounds. A single Has outcome (either inserted value) SHALL close the
record as delivered.
Has { inserted: false } replies close the record and
no recreation happensThe delivery monitor SHALL treat a foreign event whose DAG ancestry includes one of its tracked outbound events as positive delivery evidence, closing the outbound record as delivered. This is a local computation over already-received events and adds no wire traffic.
darkirc SHALL create a replacement event from the stored plaintext — fresh timestamp and DAG parents, fresh encryption nonce — only when either:
TooOld nacks) and no
peer has answered Has for it, orInvalidThe following observations SHALL NOT trigger recreation while the
rotation window is open: silence, Nack { NotSynced }, and
Nack { Busy } mean retry later. Silence SHALL NOT trigger recreation
even after the window closes when no status reply of any kind has ever
been received from status-capable peers. A replacement links back via
the supersession chain so the sender can correlate attempts locally.
Recreation SHALL consume a fresh rate-limit slot when rate limiting is enabled; if the epoch budget is exhausted the attempt SHALL be parked and retried after epoch rollover rather than dropped or sent unproven. Recreation attempts SHALL be capped; once the cap is reached the record SHALL be closed as failed and the failure surfaced to the client.
Has after a rebroadcast roundHas { inserted: false } from any peer holding the event in a
retained slotNack { NotSynced } or Nack { Busy }
while the rotation window is openInvalid