> For the complete documentation index, see [llms.txt](https://docs.triton.one/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.triton.one/solana/real-time-streaming/preconfirmations-grpc/feeds-and-regions.md).

# Feeds and regions

Harmonic and BAM region codes for Preconfirmations gRPC, how to choose regions near your servers, and which point of presence answered GetVersion.

Three things carry the word region; they are not the same thing.

```
Solana leaders                        Solana leaders
      │                                     │
      ▼                                     ▼
Harmonic builders                       BAM nodes
ams ewr fra lon tyo sgp slc             ams dfw dub ewr fra hkg iad lax
                                        lon pit sea sin slc sqq tyo
      │                                     │
      └─────────────────┬───────────────────┘
                        ▼
          Triton points of presence
          ams1 fra1 lon1 nyc1 ...   each relays every region
                        │
                        ▼
          https://preconfs.rpcpool.com   anycast picks the closest
                        │
                        ▼
          your stream: one feed, one region
```

**Feed regions** are the ones in the proto (`HarmonicRegion`, `BamRegion`) and in the subscribe request. Harmonic runs a block builder in each of its regions and BAM runs a node in each of its; a leader is served by one of them, and that location's stream carries the slots of the leaders it serves. The regional streams are not copies of each other: `harmonic:ams` and `harmonic:fra` deliver different slots. Subscribe to the ones near your servers; see [Choosing regions](#choosing-regions). The locations in the table below are where the feed operators run their builders and nodes. They are not Triton points of presence.

**Origin on each transaction**: `region` on a Harmonic transaction and `node` on a BAM transaction name the builder or node it came through. On a single region stream it matches what you subscribed; it is there so transactions stay self describing when you merge streams.

**Points of presence** are Triton's servers, named after their site (`ams1`, `fra1`, `lon1`, `nyc1` and so on). The anycast address lands you on the closest one and every point of presence relays every feed region, so where you connect never limits what you can subscribe to. `Client::version` returns the one that answered in its `region` field.

A stream serves one feed in one region. A region of the other feed is refused.

## Regions

| Location             | Harmonic region | BAM region |
| -------------------- | --------------- | ---------- |
| Amsterdam            | `ams`           | `ams`      |
| Dallas               | -               | `dfw`      |
| Dublin               | -               | `dub`      |
| New York (Newark)    | `ewr`           | `ewr`      |
| Frankfurt            | `fra`           | `fra`      |
| Hong Kong            | -               | `hkg`      |
| Washington (Ashburn) | -               | `iad`      |
| Los Angeles          | -               | `lax`      |
| London               | `lon`           | `lon`      |
| Pittsburgh           | -               | `pit`      |
| Seattle              | -               | `sea`      |
| Singapore            | `sgp`           | `sin`      |
| Salt Lake City       | `slc`           | `slc`      |
| Siauliai             | -               | `sqq`      |
| Tokyo                | `tyo`           | `tyo`      |

## Harmonic

The builder executes the transaction and reports the outcome: success, execution failure or fees only. Streams are framed per slot (see [Slot framing](/solana/real-time-streaming/preconfirmations-grpc/stream-and-message-format.md#slot-framing)).

## BAM

The leader commits the transaction and reports to the node whether it succeeded; a node that does not report it leaves the outcome unset. There is no slot framing: each transaction names its slot.

## Choosing regions

Pick the regions near you. Do not subscribe to all of them.

A preconfirmation is worth what its lead over the landed block is worth, and that lead is spent on the wire. Cables do not run in straight lines: a round trip between Tokyo and Frankfurt takes around 250 ms on a normal route, so a slot preconfirmed in Tokyo reaches a server in Frankfurt more than 100 ms after it reached a server in Tokyo. By then a trader sitting in Tokyo has acted on it. On a far region you still come second every time. This is not a limit we impose; it is geography.

So: subscribe to the regions whose builders and nodes sit in your own data center or metro, one stream per region. Someone in Frankfurt subscribes to `harmonic:fra` and `bam:fra`, maybe `ams` and `lon` next to them; someone in New York to `harmonic:ewr` and `bam:ewr`, maybe `bam:iad` and `bam:pit`. Subscribe to a region elsewhere when you have a server placed there. The streams of the regions you pick share one connection.

If you do open several, merge them. Each stream carries different slots, so there is nothing to deduplicate between regions of the same feed, and framing is per stream: a `SlotEnd` on `ams` says nothing about `fra`. In Rust, `select_all` polls whichever region has an event ready:

```rust
use futures::stream::{StreamExt, select_all};
use solana_pubkey::Pubkey;
use triton_preconfs_client::{Connector, Event, Feed, Filter, Filters, Region, parse};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = Connector::new("https://preconfs.rpcpool.com")
        .x_token(Some(std::env::var("PRECONFS_TOKEN")?))
        .connect()
        .await?;

    let token_program: Pubkey = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA".parse()?;
    let filter = Filter::new().accounts([token_program]);

    // The regions next to this server, one stream each, on one connection,
    // tagged with the region name so the merged output stays self describing.
    let mut streams = Vec::new();
    for name in ["fra", "ams", "lon"] {
        let region = Region::parse(Feed::Harmonic, name)?;
        let stream = client
            .subscribe_harmonic(region, Filters::single(filter.clone()))
            .await?;
        streams.push(stream.map(move |event| (name, event)).boxed());
    }

    let mut merged = select_all(streams);
    while let Some((region, event)) = merged.next().await {
        match event? {
            Event::Transaction(matched) => {
                let signature = parse::parse_signature(&matched.transaction.transaction)?;
                println!("{region} slot {} {signature}", matched.transaction.slot);
            }
            Event::SlotEnd { slot } => println!("{region} slot {slot} complete"),
            Event::Clip { transactions } => println!("{region} clipped {transactions}"),
            Event::Reconnected { attempts } => println!("{region} reconnected after {attempts}"),
            _ => {}
        }
    }
    Ok(())
}
```

Add `futures = "0.3"` for `select_all`, next to the dependencies from the [Quick start](/solana/real-time-streaming/preconfirmations-grpc.md#quick-start). The same shape with `Feed::Bam` and `subscribe_bam` covers the BAM regions near you; the two feeds have different event types, so merge them separately or map both into your own type first. Each stream reconnects on its own, so a point of presence restart shows up as one `Reconnected` per region.

When you open several streams:

* Each feed and region has its own coverage share; see [Coverage](/solana/real-time-streaming/preconfirmations-grpc/coverage-billing-and-limits.md#coverage).
* Streams on the same feed and region draw on that share together at one point of presence; each point of presence measures on its own. Streams on other regions or on the other feed do not.
* A wide filter reaches the share quickly.
* The share is meant for filters that select what you actually use.
* Streams are counted against a cap of 1000 per account, both feeds together; see [Limits](/solana/real-time-streaming/preconfirmations-grpc/coverage-billing-and-limits.md#limits).
* The cap is far above what any placement needs.

## In the client

```rust
let region = Region::parse(Feed::Harmonic, "ams")?;
let region: Region = "bam:fra".parse()?;
Feed::Bam.regions(); // the names above
```

To pin one point of presence instead of letting anycast choose, call `.dial("host:port")` on the `Connector` with that point of presence's address. The TCP connection goes to that address, while TLS still verifies `preconfs.rpcpool.com`, so the endpoint and token stay the same. `Client::version` returns the server version and the point of presence answering, which confirms the pin:

```rust
use triton_preconfs_client::Connector;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // The TCP connection goes to the pinned address; TLS still verifies
    // preconfs.rpcpool.com, so the endpoint stays the same.
    let client = Connector::new("https://preconfs.rpcpool.com")
        .x_token(Some(std::env::var("PRECONFS_TOKEN")?))
        .dial("<point-of-presence-ip>:443")
        .connect()
        .await?;

    let version = client.version().await?;
    println!("point of presence {} (server {})", version.region, version.version);
    Ok(())
}
```

`.dial()` works with `connect_lazy()` too. Every stream opened from the client, resubscribes included, goes to the pinned address.

## Which point of presence answered

`GetVersion` returns the server version and the point of presence that took your connection, and needs the token like every other call. With `preconfs.proto` in the current directory (see [Trying it with grpcurl](/solana/real-time-streaming/preconfirmations-grpc.md#trying-it-with-grpcurl)):

```bash
grpcurl -import-path . -proto preconfs.proto -H "x-token: $TOKEN" \
  preconfs.rpcpool.com:443 preconfs.Harmonic/GetVersion
```

```json
{
  "version": "0.6.2",
  "region": "fra1"
}
```

The version changes with every server release. The same call is `Client::version()` in the Rust client.

***

<i class="fa-life-ring">:life-ring:</i> Contact support by clicking the chat icon in your [customer dashboard](https://customers.triton.one)\ <i class="fa-briefcase">:briefcase:</i> Sales questions? [Contact us](https://triton.one/contact)\ <i class="fa-sparkles">:sparkles:</i> AI agent? Read [llms.txt](https://docs.triton.one/llms.txt)\ <i class="fa-rss">:rss:</i> Follow updates: [Blog](https://blog.triton.one) · [X](https://x.com/triton_one) · [YouTube](https://www.youtube.com/@triton_one_ltd) · [Telegram](https://t.me/tritonone) · [GitHub](https://github.com/rpcpool)
