Conversation
A local key can now pin its traffic to a subset of nodes with settings.upstreams.group-labels: only upstreams carrying at least one of the labels serve the key's requests and subscriptions. It is a filter, not a balancer - rating, base or label-balancing still picks among the admitted upstreams. The key adds a RequestGroupLabelSelector to every upstream request in PostCheckSetting, so it rides the existing selector routing: all strategies apply it as a matcher, retries and hedges included, and the subscription aggregation key separates differently restricted keys. It does not enter the cache key - it chooses which nodes answer, not what they answer. A request type that cannot carry the selector is rejected instead of served unrestricted. Locally-synthesized newHeads and newPendingTransactions merge every upstream of the chain, so, like logs already did, a selector-bearing subscription now takes the node-backed path. drpc_pendingTransactions has no such path and fails with selectors. Every configured label must exist on some upstream, so a typo fails at startup instead of leaving the key with no upstream.
When an eth_blockNumber/eth_getBlockByNumber answer looks stale, the integrity processor re-sends the request to higher upstreams through a SpecificOrderUpstreamStrategy built without matchers. That retry ignored the request's selectors, so a client's selector - or an API key's upstream restriction - could be escaped to an excluded node. Build the selector matchers for the retry too, keeping the handler's height order.
KirillPamPam
reviewed
Oct 5, 2026
| // validateKeyUpstreams rejects a key filter naming a group-label no upstream | ||
| // carries: such a key would silently be served by nothing, and a typo is far | ||
| // likelier than an intent to disable the key. | ||
| func (a *AuthConfig) validateKeyUpstreams(upstreamConfig *UpstreamConfig) error { |
Collaborator
There was a problem hiding this comment.
There is already func (a *AuthConfig) validate, let's reuse it instead of creating a new one
Collaborator
There was a problem hiding this comment.
There is a method func (l *LocalKeyConfig) validate() where we can add a validation of KeySettingsConfig with new Upstreams
| return err | ||
| } | ||
| if a.AuthConfig != nil { | ||
| if err := a.AuthConfig.validateKeyUpstreams(a.UpstreamConfig); err != nil { |
Collaborator
There was a problem hiding this comment.
There is already a validation of AuthConfig above, let's reuse it
| // A selector-bearing request takes the node-backed path instead, where the | ||
| // strategy routes on its selectors. | ||
| routed := hasEffectiveSelectors(request.Selectors()) | ||
| if settings.NewHeads && !routed && isNewHeadsRequest(request) && localNewHeadsAvailable(chain, supervisor) { |
Collaborator
There was a problem hiding this comment.
I suggest do not fix local subs so far. There should be a more complex fix that we will add as soon as possible. As a workaround local subs can be disabled via LocalSubscriptionsConfig until this fix
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #391
What
Local API keys can restrict which upstreams serve them via
settings.upstreams.group-labels. Only upstreams carrying at least one of the listedgroup-labelsare used for the key's requests and subscriptions:It is a filter, not a balancer: among the admitted upstreams the chain's strategy (
rating/base/label-balancing) still picks as usual, and excluded upstreams are never used — not as a fallback, not on a retry or hedge.How
LocalKey.PostCheckSettingadds an internalRequestGroupLabelSelectorto every upstream request (newprotocol.SelectorAppender, implemented by the JSON-RPC, REST and gRPC holders). A request that cannot carry it is rejected instead of being served unrestricted.compileSelectorturns it into aGroupLabelMatcherthat resolves an upstream's group-labels through the supervisor, so every strategy applies it.LabelCacheKeytreats it as unconstrained — it chooses which nodes answer, not what they answer).newHeads/newPendingTransactionsmerge every upstream of the chain, so — aslogsalready did — a selector-bearing subscription now takes the node-backed path.drpc_pendingTransactionshas no such path and fails with a client error when selectors are present.Also fixed
The integrity retry (
IntegrityRequestProcessor.handleResponse) re-sent staleeth_blockNumber/eth_getBlockByNumberanswers to higher upstreams through a strategy without matchers, so it ignored the request's selectors — both a client's own and the new key restriction. It now applies the selector matchers while keeping the height order. Separate commit:fix(flow): honor request selectors on the integrity retry.Behavior changes
NativeSubscribeclients that send effective selectors onnewHeads/newPendingTransactionsnow get them honored via a node-backed subscription; previously the selectors were silently ignored by the local source.Not covered
drpc-managed keys: their settings come from the dRPC platform, which has no such field.key-managementat all; documented in03-auth.md.Testing
AppendSelectors,GroupLabelMatcher,LocalKeyrestriction (incl. fail-closed),resolveSourcebypass of local sources, subscription key separation.createStrategyend-to-end with a real chain supervisor: requests and subscriptions only ever land on the admitted upstream.go test -race ./...andgolangci-lintv2.14.0 pass locally (exceptcaches/*_e2e, which need Docker).Docs:
docs/nodecore/03-auth.md(new "Upstream restriction" section) anddocs/nodecore/13-subscriptions.md.🤖 Generated with Claude Code