Adding a new permission today requires manual edits in three places, with no compile-time link between them:
- `agent/internal/auth/permissions.go` -- the source of truth (`Permission` constants)
- `ui/src/types/index.ts` -- the `Permission` union type (manually mirrored)
- `ui/src/utils/permissions.ts` -- `adminPermissions` / `operatorPermissions` / `viewerPermissions` arrays (manually mirrored)
Plus the role-permission defaults must be kept consistent across both, and the UI permission picker labels must be updated. Drift between the agent and UI is unenforced and silent. A forgotten mirror means the UI either omits a real permission (admins can't grant it) or offers one that the agent rejects.
Approach options
- Generated TypeScript from Go -- e.g. a `go generate` step that emits `ui/src/generated/permissions.ts` from the agent's constant declarations. UI imports from generated file; drift becomes a build error.
- API-driven -- agent exposes `GET /api/permissions` returning the live list and role defaults; UI fetches at boot. Decouples deploys but adds a runtime dependency.
- Single shared OpenAPI/proto schema that both sides codegen from.
Acceptance
- Adding a new `Permission` constant in the agent automatically (a) appears in the UI's Permission union, (b) gets included in the appropriate role defaults if listed there, (c) renders in the permission picker.
- A CI check fails if the lists drift.
- No new permission is silently dropped on either side.
Spotted while implementing #123 (API key edit) -- the UI's permission picker is a static list, and there's no signal that a new permission landed server-side.
Adding a new permission today requires manual edits in three places, with no compile-time link between them:
Plus the role-permission defaults must be kept consistent across both, and the UI permission picker labels must be updated. Drift between the agent and UI is unenforced and silent. A forgotten mirror means the UI either omits a real permission (admins can't grant it) or offers one that the agent rejects.
Approach options
Acceptance
Spotted while implementing #123 (API key edit) -- the UI's permission picker is a static list, and there's no signal that a new permission landed server-side.