> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbitflare.com/llms.txt
> Use this file to discover all available pages before exploring further.

# JetStream Client

> OrbitFlare's decoded shreds delivered as gRPC streams with transaction and account filters.

## Install

```bash theme={null}
cargo add orbitflare-sdk --features jetstream
```

## Building the client

```rust theme={null}
use orbitflare_sdk::{JetstreamClientBuilder, RetryPolicy, Result};
use std::time::Duration;

let client = JetstreamClientBuilder::new()
    .url("http://ny.jetstream.orbitflare.com")
    .fallback_url("http://fra.jetstream.orbitflare.com")
    .retry(RetryPolicy {
        initial_delay: Duration::from_millis(100),
        max_delay: Duration::from_secs(30),
        multiplier: 2.0,
        max_attempts: 0,
    })
    .timeout_secs(30)
    .keepalive_secs(60)
    .ping_interval_secs(10)
    .max_missed_pongs(3)
    .channel_capacity(4096)
    .build()?;
```

Minimal:

```rust theme={null}
let client = JetstreamClientBuilder::new()
    .url("http://ny.jetstream.orbitflare.com")
    .build()?;
```

URL falls back to `ORBITFLARE_JETSTREAM_URL` env var. All builder methods are identical to the [gRPC client](/sdk/rust-grpc) - same defaults, same behavior.

## Writing a YAML config

JetStream supports transaction and account filters. No slots, blocks, or commitment - those are Yellowstone-specific.

```yaml theme={null}
# jetstream.yml
transactions:
  raydium:
    account_include:
      - "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8"
  pumpfun:
    account_include:
      - "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"

accounts:
  my_wallet:
    account:
      - "YOUR_WALLET_ADDRESS"
    owner:
      - "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
```

### YAML filter reference

**`transactions`** - named filters. `account_include` matches transactions involving those addresses. `account_exclude` removes matches. `account_required` means all listed addresses must appear.

**`accounts`** - watch specific addresses with `account`, or all accounts owned by a program with `owner`.

Supports `${ENV_VAR}` expansion.

## Subscribing and reading events

### From YAML

```rust theme={null}
let mut stream = client.subscribe_yaml("jetstream.yml")?;
```

### Programmatically

```rust theme={null}
use std::collections::HashMap;
use orbitflare_sdk::proto::jetstream::*;

let mut filters = HashMap::new();
filters.insert("target".into(), SubscribeRequestFilterTransactions {
    account_include: vec![some_address.to_string()],
    account_exclude: vec![],
    account_required: vec![],
});

let request = SubscribeRequest {
    transactions: filters,
    accounts: HashMap::new(),
    ping: Some(SubscribeRequestPing { id: 1 }),
};

let mut stream = client.subscribe(request);
```

### With the typed builder

`SubscribeRequestBuilder` and `TransactionFilter` build the same request without hand-writing proto. Each `TransactionFilter` exposes `account_include`, `account_exclude`, and `account_required`, and accepts any iterator of string-like values.

```rust theme={null}
use orbitflare_sdk::jetstream::{SubscribeRequestBuilder, TransactionFilter};

let request = SubscribeRequestBuilder::new()
    .transactions(
        "pumpfun",
        TransactionFilter::new()
            .account_include(["6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"])
            .account_required(["So11111111111111111111111111111111111111112"]),
    )
    .build();

let mut stream = client.subscribe(request);
```

### Reading the stream

```rust theme={null}
use orbitflare_sdk::proto::jetstream::subscribe_update::UpdateOneof;

while let Some(update) = stream.next().await {
    let update = update?;
    match update.update_oneof {
        Some(UpdateOneof::Transaction(tx)) => {
            // tx.slot - the slot number
            // tx.transaction - transaction info with signature, account_keys,
            //   instructions, address_table_lookups
        }
        Some(UpdateOneof::Account(acct)) => {
            // acct.slot - the slot
            // acct.account - account info (pubkey, lamports, owner, data)
            // acct.is_startup - true during initial snapshot
        }
        _ => {}
    }
}
```

Closing, multiple streams, reconnection, and ping/pong all work identically to the [gRPC client](/sdk/rust-grpc).

## Full example

A stream that watches Raydium AMM swaps and prints each transaction's signature and instruction count.

```rust theme={null}
use orbitflare_sdk::{JetstreamClientBuilder, Result};
use orbitflare_sdk::proto::jetstream::subscribe_update::UpdateOneof;

#[tokio::main]
async fn main() -> Result<()> {
    let client = JetstreamClientBuilder::new()
        .url("http://ny.jetstream.orbitflare.com")
        .build()?;

    let mut stream = client.subscribe_yaml("jetstream.yml")?;
    let mut count: u64 = 0;

    println!("streaming raydium txs...");

    while let Some(update) = stream.next().await {
        let update = update?;

        if let Some(UpdateOneof::Transaction(tx)) = update.update_oneof {
            count += 1;
            if let Some(info) = &tx.transaction {
                let sig = bs58::encode(&info.signature).into_string();
                let num_ix = info.instructions.len();
                let num_accounts = info.account_keys.len();

                println!(
                    "#{count} slot={} sig={}... ix={num_ix} accounts={num_accounts}",
                    tx.slot,
                    &sig[..16],
                );
            }
        }
    }

    Ok(())
}
```

With this `jetstream.yml`:

```yaml theme={null}
transactions:
  raydium:
    account_include:
      - "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8"
```

## JetStream v2

JetStream v2 (`orbitflare_sdk::jetstream::v2`) runs on the **same endpoints and authentication** as v1 and is fully additive: v1 keeps working unchanged. What v2 adds:

* **Runtime-managed filters** - add and remove filters on a live stream without reconnecting.
* **Per-message sequence numbers** - every response carries a monotonic `sequence`, so you can detect dropped messages.
* **Opt-in enrichment** - request fee payer, program ids, compute-unit price, compute limit, resolved address-table addresses, and more, per transaction.
* **Slot lifecycle events** - a separate server stream of slot alive/complete/dead events.

The v2 client lives under `jetstream::v2`. The builder is identical to v1 (same methods, defaults, `ORBITFLARE_JETSTREAM_URL` env var, and failover):

```rust theme={null}
use orbitflare_sdk::jetstream::v2::{JetstreamClientBuilder, TransactionFilter};

let client = JetstreamClientBuilder::new()
    .url("http://ny.jetstream.orbitflare.com")
    .fallback_url("http://fra.jetstream.orbitflare.com")
    .build()?;
```

### Building typed filters

Each filter is a `TransactionFilter` with a client-chosen id (via `.with_id()`). The id is echoed back on every matching transaction and in the filter-validation acknowledgement, so you can correlate matches and remove the filter later.

```rust theme={null}
let filter = TransactionFilter::new()
    .account_include(["6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"])
    .include_enrichment(true)
    .with_id("pumpfun");
```

A filter must set at least one of `account_include`, `account_exclude`, or `account_required`; empty (match-everything) filters are rejected. `include_enrichment` is optional (default off) and applies to the whole subscription: if any active filter enables it, every transaction you receive is enriched.

### Subscribing to transactions

```rust theme={null}
use orbitflare_sdk::proto::jetstream::v2::subscribe_transactions_response::Payload;

let mut stream = client.subscribe_transactions(vec![filter]);

while let Some(resp) = stream.next().await {
    let resp = resp?;
    match resp.payload {
        Some(Payload::Transaction(ft)) => {
            // resp.sequence - monotonic per-stream sequence number
            // ft.filter_ids - which of your filters this tx matched
            if let Some(tx) = &ft.transaction {
                // tx.slot, tx.signature, tx.account_keys, tx.instructions
                // enrichment (only when include_enrichment is set): tx.fee_payer,
                //   tx.program_ids, tx.compute_unit_price, tx.compute_limit,
                //   tx.loaded_writable_addresses, tx.loaded_readonly_addresses
                println!("seq={} slot={} cu_price={}", resp.sequence, tx.slot, tx.compute_unit_price);
            }
        }
        Some(Payload::FilterValidation(r)) => {
            // one per filter after each add/remove
            println!("filter {} accepted={} {}", r.filter_id, r.accepted, r.rejection_reason);
        }
        Some(Payload::Heartbeat(hb)) => {
            // hb.server_ts_ms - server wall-clock, ms since epoch
            let _ = hb;
        }
        Some(Payload::Pong(p)) => {
            // p.ping_id
            let _ = p;
        }
        None => {}
    }
}
```

### Managing filters on a live stream

Take a handle from the stream and add or remove filters without reconnecting. Removals reference the filter id you assigned with `.with_id()`.

```rust theme={null}
let handle = stream.handle();

handle.add_filters(vec![
    TransactionFilter::new()
        .account_include(["675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8"])
        .with_id("raydium"),
])?;

handle.remove_filters(vec!["pumpfun".to_string()])?;
```

### Slot lifecycle events

`subscribe_slots()` is a separate server stream and takes no filters.

```rust theme={null}
let mut slots = client.subscribe_slots();

while let Some(event) = slots.next().await {
    let event = event?;
    // event.slot, event.status() (a SlotStatus enum: alive / complete / dead),
    // event.parent_slot, event.current_leader, event.sequence
    println!("slot={} status={:?}", event.slot, event.status());
}
```

### Health probes

`get_version()` and `ping()` are unary calls with the same failover as the streams.

```rust theme={null}
println!("version={}", client.get_version().await?);
println!("ping={}", client.ping(7).await?);
```

For the full protobuf specification, message shapes, enrichment fields, and slot statuses, see the [JetStream v2 Protocol Reference](/data-streaming/jetstream-v2-reference).
