Most Solana teams do not have a streaming problem. They have a classification problem.
They bought a firehose for a doorbell job, or a doorbell for a firehose job. Then they tune poll intervals and blame the provider.
This guide is a decision matrix for stream classes, not a product ranking. As of July 2026 the ladder still holds: poll → webhook → WSS → Geyser-class gRPC → earliest shred-class signal. Names of vendors change. The physics and ops tradeoffs do not.
Kill the default approach
Default: "just use gRPC" for everything, or "just poll faster" until rate limits scream.
What breaks first:
- Wallet UX pays for a trading stack and still feels laggy for the wrong reasons
- Indexers drop gaps because they optimized for first-seen instead of completeness
- Trading bots treat
confirmedas if it were first-seen - One long-lived stream on a serverless function that dies every few minutes
If you cannot name the workload (interactive app, complete indexer, latency trader), you cannot name the stream.
By the end you will have
- Three workload types that pick streams for you
- A six-row stream ladder with lag class, reconnect story, and when wrong
- A one-page decision matrix you can paste into a design doc
- Commitment rules so "done" is not a vibe
- Anti-patterns that waste a quarter
Three workloads (pick one first)
| Workload | What "good" means | Typical wrong buy |
|---|---|---|
| Interactive app | User sees balances and history that match reality; simple ops | Full shred stack for a portfolio page |
| Complete indexer | No silent gaps; resume after crash; reproducible order | Raw shreds without a completeness plan |
| Latency trader | Earliest usable signal + a send path that can act | Pretty APIs a continent away from stake |
You can run all three in one company. You almost never run them on one connection design.
Mechanism: streams are different contracts
A stream is not "faster RPC." It is a contract about:
- When you learn about change (poll interval vs push vs propagation layer)
- What commitment that change claims
- Who owns reconnect and gaps (you vs the service)
- How much ops you carry (filters, fans, retention)
Treat those as product requirements. Then pick a class.
Magnet: Solana Stream Ladder (decision matrix)
Name: Solana Stream Ladder
Copy into a design doc or runbook. Rows are classes, not vendors.
| Class | How you learn | Lag class (order of magnitude) | Reconnect / gaps | Ops burden | Start here when… | Wrong when… |
|---|---|---|---|---|---|---|
| 1. Polling JSON-RPC | Client asks on a timer | Poll interval + RTT (often 100s of ms of built-in staleness) | You re-query; no cursor | Low | One-shot reads, dashboards, low-frequency jobs | Contested trading, dense live state |
| 2. Webhooks | Provider POSTs after an event | After the node already processed (often post-confirm class) | Provider retry policy varies | Low-med | Alerts, bots that can tolerate delay | First-seen arb, sub-second loops |
| 3. Standard WebSocket | accountSubscribe / logs-style push | Better than poll; still commitment-bound | Fragile; you write retry + resync | Med | Few accounts, simple live UI | Thousands of accounts, heavy fan-in |
| 4. Enhanced / scaled WSS class | Hardened push over WSS semantics | Same commitment family as node; less noise if filtered well | Better than bare WSS; still not a full archive cursor | Med | Product UIs that need live updates without running Geyser | You need server-side multi-filter fan-out at trading scale |
| 5. Geyser-class gRPC (Yellowstone-shaped) | Validator plugin pushes accounts/txs/slots | "As processed" (or higher commitment you request) | You own client reconnect unless the product adds persistence | High if self-run; med if managed | Bots, live indexers, multi-filter live state | You only needed a single balance once a minute |
| 6. Shred / deshred-class signal | Propagation-layer fragments (and tools that reassemble early) | Earliest network view; not the same as confirmed truth | Complex; wrong decode = wrong world | Highest | HFT / RFQ / liquidations where first-seen is the job | Compliance replay, wallet truth, "did it finalize?" |
How to use the matrix in five minutes
- Circle your workload row in the first table.
- Circle the minimum class that matches "good" in the second table.
- Write one sentence: I accept lag class X and own reconnect story Y.
- Only then pick a provider and a product SKU.
You should see: a filled matrix row for your workload with lag class, reconnect owner, and one explicit "wrong when" line — pasteable into a design doc without a vendor name on it.
Commitment is not a stream setting you ignore
Streams deliver updates at a commitment (or at a pre-commitment layer for shreds).
| Commitment | Rough meaning for operators |
|---|---|
| Processed | Leader/runtime view; can fork away |
| Confirmed | Supermajority voted; still not irreversible forever in the abstract |
| Finalized | Locked out under normal fork rules; slowest "safe" for many product truths |
Rules of thumb:
- Wallet "your balance is" → prefer confirmed or finalized for user-facing truth
- Strategy loop "something might be happening" → processed or shred-class is fine if you treat it as signal, not ledger law
- Indexer "this block is done" → decide explicitly; many pipelines use confirmed with a finality catch-up
Do not mix: shred-class first-seen into a database row labeled finalized without a second pass.
Anti-patterns
| Anti-pattern | Why it hurts | Fix |
|---|---|---|
| Serverless long-lived gRPC | Cold starts and idle kills break the contract | Always-on worker; persistence product if you need gaps filled |
| One stream for all ICPs | Traders and accountants need different lag and completeness | Split connections and retention by workload |
| Polling harder | Burns credits and still loses half the interval on average | Step up a class on the ladder |
| "gRPC everywhere" | Ops cost and complexity for a settings page | Stay on poll or WSS until a live requirement appears |
| Treating earliest signal as settled truth | False fills, wrong balances in UI | Dual path: signal stream + truth read |
Practice: one week reliability
Pick one workload and one stream class.
Week success means:
- It ran without you babysitting the process
- Gaps were either zero or detected
- You can explain reconnect in two sentences
- You did not change class mid-week because of FOMO
That is the same discipline as any overnight agent system: trigger, capable path, verification. Here verification is "gap detector + commitment policy," not a unit test.
Failure modes
| Failure | Signal | Fix |
|---|---|---|
| Silent indexer hole | Metrics show slot jumps | Completeness-class stream or scheduled backfill |
| UI shows ghost balance | Processed signal painted as truth | Confirm on read path |
| Bot "slow" on good stream | Send path is the bottleneck | Landing checklist (fees, blockhash, SWQoS) separate from read stream |
| Bill shock | Poll storm or unfiltered gRPC | Filters + right class |
When not to use this matrix
Skip the full ladder exercise when:
- You only need a one-shot historical read (no live stream)
- You already run a production class with measured gaps and a written reconnect story
- You are comparing vendors inside one class (use a bake-off sheet, not this matrix)
- Compliance requires finalized-only truth and you refuse dual-path signal+truth designs
Related
- Solana + AI agents beginner stack (gated writes pair with stream choice)
- Vibe coding verification loop (proof habits transfer to gap detection)
Bottom line
Streaming is a ladder of contracts, not a single upgrade button. Name the workload, pick the minimum class that matches lag and completeness, write the reconnect story, and separate signal from finalized truth. Most expensive mistakes are not slow nodes. They are wrong class for the job.
Your next action: Solana Stream Ladder — fill one row for this week's workload, write the reconnect sentence, and ship that class for seven days before you upgrade.