Problem (follow-up to closed #616)
PR for #616 shipped Option A — the static serve(public_url=...) kwarg — and the issue closed COMPLETED. Option B (trust_forwarded_headers for per-request resolution) was explicitly deferred per the dx-expert review.
This is fine for single-host deployments. For multi-tenant subdomain deployments, where every tenant has its own public host, the static kwarg can only advertise one URL. Adopters still need a per-request rewrite to surface the correct tenant host in /.well-known/agent-card.json.
Concrete adopter scenario
bokelley/salesagent runs multi-tenant on subdomains: acme.scope3.com, beta.scope3.com, etc. Each tenant's buyer agent fetches the agent card from its own host. A static public_url=https://scope3.com/ would advertise the wrong host to every non-apex tenant.
Today we work around this with a 190-LOC ASGI middleware (core/middleware/agent_card_public_url.py) that buffers the /.well-known/agent-card.json response and rewrites localhost URLs from X-Forwarded-Host per request. It composes cleanly with the new static kwarg (only rewrites loopback, so it no-ops when public_url is set) but it's adopter-side code maintaining an SDK-shaped behaviour.
Proposed shape
Two options, both addressing the per-request case the deferral explicitly called out:
A) serve(trust_forwarded_headers=True) — when set, the agent-card route honours X-Forwarded-Host and X-Forwarded-Proto at response-build time. Defaults to False so unconfigured behaviour is unchanged. Concerns from the original deferral:
- Trust boundary: gate via the kwarg — adopters explicitly opt in only when they know they're behind a trusted proxy. Matches how Flask / FastAPI / starlette handle the same headers.
- Thread-safety on shared
pb.AgentCard: build the response card per-request rather than once at server-init. The card payload is small, the marginal CPU is negligible vs. the network IO.
B) serve(public_url=Callable[[Request], str]) — accept a callable in addition to a static string. Adopters provide the resolution logic themselves. More general, less opinionated about trust boundaries.
Either closes the multi-tenant subdomain gap. A is the cleaner zero-config option for the common case (most multi-tenant adopters have a reverse proxy that sends X-Forwarded-Host); B keeps the SDK out of the trust-policy business at the cost of every adopter writing the resolver.
Why a new issue vs reopening #616
#616 closed COMPLETED — fine, the static kwarg is real and useful. Re-opening it would muddy the changelog. This is a separate ask that depends on the closed work landing first.
Source
Our deferral entry in bokelley/salesagent core/SDK_FEEDBACK.md: "make the agent-card URL X-Forwarded-Host-aware when no static public_url is configured, OR accept a Callable[[Request], str] for per-request resolution."
Local tracker: bokelley/salesagent#103.
Related
Problem (follow-up to closed #616)
PR for #616 shipped Option A — the static
serve(public_url=...)kwarg — and the issue closed COMPLETED. Option B (trust_forwarded_headersfor per-request resolution) was explicitly deferred per the dx-expert review.This is fine for single-host deployments. For multi-tenant subdomain deployments, where every tenant has its own public host, the static kwarg can only advertise one URL. Adopters still need a per-request rewrite to surface the correct tenant host in
/.well-known/agent-card.json.Concrete adopter scenario
bokelley/salesagent runs multi-tenant on subdomains:
acme.scope3.com,beta.scope3.com, etc. Each tenant's buyer agent fetches the agent card from its own host. A staticpublic_url=https://scope3.com/would advertise the wrong host to every non-apex tenant.Today we work around this with a 190-LOC ASGI middleware (core/middleware/agent_card_public_url.py) that buffers the
/.well-known/agent-card.jsonresponse and rewrites localhost URLs fromX-Forwarded-Hostper request. It composes cleanly with the new static kwarg (only rewrites loopback, so it no-ops whenpublic_urlis set) but it's adopter-side code maintaining an SDK-shaped behaviour.Proposed shape
Two options, both addressing the per-request case the deferral explicitly called out:
A)
serve(trust_forwarded_headers=True)— when set, the agent-card route honoursX-Forwarded-HostandX-Forwarded-Protoat response-build time. Defaults toFalseso unconfigured behaviour is unchanged. Concerns from the original deferral:pb.AgentCard: build the response card per-request rather than once at server-init. The card payload is small, the marginal CPU is negligible vs. the network IO.B)
serve(public_url=Callable[[Request], str])— accept a callable in addition to a static string. Adopters provide the resolution logic themselves. More general, less opinionated about trust boundaries.Either closes the multi-tenant subdomain gap. A is the cleaner zero-config option for the common case (most multi-tenant adopters have a reverse proxy that sends
X-Forwarded-Host); B keeps the SDK out of the trust-policy business at the cost of every adopter writing the resolver.Why a new issue vs reopening #616
#616 closed COMPLETED — fine, the static kwarg is real and useful. Re-opening it would muddy the changelog. This is a separate ask that depends on the closed work landing first.
Source
Our deferral entry in bokelley/salesagent core/SDK_FEEDBACK.md: "make the agent-card URL
X-Forwarded-Host-aware when no staticpublic_urlis configured, OR accept aCallable[[Request], str]for per-request resolution."Local tracker: bokelley/salesagent#103.
Related