Skip to content

roles: collapse vault roles into a single instance-role tier#154

Draft
dangtony98 wants to merge 4 commits intomainfrom
roles/unified-instance-tier
Draft

roles: collapse vault roles into a single instance-role tier#154
dangtony98 wants to merge 4 commits intomainfrom
roles/unified-instance-tier

Conversation

@dangtony98
Copy link
Copy Markdown
Contributor

Summary

Replaces the two-axis permission model (instance role + per-vault role) with a single instance-level role: owner, admin, or agent. Owners auto-access every vault; admins and agents are scoped via vault_grants, which becomes a pure scope table (no role column). The new agent role is programmatic-only and proxy-only — agents can use the proxy and raise proposals but cannot reveal credentials or approve/reject proposals.

The plan that drove this change: /Users/tuandang/.claude/plans/at-the-moment-there-deep-honey.md.

Highlights

  • Migration 045_unified_instance_roles.sql maps existing data with highest-wins semantics: any admin/member grant keeps the admin instance role, proxy-only grants downgrade to agent, owner grants are dropped (owners now auto-access every vault).
  • Middleware collapses to requireVaultAccess + requireVaultAdmin via a shared resolveVaultActor helper that always returns the resolved actor — proposal handlers no longer re-resolve.
  • Inviter constraints: admins cannot grant a role above their own and cannot pre-assign vaults outside their own scope; agents cannot invite at all. Covered by TestUserInviteAdmin* / TestAgentInviteAdmin*.
  • Proposal notifications include all active instance owners alongside scoped admins (owners no longer have explicit grants).
  • Skill docs, public docs, CLI flags, and SPA invite/scope flows are aligned with the new model. The legacy vault_role field is gone everywhere.
  • Cleanup pass: extracted actorVaultScope / scopeContains / canManageInvite / vaultGrantsToJSON / inviteVaultsToJSON to deduplicate four scope-build patterns, three invite-auth blocks, and six vault-list flatten loops; replaced raw \"owner\" compares with User.IsOwner() / Agent.IsOwner() predicates on the hot paths.

