RFC: report compute providers and release stage per Namespace - #867
Draft
rossnelson wants to merge 4 commits into
Draft
RFC: report compute providers and release stage per Namespace#867rossnelson wants to merge 4 commits into
rossnelson wants to merge 4 commits into
Conversation
RFC, not for merge as-is. Proposes reporting compute providers as a repeated message on GetSystemInfoResponse instead of adding one boolean to Capabilities per provider. - enums/v1/compute.proto: ComputeProviderReleaseStage - compute/v1/status.proto: ComputeProviderStatus (type, enabled, release_stage) - GetSystemInfoResponse.compute_providers = 3 - Marks server_scaled_provider_cloud_run as superseded, kept for wire compatibility, and closes the door on further per-provider booleans The boolean pattern answers only "is this one provider usable". Every new provider costs a proto field, an api release, and a server release before a client can gate on it, and it carries no release stage, so each client hardcodes its own table. Both problems are live: Cloud Run's capability landed a month after the UI shipped the option, the server still does not set it, and AgentCore is merged in temporal-auto-scaled-workers and the CLI with no capability field at all. OpenAPI specs are not regenerated here; buf could not be installed in this environment. Regenerate before this leaves draft.
6 tasks
Quinn Klassen pointed out on #867 that GetSystemInfo is the wrong carrier: Cloud serves that endpoint as a fixed response at the reverse proxy layer, so nothing per-account or per-Namespace can reach a client through it, and suggested a Namespace-scoped surface instead. Both halves check out in the client code. temporalio/ui's fetchSystemInfo returns {} outright when isCloud, and cloud-ui synthesizes the whole systemInfo object client-side from account feature flags rather than calling the endpoint. Meanwhile cloud-ui already reads namespace.namespaceInfo.capabilities in standalone-nexus-guard.svelte, so NamespaceInfo demonstrably does reach Cloud. Namespace scope is also the correct scope on the merits, not just a way around the proxy. A compute provider is tied to the cloud its Namespace runs in, and cloud-ui already derives the provider list from the Namespace's RegionID_CloudProvider: an AWS Namespace offers Lambda and AgentCore, a GCP Namespace offers Cloud Run. One Service-wide list cannot say that. - NamespaceInfo.compute_providers = 9 - GetSystemInfoResponse otherwise reverted; the note on server_scaled_provider_cloud_run now records why no further per-provider booleans belong there - OpenAPI specs regenerated
api#704 carried a typed ProviderDetailAWSLambda before #752 replaced it with an opaque Payload, so proposing a typed message here invites the obvious objection. The distinction is that provider config is open-ended and genuinely provider-specific, while provider status is uniform across every provider, and the comment now says so where a reviewer reads the message. Specs regenerated.
rossnelson
added a commit
to temporalio/ui
that referenced
this pull request
Sep 9, 2026
…in Cloud Capability gating was the wrong mechanism, and it produced the visible nonsense: a provider selected, disabled, and badged "Coming Soon" at once. Self-hosted has no per-account entitlement to express, and a Service that cannot run a provider rejects the Version with a reason, so gating the picker only hid a choice behind a badge nobody could act on. The default list is now every provider, selectable. Restriction belongs to the caller that has grounds for it. cloud-ui already passes `providers` derived from the Namespace's own cloud, so an AWS Namespace offers Lambda and AgentCore and a GCP Namespace offers Cloud Run. That path is untouched: it always passes the prop, so it never used these defaults. A Version's provider cannot be changed, so the edit form shows only the provider in use rather than alternatives that cannot be applied. The previous commit had this backwards, reading an accepted update-mask path as product behaviour. This also removes the local serverScaledProviderAgentCore augmentation on Capabilities. Nothing gates on it now, so the UI no longer depends on a capability field that does not exist and that temporalio/api#867 no longer proposes.
rossnelson
added a commit
to temporalio/ui
that referenced
this pull request
Sep 9, 2026
…in Cloud Capability gating was the wrong mechanism, and it produced the visible nonsense: a provider selected, disabled, and badged "Coming Soon" at once. Self-hosted has no per-account entitlement to express, and a Service that cannot run a provider rejects the Version with a reason, so gating the picker only hid a choice behind a badge nobody could act on. The default list is now every provider, selectable. Restriction belongs to the caller that has grounds for it. cloud-ui already passes `providers` derived from the Namespace's own cloud, so an AWS Namespace offers Lambda and AgentCore and a GCP Namespace offers Cloud Run. That path is untouched: it always passes the prop, so it never used these defaults. A Version's provider cannot be changed, so the edit form shows only the provider in use rather than alternatives that cannot be applied. The previous commit had this backwards, reading an accepted update-mask path as product behaviour. This also removes the local serverScaledProviderAgentCore augmentation on Capabilities. Nothing gates on it now, so the UI no longer depends on a capability field that does not exist and that temporalio/api#867 no longer proposes.
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.
RFC — not for merge as-is
Proposes one source of truth for which compute providers a Namespace can use, whether each is enabled, and what release stage each is in — replacing the pattern of adding a boolean to
Capabilitiesper provider.Revised after review. The first draft put this on
GetSystemInfoResponse. @Quinn-Klassen pointed out that will not work, because Cloud serves that endpoint as a fixed response at the reverse proxy layer, and suggested a Namespace-scoped surface. That is what this now does, and the objection turned out to be right on the mechanism and on the correct scope. History is kept so the argument is reviewable.The AgentCore work this came out of does not depend on this and is not blocked by it.
The problem with a boolean per provider
server_scaled_provider_cloud_run = 13(#799) works, but it only answers "is this one provider usable, Service-wide". Three costs follow.A new provider needs a full release train before a client can even gate on it. Proto field → api release → server release. Cloud Run is the worked example: the UI shipped the option in temporalio/ui#3518 on 2026-06-12, the capability field landed 2026-07-06, and the UI could not consume it until an api bump on 2026-08-13. Two months, for a boolean.
It carries no release stage, so every client hardcodes its own. temporalio/ui has this table today:
Moving Lambda to GA is a UI release. cloud-ui keeps a parallel list, the CLI keeps one for scaler pairing, and temporal-auto-scaled-workers — the actual authority — keeps the real one.
Service scope cannot express what is actually true. Provider availability varies per Namespace: a provider is tied to the cloud its Namespace runs in. cloud-ui already encodes this, deriving the list from the Namespace's
RegionID_CloudProvider— an AWS Namespace offers Lambda and AgentCore, a GCP Namespace offers Cloud Run. A single Service-wide boolean has no way to say that.All three are live right now:
ServerScaledProviderCloudRun.GetSystemInfoinworkflow_handler.gostops atServerScaledDeployments: true. Two months after the field landed, Cloud Run is still "Coming Soon" on OSS.Proposed shape
on
NamespaceInfo, alongside the existingcapabilitiesandlimits:A Service adds a provider without a proto change. A provider moves from pre-release to GA without a client release. Availability can differ per Namespace. And clients drop their hardcoded tables.
Why NamespaceInfo, concretely
Both halves of Quinn's point check out in the client code:
GetSystemInfodoes not reach Cloud. temporalio/ui'sfetchSystemInforeturns{}outright whenisCloud, and cloud-ui synthesizes the entiresystemInfoobject client-side from account feature flags rather than calling the endpoint. A per-provider boolean there is not a reachable signal.NamespaceInfodoes. cloud-ui already readsnamespace.namespaceInfo?.capabilitiesinstandalone-nexus-guard.svelte.NamespaceInfo.Capabilitiesis a live, actively growing surface — 17 fields, most recentlystandalone_activity_operator_commands.Cloud already has a working model for this, and it is two-key
Worth spelling out, because it decides whether the server populating this is
useful or academic.
NamespaceInfo.capabilitiesis not synthesized by cloud-ui the waysystemInfois. cloud-ui fetches the real namespace from the data plane andreads the capabilities off the wire (
src/lib/services/settings.ts):It then overlays account state on top, with two distinct idioms:
?? truefor capabilities Cloud knows are universally on, so an older dataplane that does not report one is treated as capable. Feature-flag
substitution for a capability still rolling out, with the real read commented
out beneath it and a TODO to swap at GA.
And consumers AND the two keys rather than choosing between them
(
standalone-nexus-guard.svelte):The namespace capability answers is this possible here; the account flag
answers is this account permitted. That is the shape a provider list wants
too, and it already works.
The
standaloneActivityStartDelaylines are also, by hand, exactly thelifecycle this proposal describes: flag first, server-reported value later,
with the swap tracked in a comment.
release_stageis what lets the server say"this is generally available now" instead of a client releasing to delete a
TODO.
Prior art: this package started typed and moved away from it
api#704 introduced
compute.v1on theserverlessbranch in February, and#752 reshaped it when that branch merged to master. Two things did not survive,
and both bear on this proposal.
Per-provider config was typed, then deliberately untyped. #704 carried a
ProviderDetailAWSLambdamessage holding the function ARN and an optional roleARN. Today
ComputeProvider.detailsis an opaquePayload. So a reviewershould ask why this proposes a typed message after the package moved the other
way.
The answer is that those two things are different in kind. Provider config is
open-ended and genuinely provider-specific — a Lambda ARN, a Cloud Run worker
pool, an AgentCore endpoint ARN share no shape, and every new provider would
otherwise cost a proto message. Provider status is uniform: every provider has
an identity, an enabled bit, and a release stage, and no provider needs a field
the others do not. Untyping config was right for the same reason typing status
is: the shape either varies per provider or it does not.
ComputeConfig.task_queueswas removed. #704 declared task queues asname-and-type tuples; main keeps only
task_queue_types. That is why aserverless Version has no task queue until something teaches matching one: the
first invoke starts a Worker, it polls with versioning, and that registration
is the association. Nothing in the current API can state it up front. Not this
proposal's problem, but it is the reason
create-versiontakes no task queue,which surprises everyone who meets it.
Two other deliberate choices
It reports disabled providers too, rather than omitting them. That lets a client distinguish "this Namespace has never heard of AgentCore" from "it knows AgentCore but it is off here" and say so, instead of silently dropping the option. It is the difference between a "Coming Soon" badge and a provider that appears not to exist.
A repeated message rather than more booleans in
NamespaceInfo.Capabilities. Booleans there would inherit the same two problems — a proto field per provider, and no release stage.server_scaled_provider_cloud_runis kept for wire compatibility and marked superseded, with a note recording why no further per-provider booleans belong on that response.Open questions for reviewers
NamespaceInfo, or a dedicated RPC?NamespaceInfois where namespace capability data already lives and already reaches Cloud, so it is the cheap and consistent choice. AListComputeProviderswould be more discoverable and could carry more per-provider detail later.Should
release_stagelive in the api at all? It is arguably product metadata, not a capability. The counter-argument is that it is already in every client, just duplicated and going stale.Where does the value come from? temporal-auto-scaled-workers has the real registry —
iface.ComputeProviderTypeandRegisterComputeProvider. The server should derive this from it rather than maintaining a third list. Worth confirming that is exposable.Does Cloud populate this per Namespace, or keep overlaying account flags in cloud-ui?Resolved: neither is a lift. The data plane populatesNamespaceInfo.capabilitiestoday and Cloud passes it through; saas-control-plane writes no namespace capabilities at all, so nothing new is asked of the control plane. cloud-ui keeps its account-flag overlay during rollout and drops it at GA, which is what it already does forstandaloneActivities. See "Cloud already has a working model" above.Should the enabled bit be per Namespace, or is per Service enough in practice? Availability varies per Namespace because of the cloud a Namespace runs in, which is static. If nothing varies it dynamically, a Service-level list plus the Namespace's cloud provider would also work and be cheaper to populate.
Verification
make http-api-docsrun;openapi/openapiv2.jsonandopenapi/openapiv3.yamlregenerated and committed.make buf-lintclean.make api-linterclean.protoc -I. --descriptor_set_out=/dev/null $(find temporal -name '*.proto')clean across the whole tree.make buf-breakingreports 18 errors, all pre-existing —ExecutionTypeinenums/v1/common.proto,CallbackInfo.request_id, and several Nexus fields. Cleanmainreports the same 18. None involvecompute/v1ornamespace/v1; this change adds no breaking change.Nothing else already does this
Checked before proposing, on current
mainof each repository:NamespaceInfohas no compute, provider, agentcore, or serverless field, and no capability has been added to it since this RFC opened.namespace_handler.gosets five worker-related capabilities and nothing provider-related.isServerlessflag on internal service-account provisioning, andserverless_workeras a Key Vault cert purpose. Cloud has built certs and identity for server-scaled Workers; it has not built a surface for which providers a Namespace may use.What is not done
compute_providersyet. This PR is the contract argument only.Context
compute.v1originated, on theserverlessbranch