fix(versa_bedrock): Versa Bedrock and the public Amazon Bedrock card stop steering each other - #248
Merged
Broccolito merged 7 commits intoSep 11, 2026
Conversation
These rows FAIL at this commit, on purpose: they assert the routing the next commits establish, and this is the code they were run against. Versa Bedrock (Private, UCSF) and the public Amazon Bedrock card shared the AWS_* namespace, and it went wrong in both directions: - The public card's AWS_REGION, and an AWS_ENDPOINT_URL_BEDROCK in config or the process environment, became Versa's region and endpoint. The instance turned Public and signed with UCSF-issued keys for someone's AWS region. - AWS_BEARER_TOKEN_BEDROCK in the environment (the AWS SDK's own variable for a Bedrock API key) made the SDK authenticate Versa's requests with that bearer token instead of signing them, so a Versa chat that looked entirely right (UCSF gateway, us-west-2, Private) sent the public card's API key to UCSF, and Versa's own keys signed nothing. - The AWS_ENDPOINT_URL_BEDROCK Versa's setup persisted became the PUBLIC provider's endpoint once bedrock.rs exported and promoted it, so the user's own AWS-signed requests went to UCSF's gateway. - Versa declared AWS_REGION, which the public card declares too, so a Versa setup marked that card Configured; the onboarding card wrote both AWS keys. Each provider is built through from_env and only its HTTP transport is then swapped for the SDK's capture client, so every assertion is on the request production would have sent, and nothing leaves the process. Environment rows run in a re-executed child process that starts with the scenario's environment and its own config root, because set_var in the test binary is unsound and the public provider's own set_var is part of what is measured.
…espace Versa Bedrock read the public Amazon Bedrock card's AWS_REGION and an AWS_ENDPOINT_URL_BEDROCK key, then fell back to AWS_ENDPOINT_URL_BEDROCK and AWS_ENDPOINT_URL_BEDROCK_RUNTIME in the process environment. bedrock.rs exports every AWS_* config value into that environment, and a shell holds them for the AWS CLI, so whatever the public side was set up with steered Versa: UCSF-issued keys signed requests for someone's own AWS region, which refused them, and the instance turned Public. It reads VERSA_BEDROCK_ENDPOINT and VERSA_BEDROCK_REGION now, and nothing else; blank is absent, absent is the UCSF gateway and us-west-2. It also stops declaring the two AWS keys. AWS_REGION is one of the two keys the public card declares, both defaulted, so check_provider_configured called that card Configured as soon as a Versa setup persisted it. Privacy key scan (privacy/config_keys.rs): the scanned count stays 23, with two rows swapped rather than added. AWS_ENDPOINT_URL_BEDROCK and AWS_REGION are deleted, since no tier-input file reads them now, and the reverse check insists on that. VERSA_BEDROCK_ENDPOINT keeps its predecessor's classification, NOT a capability key. The stated reason was false: tier() has followed the resolved endpoint since e2e4eb9, so deleting an off-site value does raise the tier. What holds instead is that only the UCSF gateway's own host reads Private, so no write can forge the badge. Whether that raise should still be a user act is left as an open DR-16 question in the comment. VERSA_BEDROCK_REGION is the SigV4 signing region; the endpoint, not the region, decides where a request goes. The manual versa_stream_wire_probe mirrors from_env and reads the same keys. At this commit the environment rows still fail, on the bearer token alone: renaming the keys does not stop the AWS SDK reading AWS_BEARER_TOKEN_BEDROCK. That is the next commit.
…API key The AWS SDK reads AWS_BEARER_TOKEN_BEDROCK from the process environment by itself. Unless the auth scheme was chosen in code, it then authenticates with that bearer token instead of signing. That variable is where AWS tells a user to put a Bedrock API key, i.e. the PUBLIC card's credential. So a Versa chat that looked entirely right, with the UCSF gateway, us-west-2 and a Private tier, sent the public card's API key to UCSF in its Authorization header, and Versa's own keys signed nothing. Measured through the real from_env with the SDK's capture client: authorization: "Bearer public-bedrock-api-key" (host unified-api.ucsf.edu) Versa's loader now prefers SigV4 explicitly. A preference set on the loader counts as chosen in code (Origin::is_client_config), so the SDK's env branch leaves it alone, and the request is signed with VERSA_BEDROCK_* for the gateway's region. from_resolved is the one constructor, so a restored chat gets the same pin. The endpoint was never exposed this way, and that was checked rather than assumed: an endpoint set on the loader is also chosen in code, so AWS_ENDPOINT_URL_BEDROCK_RUNTIME in the environment does not re-aim the client. nothing_in_the_process_environment_steers_versa pins both.
…he public provider Versa Bedrock's setup persisted AWS_ENDPOINT_URL_BEDROCK pointing at UCSF's gateway: its onboarding card on every connect, its Settings form on every save. bedrock.rs exports every AWS_* config value into the process environment and then promoted AWS_ENDPOINT_URL_BEDROCK to AWS_ENDPOINT_URL_BEDROCK_RUNTIME, the variable the AWS SDK aims Bedrock Runtime at. So once the PUBLIC Amazon Bedrock provider was built on an install that had set up Versa, the user's own AWS-signed requests went to UCSF's gateway: host: "unified-api.ucsf.edu" authorization: "AWS4-HMAC-SHA256 Credential=PUBLICTESTACCESSKEY/.../us-west-2/bedrock/..." The promotion is gone. Commit ac7def8 added it on 2026-04-12, a month before Versa Bedrock existed, for "configs and setup scripts" that used the short name. Every Biorouter surface that has written the key since wrote it for Versa. The previous commits stop new writes, but installs keep the key, so this provider has to ignore it, not merely stop receiving it. The short name is also not Bedrock Runtime's: the SDK derives AWS_ENDPOINT_URL_BEDROCK for the Bedrock control plane. The public provider still follows the SDK's own mechanisms, which is how a VPC endpoint or a proxy is meant to be set: AWS_ENDPOINT_URL_BEDROCK_RUNTIME in the environment, the same key in config.yaml via the export, or an AWS profile's services section. the_public_provider_still_follows_the_sdks_ endpoint_variable is the positive control, and it passes before and after. This also removes one of the unlocked read-then-write set_var sites that docs/testing/process-global-state.md lists.
…S keys The onboarding card upserted AWS_ENDPOINT_URL_BEDROCK and AWS_REGION on every Versa Bedrock connect, with the prefilled UCSF gateway and us-west-2, from an Advanced panel that also offered them for editing. Both belong to the public Amazon Bedrock card: AWS_REGION marked that card Configured and replaced its region, and the public provider took the gateway as its endpoint. The card now writes VERSA_BEDROCK_ENDPOINT and VERSA_BEDROCK_REGION, the only keys versa_bedrock.rs reads, and labels the Advanced fields with those names. The Settings form's versa_bedrock entry in PROVIDER_KEY_DEFAULTS is dead now: a default only reaches a key the provider declares, and versa_bedrock declares its key and secret alone. It is deleted rather than renamed. Persisted values are not touched; the provider no longer reads them.
- privacy-tiers.md 5.1: a dated Bedrock note beside the Azure one. It says what closed, and names the crossing no endpoint check could see: the SDK's own read of AWS_BEARER_TOKEN_BEDROCK put the public card's API key on Versa requests to the gateway. The demotion rule stays, because VERSA_BEDROCK_ENDPOINT is user-writable. - process-global-state.md: the promotion at bedrock.rs:90-92 leaves the table of production env writers (five sites become four, with a note saying what the fifth did), and the ledger gains a row for AWS_BEARER_TOKEN_BEDROCK. That reader lives inside a dependency, which is why no env::var scan in the document could have found it. - providers/mod.rs and tier_tests.rs: the reason given for the demotion named both shared-key hazards as current. The reason that holds now is that each Versa endpoint is user-writable config.
The helper waited for checkProvider, so a write the card makes after it,
such as BIOROUTER_PROVIDER, landed after the assertions ran. A late AWS_*
write would have slipped past the rule the first test states. It now waits
for onSuccess('versa_bedrock'), as #234 does for the Azure flavour, and the
second test pins the exact write sequence: key, secret, the two
VERSA_BEDROCK_* overrides with the shipped defaults, then the provider.
Run against the pre-fix card (778381d4), both fail, and the diff shows what
it wrote: the same shipped defaults, under AWS_ENDPOINT_URL_BEDROCK and
AWS_REGION.
Broccolito
merged commit Sep 11, 2026
607baf3
into
fix/versa-azure-no-public-azure-deployment
16 checks passed
Broccolito
added a commit
that referenced
this pull request
Sep 11, 2026
…e-onboarding-no-deployment-write Brings in the two sibling fixes that landed on the shared base: Versa no longer reads the public Azure OpenAI card's keys (#238), and Versa Bedrock and the public Amazon Bedrock card no longer share config keys (#248). Both conflicts were unions: - DefaultProviderSetupForm.tsx: each side deleted a different dead PROVIDER_KEY_DEFAULTS entry (this branch versa_azure, the base versa_bedrock). Both stay deleted, and the comment beside the table now names both providers: each declares only its credentials. - InstitutionalSetupCard.test.tsx: each side added a helper next to the Azure connect helper (this branch writtenKeys, the base connectVersaBedrock). Both kept; the file now carries the five Azure tests from this branch and the two Bedrock tests from the base.
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.
This is the Bedrock twin of #238. UCSF's private
versa_bedrockand the publicaws_bedrockcard shared theAWS_*config namespace, and each steered the other. The AWS SDK added a crossing of its own that the brief didn't anticipate: the public card's Bedrock API key rode along on Versa requests. After this PR, Versa reads, declares and writes onlyVERSA_BEDROCK_*. It also signs with its own keys whatever the environment holds, and the public provider no longer takes Versa's persisted endpoint.What was wrong
Every row was reproduced through the real
from_env. Only the HTTP transport was swapped for the SDK's own capture client, so each assertion is on the request production would have sent, and nothing left the machine.AWS_REGION, or anAWS_ENDPOINT_URL_BEDROCKin config or the environment (row value:eu-central-1)us-west-2, PrivateAWS_BEARER_TOKEN_BEDROCKin the environment, where AWS tells you to put a Bedrock API keyAuthorization: Bearer <the public card's API key>, and Versa's own keys signed nothing.VERSA_BEDROCK_*,us-west-2AWS_ENDPOINT_URL_BEDROCKVersa's setup persisted (the UCSF gateway)bedrock.rsexported it and promoted it toAWS_ENDPOINT_URL_BEDROCK_RUNTIME, so the public provider sent the user's own AWS-signed requests tounified-api.ucsf.edu, which refused them.bedrock-runtime.<region>.amazonaws.comAWS_REGION, and the onboarding card wrote both keysVERSA_BEDROCK_*The brief's claims, checked
versa_bedrockdeclaredAWS_ENDPOINT_URL_BEDROCK(UCSF default) andAWS_REGION(us-west-2), andaws_bedrockdeclaresAWS_PROFILEandAWS_REGION, both required with defaults.get_paramreads the environment beforeconfig.yaml, so a shellAWS_ENDPOINT_URL_BEDROCKbeat Versa's own persisted value too.AWS_*value and secret was exported withset_var, then promoted.check_provider_configured's branch for a provider whose required keys are all defaulted: any one of them inconfig.yamlmeans Configured.Origin::shared_config(), so I expected the service-specificAWS_ENDPOINT_URL_BEDROCK_RUNTIMEto outrank it. It doesn't, becauseOrigin::is_client_config()counts any programmatic origin. A probe confirmed Versa's requests stayed on the gateway with that variable set, andnothing_in_the_process_environment_steers_versanow pins it.Reproduced before the fix
Commit 1 (
bb988b64) adds the tests alone, on #238's headd122b125. At that commit:And the onboarding card, as it was before commit 5, under the final card tests. They were run with the pre-fix card swapped in. The same shipped defaults are written, under the public keys:
Commits
bb988b64test: the reproduction. It fails on purpose, and it is the evidence above.038b4818Versa's own namespace.from_envreadsVERSA_BEDROCK_ENDPOINTandVERSA_BEDROCK_REGIONand nothing else. Blank is absent, and absent is the shipped default. The metadata declares only the key and secret, which is what its description already said. This commit also updates the key scan and the manualversa_stream_wire_probe. After it, the environment rows still fail, on the bearer token alone: renaming keys cannot stop the SDK readingAWS_BEARER_TOKEN_BEDROCK.1bf44605SigV4 chosen in code..auth_scheme_preference(["sigv4".into()])on Versa's loader. A preference set there counts as client config, so the SDK's env branch leaves it alone.from_resolvedis the one constructor, so restored chats get it too.f9f16e78The public provider. TheAWS_ENDPOINT_URL_BEDROCK→_RUNTIMEpromotion is gone. It was added on 2026-04-12 (ac7def8c), a month before Versa Bedrock existed, and every Biorouter surface that has written the key since wrote it for Versa. This also removes one of the unlocked env writersprocess-global-state.mdlists.981b88fbDesktop. The onboarding card writesVERSA_BEDROCK_ENDPOINT/VERSA_BEDROCK_REGION, and the Settings form'sversa_bedrockdefault entry is deleted. That entry is dead: a default reaches only a declared key.35f69b2aDocs. A dated note inprivacy-tiers.md§5.1, theprocess-global-state.mdledger, and the demotion comments inproviders/mod.rs/tier_tests.rs.50a55ed4test: the card tests wait foronSuccess(as fix(desktop): the Versa Azure setup card no longer writes or offers a deployment #234 does), so a late write cannot slip past them, and pin the exact write sequence.How the tests measure
with_config_overridescannot reach, andset_varin the test binary is unsound. The public provider's ownset_varis part of what is measured. So those rows re-execute the test binary, and the child starts with the scenario's environment, aslisting_workflows_survives_a_deleted_working_directorydoes. The child is hermetic:BIOROUTER_PATH_ROOTandconfig.yaml;BIOROUTER_DISABLE_KEYRING=true;AWS_*/VERSA_*/BEDROCK_*variable removed;aws-providersis a default feature, socargo test -p biorouter --libruns these rows. There are 7 new tests: the lib count goes from fix(versa_azure): the public Azure OpenAI card's keys no longer steer Versa #238's 3807 to 3814.Migration notes
What Versa's setup surfaces ever wrote, from git history:
Onboarding card (
InstitutionalSetupCard.tsx), from its creation on 2026-05-30 (b4f3d85a) until this PR. It prefilledAWS_ENDPOINT_URL_BEDROCK=https://unified-api.ucsf.edu/general/awsaiandAWS_REGION=us-west-2, and upserted both on every connect. Both were editable under Advanced.Settings form (
DefaultProviderSetupForm.tsx):b657e140):AWS_PROFILE=defaultandAWS_REGION=us-west-2.15084269): the gateway inAWS_ENDPOINT_URL_BEDROCKandus-west-2, matching the declared backend defaults.⚠ The form shows the stored value when there is one. So on an install where the public card had set
AWS_REGION, Versa's form displayed that region and saved it back.biorouter configure: an optional key is written only when the user picks it, prefilled with the stored value or the default.Before v1.75.2 (2026-05-12), no surface wrote the endpoint. It worked only if
config.yamlalready heldAWS_ENDPOINT_URL_BEDROCK(see the v1.75.2 notes).The shipped defaults never changed. The region has been
us-west-2since 2026-05-07, and the endpoint constant has been the gateway since 2026-05-12.Consequences:
VERSA_BEDROCK_ENDPOINT/VERSA_BEDROCK_REGION(inconfig.yaml, the environment, or the onboarding card's Advanced panel). The Settings form no longer offers these two fields, the same asversa_azuresince 2026-09-03.AWS_ENDPOINT_URL_BEDROCK, in config or the environment, must useAWS_ENDPOINT_URL_BEDROCK_RUNTIME, the SDK's own name for this service. The short name is what the SDK derives for the Bedrock control plane. A setup from before Versa existed that pointed the public card at UCSF's gateway will now reach real AWS and fail; it should use Versa Bedrock instead.choosing-a-model-provider.mdstill sends UCSF readers to the public card, and that is already an open item inopen-documentation-issues.md.AWS_ENDPOINT_URL_BEDROCKis now inert for both providers.AWS_REGIONis the public card's own key, and so isAWS_PROFILE, which Versa's Settings form wrote from 2026-05-07 to 05-12. Nothing distinguishes Versa's old write from the public card's own, so on installs Versa's surfaces touched, the public card keeps its Configured check until removed. A possible follow-up fingerprint: Versa's onboarding card wroteAWS_REGIONwithoutAWS_PROFILE, while the public card's form always writes both.from_env. The SigV4 pin applies to restored chats.Privacy key scan (
privacy/config_keys.rs)AWS_ENDPOINT_URL_BEDROCKandAWS_REGIONare deleted: no tier-input file reads them any more, and fix(versa_azure): the public Azure OpenAI card's keys no longer steer Versa #238's reverse check insists on that. Every key is still a string literal.VERSA_BEDROCK_ENDPOINTkeeps its predecessor's classification, not a capability key. But the reason given was false, as fix(versa_azure): the public Azure OpenAI card's keys no longer steer Versa #238 noted. Sincee2e4eb9d,tier()follows the endpoint, so deleting an off-site value does raise the tier. I rewrote the ⚠ block to state the argument that actually holds: only the UCSF gateway's own host reads Private, so no write can forge the badge. I also recorded as an open DR-16 question whether that truthful raise should still be a user act, as it is forOLLAMA_HOST.VERSA_AZURE_ENDPOINT's row uses the same corrected wording. No classification changed.VERSA_BEDROCK_REGIONis the SigV4 signing region. The endpoint, not the region, decides where a request goes.Merging beside #234
Both PRs descend from #230, and
git merge-treeshows two textual conflicts:DefaultProviderSetupForm.tsx: adjacent deletions of theversa_azureandversa_bedrockentries. Keep both deletions. fix(desktop): the Versa Azure setup card no longer writes or offers a deployment #234's new comment about undeclared keys then coversversa_bedrockas well.InstitutionalSetupCard.test.tsx: fix(desktop): the Versa Azure setup card no longer writes or offers a deployment #234 rewrites the Azure tests. Keep fix(desktop): the Versa Azure setup card no longer writes or offers a deployment #234's version and append this PR'sconnectVersaBedrockhelper and its two tests.InstitutionalSetupCard.tsxitself merges cleanly.Tests
BIOROUTER_DISABLE_KEYRING=true cargo test -p biorouter --lib -- providers::bedrock_namespace_tests providers::versa_bedrock providers::bedrock privacy::config_keys: plusproviders::tier_tests providers::affiliation_tests: 49 passed, 0 failedBIOROUTER_DISABLE_KEYRING=true cargo test -p biorouter --lib: 3814 passed, 0 failed, 2 ignored. That is fix(versa_azure): the public Azure OpenAI card's keys no longer steer Versa #238's 3807 plus the 7 new rows.cd ui/desktop && npx vitest run src/components/onboarding src/components/settings/providers: 9 files, 101 passed.npm run typecheck: clean. Prettier--checkand ESLint on the touched files: clean.BIOROUTER_DISABLE_KEYRING=true cargo test -p biorouter --test versa_stream_wire_probe: 15 passed, 1 ignored (the manual network probe)cargo fmt --all -- --check: clean./scripts/clippy-lint.sh: clean: strict-D warningsover--all-targets, thetoo_many_linesbaseline, and the banned-TLS-crate checkNot changed (noticed, out of scope)
bedrock.rs:79andsagemaker_tgi.rs:52still export everyAWS_*config value and secret into the daemon's environment. Thatset_varis unsound, and it leaks the user's AWS credentials into every subprocess spawned afterwards, including the agent's shell. It is already open inprocess-global-state.md. Versa no longer reads any of it.BEDROCK_*retry and timeout keys are still shared by both providers. They tune retries; they do not route or authenticate.🤖 Generated with Claude Code