RFC: make the public framework route prefix configurable (proposal only, no code) - #4482
RFC: make the public framework route prefix configurable (proposal only, no code)#4482multiplehats wants to merge 1 commit into
Conversation
Proposal only. No file changes; the proposal is in the pull request body.
There was a problem hiding this comment.
Builder reviewed your changes — looks good ✅
Review Details
Code Review Summary
PR #4482 is an RFC-only proposal carried by an empty commit: the diff contains 0 changed files and 0 changed lines. There is therefore no implementation, configuration, documentation, or UI code to validate, and no diff location where an actionable review comment could be placed.
The proposal's design questions—separating local serving prefixes from remote federation destinations, validating the prefix, composing it with an app base path, and mapping requests at the boundary—are maintainer decisions for a future implementation rather than reviewable changes in this PR. Both independent review passes found no reportable issues in the empty diff.
Risk assessment: low, because this PR proposes no code or behavior change.
🧪 Browser testing: Skipped — PR only modifies the PR description/empty commit, with no UI impact.
|
thanks @multiplehats, I'm game for a PR that makes this configurable 👍 |
Excellent, thank you. I'll get the "team" on it ✌️ |
This pull request is a proposal. It contains no code. It asks the maintainers for a decision before I write an implementation.
The commit is empty. It changes no files. The proposal is in this description.
1. The problem
Agent-Native serves framework routes under
/_agent-native. This keeps framework routes separate from application routes under/api/*. PR #132 added this namespace. The separation is correct. It must stay the default.Some deployments must serve these endpoints under a different namespace. A gateway or a reverse proxy selects that namespace.
The framework cannot do this today. The prefix
/_agent-nativeis a fixed value in the browser client, the authentication URLs, the server mounts, and the deployment output. A rewrite rule in a proxy changes the incoming request only. It does not change the URLs that the framework generates.2. Evidence from a production application
This application serves framework requests under
/_api. It does this with version-pinnedpnpm patchfiles. The cost is high:@agent-native/core@0.176.5is 9438 lines. The patch for@agent-native/toolkit@0.19.3is 274 lines.pnpm build. The build fails if the package version is not reviewed, or if an old prefix remains. This check is necessary. A missed occurrence causes a broken OAuth callback in production. A test does not find this error./_agent-native/auth/resetis a separate route. A simple text replacement does not find it.I do not ask you to merge this patch. The patch shows that applications need this option. It also shows that the current workaround is unsafe.
3. An important detail: two different prefixes
A prefix setting applies to the local deployment only. The framework must not apply it to the URL of a different installation.
Core contains four requests to other installations. A global text replacement breaks these requests:
mcp-client/hub-client/_agent-native/mcp/hub/serversmcp-client/remote-store/_agent-native/mcp(discovery fallback)mcp/org-directory/_agent-native/org/appsorg/handlers/_agent-native/org/a2a-secret/receiveMy patch tool stores these four lines as exact text. The tool stops if any of these lines changes upstream.
Therefore this feature is not one constant. It is two concepts that use one text value today:
A remote destination keeps its own declared endpoint. A destination that declares no endpoint keeps the current default.
4. Proposed configuration
If you do not set this field, the behavior does not change. The environment variable follows the existing descriptor convention:
AGENT_NATIVE_CONFIG_RUNTIME_FRAMEWORK_ROUTE_PREFIX.Resolution time. The framework resolves the value one time. It resolves it at development startup, or at deployment build. It writes the value into the client output and the server output together. A change to the value needs a restart or a new build. This prevents a condition where the browser and the server use different values.
Validation. The value is one absolute path segment. It contains ASCII letters, digits,
_, and-. It contains a minimum of one letter or digit. The framework rejects these values://api,/mcp, or/.well-knownA simple format keeps URL matching and security checks unambiguous. You can add nested namespaces later as a separate change.
Base path. The prefix and the application base path combine. An application at
/mailwith the prefix/_platformserves/mail/_platform/actions/.... The framework applies the base path one time only.Collisions. At startup or at build, the framework compares the prefix with the existing application routes and the workspace mounts. If a collision occurs, the diagnostic message names both owners.
5. Routing contract
Keep
/_agent-nativeas the internal namespace. Route registration and route discovery do not change. Translate between the public namespace and the internal namespace at the request boundary.Add one shared path module. The module must run in the browser and on the server. The module owns these functions:
/_platformmust not match the path/_platform-extra.Incoming requests
Map the public path to the internal path before route selection, before authentication classification, and before the CSRF check.
The mapping must keep the query, the method, the headers, and the streaming body. It must also keep the original public URL. Origin checks and OAuth validation need the original public URL. The mapping must be idempotent.
Outgoing URLs
Build every outgoing URL with the shared path module. Build it at the point of construction. Do not rewrite response bodies.
These surfaces construct URLs:
A warning about the adapters
framework-request-handlercentralizes plugin mount matching. It is the natural request boundary.However, two other components handle the base path themselves: the Vite development gateway, and the generated worker. In addition, the generated action routes do not use the plugin mount shim. Therefore a successful Vite build does not prove that the deployment adapters work. Each adapter needs its own test.
Do not rewrite HTML, JavaScript, or JSON payloads.
6. Compatibility
If a deployment does not set the field, these items do not change: the URLs, the helper results, the authentication behavior, and the deployment output.
A custom prefix changes URLs that are registered in external systems. These systems include:
A change to this value must not rewrite a stored third-party destination.
Migration aliases are your decision. If you support an alias, then the alias must obey these rules:
If you do not support an alias, then two conditions apply. The internal namespace must not remain reachable from outside. Self-dispatch must use the shared path module.
7. Acceptance criteria
Additional requirements:
.jsonand image files. These must reach the framework handlers. They must not reach the static-file middleware.no-storeheader rules must follow the configured namespace.8. Entry points
I reviewed these files in
packages/core/src/:config.ts·client/api-path.ts·server/framework-request-handler.ts·server/core-routes-plugin.ts·server/auth.ts·server/better-auth-instance.ts·server/google-oauth.ts·client/auth/AuthPage.tsx·server/csrf.ts·server/action-routes.ts·vite/client.ts·deploy/build.ts·deploy/route-discovery.ts·deploy/workspace-deploy.ts·deploy/netlify-static-headers.ts9. Decisions requested
runtime.frameworkRoutePrefixthe correct location for the setting?I have a working implementation today. It uses the patches described in section 2. It includes the peer-endpoint exclusions from section 3 and the build-time check. Give me a decision on items 1 to 3, and I will open the implementation pull request in the form that you select.
Note: the commit is empty on purpose. It changes no files. This puts the proposal in the pull request list, because the issue tracker receives a high volume of automated reports. The empty commit starts a CI run. The tree is identical to
main. Tell me if you prefer proposals in a different location, and I will move it.