[dataplane]: Translate the methods Cloud does not serve on a frontend - #154
Open
Vaughan-Temporal wants to merge 1 commit into
Open
[dataplane]: Translate the methods Cloud does not serve on a frontend#154Vaughan-Temporal wants to merge 1 commit into
Vaughan-Temporal wants to merge 1 commit into
Conversation
|
|
This was referenced Sep 4, 2026
Vaughan-Temporal
force-pushed
the
vaughan/translation-integration
branch
from
September 4, 2026 16:49
0cdb318 to
afc8c0b
Compare
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! 🚀 New features to boost your workflow:
|
Vaughan-Temporal
force-pushed
the
vaughan/translation-integration
branch
from
September 4, 2026 17:24
afc8c0b to
ab16050
Compare
Nothing installed the translation registry, so the ListNamespaces translation was compiled in but never reached. Install it on a Cloud upstream, which needs no new configuration to say so: an upstream is already known to be Temporal Cloud, by declaration or by its address, and translating the methods Cloud does not serve on a frontend follows from exactly that. The control plane is derived from the upstream: cloud.APIHostPort, the upstream's own credentials (one Temporal Cloud API key authorizes both the frontend and the control plane), and a default outbound TLS configuration. TLS is not inherited, since the control plane is a different host and the upstream's server name or client certificate would not apply to it. Where CloudService lives is a fixed fact about Cloud rather than a policy, so it travels with the translation instead of being restated as routing an operator could get wrong in either direction. Deriving per upstream rather than sharing one connection matters for pooling. A static resolver keys the pool by dial target, which is unique among upstreams but not among control planes: every Cloud upstream resolves to the same address, so a shared key would hand the second upstream whichever connection the first created, and with it the first's credentials. The pool key is the derived name instead. The connection is not opened eagerly. Translation is incidental to an upstream's normal traffic, so a control plane that is unreachable must not stop the proxy serving everything else. An optional cloudApi block overrides the endpoint for another Cloud environment, or supplies credentials or TLS the upstream does not carry. An address that is not a Cloud endpoint is reported at startup rather than rejected, since a test double or a private environment legitimately carries no Cloud domain and that cannot be told apart from a typo. Translation is installed last so it is the innermost interceptor. Via leaves the chain when a translation fires, so anything below it would not run for a translated call; innermost, the namespace translator and payload codecs above still see the method and message types the caller asked for, and a configured namespace suffix rewrites the converted reply with no special casing. That ordering is load-bearing and otherwise invisible, so it has a test of its own.
Vaughan-Temporal
force-pushed
the
vaughan/translation-integration
branch
from
September 4, 2026 17:31
ab16050 to
787a9e4
Compare
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.
Nothing installed the translation registry, so the mapping from #153 was compiled in but never reached. This installs it
on a Cloud upstream — which needs no configuration to say so. Building on #147: an upstream is already known to be
Temporal Cloud, by declaration or by its address, and translating the methods Cloud does not serve on a frontend follows
from exactly that.
Design
The control plane is derived from the upstream —
cloud.APIHostPort, the upstream's own credentials (one TemporalCloud API key authorises both frontend and control plane), and a default outbound TLS configuration. TLS is not
inherited: the control plane is a different host, so the upstream's server name or client certificate would not apply.
Where
CloudServicelives is a fixed fact about Cloud, not a policy, so it travels with the translation rather thanbeing restated as routing an operator could get wrong in either direction.
An on-prem upstream is never translated — it is not
IsCloud(), so the interceptor is never installed on it. Theguard is at installation rather than per-call deliberately: a runtime check on the dial target would break private-link
Cloud upstreams, whose per-VPC hostnames carry no Cloud domain and are the reason
cloud: trueexists.Config
Everything above needs no configuration. One optional block overrides it:
enabled: falseforwards the method untranslated, for a Cloud upstream where the untranslated failure is preferred toa translated answer.
Enabledis a*boolbecause a plain one cannot distinguish absent from false, and the defaulthas to be on — translation makes a method work that cannot work otherwise, so an operator opts out of a fix.
cloudApi:reaches a different Cloud environment, or supplies credentials or TLS the upstream does not carry.Two subtleties worth reviewing
Interceptor ordering is load-bearing. Translation is installed last so it is innermost.
Vialeaves the chain whena translation fires, so anything below it would not run for a translated call. Innermost, the namespace translator above
still sees the converted reply — a configured suffix rewrites
payments.a1b2ctopaymentswith no special casing.Move it up and that silently stops working while every other test still passes, so
TestEndToEndNamespaceRulesApplyToATranslatedReplyguards it. Mutation-checked.Pool keying.
staticResolverkeys the pool by dial target, safe "because upstream hostPorts are unique (enforced byconfig)" — see its comment. Control-plane connections break that: every Cloud upstream resolves to the same address, so
two upstreams with different API keys would share whichever connection was created first, and with it the first's
credentials.
keyedResolverkeys on a derived name instead.A non-Cloud
cloudApi:address warns rather than fails. A test double or private environment legitimately carries noCloud domain and that is indistinguishable from a typo — matching how #147 handles a namespace it cannot validate on a
templated upstream.
The connection is not opened eagerly: translation is incidental to normal traffic, so an unreachable control plane must
not stop the proxy serving everything else.
mTLS upstreams must configure a key
The Cloud Ops API accepts an API key only — unlike a namespace frontend it does not
accept mTLS. So a Cloud upstream authenticating with a client certificate has no credential to inherit, and must name an
API key under
apiTranslations.cloudApi.credentialsor its translated methods are refused. That is the case the blockexists for; documented on the type.
Scope
+768 / −0, 7 files.
dataplane.gois the whole integration in 96 lines. Six e2e tests drive the full stack (gateway →router → forwarder → interceptor) against a fake CloudService.
Stack
Merge bottom-up. Each PR targets the one above it, so its diff shows only its own change.
🤖 Generated with Claude Code