Gives senders of event-graph broadcast messages evidence about whether peers accepted them, and defines a durable, oldest-first broadcast queue that retains messages until acknowledged rather than silently losing them to DAG rotation. Covers the reply wire format, delivery-state exposure to applications, and darkirc's head-only retry/recreate flow as the reference implementation. ACKs are unverified network-delivery evidence, not recipient receipts or a guarantee of recipient display order.
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 a transient parent-fetch failure or an
internal condition prevented processing, without established invalidityReplies 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 }Nack { reason: Busy }, not InvalidEventPut 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.
darkirc SHALL durably enqueue each accepted outgoing chat message in one global FIFO, preserving its plaintext rebuild fields and enqueue order across restart. Only the oldest unacknowledged logical message, the head, SHALL be eligible for event creation or network exposure. Later messages SHALL remain queued without publishing events, including through DAG relay/sync. The head SHALL be materialized and sent only when darkirc is DAG-synced and has the configured minimum number of peer connections. FIFO guarantees local send sequencing, not recipient display ordering.
Before exposing a head generation to the network, darkirc SHALL durably retain its exact event and blob. Unchanged retries SHALL remain possible after local DAG pruning. Replacements SHALL keep the logical message's queue position and preserve old-to-new supersession links and generation ids until ACK. Committing a replacement SHALL be crash-recoverable, with only one active generation; restart SHALL NOT bypass the head or generate a second replacement for an already committed transition.
A Has outcome with either inserted value for any generation of the head
SHALL ACK its logical message. An accepted foreign event whose ancestry
includes any such generation SHALL also ACK it, using local computation
over received events without additional wire queries. Locally generated
events alone SHALL NOT constitute foreign delivery evidence.
ACK SHALL durably dequeue the logical message exactly once, cancel its pending retries/recreation, and permit service of the next head. ACK processing SHALL NOT depend on retry timers or the current connection count. Duplicate, unrelated, or late statuses SHALL NOT remove another message. Concurrent recreation work SHALL NOT resurrect an ACK'd entry. An ACK is unverified network evidence, not proof of forwarding or recipient receipt; already-broadcast generations cannot be recalled.
Has { inserted: true } or
Has { inserted: false } for one of its generationsFor a materialized, unacknowledged head, darkirc SHALL rebroadcast the active event unchanged when sync/connectivity requirements are met. It SHALL use backoff for a configured number of rounds and continue at a capped slow rate thereafter. There SHALL be no total retry-round, recreation-attempt, or lifetime limit that discards an unacknowledged message or advances the queue. Negative replies SHALL NOT bypass backoff.
darkirc SHALL recreate the active head event only when its rotation
window has closed, known locally or indicated by TooOld, and a
rebroadcast round completes without ACK for any head generation. This
SHALL apply even when no status replies have ever arrived. The replacement
SHALL use fresh timestamp, parents, and encryption nonce, preserve queue
position, and link the old generation to the new one locally.
Silence, Invalid, NotSynced, and Busy alone SHALL NOT cause recreation
or dequeue. Any ACK SHALL stop further recreation of that logical message.
Has { inserted: false } from a peer
holding it in a retained slotInvalid, NotSynced, or BusyEvery new generation SHALL use a fresh rate-limit slot when rate limiting is enabled. Exhausted budget SHALL park the head until epoch rollover; missing identity SHALL park it until identity is available. Processing or storage failures SHALL preserve the head and prevent later sends from bypassing it. Retrying the same event SHALL NOT re-encrypt it or consume a new rate-limit slot. No unproven replacement SHALL be sent. Retryable errors SHALL be retried at bounded rate, not treated as terminal dequeue. Blocked-send error notices to connected clients SHALL NOT imply removal from the queue; receipt/status display remains out of scope.
Queue storage SHALL have a configured bound covering pending messages, active event/blob data, and retained generation metadata. When a new enqueue cannot fit or cannot be persisted, darkirc SHALL explicitly reject it to the connected client rather than report acceptance. Storage pressure SHALL NOT evict unacknowledged entries or their required retry/correlation data. Recreation that cannot fit SHALL park the head. ACK'd entries MAY be removed to reclaim queue storage.