You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
feat(cli): campus user * management commands — currently nonexistent, and gated on a users:* server-side vocabulary #42
The CLI ships three command groups: auth, client, vault. There are nocampus user * commands — user management (list / get / create / activate / delete, served by /auth/v1/users/*) is unreachable from the CLI.
Even if the commands were added today, they would 403 for every caller: the users blueprint requires the operator principal (campus#854), and the users:* management-scope vocabulary was explicitly reserved in campus#865's phasing ("stay operator-only until a need exists"). The CLI authenticates with user bearer tokens only (no client-credential mode), and since #865 a user token can carry management authority only through the ANDed legs: AUTH_ADMIN_USER_IDS designation + a management scope — none of which exist yet for users:*.
Path to add them (when a need exists — that's the deliberate gate)
Mirroring the clients rollout (campus#865 → #866):
Server (nyjc-computing/campus): define the users:read|write|admin vocabulary, wire require_operator(action, admin_scope=...) on the users routes (the composite gates already support it — clients did exactly this in #866), extend the invariant A8 wording, add the contract matrix. Deploy fail-closed (empty allowlist change).
Operator: widen the CLI client's allowed_scopes with the users scopes + designate the admin(s).
CLI:campus user list/get/new/activate/delete passing --scope users:admin at login; 403 messages already distinguish the failed leg server-side.
Related (same class of gap, separate decisions)
campus vault * commands exist but are equally inert from the CLI today: vault routes deny user principals outright (require_vault_permission), authority lives in per-label vault_access bitflags held by client principals. A bearer-only CLI can never satisfy that. Options: a vaults:* vocabulary, client-credential mode for the CLI, or documenting vault as operator/tooling-only.
credentials:* — same reserved bucket in campus#865.
Acceptance (when picked up)
Server vocabulary landed and deployed fail-closed (prerequisite).
campus user list/get/new/activate/delete implemented with dry-run support like client/vault.
README "Token scopes" section updated with the users:* vocabulary.
PATCH /users implemented in the same PR (was an unguarded 501 stub), accepting name only: a user's id IS its email, so identity fields stay fixed. Revisit if email changes are ever needed.
Audit: verified no new wiring needed — tracing spans bind g.current_user deployment-wide (#802) and already cover /users.
Activity
campus auth status— show stored token identity and scopes #45ngjunsiang commentedon Oct 9, 2026
Phase 1 (server) is up for review: nyjc-computing/campus#873
Decisions locked in during implementation (supersede the issue's step-3 shorthand where they differ):
users:read(list/get) <users:mod(activate only — NEW level between read and write in the shared algebra) <users:write(create + update, implies mod) <users:admin(delete, implies write).campus usercommand mapping: list/get→read, activate→mod, new/edit→write, delete→admin.users:*scopes consult a dedicatedAUTH_USERS_ADMIN_USER_IDSenv var — a clients-designated admin is NOT a users-admin. Generalization matrix tracked in feat(auth): per-resource designated-admin matrix (generalize AUTH_ADMIN_USER_IDS) campus#872.nameonly: a user's id IS its email, so identity fields stay fixed. Revisit if email changes are ever needed.g.current_userdeployment-wide (#802) and already cover /users.Related sub-issues: #45 (
campus auth status, filed).Phase 2 (client-side):
users.update()in campus-api-python, then thecampus usersub-app in campus-cli — next up.ngjunsiang commentedon Oct 9, 2026
Phase 2 (client-side) is up for review:
Users.User.update(), rename-only PATCH) — merge firstcampus usersub-app: list/get/new/activate/edit/delete, scope-per-command help, dry-run, TTY-safe confirmation) — needs #873 (server vocabulary) + #105 foredit; pyproject pinsbranch = "main"so relock after #105 mergesCommand → scope map: list/get →
users:read, activate →users:mod, new/edit →users:write, delete →users:admin.Merge pull request #46 from nyjc-computing/feat/user-commands