14. Finalized events
A finalized event is the protocol's derived report of what a finalized block did. Events carry no authority of their own: they are reproducible by anyone who holds the certified inputs, and nothing in a block commits to them. This chapter fixes the event identifier, the emission and ordering rules, the deposit fields an event carries, and the boundary that keeps an event out of every root.
14.1 Definitions
An event is a record emitted for a finalized block, derived from that block and the state it
executed against. An event batch is the complete ordered set of events for one height. The
EventId is the identifier a consumer deduplicates on, computed under the single digest
registry of section 6. A domain event is the event a command family emits for its own
transition, as distinct from the CommandFinalized event that every applied command emits.
EventId = BLAKE3("makechain.event" || chain_id || block_hash || command_index || event_index)
R-14.1. Events are a pure function of the active release, the finalized block, the authenticated parent state, and the execution outcome.
R-14.2. A block-level system event uses the sentinel command_index = 0xFFFF_FFFF and exists
even in a block with no commands; max_commands_per_block is below that sentinel, so no command
event can share an EventId with a block-level event, and a wire command_index equal to the
sentinel is a decode error (encoding section 13).
14.2 Which events exist
The registry of event kinds is closed. The kinds are named below, and every kind's tag and payload layout is fixed by encoding section 12, which also fixes the batch framing and the order of a batch. A later release adds a kind only by appending a tag to that registry, and R-14.16 binds every implementation to it.
| Kind | Emitted for | Contents named here |
|---|---|---|
CommandFinalized |
every applied command | the applied command's identity and outcome |
AccountInitialized |
INIT_ACCOUNT |
the initialized account policy (section 7) |
RootConfigured |
ROOT_CONFIGURE |
the new configuration commitment and version |
KeyAuthorized |
KEY_AUTHORIZE |
the authorized or renewed key, with its deposit lists (section 14.4) |
KeyRevoked |
KEY_REVOKE(One) |
the deleted key and its discharged deposit |
DelegationEpochAdvanced |
KEY_REVOKE(All) |
the old and the new delegation epoch |
ProjectCreated |
PROJECT_CREATE |
the project, its metadata, and the slug claim's deposit |
ProjectMetadataSet |
PROJECT_SET_METADATA |
the stored metadata and any slug claim exchange |
ProjectStatusSet |
PROJECT_SET_STATUS |
the status and the advanced lifecycle sub-revision |
RoleChanged |
ROLE_SET |
the grant or revocation and its deposit lists |
TransferOfferChanged |
TRANSFER_OFFER |
the offer set or cleared and its deposit lists |
ProjectTransferred |
TRANSFER_ACCEPT |
the new controller and the rotated control generation (section 8.4) |
CommitmentAdded |
each commitment in an applied PUSH |
one event per commitment |
RefsChanged |
an applied PUSH |
one event covering every affected ref |
RecordReclaimed |
RECLAIM |
the deleted record and its discharged deposit (section 9.6) |
UsernameLeaseChanged |
USERNAME_CLAIM |
the affected lease; for a rename, the destination name only |
UsernameReleased |
USERNAME_RELEASE, and a rename's source name |
the ended resolution and the retained reservation |
CreditsImported |
IMPORT_CREDITS |
the credited frontier or the refreshed checkpoint (section 16) |
ReleaseScheduled |
SCHEDULE_RELEASE |
the appended schedule entry (section 18) |
ValidatorSetScheduled |
VALIDATOR_SET_SCHEDULE |
the appended or cancelled entry (section 13.3) |
DkgCarryOver |
block level | a reshare carry-over (section 13.5) |
ReleaseActivated |
block level | the activation of a release (section 18) |
ValidatorSetLockedIn |
block level | the lock-in of a scheduled validator-set entry (section 13.3) |
14.3 Emission
R-14.3. Every applied command emits CommandFinalized first and then at most one domain event,
except PUSH, which emits one CommitmentAdded per commitment and one RefsChanged, and a
USERNAME_CLAIM rename, which emits one event per name it touches.
R-14.4. KEY_REVOKE(All) emits one DelegationEpochAdvanced event with the old and new epochs
and the keychain revision after the command, and carries no discharge list and no key count.
R-14.5. Event payloads are length-prefixed, so a consumer can skip an unknown kind.
R-14.6. CommandFinalized is a derived event and not a proof-bearing receipt, and a service
that supplies it without command inclusion and finality evidence MUST NOT represent it as
independently verified inclusion (section 15).
An invalid command emits nothing (section 12). Logical expiry, meaning a record read as absent before it is reclaimed, is projected virtually by the indexer from the deadline fields it already holds, so an indexer never shows expired authority as live.
14.4 Deposit fields on events
R-14.7. Every domain event of a discharging transition carries a bounded, canonically ordered
list of discharged {record_identity, incarnation, deposit_payer, deposit_paid} entries, and every
creation event carries any new locked deposits in the same shape.
R-14.8. A transfer event covers both the consumed offer and the old and new slug claims, and a ref batch covers every affected ref.
R-14.9. These lists are bounded by the command's read and write limits (section 12) and require no scan.
14.5 Ordering and delivery
R-14.10. Delivery is at-least-once, as one batch per height, in ascending height order.
R-14.11. A consumer deduplicates by EventId, persists a cursor only together with the effects
of the events it covers, and is idempotent under redelivery.
R-14.12. An exporter MUST perform exactly one bounded local durable append inside the finalization hook, which is the queue append of section 17, and MUST NOT wait there on any network, store, or mirror call, other than the queue-bound wait of R-12.25; it observes durable publication progress asynchronously and scans for gaps against the mirror's height index (section 17).
The finalization hook runs on readable but not yet durable state and skips heights covered by state sync (section 12), which is why durable progress is observed outside it and why an exporter's sync floor is bounded by its own watermark (section 17).
INV-EVENT-01. Redelivery is idempotent; consumers dedup by
EventIdand persist cursors only with effects.
14.6 Events, roots, and trust
R-14.13. Events enter no root and never alter command_root (section 11).
R-14.14. Every event batch is exactly reproducible from certified inputs.
R-14.15. A consumer MUST NOT trust a server-supplied event list, and re-derives an event batch rather than accepting it as supplied.
INV-EVENT-02. Every event batch is exactly reproducible from certified inputs.
INV-EVENT-03. Events enter no root; a server-supplied event list is never trusted.
R-14.16. An implementation MUST emit exactly the kinds of the section 14.2 registry, with the registry's payload layouts, and MUST NOT emit a kind the registry does not carry.
14.7 Rationale
This subsection is informative.
Events are emitted only after finalization and enter no root because an event is a projection: it is reproducible from certified inputs by anyone, and a consumer never has to trust the node that emitted it. Only what execution must prove is merkleized, so an event root in the header would bind block validity to a projection that adds no fact beyond what the block already carries.
The pre-finalization consensus stream stays outside the event contract, because forcing it in would attach guarantees to observations that consensus itself does not make.
What others can check, and what the chain depends on.