Entries
The layout of the serialized entries payload, the decoders that read it, and the delivery properties of the feed.
Every ShredPrism response carries a slot number and an entries byte string: this page covers what is in those bytes, what decodes them, and what the feed does not promise.
ShredPrism delivers serialized entries, so deserialization is the first step the reader takes. Data Stages places that step in the pipeline.
Payload layout
entries holds a serialized list of proof-of-history entries. An entry is a link in the hash chain, with the transactions observed before that hash was generated. Most entries hold no transactions: an entry with an empty transaction list is a tick.
| Field | Bytes | Encoding |
|---|---|---|
| Entry count | 8 | Unsigned 64-bit, little-endian |
Per entry: num_hashes | 8 | Unsigned 64-bit, little-endian |
Per entry: hash | 32 | The proof-of-history hash |
| Per entry: transaction count | 8 | Unsigned 64-bit, little-endian |
| Per entry: transactions | Variable | Each one in the standard transaction wire format |
constant-k confirmed this layout on 2026-09-20 against the Agave 4.2 serializer. Transaction bytes inside an entry are the same bytes a base64 RPC response carries, so a decoder that reads one reads the other. Their lengths are not in the layout, so reaching the second transaction means decoding the first.
Serialization
Since Agave 4.2 the encoding is wincode, a bincode-compatible serializer from Anza. constant-k nodes run Agave 4.3.0.
bincode cannot decode this payload
A version 1 transaction has no bincode representation, at any version of solana-entry. bincode::deserialize either fails or, on a mixed batch, stops at the first v1 transaction. Jito's own deshred example still calls bincode::deserialize and is out of date.
Rust decoding
| Crate | Version | Why |
|---|---|---|
solana-entry | 4.2 or later, feature agave-unstable-api | The Entry type. The whole crate sits behind that feature, so a build without it sees nothing at all. |
wincode | The one solana-entry already depends on: 0.5 for solana-entry 4.2, 0.6 for 4.3 | The serializer. Two wincode versions in one build put Entry on one set of traits and the decode call on the other, which does not compile. |
The call is wincode::deserialize::<Vec<Entry>>, the same call Agave uses to read a deshredded payload. The Quickstart has a consumer that runs.
A truncated payload fails with a read-size error. Trailing bytes after a complete list are ignored rather than reported, so a payload that decodes to fewer entries than its length suggests raises nothing.
TypeScript decoding
No JavaScript package implements wincode. The outer layout is four fixed-width fields, which DataView reads directly, and the transactions inside are in the standard wire format, which @solana/kit decodes.
getCompiledTransactionMessageDecoder() from @solana/kit 8.0.0 or later exposes read(bytes, offset), which returns the decoded message and the offset after it. That offset is what walks the payload from one transaction to the next. getTransactionDecoder() does not: it treats everything after the signatures as message bytes and consumes to the end of the buffer, so it only works on a payload holding exactly one transaction.
The transaction's first byte decides which branch reads it:
| First byte | Format | Signatures |
|---|---|---|
Below 0x80 | Legacy or v0 | A one-byte count, then that many 64-byte signatures, then the message |
0x81 | Version 1 | The message, then header.numSignerAccounts 64-byte signatures |
The Quickstart has the walk in full. Transaction v1 covers the rest of that format.
Delivery properties
The response message has two fields. There is no sequence number, no batch index and no completion flag, so everything below is the consumer's responsibility.
| Property | What a consumer handles |
|---|---|
| Granularity | A message carries a batch of entries, not necessarily every entry of its slot. Several messages can share a slot value. |
| Order | Slot values do not only increase. Code that keys on the last slot seen needs a tolerance window. |
| Duplicates | The protocol has no message identifier and makes no at-most-once promise. A transaction signature is the key to deduplicate on, held in a set bounded by slot or by time. |
| Completeness | No message marks the end of a slot, and a batch whose shreds cannot be recovered never arrives. The feed cannot confirm that a slot is whole. |
| Empty batches | A message that decodes to zero entries is valid. Treating it as a failure rejects good data. |
Absent from the feed
Entries come from a block while its leader is producing it. A block can be skipped or lose its fork, so a transaction in the feed is not a transaction that landed.
| Not in the feed | Source for it |
|---|---|
| Execution result: status, fee, logs, compute units | Kaldera, getTransaction |
| Account state after execution | Kaldera, getAccountInfo |
| Confirmation that a transaction landed | getSignatureStatuses |
| Block metadata: parent slot, blockhash, rewards | getBlock |
| Server-side filters | Kaldera filters |
| Past slots | Kaldera replay |
The usual shape pairs the two: ShredPrism decides what to act on, and a post-execution source says what happened.
Back-pressure
gRPC runs on HTTP/2, which has flow control. A client that stops reading stops the sender instead of losing data, so falling behind costs latency rather than messages.
- Read the socket on a task that does nothing else.
- Hand each payload to a bounded queue.
- Decode and filter behind that queue, never in the reading loop.
- Decide what the queue does when it is full, and count the drops.
Differences from a self-hosted proxy
Jito's shredstream-proxy is a process an operator runs next to a node. ShredPrism serves the same service and method as a hosted endpoint.
| Self-hosted proxy | ShredPrism | |
|---|---|---|
| Deployment | A process to run and keep running | An endpoint to connect to |
| Shred ingest | UDP on a bind port, 20000 by default. NAT is not supported and the source addresses need firewall rules. | None |
| Authentication | A Solana keypair, approved in advance, authenticates the proxy upstream | x-token metadata on the gRPC call |
| Transport to your code | Plaintext gRPC on a local port | gRPC over HTTP/2 with TLS |
| Region selection | --desired-regions, at most two | The endpoint hostname |
| Service and method | shredstream.ShredstreamProxy/SubscribeEntries | Identical |
Upstream references
| Source | What it covers |
|---|---|
| Jito ShredStream documentation | The service the protocol comes from. The page carries a sunset notice for Jito's own hosted service, dated 5 September 2026. The protocol it describes is unchanged. |
shredstream.proto | The service definition, alongside the heartbeat and trace messages this call does not use |
| jito-labs/shredstream-proxy | The reference implementation, and the deshred example whose decoding is out of date |
solana-entry | The Entry type and its fields |
wincode | The serialization format |