Skip to content

feat(billing): Credits Manager role lets Operators issue and revoke credit grants [urgent] - #1775

Merged
ctkm-aelf merged 1 commit into
mainfrom
feat/credits-manager-role
Oct 5, 2026
Merged

ctkm-aelf merged 1 commit into
mainfrom
feat/credits-manager-role

Conversation

@ctkm-aelf

@ctkm-aelf ctkm-aelf commented Oct 5, 2026 •

Copy link
Copy Markdown
Collaborator

Priority: urgent. We need non-admins (Operators) to be able to grant credits without giving them full platform Admin.

What changes

  • New system role: Credits Manager (credits_manager), seeded at startup, with permission nyxid:billing:credit_grants:write.
  • New check: require_credit_grant_manager (handlers/admin_helpers.rs) passes for every Admin, and for an Operator whose direct or group roles carry that permission. Regular users holding the role are still refused.
  • Guarded endpoints: POST /admin/credits/grants and DELETE /admin/credits/grants/{id} now use that check. Allowances and recurring schedules stay admin-only. Credit list endpoints are unchanged (Operators could already read them).
  • GET /users/me: adds capabilities.manage_credit_grants. This is an additive field; older frontends ignore it.
  • Admin → Credits: "Issue grant" and revoke controls follow canManageCreditGrants. Allowance and schedule controls keep canAdminWrite.

No new environment variables and no migration. The role is created by the existing system-role seed.

How to use it

An Admin assigns Credits Manager to a user who is already an Operator, in Admin → Users → Roles. The Operator sees grant controls after their next page load. Revoking works from the same Roles section: it previously hid Revoke for every system role, and now hides it only for the Admin, Operator and User platform roles.

Tests

  • credits_manager_operator_issues_and_revokes_grants_only:
    • an Operator with the role passes authorization on issue and revoke;
    • the same Operator is still refused allowances;
    • a regular user holding the role is refused.
  • The existing operator_reads_credit_admin_surfaces_but_cannot_mutate_them still passes, so a plain Operator stays read-only.
  • Frontend: canManageCreditGrants unit tests.
  • Local runs:
    • cargo test -p nyxid --bin nyxid-server -- billing_credits role_service handlers::users: 58 passed.
    • Vitest on types/api.test.ts and admin-credits-safety.test.tsx: passed.
    • tsc and eslint: clean.

🤖 Generated with Claude Code

…redit grants

Adds a seeded system role, Credits Manager (`credits_manager`), carrying
`nyxid:billing:credit_grants:write`. An Operator holding it (directly or
through a group) may issue and revoke one-off credit grants; Admins keep full
access and regular users gain nothing from the role. Allowances and recurring
schedules remain admin-only.

`GET /users/me` adds `capabilities.manage_credit_grants` so Admin -> Credits
shows grant controls to Credits Managers while allowance and schedule controls
stay behind canAdminWrite.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 5, 2026

Copy link
Copy Markdown

📊 Code coverage

Component Lines Threshold Status Δ vs base
Backend (nyxid) 87.06% 73% ✅ — 0.00
Frontend (vitest) 72.27% 15% ✅ 🔺 +0.01

Gate: line coverage must stay at or above the threshold. Ratchet plan (W21): Backend → 55%, CLI → 50%, Frontend → 30% by quarter end.

@ctkm-aelf
ctkm-aelf merged commit c280e46 into main Oct 5, 2026
37 checks passed
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