Skip to content

feat(tools)!: tool packages call stock routes with the run bearer; delete workflow-* hub mounts (CL-8159) - #764

Merged
TheGreatAxios merged 4 commits into
mainfrom
cl-8159-tools-stock-routes
Sep 17, 2026
Merged

TheGreatAxios merged 4 commits into
mainfrom
cl-8159-tools-stock-routes

Conversation

@TheGreatAxios

Copy link
Copy Markdown
Contributor

Agent tool packages now address stock Interchange tenant routes with the workflow run's own bearer credential, instead of Workbench-specific /api/workflow-* mirrors. CL-8159.

How the run bearer reaches a stock route

Two pieces, both Workbench-owned — no vendored file changed.

Addressing. The hub's signed deploy frame is a HarnessConfig carrying tenantId and principalId. The sidecar now threads both into the workflow child's substrate config as WORKFLOW_TENANT_ID / WORKFLOW_PRINCIPAL_ID (arktype-validated, required, no fallback) and exposes them on the step env beside the existing definitionId, so a tool package can fill the :tenantId segment every stock tenant route carries. The on-disk deployment record carries the pair too, at schema version 2, so a boot-time restore reseeds it; a version-1 record fails the parse and is redeployed rather than restored into a child whose tools cannot name their tenant.

Authorization. @intx/hub-api already resolves a sidecar bearer plus run address into principal + tenant (workflow-run-deploy-auth), but mounts it only on /api/tenants/:tenantId/workflows/deployments. apps/hub now wraps the built app and mounts that same middleware on /api/tenants/:tenantId/*, ahead of the stock resolveTenant — an outer wrap because createApp() has already registered its own middleware by the time it returns. A request with no bearer credential is untouched; the :tenantId segment is never trusted, since the middleware sets the tenant from the authenticated run alone.

Mapping

Tool Was Now
list_principals GET /api/workflow-access/principals GET /api/tenants/:t/principals, all pages
list_grants GET /api/workflow-access/grants GET /api/tenants/:t/grants, all pages
grant_access POST /api/workflow-access/grants (multi-action) ceiling check, then one POST /api/tenants/:t/grants per action
revoke_access DELETE /api/workflow-access/grants/:id GET the grant, ceiling check, DELETE /api/tenants/:t/grants/:id
list_connections GET /api/workflow-connections/connections GET /api/tenants/:t/providers + /credentials, paired with the build's own connector registry
request_connection in-process + the same listing in-process; the connected check uses the stock listing

The delegation ceiling stock POST /grants does not have moves into the tool: it reads the caller's own grants in the caller's own tenant and evaluates each requested resource/action pair with @intx/authz's own evaluateGrants, refusing anything outside the caller's reach before any write. Grants are not inherited across the tenant ancestor tree, so a parent tenant's grant is never read as authority in a child. Credentials and providers are read through the stock routes as-is, with no own-tenant filter added.

Dropped

  • mcp_list_servers is not dropped, but list_connections no longer reports MCP-connected servers. MCP servers are not a stock Interchange concept, so /api/workflow-connections survives only as the mcp:<slug> registry @corbits/mcp-tools resolves a server URL through — GET /connections is deleted. Deleting the whole mount would have broken every @corbits/mcp-tools operation, not just the listing. CL-8164 retires it with the rest of MCP server configuration. A curated preset is still offered as a card and counts as connected when a credential exists under its own id.
  • @corbits/access-tools no longer exports ./routes; packages/access-tools/src/routes.ts and its two test suites are deleted, as is the ConnectionSummary export from @corbits/connections.

Not in this PR

Two of the four mounts need work that does not fit a route swap, each filed as its own issue under CL-8155:

  • CL-8170 — catalog-tools. createWorkflowCatalogRoutes resolves a model chain per request from ResolvedOffering rows, modelPricing rows, and the bench model policy. Stock /offerings returns a different shape, there is no stock pricing route, and the bench model policy (the overCeiling flag) has no stock route at all. That last one needs a ruling: a route, or an accepted loss.
  • CL-8171 — workflow-authoring-tools. deploy maps cleanly onto stock POST /workflows/deployments and is ready to do, but readSource / author / republish read and write git trees whose only stock equivalent is git smart-HTTP on the asset repo, which the sandbox would have to speak. Deleting the mount before that works would delete three operations outright, so both halves land together.

Upstream asks

  • Server-side grant ceiling (recorded on CL-7575). Two fidelity gaps against the tool-side check: role-derived grants are invisible to the stock principal-filtered listing, and the read-then-write is not atomic.
  • Ancestor-aware credential listing. Stock GET /providers includes ancestor tenants; stock GET /credentials does not, so a credential held only by a parent tenant reads as not connected.

https://claude.ai/code/session_01RweuMUskU2nvcFhdckc7EG

The hub's signed deploy frame already carries the run's tenantId and
principalId (HarnessConfig). The sidecar now threads both through the
child's substrate config as WORKFLOW_TENANT_ID / WORKFLOW_PRINCIPAL_ID
and exposes them on the step env, so an agent tool package can address
stock /api/tenants/:tenantId/* routes instead of a Workbench-specific
mirror. The on-disk deployment record carries the pair too (version 2),
so a boot-time restore reseeds it rather than standing up a child whose
tools cannot name their tenant.

The hub widens @intx/hub-api's existing run-bearer middleware from the
single deploy endpoint to the whole tenant subtree, as an outer wrap of
the built app. A request with no bearer credential is untouched.

Claude-Session: https://claude.ai/code/session_01RweuMUskU2nvcFhdckc7EG
…un bearer

list_principals, list_grants, grant_access and revoke_access now speak the
stock Interchange tenant routes with the workflow run's own bearer
credential, instead of a Workbench-specific /api/workflow-access mirror.
That mirror, its hub mount and its tests are deleted.

The delegation ceiling the mirror enforced moves into the tool: before it
grants or revokes, it reads the caller's own grants in the caller's own
tenant and evaluates each requested resource/action pair with @intx/authz's
own engine, refusing anything outside the caller's authority. Grants are not
inherited across the tenant ancestor tree, so a parent tenant's grant is
never read as authority in a child.

Two fidelity gaps against a server-side ceiling remain, tracked as upstream
asks on CL-7575: role-derived grants are invisible to the stock
principal-filtered listing, and the read-then-write is not atomic.

Claude-Session: https://claude.ai/code/session_01RweuMUskU2nvcFhdckc7EG
list_connections now answers from the stock Interchange provider and
credential routes, read with the workflow run's own bearer credential: a
connector is connected when the tenant holds an active credential against a
provider named for it, the same keying an agent launch resolves a credential
by. The tool pairs that answer with the deploying build's own connector
registry for display names and links, which the hub does not know.

The /api/workflow-connections/connections route is deleted along with its
tests. The mount survives only as the MCP-server registry @corbits/mcp-tools
resolves an mcp:<slug> server through; MCP servers are not a stock
Interchange concept, so CL-8164 retires it with the rest of MCP server
configuration. Until then list_connections no longer reports MCP-connected
servers: a curated preset is still offered as a card, and counts as
connected when a credential exists under its own id.

Known gap, recorded upstream: the stock provider listing includes ancestor
tenants but the stock credential listing does not, so a credential held only
by a parent tenant reads as not connected.

Claude-Session: https://claude.ai/code/session_01RweuMUskU2nvcFhdckc7EG
@linear-code

linear-code Bot commented Sep 17, 2026

Copy link
Copy Markdown

CL-8159

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