Skip to content

feat(auth): an agent changes its own shape only with a grant (ent#164) - #3236

Open
dolho wants to merge 1 commit into
devfrom
feature/ent164-self-change-grants
Open

dolho wants to merge 1 commit into
devfrom
feature/ent164-self-change-grants

Conversation

@dolho

@dolho dolho commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

Summary

Implements the re-scoped abilityai/trinity-enterprise#164 (2026-10-02): grants, not per-action approval. An agent key resolves to its owner (Invariant #8), so until now any agent could reshape any agent of the same owner. ent#596 closed this for skills. This PR extends the same capability-grant seam to the rest of an agent's shape.

Capability Covers Exempt
schedules.manage create / update / delete / enable / disable a schedule, plus its webhook and webhook secret the agent's own schedules (#2996 kept); trigger
instructions.manage CLAUDE.md, AGENTS.md and .claude/** except skills, through the file routes; git/reset-to-main-preserve-state —
agents.manage durable create, delete, deploy-local, systems/deploy, PUT /model, and read-only / resources / timeout / public-channel-model / guardrails spawning or discarding an ephemeral agent (ent#69)

How it behaves:

  • Without the grant: the agent gets a named 403 (*_management_not_permitted).
  • What the 403 says: raise an ask with ask_class: permission-request. Approving that ask does not grant anything; an admin grants the permission in Settings.
  • Audit: every refusal is audited as capability_refused.
  • Not affected: humans and trinity-system are never fenced.
  • Still person-only: autonomy, api-key-setting, capabilities, capacity and rename.
  • Calibrating companions: cannot be granted instructions.manage (422 calibrating_agent, ent#663).

New routes (the backend for ent#756's Settings toggles):

  • GET /api/agents/{name}/capability-grants: owner-level. Returns all four capabilities, held or not, with who granted each and when.
  • PUT /api/agents/{name}/capability-grants/{capability} {granted}: admin and interactive, idempotent, audited.

⚠️ Behaviour change on upgrade

After deploy, no agent holds the three new capabilities. An orchestrating agent that does any of the following gets a 403 until an admin grants the permission:

  • creates durable agents;
  • writes another agent's schedules or instructions;
  • deletes or deploys agents;
  • reconfigures an agent.

Self-scheduling and ghost spawning keep working.

Decisions taken (2026-10-05)

Tests

  • tests/unit/test_ent164_self_change_grants.py (40 tests):
    • fences read off FastAPI's dependant graph;
    • behaviour per capability class;
    • ghost and own-schedule exemptions;
    • instruction-path classification;
    • the create endpoint refusing before anything is created;
    • the calibrating refusal.
  • test_2996_owner_config_person_only.py: real keys through the real get_current_user and grant table.
    • An ungranted agent gets the named refusal.
    • A granted agent changes the stored value.
    • The grant does not open the PERSON-only writes.
    • Grant-route tests: only an interactive admin can grant; a repeat grant is idempotent; both grant and revoke are audited.
  • test_2996_human_only_routes.py: new census class person_or_grant; the GET route is listed agent_callable with a reason.
  • Mutation battery: I broke the schedules fence, the instructions gate, the calibrating check, the own-agent exemption, the ghost-create exemption and the create-gate call site in turn. Each mutation turned the suite red.
  • Broad related run (169 unit files): green except test_ent679_*. Those fail identically with this change stashed (ModuleNotFoundError: payments_py.a2a.inband, local venv only).

Not in this PR

  • ent#756: the Settings toggles UI.
  • ent#753: the approval map.
  • Known limit: the fence covers platform routes. An agent can still edit its own container's files with its own tools.

Related to abilityai/trinity-enterprise#164

🤖 Generated with Claude Code

Re-scoped 2026-10-02: grants, not per-action approval. Three capabilities
join skills.manage (ent#596) in the closed set:

- schedules.manage: schedule create/update/delete/enable/disable and the
  webhook routes on ANOTHER agent. An agent's own schedules stay free
  (#2996); trigger is not fenced.
- instructions.manage: CLAUDE.md, AGENTS.md and .claude/** except skills
  through the file routes, plus git reset-to-main-preserve-state. Not
  grantable to a calibrating companion (ent#663).
- agents.manage: durable create, delete, deploy-local, systems/deploy,
  the chat model, and the read-only/resources/timeout/
  public-channel-model/guardrails writes (now person-or-grant). Ghost
  spawn and discard are exempt (ent#69).

Without the grant, an agent gets a named 403
(*_management_not_permitted). The 403 tells the agent to raise a
permission-request ask and says an admin grants the permission in
Settings. Every refusal is audited. Autonomy, api-key-setting,
capabilities, capacity and rename stay person-only.

New: GET/PUT /api/agents/{name}/capability-grants[/{capability}], with
PUT limited to an interactive admin and audited; this is the backend for
ent#756. The route census gains a person_or_grant class.

Related to Abilityai/trinity-enterprise#164

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dolho
dolho requested a review from vybe October 5, 2026 14:30
@vybe

vybe commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

merge-train (2026-10-05, evening run): not on this train. It rides the next one once fixed. CI is green at 02a21145.

Blocking

  1. The instructions.manage fence on the file routes can be bypassed. The cause is in _normalize_user_path (src/backend/services/agent_service/files.py:52-64), which _touches_instructions (:127) relies on, and it predates this PR. Details, proof and the one-line fix are in abilityai/trinity-enterprise#792, kept off this thread because the repo is public. This PR should land after that fix or carry it, with the path-classification parametrize extended to match.

Needs a decision before it lands

  • A second door for "reset the git workspace". POST /api/agents/{agent_name}/git/pull with strategy=force_reset (routers/git.py:339, MCP git_pull) discards local changes and is unfenced; git/sync with pull_first is the same class. The refusal text says "resetting its git workspace", so either fence them or narrow the claim.
  • Instruction writes outside the path list: CLAUDE.local.md, home-level .claude.json, a symlink inside the target workspace, and the DB-stored prompts (PUT /{agent_name}/public-prompt, PUT /{name}/voice/prompt).
  • "Covers every reconfigure route" holds for the five in agent_config.py plus /model. Owner-tier writes elsewhere stay agent-reachable without a grant, for example circuit-breaker, mcp-exposed, folders, file-sharing, git/auto-sync, github-pat, access-policy, and git/freeze-schedules-if-failing on the schedules side. A holder can also lift its own read-only and guardrails. Fence them or list them as out of scope in the flow doc.
  • The calibrating check is grant-time and holder-only (capability_grant_service.py:83): a ready holder can rewrite a calibrating sibling's CLAUDE.md, and a grant made before the stamp persists.
  • Open-core placement. ent#164 has no recorded OSS-core ruling; the 09-11 comment there calls it a recommendation pending confirmation. That needs to be on the issue before this merges.

Mechanical, for the same push

  • No closing keyword: the body says "Implements" / "Related to". It completes ent#164 in substance, so Fixes abilityai/trinity-enterprise#164.
  • test_ent164 asserts fixed route lists; test_ent596 derives the writer set from the router and pins it. A new schedule write route without the fence stays green today.
  • Coverage is wiring-only (dependant-graph name match) for eight of the nine schedule routes, PUT /model, deploy-local, systems/deploy and git/reset-to-main-preserve-state. A validator probe with a real agent key got the named 403 on all of them, so the behaviour is right; it is just not pinned.
  • The grant-route test's agent key belongs to a role=user owner, so the role check alone refuses it. Drive an admin-owned agent key and an admin user-scoped key.
  • docs/memory/architecture/security.md §5a and §6 are untouched and no longer match; the flow doc shows the 403 key as error: where the payload key is message.
  • MCP tool descriptions for the schedule, agent, system and git tools do not mention the grant (ent#596 did this for skills.ts).

What held up: the grant route is human-only against every key type tried, the own-agent and ghost exemptions are not spoofable, the table already exists on both migration tracks, and trinity-system is unaffected.

@vybe vybe added the status-needs-fix PR has an unaddressed review/validation finding; cleared by the author's next push (#2815) label Oct 5, 2026

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

status-needs-fix PR has an unaddressed review/validation finding; cleared by the author's next push (#2815)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants