> For the complete documentation index, see [llms.txt](https://mute-sh.gitbook.io/mute.sh/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://mute-sh.gitbook.io/mute.sh/api/api-reference/grpc-streaming.md).

# gRPC Block Streaming (Enterprise)

Stream **new HyperEVM block headers** and **new HyperCore block markers** the instant they reach our co-located Hyperliquid node — over a single hot gRPC connection. Built for latency-sensitive traders and HyperEVM indexers.

* **Endpoint:** `grpc.mute.sh:443` (TLS, publicly-trusted cert)
* **Auth:** API key in gRPC metadata — `authorization: Bearer <your-key>`
* **Service:** `hlstream.v1.BlockStream` · server-streaming
* **Reflection:** enabled — tools like `grpcurl` need no `.proto` file

> **Latency reality (read this):** the gateway adds well under 1 ms on top of the node. Your end-to-end latency is dominated by your network round-trip to Tokyo (`ap-northeast-1`). To get the edge, **peer or colocate in `ap-northeast-1`** — from elsewhere you'll see 100–250 ms of pure RTT.

## Quickstart (grpcurl)

```bash
export MUTE_STREAM_KEY="mute_stream_…"   # provided to you; keep it secret

grpcurl -H "authorization: Bearer $MUTE_STREAM_KEY" \
  -d '{"evm_headers": true, "core_blocks": true}' \
  grpc.mute.sh:443 hlstream.v1.BlockStream/Subscribe
```

Blocks stream as JSON until you Ctrl-C. Subscribe to one stream by setting only its flag (`{"evm_headers":true}` or `{"core_blocks":true}`); an empty request (`{}`) means **everything**.

## The contract

```proto
service BlockStream {
  rpc Subscribe(SubscribeRequest) returns (stream BlockEvent);
  rpc Ping(PingRequest) returns (PingResponse);   // liveness + RTT
}

message SubscribeRequest { bool evm_headers = 1; bool core_blocks = 2; } // both false = all

message BlockEvent {
  uint64 seq = 1;          // per-kind monotonic; a gap means your client lagged
  Timing timing = 2;
  oneof block { EvmBlockHeader evm = 3; CoreBlock core = 4; }
}
message Timing { int64 occurred_us = 1; int64 node_seen_us = 2; int64 gateway_sent_us = 3; } // µs, UNIX epoch
message EvmBlockHeader { uint64 number=1; string hash=2; string parent_hash=3; uint64 timestamp=4;
                         uint64 gas_used=5; uint64 gas_limit=6; uint32 tx_count=7; string miner=8; }
message CoreBlock { uint64 height = 1; int64 block_time_ms = 2; }
```

A `BlockEvent` on the wire:

```jsonc
{ "seq": "241",
  "timing": { "occurredUs": "1782117947558000", "nodeSeenUs": "1782117947690110", "gatewaySentUs": "1782117947690140" },
  "evm": { "number": "38493502", "hash": "0xe6ee…", "parentHash": "0x…", "timestamp": "1782117947",
           "gasUsed": "21000", "gasLimit": "30000000", "txCount": 13, "miner": "0x…" } }
// …or "core": { "height": "1045021461", "blockTimeMs": "1782117948030" }
```

### Measuring latency (it's honest — you do the math)

We never ship a precomputed latency. Each event carries three timestamps; compute against your own receive clock:

* **pipeline** = `received − node_seen_us` — what we add (sub-ms). The number that matters.
* **end-to-end** = `received − occurred_us` — total, including consensus finality + your RTT.

EVM `timestamp` is **second-granular**, so use `node_seen_us` for precise EVM latency. (Run NTP/chrony for cross-host accuracy.)

## Examples

### Go (`google.golang.org/grpc`)

```go
creds := credentials.NewTLS(&tls.Config{})
conn, _ := grpc.Dial("grpc.mute.sh:443", grpc.WithTransportCredentials(creds))
cli := pb.NewBlockStreamClient(conn)
ctx := metadata.AppendToOutgoingContext(context.Background(), "authorization", "Bearer "+key)
s, _ := cli.Subscribe(ctx, &pb.SubscribeRequest{EvmHeaders: true, CoreBlocks: true})
for { ev, err := s.Recv(); if err != nil { break }; fmt.Println(ev.Seq, ev.GetEvm(), ev.GetCore()) }
```

### TypeScript / Node (`@grpc/grpc-js`)

```ts
const md = new grpc.Metadata(); md.set("authorization", `Bearer ${key}`);
const client = new BlockStream("grpc.mute.sh:443", grpc.credentials.createSsl());
const call = client.Subscribe({ evm_headers: true, core_blocks: true }, md);
call.on("data", (ev) => console.log(ev.seq, ev.evm ?? ev.core));
```

### Python (`grpcio`)

```python
chan = grpc.secure_channel("grpc.mute.sh:443", grpc.ssl_channel_credentials())
stub = stream_pb2_grpc.BlockStreamStub(chan)
md = (("authorization", f"Bearer {key}"),)
for ev in stub.Subscribe(stream_pb2.SubscribeRequest(evm_headers=True, core_blocks=True), metadata=md):
    print(ev.seq, ev.evm if ev.HasField("evm") else ev.core)
```

### Rust (`tonic`)

```rust
let ch = Channel::from_static("https://grpc.mute.sh")
    .tls_config(ClientTlsConfig::new().with_webpki_roots())?.connect().await?;
let mut client = BlockStreamClient::new(ch);
let mut req = Request::new(SubscribeRequest { evm_headers: true, core_blocks: true });
req.metadata_mut().insert("authorization", format!("Bearer {key}").parse()?);
let mut s = client.subscribe(req).await?.into_inner();
while let Some(ev) = s.message().await? { /* … */ }
```

Get the `.proto` from reflection (`grpcurl grpc.mute.sh:443 describe`) or ask us for `hlstream/v1/stream.proto`.

## Reconnects, ordering, backpressure

* **Ordering:** `seq` is per stream-kind and monotonic. A jump means your client fell behind and the server dropped events for you (it never blocks other subscribers). Read promptly.
* **Reconnect:** if the stream drops, just re-`Subscribe` — you resume from *now* (live tip). There is **no historical replay / resume cursor yet**; for gap-filling use the [Data API](/mute.sh/api/api-reference/markets.md).
* **One connection, many streams:** select both kinds in one `Subscribe`.

## Limits & honest caveats

* **HyperCore blocks have no block hash.** Only HyperEVM blocks expose a `hash` (it's a real EVM chain). HyperCore (HyperBFT) blocks give `height` + `block_time` today. (Tx-count / proposer enrichment is on the roadmap.)
* **After a node restart**, the EVM head briefly catches up to chain tip (node resync); HyperCore is real-time immediately.
* Auth is required in production; without a valid key, `Subscribe` returns `UNAUTHENTICATED`.

## Getting access

gRPC streaming is a premium mute.sh tier. Email the team or use your dashboard at `app.mute.sh` to request a streaming key. Your key authenticates both this gRPC stream and the REST/SSE Data API — one key, [all three products](/mute.sh/api/getting-started/readme.md).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://mute-sh.gitbook.io/mute.sh/api/api-reference/grpc-streaming.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
