Rakurai Post-Pack Confirmations — Guide
How the validator streams transaction updates to post-pack confirmation endpoints and how consumers respond with bundles.
Audience: Integrators consuming post-pack confirmations and validator operators configuring endpoints.
See also: Transaction inclusion · Tips FAQ · Validators hub
1. Overview
Rakurai scheduler provides updates (post-pack confirmations). As soon as a transaction is scheduled for execution, it forwards the update to configured post-pack confirmation endpoints over gRPC. These updates are generated from the point of no-return. Consumers of this service only see updates just before they imminently become part of the block, ensuring no front-running.
Post-pack confirmation uses the Jito packet gRPC protocol (packet.proto, block_engine.proto) — the same Packet / PacketBatch / StartExpiringPacketStream shape used by the Jito relayer.
The consumer's job is to receive the post-pack confirmation Packet, then send back a bundle that includes:
- The original post-pack confirmation packet(s) (unchanged)
- Any additional transactions (e.g., backrun / arb)
Duplicate transactions are suppressed. One Packet is sent per transaction per endpoint.
What consumers receive: transactions as Jito Packet format Packet messages (raw Solana wire bytes), streamed over StartExpiringPacketStream.
What you send back: a bundle that includes the original post-pack confirmation packet unchanged, plus any additional transactions (e.g., arb). The protocol mirrors the Jito relayer packet/bundle flow.
2. Admin RPC
Admin IPC is request/response: keep the socket open briefly so socat can read the reply before stdin closes.
2.1. getPostPackConfirmationConfig
Returns the live status maintained by the scheduler (admin + on-chain merge, blocklist, and what is actually connected).
| Field | Description |
|---|---|
onchain_entries | Entries loaded from the on-chain PDA |
blocklisted_uuids | Endpoint UUIDs blocked via setPostPackConfirmationUuidBlocklist |
blocklisted_entries | Full merged entries whose uuid is blocklisted (url + uuid) |
active_entries | Merged admin + on-chain (admin wins on same URL), excluding blocklisted UUIDs — these are the endpoints receiving scheduler updates |
(echo '{"jsonrpc":"2.0","id":1,"method":"getPostPackConfirmationConfig","params":[]}'; sleep 1) \ | socat - UNIX-CONNECT:admin.rpc | jq
Example response:
{ "admin_entries": [ {"url":"http://127.0.0.1:20000","uuid":"PostPackConfig2"}, {"url":"http://127.0.0.1:10000","uuid":"PostPackConfig1"} ], "onchain_entries": [], "blocklisted_uuids": ["PostPackConfig1"], "blocklisted_entries": [ {"url":"http://127.0.0.1:10000","uuid":"PostPackConfig1"} ], "active_entries": [ {"url":"http://127.0.0.1:20000","uuid":"PostPackConfig2"} ] }
2.2. setPostPackConfirmationUuidBlocklist
Blocklists post-pack confirmation endpoints by UUID. Each call replaces the full blocklist. Pass an empty array to clear.
Blocklisted UUIDs are removed from active_entries on the next scheduler config sync. If a blocklisted endpoint already has an open gRPC connection, it is torn down immediately on sync; other endpoints stay connected.
Example — block one endpoint by UUID:
(echo '{"jsonrpc":"2.0","id":1,"method":"setPostPackConfirmationUuidBlocklist","params":[["PostPackConfig1"]]}'; sleep 1) \ | socat - UNIX-CONNECT:admin.rpc
Example — clear blocklist (reconnect blocklisted endpoints on next sync):
(echo '{"jsonrpc":"2.0","id":1,"method":"setPostPackConfirmationUuidBlocklist","params":[[]]}'; sleep 1) \ | socat - UNIX-CONNECT:admin.rpc
Note: Use params:[[]] (one parameter: an empty UUID array). params:[] omits the parameter and will not clear the blocklist.
3. gRPC protocol
3.1. Packet shape
For each transaction the validator sends one PacketBatchUpdate with msg = batches:
PacketBatchUpdate └── batches: ExpiringPacketBatch ├── header.ts ├── batch: PacketBatch │ └── packets[]: Packet │ ├── data ← raw Solana wire transaction bytes │ └── meta ← Packet meta (size, addr, port, flags, sender_stake) └── expiry_ms = 0
Decode in Rust:
use solana_transaction::versioned::VersionedTransaction; let txn: VersionedTransaction = bincode::deserialize(&packet.data)?;