Repository navigation
feat(gateway): a keyed deploy's role key, forwarded headers, and triggers by route (FD-4683) - #443
Open
williamloo-scale wants to merge 15 commits into
Open
williamloo-scale wants to merge 15 commits into
williamloo-scale wants to merge 15 commits into
Conversation
…le key (FD-4683) A gateway deploy now draws a secret role key, gives it to the gateway (AGENTENV_ROLE_KEY) and to each website server (WEBSITE_ROLE_KEY), and records it on the deployed env. The gateway attributes a request to a role other than default, on REST and MCP, only when it carries AgentEnv-Role-Key equal to the key; it strips the client's key and sends its own upstream only beside a verified role, so SDK sites believe it. A gateway with no key keeps believing the header alone. deploy_agent registrations, the in-process episode proxy, post-turn hook /step calls and the generated MCP CLI send the key. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…at (FD-4683) The /svc and /website proxy now sets X-Forwarded-Host (a front's own, else the client's Host), X-Forwarded-Proto (a front's own http/https, else the request scheme) and X-Forwarded-Prefix (the /svc/<name> or /website/<name> route prefix it strips), dropping the client's copies. The website SDK takes the prefix as the scope's root_path, so base_url, request.url and the trailing-slash redirect's Location name the gateway's address, while routing and the static fallback still see the site's own path. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…(FD-4683) A website trigger's `when` may name `op`, a route of the site's website/v1 card, and then matches only requests that reach it: the gateway reads each site's routes from its card (when it composes the env card, and at a registration naming an op), resolves a request's method and path against their Starlette paths, and hands the name to the engine. Registration refuses an op the card does not name; the http_request trajectory event records the op; triggers/v1 declares `website_op` so register_env_triggers refuses an older gateway that would ignore it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… (FD-4683) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The role key is now opt-in per deploy: deploy_env's keyed_roles (and env deploy --keyed-roles) asks the gateway provider to draw one, and MultiEnv, MCPServerEnv and WebsiteEnv pass it on only when set. Any other deploy draws none, so its gateway believes the AgentEnv-Role header alone, as before the key, and callers that send no key (the hub, agentenvhub-sdk, tests) keep acting as their roles. A step with keyed_roles is refused at save by a provider that doesn't take it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
# Conflicts: # src/agent_env/env/gateway/gateway.py
williamloo-scale
marked this pull request as ready for review
November 3, 2026 00:24
# Conflicts: # src/agent_env/env/gateway/constants.py # src/agent_env/providers/env_providers/env_gateway_provider.py
… nested paths reach their trigger (FD-4683)
ApiRoute.advertised() stripped Starlette converters, so /files/{rest:path} was
advertised as /files/{rest} and the gateway matched one segment only. The card
now carries the served path; the OpenAPI document keeps the template form.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… (FD-4683) Reading the routes of a register body's website triggers collected when.app into a set before checking it was a string, so an unhashable app raised TypeError. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…4683) The agent's process-wide AGENT_ENV_ROLE_KEY was set only when every bound deployment shared one key, so an agent bound to two keyed deployments had none and its CLIs acted as default. Installing a CLI now writes its deployment's key to the CLI's bin/.env beside the gateway URL, and deploy_agent no longer sets it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…4683) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…s server receives (FD-4683) An encoded slash beside a dot segment (public%2Fx/../api/notes) routes here as one path and reaches the site's server, once httpx removes the dot segments, as another. site_request_path computes the path the server receives, reserved_site_request uses it, and so now do the route a request is named by and the path fed to the trigger engine. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This branch has not been deployed
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.
Stacked on #429.
The gateway side of website/v1: roles that can't be spoofed, the address a site is reached at, and triggers matched by route.
keyed_roles(deploy_env'skeyed_roles: true, orenv deploy --keyed-roles) draws a secret role key, gives it to the gateway (AGENTENV_ROLE_KEY) and to each website server (WEBSITE_ROLE_KEY), and records it on the deployed env (DeployedEnv.role_key, sent byrole_headers(role)). Its gateway believes a role other thandefault, on REST and MCP, only besideAgentEnv-Role-Keyequal to the key. It strips a client's key and sends its own upstream only beside a verified role, so SDK sites believe it.deploy_agent, the episode proxy, post-turn hook/stepcalls and the generated MCP CLI send the key. Any other deploy draws none, and its gateway believes the role header alone, as before. A step asking forkeyed_rolesof an env whose provider doesn't take them is refused at save./svcand/websitesetX-Forwarded-Host,-Protoand-Prefixand drop the client's copies. The SDK takes the prefix as the root path, sobase_url,request.urland redirects name the gateway's address.whenmay nameop, a route of the site's card, and then matches only requests that reach it. Registration refuses an op the card doesn't name. triggers/v1 declareswebsite_op, so an older gateway is refused.Before merging
keyed_rolesdeploy, a caller that sends a non-defaultrole withoutrole_headers(role)isdefault; agentenvhub-sdk and the hub don't send the key yet, so their tasks shouldn't turn it on until they do. Unkeyed deploys, the default, are unaffected.cliisdefaulton a keyed deploy.Test
Unit suite: 12,712 passed (one GitHub-clone test from main, which hangs on the local machine on main too, deselected).
🤖 Generated with Claude Code


Confidence Score: 5/5
The PR appears safe to merge, with the previous trigger-path issue fixed and no new blocking findings.Summary
Adds opt-in role keys, forwards the site's public address, and lets website triggers match named routes.
Diagram
sequenceDiagram participant Caller participant Gateway participant Website participant Triggers Caller->>Gateway: Request with role and optional key Gateway->>Gateway: Check role key and replace identity headers Gateway->>Website: Forward request and public address Website-->>Gateway: Response Gateway->>Triggers: Match forwarded path to named route Triggers-->>Gateway: Run actions and wait for required barriers Gateway-->>Caller: ResponseReviews (3) · Last reviewed commit: "Merge wloo/website-notes-example into wl..." · Reviewed by Greptile