Skip to content

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
wloo/website-notes-examplefrom
wloo/website-gateway-auth
Open

williamloo-scale wants to merge 15 commits into
wloo/website-notes-examplefrom
wloo/website-gateway-auth

Conversation

@williamloo-scale

@williamloo-scale williamloo-scale commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

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.

  • Role key, opt-in per deploy. A deploy with keyed_roles (deploy_env's keyed_roles: true, or env 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 by role_headers(role)). Its gateway believes a role other than default, on REST and MCP, only beside AgentEnv-Role-Key equal 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 /step calls 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 for keyed_roles of an env whose provider doesn't take them is refused at save.
  • Forwarded headers. /svc and /website set X-Forwarded-Host, -Proto and -Prefix and drop the client's copies. The SDK takes the prefix as the root path, so base_url, request.url and redirects name the gateway's address.
  • Triggers by route. A website trigger's when may name op, 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 declares website_op, so an older gateway is refused.

Before merging

  • On a keyed_roles deploy, a caller that sends a non-default role without role_headers(role) is default; 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.
  • An agent deployed with a role receives the key, so the key protects against pages and browsers, not a hostile agent.
  • The key is stored in plaintext on the instance record, beside the control token.
  • The CLI's default role cli is default on 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

RetriggerView in GreptileConfidence 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.

  • Keyed deployments let the gateway verify role claims before passing them to servers.
  • Proxied websites use the gateway address when they build URLs and redirects.
  • Website triggers can match the named route a request reaches.

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: Response
Loading

Reviews (3) · Last reviewed commit: "Merge wloo/website-notes-example into wl..." · Reviewed by Greptile

williamloo-scale and others added 4 commits November 2, 2026 10:32
…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>
williamloo-scale and others added 3 commits November 2, 2026 14:22
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 williamloo-scale changed the title website: gateway role key, forwarded headers, triggers by route (FD-4683) feat(gateway): a keyed deploy's role key, forwarded headers, and triggers by route (FD-4683) Nov 3, 2026
@williamloo-scale
williamloo-scale marked this pull request as ready for review November 3, 2026 00:24
@williamloo-scale
williamloo-scale requested a review from a team as a code owner November 3, 2026 00:24
Comment thread src/agent_env/env/gateway/rest_proxy.py Resolved
Comment thread src/agent_env/env/gateway/gateway.py Resolved
williamloo-scale and others added 6 commits November 2, 2026 16:33
# 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>
Comment thread src/agent_env/env/gateway/gateway.py Resolved
williamloo-scale and others added 2 commits November 2, 2026 17:50
…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

No deployments
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.

1 participant