fix(egress): a backend is defined in two maps, and the canary only had one

First canary run failed: phase failed, nothing delivered, and the streamed log
said exactly why — "Failed to authenticate. API Error: 403 api.anthropic.com is
not on the egress allow-list".

Not a 2.1.226 regression. `canary-claude` was added to the server's credential
map and not to the node's `provider_hosts`, so the VM booted with a valid
subscription token and a door that only opened onto the forge. The fail-closed
branch was working correctly: a backend nobody taught that function about
reaches no model API, deliberately, so it cannot silently borrow another
provider's door.

Both maps now name it, each pointing at the other, with a test asserting the
canary reaches the same provider as `claude` AND that unknown backends still
resolve to nothing.

Worth noting what made this a five-second diagnosis instead of an afternoon: the
live log streaming built earlier today. The failure was a 403 inside a microVM
that no longer exists, and its reason was sitting in the run's checkpoint.
This commit is contained in:
Omar Sobh
2026-08-07 23:42:58 -07:00
parent 4193ae2cda
commit 099a716bfd
2 changed files with 39 additions and 2 deletions
+29 -1
View File
@@ -62,7 +62,15 @@ const COMMON_ALLOW: &[&str] = &["git.redclaw.dev"];
/// proxy's log of which host it dialled does.
fn provider_hosts(backend: Option<&str>) -> &'static [&'static str] {
match backend {
None | Some("") | Some("default") | Some("claude") => {
// `canary-claude` is the same provider, from a candidate CLI image —
// see `mission_runtime::microvm_credential_for`, which must grant it the
// same credential. A backend is defined in TWO maps: the credential one
// on the server and this one on the node. Adding it to only the first is
// exactly what happened here: the mission launched, the VM booted, the
// agent ran, and the turn died on
// "403 api.anthropic.com is not on the egress allow-list" — which is the
// fail-closed branch below working correctly.
None | Some("") | Some("default") | Some("claude") | Some("canary-claude") => {
&["api.anthropic.com", ".anthropic.com"]
}
Some("glm") => &["api.z.ai"],
@@ -348,6 +356,26 @@ pub fn start(
#[cfg(test)]
mod tests {
/// A backend is defined in TWO places — the server's credential map and this
/// egress map — and granting it one without the other produces a mission
/// that launches, boots, runs, and dies on a 403 from our own proxy.
///
/// Measured exactly that way: `canary-claude` was credentialed on the server
/// and unknown here, and the turn failed with
/// "api.anthropic.com is not on the egress allow-list".
#[test]
fn the_canary_backend_reaches_the_same_provider_as_claude() {
assert_eq!(
provider_hosts(Some("canary-claude")),
provider_hosts(Some("claude")),
"a canary of the Claude image must reach Anthropic, or it tests nothing"
);
// And the fail-closed branch must still hold for anything unknown: this
// is what stops a new backend silently borrowing another provider's door.
assert!(provider_hosts(Some("canary-something-else")).is_empty());
assert!(provider_hosts(Some("definitely-not-built")).is_empty());
}
use super::*;
fn allow() -> Vec<String> {