Public API changes

  • Removed: POST /v1/vaults/{name}/users/{email}/role, POST /v1/vaults/{name}/agents/{name}/role — per-vault role no longer exists.
  • Modified: invite request bodies drop vault_role from the per-vault entries; agent-invite role accepts owner | admin | agent.
  • Modified: GET /v1/vaults/{name}/context now returns role (the actor's instance role) instead of vault_role.
  • POST /v1/vaults/{name}/join becomes a no-op compatibility shim for owners.

Test plan

  • CI: make test (16 packages green locally)
  • CI: make build (frontend + Go binary green locally)
  • Migration: stand up a copy of an older dev DB with populated vault_grants; run the binary; confirm migration applies, owners auto-access vaults, agents with only proxy grants downgrade to instance agent.
  • End-to-end: register as owner, invite a scoped admin, invite a scoped agent. Verify the agent can use the proxy + raise proposals but cannot reveal credentials or approve proposals; the admin can do both.
  • Inviter constraints: scoped admin tries to invite (a) an owner — rejected, (b) an admin scoped to a vault outside their scope — rejected, (c) an agent inside their scope — accepted.
  • Browser: vault Settings / Users / Agents / Credentials tabs render correctly under all three roles.

@infisical-review-police
Copy link
Copy Markdown

💬 Discussion in Slack: #pr-review-agent-vault-154-roles-collapse-vault-roles-into-a-single-instance-role

Posted by Review Police — reviews, comments, new commits, and CI failures will stream into this channel.

Comment thread internal/server/handle_agents.go
Comment thread internal/store/sqlite.go
Comment thread internal/store/migrations/045_unified_instance_roles.sql Outdated
@mintlify
Copy link
Copy Markdown

mintlify Bot commented May 8, 2026

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
agent-vault 🟢 Ready View Preview May 8, 2026, 9:57 AM

💡 Tip: Enable Workflows to automatically generate PRs for you.

dangtony98 and others added 4 commits May 8, 2026 09:13
Replaces the two-axis permission model (instance role + per-vault role)
with a single instance-level role: owner, admin, or agent. Owners
auto-access every vault; admins and agents are scoped via vault_grants
(now a pure scope table). The agent role is programmatic-only and
proxy-only — agents can use the proxy and raise proposals but cannot
reveal credentials or approve/reject proposals.

- Migration 045 maps existing data with highest-wins semantics: any
  admin/member grant keeps an admin instance role; proxy-only grants
  downgrade to the agent role; owner grants are dropped.
- Middleware collapses to requireVaultAccess + requireVaultAdmin via a
  shared resolveVaultActor helper that always returns the actor.
- Admin invites enforce two safety rails: cannot grant a role above the
  inviter's own, and cannot pre-assign vaults outside the inviter's
  scope. Agents cannot create invites.
- Proposal notifications now include all instance owners alongside
  scoped admins.
- Skill docs, public docs, CLI flags, and the SPA invite/scope flows
  are aligned with the new model.
- migration 045: fold the proxy-only admin downgrade into the
  agents_new INSERT...SELECT so the new value 'agent' is written
  against the relaxed CHECK rather than the existing
  CHECK(role IN ('owner','admin')) on the live agents table. The
  prior standalone UPDATE bricked any deploy that had at least one
  proxy-only admin agent — exactly the cohort this PR targets.

- RegisterFirstUser: drop the legacy vault_grants INSERT that still
  referenced the removed `role` column. After 045, every fresh
  install would 500 on POST /v1/init. Owners auto-access every vault
  under the unified instance-role model, so no per-vault grant is
  needed; defaultVaultID is now removed from the signature, the
  Store interface, mockStore, and both callers.

- agent listings: include owner agents in /v1/agents and
  /v1/vaults/{name}/agents for non-owner viewers. Owner agents have
  no vault_grants rows, so the previous filter (and the inner-join
  in store.ListAgents) silently dropped them, hiding which owner
  agents may be hitting a scoped admin's vault through the proxy.
  Pending owner-role invites are also surfaced.

Tests: added regression coverage for first-user onboarding after
migration 045 and for owner-agent visibility to a scoped admin.
A deeper review surfaced two correctness gaps beyond the originally
flagged review comments:

1. sessions.agent_id lost ON DELETE CASCADE in migration 045's table
   rebuild. Migration 035 originally set it, and the cascade survived
   through 044 (only ALTER TABLE was used). The 045 rebuild silently
   dropped it. In practice agents are revoked rather than deleted, but
   this is a regression in defensive behavior — restored to match the
   prior schema.

2. UpdateUserRole and UpdateAgentRole did not maintain the migration's
   invariant that owners have no vault_grants rows. Promoting an admin
   with explicit scope to 'owner' left orphan grant rows; demotion was
   already correct (operator must explicitly re-grant). Wrapped both
   updates in a transaction that DELETEs the actor's grants when the
   new role is 'owner'.

Test added: TestUpdateRolePromotionToOwnerClearsGrants exercises both
the user and agent paths.

go test ./... and go vet ./... both clean.
Under the unified instance-role model, owners auto-access every vault
and non-owners only see vaults they're explicitly scoped to — there is
no longer any reason for a "Join" affordance. But the SPA was still
splitting vaults into "My Vaults" / "Other Vaults" and showing a Join
button on every entry whose `membership` field was `"implicit"`.

The backend listed *every* vault as `"implicit"` for owners, so the
UI rendered every vault — including ones the owner just created —
as unclickable with a Join button. The Join endpoint itself was
already a no-op shim that only owners could call, making the button
doubly useless.

Rip out the whole concept:

- handleVaultList: drop the `membership` field and collapse the two
  branches; both owners and non-owners get a flat list.
- Delete handleVaultJoin and its `POST /v1/vaults/{name}/join` route.
- Delete `agent-vault owner vault join` and its docs accordion.
- VaultsListTab: drop the membership field, the My/Other split, the
  Join button, and the Link/div fallback — every card is just a Link.
- Tests: drop the three join tests and the membership assertions in
  the list tests.
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.

2 participants