Skip to content

Name the deployment's own agent addresses as allowed endpoints - #899

Open
A2wkiller wants to merge 1 commit into
CopilotKit:mainfrom
A2wkiller:fix/managed-agent-loopback
Open

A2wkiller wants to merge 1 commit into
CopilotKit:mainfrom
A2wkiller:fix/managed-agent-loopback

Conversation

@A2wkiller

Copy link
Copy Markdown

Fixes #888.

What

A Bot created in the app with no endpoint ("Run it here") is bound to the deployment's managed agent, MANAGED_AGENT_AG_UI_URL. When that address is on loopback - the normal shape of a local deployment - the endpoint check refused it as a private address and every message to the Bot failed with This deployment will not dial http://127.0.0.1:<port>/ag-ui.

The private-network refusal exists for addresses a person types into a form. This address is the deployment's own setting.

Change

agentEndpointAllowedHosts in server/src/config.ts now also names MANAGED_AGENT_AG_UI_URL and an installed PICKED_HARNESS_URL, pinned to the configured port (or the scheme's default when the URL names none).

This goes through the existing namedAsAllowed path, which re-runs the strict check with the private-host floor down, so the naming still only overrules a private-network refusal: the cloud metadata address and anything that is not a web address stay refused regardless.

Tests

Four new cases in server/tests/config.test.ts (loopback managed agent named with its port; scheme-default port pinning; picked harness named the same way; operator entries and the deployment's agent coexist). bun test server/tests/config.test.ts (151 pass), bun test server/tests/agent-endpoint.test.ts (48 pass), biome lint clean.

Prepared with AI assistance (Instinct); tested as noted above.

@davidmckayv davidmckayv left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, #888 is a real bug and the deployment's own agent should be reachable. But this allows more than that agent, so requesting changes.

Adding the managed agent's host:port to agentEndpointAllowedHosts exempts every path on that address from the private-network check. That set applies wherever the check runs: registering a Bot and Test connection, which any signed-in person can use, and every redirect hop at run time (index.ts, allowedHosts). On a deployment whose managed agent sits behind a shared internal gateway (say http://10.0.3.7:80/agent/ag-ui), anyone could register or test a Bot at another service on that host and port. Any outside agent could also redirect a run there.

What we'd take instead:

  • Allow only the exact managed URLs, using the same identity check that decides whether the deployment token is attached (isManagedEndpoint). Other paths on that host and port, and redirects to it, stay refused.
  • Test through checkAgentEndpoint and createAgentFetch, not the contents of the allowed-hosts set.
  • A URL with no port currently pins host:443, which never matches, because namedAsAllowed drops a default port (agents/endpoint.ts).
  • Add PICKED_HARNESS_URL only when PICKED_HARNESS_IMAGE is set, as the comment says, or fix the comment.

A Bot created in the app ("Run it here") is bound to the deployment's
managed agent, MANAGED_AGENT_AG_UI_URL. A local deployment keeps that
agent on loopback, and the endpoint check refused it as a private
address, so every message to every such Bot failed with "will not
dial". The refusal exists for addresses a person types in; this one is
the deployment's own setting.

checkAgentEndpoint and createAgentFetch now accept the deployment's
own agent addresses - the managed Bot's URL, and the picked harness's
only when an image is installed - and allow exactly those URLs, using
the same identity the run path uses to decide whether the deployment
token is attached (isManagedEndpoint). Another path on the same host
and port is not the deployment's agent and stays refused, at
registration, in the connection test, and on every redirect hop. The
exemption only ever overrules a private-network refusal: the metadata
address and non-web addresses stay refused whatever is configured.

Fixes CopilotKit#888
@A2wkiller
A2wkiller force-pushed the fix/managed-agent-loopback branch from f735074 to f87453c Compare October 31, 2026 03:01
@A2wkiller

Copy link
Copy Markdown
Author

Thanks for the detailed review - all four points addressed in the reworked commit:

  1. Exact URLs only. checkAgentEndpoint/createAgentFetch now take the deployment's own agent addresses and allow exactly those URLs, matched by the same managedEndpointIdentity the run path uses to attach the deployment token. Nothing is added to agentEndpointAllowedHosts anymore - another path on the same host:port stays refused at registration, in the connection test, and on redirect hops.
  2. Tested through the check, not the set contents: 9 new cases in agent-endpoint.test.ts (exact-URL allow, trailing-slash identity, other-path/other-port refusal, redirect-hop behavior, metadata floor).
  3. Default port: comparison is on the parsed URL identity now, so a managed URL with no explicit port matches itself - with a regression test on your http://10.0.3.7/agent/ag-ui shape. The host:443 pinning is gone.
  4. PICKED_HARNESS_URL: sourced from config.managedAgent.endpoint/alsoRun, which only includes the harness when PICKED_HARNESS_IMAGE is set.

One judgment call to flag: for redirects I read "redirects to it stay refused" as referring to the host:port, so a hop landing on the exact managed URL is followed (it is the deployment's own agent, same rule as registration) while a hop to any other path on that host:port is refused. There's a test for each direction; happy to flip it if you want hops refused outright.

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.

Bots made with "Run it here" can't run when the managed agent is on loopback ("will not dial")

2 participants