Last updated

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:

  1. The original post-pack confirmation packet(s) (unchanged)
  2. 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).

FieldDescription
onchain_entriesEntries loaded from the on-chain PDA
blocklisted_uuidsEndpoint UUIDs blocked via setPostPackConfirmationUuidBlocklist
blocklisted_entriesFull merged entries whose uuid is blocklisted (url + uuid)
active_entriesMerged 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

packet.proto

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)?;