Repository navigation
Add @corbits/access-tools: principals and scoped grants for agents (CL-7467) - #656
Merged
Merged
Conversation
Covers the tool bundle's client (auth headers, response parsing, 403/404 mapped to typed errors) and the tool handlers themselves (declarations, approval gating, invalid input short-circuiting fetch, a forbidden grant surfacing as a clear tool error, and a successful grant round-tripping through list_grants).
Lets an agent see who exists in its tenant and, once a human approves, create or revoke scoped grants for the specialist agents it stands up. list_principals and list_grants are plain reads; grant_access and revoke_access declare approval: "ask" so a human signs off on each grant before it takes effect. The package's own routes.ts adds a workflow-run-authenticated mirror of the tenant's principal/grant surface at /api/workflow-access, gated by Interchange's real grant store and condition registry -- the same authorization every session-authenticated route runs, not a bundle-local approximation. Mounted in apps/hub alongside the other workflow-run surfaces, threaded a hub URL by the sidecar's step-env builder, and published into the corbits-tools registry. Extends SEED_GRANTS with the principal/grant reads and grant create/manage a seeded principal needs to actually use this surface.
One paragraph per tool, plus a pointer to the workflow-run-authenticated routes they call.
Run scripts/generate-tsconfig-references.ts so the composite build graph picks up the new package's dependencies -- required for tsc --build and the tsconfig-references structural check.
…local # Conflicts: # bun.lock
Covers the caller-authenticated /principals and /grants routes: 401 without a recognized sidecar bearer token, 403 when the caller lacks grant:*/create, 403 when a requested (resource, action) pair exceeds the caller's own authority, 201 within the ceiling, revoke 403 outside the ceiling, and 404 for an unknown grant.
A caller holding grant:*/create could previously mint any (resource, action) pair, including ones its own principal was never granted -- self-escalation. POST /grants and DELETE /grants/:grantId now check each requested pair against the caller's own authority through the same @intx/authz authorize() call createRequireGrant uses, and refuse with a 403 naming the first disallowed pair.
The four access-tools grants (principal:*/read, grant:*/read+create+ manage) were landing in SEED_GRANTS, which reconcileSeedGrants plants on every tenant's shared principal -- letting that principal read and mint grants for every principal in the tenant, not just the specialist agents Myra stands up herself. Plant them instead on the assistant workflow's own run principal, derived the same deterministic way @intx/hub-api's workflow-run- trigger mints one on a deployment's first materialized trigger (deriveRunPrincipalId(tenantId, deploymentId)). That id is stable across a relaunch because it's keyed to the deployment's own top-level run id, not to a fresh per-invocation run. Only planted once confirmDeploymentAnswers has actually triggered the assistant deployment, since that trigger is what commits the run-principal row these grants reference.
The merge from main picked up a v2-format lockfile from a newer local bun than the .bun-version this repo pins (1.3.14) can read, so CI's frozen-lockfile install failed before any check ran. Regenerating with 1.3.14 restores the v1 format CI expects.
check:structural's db-gate check flagged the hand-rolled describeIfDb; switched to the shared dbGate helper. That import reaches scripts/e2e/db-gate.ts, which the tsconfig-references generator treats as disqualifying for a package's composite src/ project when the import comes from src/ itself -- moving the DB-gated composition suite to test/ (where multi-module suites already live per this repo's convention) keeps access-tools in the composite graph.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Myra (the assistant workflow) can create new specialist agents, but had
no way to give them scoped access --
reconcileSeedGrantswas the onlygrant writer. This adds
@corbits/access-tools, a tool package with:list_principals-- list principals (users, agents, workflow runs) inthe calling agent's tenant.
list_grants-- list grants, optionally filtered byprincipalIdorresource.grant_access(approval: "ask") -- grant a principal one or moreactions on a resource; a human approves each grant before it lands.
revoke_access(approval: "ask") -- revoke a grant by id.The package's own
routes.tsadds a workflow-run-authenticated mirrorof the tenant's principal/grant surface at
/api/workflow-access,authorized through Interchange's real grant store and condition
registry (
createRequireGrant/@intx/authz) -- the same check everysession-authenticated route runs, not a bundle-local approximation.
Delegation ceiling (review fix)
POST /grantsandDELETE /grants/:grantIdpreviously only checkedthat the caller held
grant:*/create(ormanage), never that therequested
(resource, action)pairs were within what the caller's ownprincipal already holds -- a self-escalation hole. Both routes now call
@intx/authz'sauthorize()per requested pair, against the SAMEgrant store
createRequireGrant's middleware checks, and refuse with a403 naming the first disallowed pair. Revoke applies the identical
ceiling to the grant's own
(resource, action)before deleting it.Myra-only grant scoping (review fix)
The four access-tools grants (
principal:*read,grant:*read/create/manage) previously lived in
SEED_GRANTS, whichreconcileSeedGrantsplants on every tenant's shared principal --letting that principal read and mint grants for every principal in the
tenant, not just the specialist agents Myra stands up herself. They are
now planted only on the assistant workflow's own run principal, via
plantAssistantAccessToolsGrantsinpackages/seeding/src/seed.ts.That principal id is derived with
deriveRunPrincipalId(tenantId, deploymentId)-- the same deterministic derivation@intx/hub-api'sworkflow-run-triggeruses to mint a run principalon a deployment's first materialized trigger. It stays stable across a
relaunch because it's keyed to the deployment's own top-level run id,
not to a fresh per-invocation run id. The grants are planted only after
confirmDeploymentAnswershas actually triggered the assistantdeployment (
confirmDeployments: true), since that trigger is whatcommits the run-principal row these grants reference; the connect
flow's
confirmDeployments: falsepath defers this to the tenant'snext
workbench seedrun.Wired in:
apps/hub/src/index.ts-- mounts the routes, reusing the samechatGrantStore/chatConditionRegistryevery other extension checksagainst.
apps/sidecar/src/workflow-substrate-factory/step-env.ts-- threadshubAccessUrlonto the step env, same hub origin as every othermanager-tools bundle.
packages/tool-registry-publish/src/registry.ts-- publishes thepackage into the
corbits-toolsregistry.packages/seeding/src/seed.ts-- plants the four access-tools grantson Myra's own run principal instead of
SEED_GRANTS.No vendor edits --
@intx/authz,@intx/db, and@intx/hub-api's owncreateRequireGrant/idResource/authorizeexports are consumed aspublished dependencies.
Test plan
bunx tsc --noEmit -p packages/access-toolsbunx tsc --noEmit -p apps/hubbunx tsc --noEmit -p packages/seedingbun test packages/access-tools(91 pass, incl. newroutes.test.tsDB-gated ceiling suite: 401/403/403/201/403/404)bun test packages/seedingbun run check:structural report-error tool-package-pins licensesResolves CL-7467