Skip to content

Per-user local_project_disabled flag (block create + index)#63

Merged
dvcdsys merged 3 commits into
developfrom
feat/user-local-project-disabled
Jun 3, 2026
Merged

Per-user local_project_disabled flag (block create + index)#63
dvcdsys merged 3 commits into
developfrom
feat/user-local-project-disabled

Conversation

@dvcdsys
Copy link
Copy Markdown
Owner

@dvcdsys dvcdsys commented Jun 3, 2026

What & why

Adds an admin-controlled, per-user switch that forbids a user from creating local projects and from indexing/reindexing, while keeping search of their already-indexed projects and workspace creation available. Admins are always exempt.

Requested behavior:

  • Flag on the user profile; when set, the user can no longer create projects or index anything.
  • Workspace creation stays available (it only organizes existing content).
  • Migration: all pre-existing users keep create+index (backward compat); new users default to allowed.
  • Projects created before a user is restricted remain available for search only.

Design

  • Flag: users.local_project_disabled (boolean, true = forbidden). Inverted polarity → DEFAULT 0 makes existing and new users allowed by default; an admin flips it to restrict someone.
  • Admins always exempt (role=admin and CIX_AUTH_DISABLED short-circuit the guard), matching the existing ownership/index model.
  • Enforcement lives on the server (returns 403); local projects are created via the CLI, so the user feels the block as API error (403): your account is not permitted to create or index local projects — the CLI already surfaces the server detail.

Changes

  • DB: new column + idempotent migration fix: rebuild CUDA image to patch CVEs, migrate to multi-stage build #14 (columnExists short-circuit, ALTER … DEFAULT 0).
  • User builders: column threaded through every users.User constructor, crucially the two hand-rolled auth-path JOINs that populate ac.Userapikeys.Authenticate (CLI/API-key) and sessions.Get (dashboard). Missing either would be a silent enforcement bypass.
  • Guard: requireLocalProjectActions in access.go; gates CreateProject and index begin/files/finish. index/cancel (cleanup) and index/status + search (read) stay open.
  • Admin API: field on userPayload (/auth/me + /admin/users) and a PATCH /admin/users/{id} field; SetLocalProjectDisabled setter.
  • OpenAPI + dashboard: schema fields + 403 docs; regenerated types; admin Users table gains a per-user toggle (shows "Always" for admin rows).

Tests

  • users: migration backfill leaves existing rows allowed; new Create defaults to allowed; SetLocalProjectDisabled round-trips through GetByID/Authenticate.
  • db: legacy-schema migration backfills to 0 and is idempotent.
  • httpapi: restricted user → 403 on create + index begin/finish; /auth/me reflects the flag; read (index/status) and workspace creation stay allowed; admin exempt even when flagged; re-enabling restores access.
  • docs/AUTH_REVIEW.md access matrix updated.

make test green; dashboard typecheck + build green. No build artifacts committed (dist/* and generated.ts are gitignored; dist/.gitkeep preserved).

🤖 Generated with Claude Code

dvcdsys and others added 3 commits June 3, 2026 12:38
…dex)

Add an admin-controlled, per-user switch that forbids a user from creating
local projects and from indexing/reindexing, while keeping search of their
already-indexed projects and workspace creation available. Admins are always
exempt.

- DB: users.local_project_disabled INTEGER NOT NULL DEFAULT 0 + idempotent
  migration #14. Default 0 backfills existing and new users to "allowed"
  (backward compatible).
- Carry the column through every users.User builder, including the two
  hand-rolled auth-path JOINs that populate ac.User: apikeys.Authenticate
  (CLI/API-key path) and sessions.Get (dashboard path) — missing either is a
  silent enforcement bypass.
- Enforcement: requireLocalProjectActions guard (access.go) gates CreateProject
  and index begin/files/finish with 403; admins and CIX_AUTH_DISABLED exempt.
  index/cancel (cleanup) and index/status + search (read) stay open.
- Admin API: local_project_disabled on userPayload (/auth/me + /admin/users)
  and as a PATCH /admin/users/{id} field; SetLocalProjectDisabled setter.
- OpenAPI: User + UpdateUserRequest schema fields, 403 docs; regen dashboard
  types; admin Users table gains a per-user toggle ("Always" for admins).
- Tests: migration backfill + setter round-trip; httpapi gating (403 on
  create/index for a restricted user, read/workspace stay allowed, admin
  exempt, re-enable restores). docs/AUTH_REVIEW.md updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The cix-workspace skill and its investigator sub-agent assumed a single
cix backend. The cix CLI supports several named servers (--server <alias>,
CIX_SERVER), and a workspace + all its repos live on exactly one server —
so a cross-project workflow that mixes servers silently returns empty or
wrong-repo results.

- cix-workspace SKILL: new "which server hosts the workspace?" section;
  thread --server through Step 0/1/2, the sub-agent fan-out prompt, the
  quick reference, and the TL;DR. Replace the raw curl per-project search
  (which hardcodes one server's URL/key) with `cix search -n <project>
  --server <alias>`, which resolves the right backend from config.
- cix-workspace-investigator: every cix call must carry --server <alias>
  when the workspace is on a non-default server (tools list, hard rule 1,
  the "where your project lives" preamble).
- skills/README.md: align the plugin install snippet with the dashboard
  onboarding card — `claude plugin …` console commands, drop the obsolete
  /reload-plugins.
- Sync canonical skills into the plugin bundle (sync-skills.sh); plugin
  bats suite green (15/15).

The cix skill already documented multi-server; no change needed there.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… edges

Address review feedback on the per-user restriction:

- Add an index/files 403 case to the gating test (the guard is identical
  to begin/finish, but explicit coverage closes the gap).
- Document the role-independent persistence edge: the flag is not cleared
  on promote-to-admin (admins just ignore it) and re-activates on demotion
  — intentional. Noted at SetLocalProjectDisabled and the dashboard
  "Always" cell.
- Document the mid-indexing edge: flipping the flag during an in-flight
  index session strands it; index/cancel stays open and the session TTLs
  out. Noted at requireLocalProjectActions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@dvcdsys dvcdsys merged commit 802d8c7 into develop Jun 3, 2026
3 checks passed
@dvcdsys dvcdsys deleted the feat/user-local-project-disabled branch June 3, 2026 12:08
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