Skip to content

[cloud]: Validate Cloud Namespaces - #147

Merged
pseudomuto merged 1 commit into
mainfrom
cloud-namespace-validation
Sep 3, 2026
Merged

[cloud]: Validate Cloud Namespaces#147
pseudomuto merged 1 commit into
mainfrom
cloud-namespace-validation

Conversation

@pseudomuto

Copy link
Copy Markdown
Collaborator

The canonical "Cloud" config derives its endpoint from the translated namespace: {{ .RemoteNamespace }}.tmprl.cloud:7233 with a .$TEMPORAL_ACCOUNT suffix. Config loading expands environment variables with os.Expand, which substitutes an empty value for an unset one, so a blank TEMPORAL_ACCOUNT silently leaves a bare-dot suffix, and every namespace translates to ".". The proxy would then dial "..tmprl.cloud" and the operator saw nothing but a DNS failure. More generally, the proxy had no notion of a Cloud upstream, so a namespace Cloud would reject was indistinguishable from a valid one.

Now, when an upstream is Temporal Cloud, namespaces (post-translation) are validated. For non-templated hosts, this is done at startup to prevent mistakes. For dynamic ones, a DEBUG log is printed indicating why the namespace is invalid. Requests are still forwarded upstream for now.

NOTE: Auto-detection means a config the proxy used to accept can now
fail to start, but only one that could never have worked against Cloud anyway.

The canonical "Cloud" config derives its endpoint from the translated
`namespace: {{ .RemoteNamespace }}.tmprl.cloud:7233` with a
".$TEMPORAL_ACCOUNT" suffix. Config loading expands environment
variables with os.Expand, which substitutes empty for an unset one, so a
blank TEMPORAL_ACCOUNT silently leaves a bare-dot suffix and every
namespace translates to "<name>.". The proxy would then dial
"<name>..tmprl.cloud" and the operator saw nothing but a DNS failure.
More generally, the proxy had no notion of a Cloud upstream, so a
namespace Cloud would reject was indistinguishable from a valid one.

When an upstream is Temporal Cloud, namespaces (post-translation) are
now validated. For non-templated hosts, this is done at startup to
prevent mistakes. For dynamic ones, a DEBUG log is printed indicating
why the namespace is invalid. Requests are still forwarded upstream for
now.

> NOTE: Auto-detection means a config the proxy used to accept can now
fail to start, but only one that could never have worked against Cloud
anyways.
@pseudomuto
pseudomuto requested a review from a team as a code owner September 2, 2026 19:37
@codecov-commenter

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.76271% with 5 lines in your changes missing coverage. Please review.

📢 Thoughts on this report? Let us know!

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Vaughan-Temporal Vaughan-Temporal left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved. This all makes

@pseudomuto
pseudomuto merged commit 4d09a32 into main Sep 3, 2026
5 checks passed
@pseudomuto
pseudomuto deleted the cloud-namespace-validation branch September 3, 2026 10:49
Vaughan-Temporal added a commit that referenced this pull request Sep 4, 2026
Method translation needed a cloudApi block naming Temporal Cloud's control
plane, which restated something the proxy can already work out. #147 gave the
upstream a notion of being Cloud - declared with `cloud: true`, or detected from
a .tmprl.cloud address - and translating the methods Cloud does not serve on a
frontend follows from exactly that. Enable it there instead, so a Cloud upstream
needs no configuration for it at all.

The control plane is now derived per 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.

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.

The cloudApi block survives as an override for a different Cloud environment, or
for 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 the proxy
cannot tell that apart from a typo - the same treatment #147 gives a namespace it
cannot validate on a templated upstream.

Also moves the control-plane address to internal/cloud, next to the domain suffix
it is built from, since it is a fact about Cloud rather than about configuration.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants