An indexer, an explorer or a wallet backend needs to know when a block arrives, when it is safe, and when a reorg removes it. An xOneRS node on main offers four ways: WebSocket and IPC subscriptions, a transaction lifecycle stream, a Kafka exporter, and hooks for code that runs in the node's process. All four put the node first: a slow consumer is dropped or loses events, and block import never waits for it. Most of this came in pull requests merged on 2026-11-15, so it is not in v0.1.0. We found one compatibility problem in the Kafka exporter and filed it as issue #1148.

Which eth_subscribe kinds does xOne serve?

From parse_kind in crates/xone-rpc/src/subscriptions.rs, over WebSocket and IPC:

REFERENCE TABLEScroll to explore all columns →
Kind Sends Since
newHeads A header each time a block becomes canonical earlier
newPersistedHeads (alias persisted) The same header once its store commit is durable PR #1088
newFinalizedHeads (alias finalized) The same header when the QC-committed (finalized) number advances, once per number PR #1088
logs Logs that match a filter (address, topics) earlier
newPendingTransactions Hashes, or full transactions with true earlier
txLifecycle Pool events per transaction (next section) PR #1089
syncing geth's sync status object earlier

newFinalizedHeads is the one to use when a block must not be reversed. On XDPoS v2 a block is final once the 3-chain QC rule commits it. See finality for exchanges and bridges.

A subscriber whose queue is full is dropped, never waited for. Closing the connection removes its subscriptions, and there is no replay of events sent while a client was away (exposing RPC safely).

What does the txLifecycle stream show?

One event per change of a pooled transaction, with the hash and a reason, never the body (PR #1089):

  • admission to the queued or the pending set;
  • replaced, with the new hash and the oldHash;
  • eviction;
  • mined, when a new head includes it;
  • reinjection after a reorg;
  • permanent invalidation.

The pool buffers at most 1,024 events. When the buffer is full, the oldest is dropped and counted. Admission never waits for a subscriber. A wallet backend can use this to tell a user why a transaction is not mined. For what happens to transactions in a reorg, see reorg reinjection.

How does the Kafka exporter work?

--kafka.brokers host:port,... starts it; --kafka.topic (default xone.chain) and --kafka.checkpoint (default <datadir>/xone/kafka-checkpoint) set where records go and where progress is saved (PR #1066, crates/xone-node/src/kafka.rs).

  • Record kinds: block, receipts, logs, traces and revert. A reorg is exported as revert records for the removed blocks, then the new branch.
  • Each record has a key {kind}:{hash}. Delivery is at least once: after a restart the exporter resumes from the checkpoint, and a consumer removes duplicates by key.
  • Import never waits for the broker. Records go through a bounded queue; when it is full, records are dropped and counted.
  • Credentials in the broker address are refused.

Broker versions. The exporter speaks Kafka Produce version 0 with message format 0, one record per request on partition 0. Apache Kafka 4.0 removed Produce versions 0 to 2 (KIP-896), so a 4.x broker refuses these requests. We found this by reading the code and did not test it against a broker. Until #1148 is fixed, expect the exporter to work only with brokers that still accept Produce v0.

What are in-process hooks?

For code built into the node, xone_node::hooks::LiveHooks delivers every canonical block, with header, transactions and receipts, and every reorg removal, in order (PR #1082). Import does not wait for a hook, and a hook cannot change committed state. Blocks imported before the hook was registered are not replayed. A NodeBuilder for library use came in PR #1083.

How do reads stay consistent during a reorg?

Since PR #1086, eth_getLogs and eth_feeHistory read their whole range from one snapshot of the chain: head, finalized tag, store generation and canonical hashes. If a commit, unwind or prune changes the store during the scan, the method answers canonical chain changed during the query; retry rather than mixing two branches. The xOneRS repository has a resumable event consumer that handles disconnects, reorgs and history refusals (PR #978).

Which way should an indexer use?

REFERENCE TABLEScroll to explore all columns →
Need Use
A light client or a UI that shows new blocks newHeads over WebSocket
Data that must not be reversed newFinalizedHeads, then fetch the block
A durable copy of blocks, receipts and logs in a data platform The Kafka exporter, after #1148, with dedupe on the key
Custom indexing inside the node, with no network hop LiveHooks
Transaction status for users txLifecycle

In every case, read the block again by hash after a reconnect: none of the streams replays missed events.

Frequently asked questions

How do I get only finalized blocks from an XDC node?

Subscribe to newFinalizedHeads (or finalized) over WebSocket on an xOneRS build from main after PR #1088.

Does xOne replay events I missed while disconnected?

No. Subscriptions do not replay. Read the missing range with eth_getBlockByNumber and eth_getLogs, or use the Kafka exporter's checkpoint.

Does the xOne Kafka exporter work with Kafka 4?

Probably not yet. It sends Produce version 0, which Kafka 4.0 removed. Issue #1148 tracks the fix.

Can a slow subscriber slow down block import?

No. A slow subscriber is dropped, the lifecycle buffer drops its oldest event, and the Kafka queue drops records; import never waits.

Sources