Cloud upstreams dial the Cloud control plane implicitly, and the config keys that control it are undocumented · Issue #178 · temporalio/temporal-proxy · GitHub
You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Cloud upstreams dial the Cloud control plane implicitly, and the config keys that control it are undocumented #178
I hit this setting the proxy up against a Temporal Cloud namespace from a network with an egress allowlist. The behavior itself seems reasonable, but I think the documentation gap around it is worth closing.
What happens
When an upstream resolves as Temporal Cloud, the proxy installs a method-translation layer that answers WorkflowService/ListNamespaces by dialling saas-api.tmprl.cloud:443 and calling CloudService/GetNamespaces there, reusing the upstream's API key.
internal/config/upstream.go turns this on if any of three things is true, two of which are inferred rather than declared:
where cloud.IsEndpoint is a strings.HasSuffix(host, ".tmprl.cloud"). examples/listnamespace/config.yaml demonstrates exactly this: no cloud flag, apiTranslations commented out entirely, and translation happens anyway.
Why it is hard to find out
The README never mentions method translation, CloudService, apiTranslations, or the upstreams[].cloud flag. The feature list stops at routing, allowlist, namespace translation, TLS, encryption, key management, auth, codec transparency and deployment.
"Get started" links examples/cloud, examples/kms and examples/authz, but not examples/listnamespace, which is the only place this is shown.
The configure page on docs.temporal.io lists eight top-level config keys and none of apiTranslations, cloudAPI, or the upstream cloud flag.
cloudAPIConn does log "translating methods to the Cloud API" at startup, but it tags the methods, not the endpoint, so the log never reveals that saas-api.tmprl.cloud:443 is now an egress dependency.
The sentence that made me file this
The README says:
Service allowlist. Forward only the gRPC services you name, defaulting to WorkflowService and OperatorService. Server reflection is opt-in, and a service you leave out is never forwarded.
CloudService is not in the default allowlist and cannot be named by an operator, yet on a Cloud upstream the proxy speaks it outbound to a host the operator never configured. The allowlist being inbound-only is fine, but that sentence reads as though it is not.
Why it bites
Behind an egress allowlist scoped to the namespace frontend, ListNamespaces fails opaquely. The Temporal UI calls it on load, so the symptom is a broken UI rather than an obvious network error.
A security review told "the proxy talks to our Cloud namespace" is not being told it also talks to the control plane with the same key.
internal/cloud/endpoint.go explicitly calls out private-link endpoints as the reason cloud: true exists, but an operator who needs that escape hatch cannot discover it from any document.
What I think would fix it
Document upstreams[].cloud and apiTranslations.cloudAPI in the README and on the configure page.
State the saas-api.tmprl.cloud:443 egress requirement, and that the upstream API key is reused against the control plane.
Link examples/listnamespace from "Get started".
Include the resolved control-plane host in the existing startup log line, so the egress dependency is visible without reading the source.
Happy to open a PR for the README and the log line if that is useful. The translation subsystem shipped in v0.6.0 eleven days ago, so if docs are already in flight, feel free to close this.
Activity