CHAKRA SERIES - 11
Inside listener.rs: How The Sentinel Network Hears Solana Without Being Asked
Welcome to Day 11.
Day 10 closed out the entire on chain side of CHAKRA. Everything from the 64 byte tss_pubkey in state.rs to the admin keypair I was honest about on Day 10, all of it done.
Today we cross over. We are no longer inside the Solana program. We are inside chakra-sentinel, the separate Rust application running on actual servers, the thing that listens, signs, and executes. And the first question to answer is the most basic one.
How does the Sentinel Network even know something happened on Solana.
Part 1: The Problem With Polling
The naive approach would be polling. Every few seconds, ask Solana, did anything happen. Did our program emit any events. Fetch the latest transactions. Parse them. Look for something we care about.
This works but it is ugly. You miss events if your poll happens between two transactions. You add unnecessary load to the RPC. You introduce latency that grows with your poll interval. And you are paying for compute and bandwidth on every single poll even when absolutely nothing happened.
CHAKRA does not poll. It subscribes.
Part 2: WebSocket Subscription, The Whole Idea
rust
let (_subscription, receiver) = PubsubClient::logs_subscribe(
DEVNET_WSS,
RpcTransactionLogsFilter::Mentions(vec![PROGRAM_ID.to_string()]),
RpcTransactionLogsConfig {
commitment: Some(CommitmentConfig::confirmed()),
},
)?;One function call. That is the whole setup. PubsubClient opens a persistent WebSocket connection to Solana's pubsub endpoint. The filter says only send me logs from transactions that mention this specific program ID. CommitmentConfig::confirmed means we only hear about transactions that have been confirmed by the cluster, not just seen by one node.
From this moment on, Solana pushes events to us. We do not ask. We just listen. The moment any transaction involving the CHAKRA program ID gets confirmed anywhere on devnet, our Sentinel process receives it over this open connection in real time.
No polling. No missed events. No unnecessary load. Just a permanent open ear.
Part 3: The Loop That Runs Forever
rust
while let Ok(response) = receiver.recv() {
let signature = response.value.signature.clone();
for log in response.value.logs {
if log.contains("Program data:") || log.contains("Instruction:") {
if let Err(e) = crate::processor::IntentProcessor::handle_log(
&log, &signature, &shard_path, &wallet_path
) {
eprintln!("Error processing intent: {:?}", e);
}
}
}
}receiver.recv() blocks until a message arrives. When it does we get back a response containing the transaction signature and an array of log lines that transaction produced.
We loop through every log line looking for two things. Program data: is how Anchor encodes emitted events into the transaction logs, it is the prefix for any event struct we saw back in Day 6. Instruction: marks the actual instruction being called.
If we find either of those we hand the log line and the transaction signature to IntentProcessor::handle_log which lives in processor.rs. If something goes wrong parsing or handling it we print the error and keep going. The while loop does not break on a single bad log. The sentinel keeps listening no matter what.
Part 4: What CommitmentConfig::confirmed Actually Means And Why It Matters
Solana has three commitment levels. processed means the transaction was seen by at least one node but might not survive. confirmed means it got voted on by a supermajority of validators, 66% of stake weight. finalized means it is permanently locked in and cannot be rolled back under any circumstances.
We use confirmed not finalized for a specific reason. If we waited for finalized on every ControlIntent event we would add extra latency to every single cross chain execution. confirmed is safe enough for our purposes because by the time our Sentinel processes the event, constructs the signature, sends it to Base Sepolia, waits for Base confirmation, and submits the proof back to Solana, the original transaction is going to be finalized anyway. We get the speed of confirmed with the effective safety of finalized just from the natural time the rest of the process takes.
Part 5: The Discriminator Check Coming Next
There is one more thing worth mentioning even though it technically lives in processor.rs, because it is directly triggered by what listener.rs sends over.
When Anchor emits an event it encodes it as base64 data in the transaction logs with a specific 8 byte prefix called a discriminator. The discriminator for ControlIntent is the first 8 bytes of SHA256 of the string event:ControlIntent. When handle_log receives a Program data: line it decodes the base64, checks those first 8 bytes, and only proceeds if they match ControlIntent specifically.
This means even if someone tried to send fake logs mentioning the CHAKRA program ID, the discriminator check catches it before anything happens. The Sentinel does not just react to any log that mentions CHAKRA. It reacts specifically to logs that contain a properly encoded ControlIntent event which can only come from the actual CHAKRA program running the actual initialize_intent instruction.
Part 6: Why This Design Specifically
The public wire design here, Solana transaction logs as the communication channel between the on chain program and the off chain Sentinel Network, has a property that is easy to miss until you think about it carefully.
The Sentinel Nodes do not need to trust me. They do not need a private API endpoint I control. They do not need a message queue I could tamper with. They read directly from Solana's public logs which are produced by the validators, not by me. If I disappeared tomorrow the Sentinel Nodes would still hear every ControlIntent that gets emitted because Solana keeps producing logs whether I am involved or not.
This is what pure event sourcing actually means in practice. The Sentinel Network's source of truth is the blockchain itself, not anything I control above it.
Soon we go into signer.rs which is where things get genuinely exciting. The Lagrange interpolation from Day 2 finally becomes actual running Rust code. The partial signatures being collected from multiple nodes over HTTP. The threshold math producing one valid Ethereum signature without any single node ever knowing the complete key.
See y'all tomorrow for Day 12 soon
Here are some resources
logsSubscribe WebSocket
The Solana PubSub API for subscribing to transaction logs in real time.
https://solana.com/docs/rpc/websocket/logssubscribe
Commitment Levels on Solana
What processed, confirmed, and finalized actually mean and when to use each.
https://docs.solanalabs.com/consensus/commitments

Written by Maha.. thankyou for reading
@solana @web3 @rust @anchor

