slots,
blockhash, priorityFee). The bottom half — Private Geyser feed and
Feed cadence — is per-organization configuration: you supply your own
Yellowstone-Geyser gRPC endpoint, and you choose how often the engine
publishes each field.
This page documents that per-organization half.
What a private feed is
Normallymod_sol_feeds ingests Solana state and publishes it to shared
tracks. With a private feed you give the module your own
Yellowstone-Geyser gRPC endpoint instead. The engine (shard 0) ingests
slots from that endpoint over HTTP/2 and republishes them to a MoQT track
that belongs only to your org.
Two things follow from that:
- The data path is yours. Freshness and update rate depend on the Geyser provider you point at (Triton, Helius, or a self-hosted validator), not on the shared feed.
- The track is namespaced under your org, so a relay token scoped to another org cannot subscribe to it.
Feed namespace and track names
A configured private feed occupies the namespace:geyser.get returns the namespace as namespace, so the console renders
the real value rather than deriving it. Before you sign in or before an
org is selected, the screen falls back to the literal placeholder
sol/feed/<org> — that is a display placeholder, not a usable namespace.
The header pill reflects configured from geyser.get: live · <namespace>
when an endpoint is stored, not configured otherwise.
Configuring a Yellowstone-Geyser endpoint
The panel is scoped to the active organization and is gated on sign-in. If you are not signed in, you get a Sign in to configure state with a link to the portal; the portal session is shared with the console. Two fields:
Validation runs client-side before the mutation fires. An empty or
whitespace-only
grpc_url, or a URL whose protocol is not https, blocks
the save and marks the field invalid — the request is not sent. Errors are
only surfaced after the first save attempt.
Saving calls:
geyser.get and geyser.verify, and
clears the token input so the secret does not linger in the DOM.
If the mutation itself fails, the server’s message is rendered under the
fields verbatim.
Credential sealing and the unseal failure path
Thex-token is sealed at rest under your org. The console never reads
it back. geyser.get exposes only hasToken — whether a token is stored —
and the input renders •••••••• (unchanged) as its placeholder in that
case.
Separately, geyser.verify answers a different question: can the
engine-side KEK actually open the sealed credential? The Token stat
combines both signals:
When the state is
unreadable, the panel shows:
The stored token can’t be unsealed — re-enter it to reseal.Operationally, treat this as the endpoint is effectively unauthenticated right now. The ciphertext is present but the engine cannot recover the plaintext, so ingest cannot present a valid
x-token to your Geyser
provider. There is no repair action other than resealing: paste the token
into the x-token field again and save. That runs geyser.set with a
fresh seal, and the subsequent verify refetch should flip the stat back
to sealed ✓.
Because the old value is unrecoverable by design, you need the token from
your Geyser provider, not from FirstByte.
Feed cadence: tick_ms, blockhash_every, fee_every
Cadence is stored in the same per-tenant geyser config that holds your endpoint, and it is read back bygeyser.get as tickMs,
blockhashEvery and feeEvery.
The two
*_every values are multiples of the tick, not independent
intervals. The effective publish interval for a field is:
tick_ms = 400, blockhash_every = 5,
fee_every = 8 — mean the base poll runs every 400 ms, blockhash
publishes every 2000 ms, and priorityFee publishes every 3200 ms.
Lowering tick_ms speeds up everything downstream of it; raising a
*_every thins out one field without touching the others.
Slider hydration has one quirk worth knowing: when the config loads, the
console accepts the stored tickMs only if it is at least 100, otherwise
it displays 400. blockhashEvery and feeEvery fall back to 5 and 8
respectively when absent or zero. If you saved a cadence out-of-band and
the slider shows a different number than you expect, that fallback is why.
Saving calls:
When a cadence change takes effect
A saved cadence is written to your feed config immediately andgeyser.get is refetched, so the sliders reflect the stored state as soon
as the mutation resolves.
Propagation to the publisher is separate from persistence:
mod_sol_feedspicks up a live cadence change within roughly five seconds. You do not restart anything and you do not resubscribe.- The per-tenant blockhash and priorityFee poll is still rolling out.
Until it is enabled engine-side, a private feed is slots-only: your
blockhash_everyandfee_everyvalues are persisted and validated, but no per-tenant blockhash or fee publishing happens yet. The success caption on the screen says exactly this.
blockhash / priorityFee, and a live change for the tick that
drives slot publishing.
Subscribing to your private track
Private tracks are published over MoQT and subscribed over QUIC, the same as the shared tracks listed at the top of the screen. The name to subscribe to is the one shown in the Track stat,sol/feed/<orgId>/slots.
Access is enforced by namespace scope on the relay token, not by the feed
config. The token you present on the MoQT handshake must carry a namespace
claim that covers sol/feed/<orgId> — see
Authentication for how ns claims are minted
and checked. A token scoped to a different org’s namespace is rejected at
subscribe time.
Why freshness, rate and subscribers show a dash
On the track cards and in the selected-track detail, Freshness, Rate and Subscribers render— when the underlying value is
unmeasured. The console deliberately does not substitute 0, because
“no measurement” and “measured zero” are different conditions and
conflating them hides a broken telemetry path.
A dash means no sample has arrived for that counter. It does not by itself
mean the track is down — the track’s own status pill and the
push · WS / poll · HTTP mode pill are the signals for that.
Removing a private feed
The Remove button appears only whenconfigured is true. It calls:
geyser.get and geyser.verify, which flips the
header pill back to not configured and the Token stat back to —.
Clearing removes the endpoint and the sealed token for the org. Ingest from
your Geyser endpoint stops, and with it publishing to
sol/feed/<orgId>/slots. Any subscriber still attached to that track stops
receiving objects. Re-adding a feed later requires the x-token again,
since the sealed copy is gone.
Procedures used by this screen
geyser.get, geyser.verify, geyser.set, geyser.setCadence and
geyser.clear are all keyed on the active org, and every mutation is
disabled until an org is selected.
Related
- Authentication — relay tokens and
nsnamespace scope - Telemetry — where freshness and rate metrics come from