Skip to content

[EE] User accounts, teams and roles - #226

Merged
ganisback merged 12 commits into
mainfrom
feat/ee-team-management
Oct 22, 2026
Merged

ganisback merged 12 commits into
mainfrom
feat/ee-team-management

Conversation

@ganisback

@ganisback ganisback commented Oct 21, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

E1 of the Enterprise gateway plan. Design: docs/guides/team-management.md, modeled on LiteLLM Proxy and OpenRouter Organizations.

Free vs Enterprise, aligned with LiteLLM's open-source proxy: users, invitations, teams, the administrator and member roles, and per-user keys, conversations and images are free, with no limit on the number of users. feature.lite.team_management gates only the auditor role and changes to the sign-in policy; E2–E5 (key budgets, spend reports, audit logs, SSO) will add to the Enterprise side.

Today the server listens on :11435 on every interface, so anyone on the LAN can change providers, models and settings. With this change, once the first account exists, remote callers must sign in or present an API key.

Setting up the first administrator (no window for someone on the LAN to claim the instance):

  • On the server itself: requests from this machine act as administrator, so the admin can be created directly in the web UI.
  • Remote web UI with a one-time setup token: the token is written to ~/.csghub-lite/team-setup-token (0600) and never to the log, because the log is readable over the network before accounts exist.
  • Environment variables: CSGHUB_LITE_ADMIN_USERNAME plus CSGHUB_LITE_ADMIN_PASSWORD or _PASSWORD_FILE. They are applied once at start-up while no account exists and never reset a password. The UI warns while they are still set.
  • CLI: csghub-lite user create admin --role admin.

Members joining:

  • By default, an invitation link: valid 7 days, works once, stored only as a hash. It can be reissued or revoked.
  • Alternatively, an account with a temporary password, which must be changed at first sign-in. An administrator password reset works the same way.

Roles: administrator; auditor (reads everything, including logs, usage and all keys, but changes nothing and cannot call models); member (calls models and manages their own keys, conversations and images). Everything except an administrator's actions is checked against an allowlist in internal/team/policy.go (the auditor's in ee/team/auditor.go), so new routes are protected by default.

Code layout: accounts are free, so they live in internal/team under Apache-2.0; only the Enterprise auditor rules are in ee/team. Design doc: docs/guides/team-management.md.

Ownership:

  • API keys belong to their creator and act with that user's role.
  • Keys created before accounts existed act as member keys and cannot create new keys.
  • Conversations and image jobs are private to their owner.
  • Non-administrators do not see upstream provider credentials.

Security details:

  • HttpOnly session cookie (SameSite=Lax); cookie-authenticated writes from another origin are refused.
  • Configurable session length and failed-login lockout.
  • Requests that came through a local reverse proxy are treated as remote.
  • Nothing about signing in is license-gated, so a lapsed license never reopens the instance.
  • csghub-lite user reset-password works on the host whatever the license state.

UI:

  • The sidebar is grouped into Workspace, Models & Data, and Management. Management and Settings are shown only to administrators and auditors.
  • Account menu (shown even with accounts off): profile, My API Keys, change password, Settings, Pricing, Help, language, sign out.
  • The Teams page shows member, invitation and team counts, and has four tabs:
    • Members, with search and filters
    • Teams
    • Invitations
    • Sign-in security
  • Auditors see a read-only version of the page.
  • Other pages: sign-in, invitation acceptance, forced password change, and My API Keys for members.

Docs and API: OpenAPI documents /api/auth/* (including invitations) and /api/team/* (users, reinvite, teams, settings). Also updated: environment variables, Docker, the new docs/cli/user.md, and ee-license-design.md (drops the "no per-user permissions" non-goal and answers the seats question).

Release note

  • [EE] User accounts for shared instances: invitation links, administrator / auditor / member roles, personal API keys, conversations and images, and a first-administrator setup that the LAN cannot hijack (setup token, CSGHUB_LITE_ADMIN_* or csghub-lite user).

Test plan

  • go test ./...
    • internal/team: invitations, expiry, forced password change, host reset, settings, the last-administrator rule, migration of legacy viewers, cache coherence, the policy table; ee/team: the auditor rules.
    • internal/server: setup token, environment bootstrap (including the unlicensed case), invitations, forced password change, sign-in settings and lockout, roles including auditor, ownership, seat cap, license lapse.
  • vitest (95 tests) and tsc --noEmit.
  • E2E against a built binary with an isolated HOME and a throwaway license, using real LAN-IP requests. Covered:
    • env bootstrap → 401 for anonymous callers
    • invitation flow, with reuse of a used link → 404
    • member restrictions
    • forced password change
    • seat cap
    • setup token required remotely and absent from /api/logs
    • CLI user create / list / reset-password, offline and with and without a license
  • Headless-Chrome screenshots, signed in as different roles through the server's own cookies: sign-in, members list, invite dialog, invitation link, invitations tab, sign-in security, account menu, member's My API Keys and sidebar without the management group, forced password change, invitation acceptance, and remote first-admin setup with the setup token.
  • Not verified: Windows. No new platform-specific code; file writes use safefile.Write.

🤖 Generated with Claude Code

ganisback and others added 12 commits October 21, 2026 17:06
A shared instance listens on every interface, so anyone on the LAN could
change providers, models and settings. With a license, an administrator now
creates user accounts; once the first one exists, remote callers must sign in
or present an API key and act as an administrator, member or viewer.

- Sign-in page, session cookie, password change and login throttling.
- Users and Teams page with seats from quota.lite.max_users and a locked
  state when unlicensed; the first gated feature, feature.lite.team_management.
- API keys, conversations and image jobs belong to their creator; members
  no longer see upstream provider credentials.
- Requests from the machine itself keep administrator access, so the CLI and
  desktop app are unaffected; proxied requests are treated as remote.
- Sign-in keeps working after a license lapses, so the instance never reopens.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reworks the account flows after LiteLLM and OpenRouter:

- First administrator: from the machine itself, a one-time setup token
  (kept in a file, never logged) for remote setup, CSGHUB_LITE_ADMIN_*
  environment variables applied once at start-up, or `csghub-lite user`.
- Members join through a 7-day one-time invitation link; a password an
  administrator sets must be changed at first sign-in.
- Roles are administrator, auditor (reads everything, changes nothing) and
  member; legacy viewers load as members.
- Configurable session length and lockout, and `csghub-lite user
  reset-password` to recover on the host whatever the license state.
- Sidebar grouped into Workspace, Models & Data and Management, with an
  account menu (my API keys, password, language, sign out); members get a
  My API Keys page and no management pages.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The sidebar foot now holds only the profile menu, which also shows while
accounts are off so these pages stay reachable.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Any instance can now require a sign-in without a license: listing users and
creating one are no longer gated, and the community seat cap of 1 stops an
unlicensed instance at its administrator. The environment bootstrap and the
offline CLI follow the same rule. Inviting more people, roles, teams and
sign-in settings stay Enterprise features.

The Teams page shows an unlicensed instance the administrator form, or the
account and a note that sign-in is on, with the Enterprise features below,
and the sidebar entry loses its lock.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…in EE

As in LiteLLM's open-source proxy, users, invitations, teams and the
administrator and member roles now work without a license and with no limit
on the number of users. feature.lite.team_management gates the auditor role
(when it is given) and changes to the sign-in policy; the seat quota is
removed.

The Teams page is the same with or without a license; the auditor role and
the sign-in policy carry an Enterprise badge and an unlock card. The page is
titled Teams to match the sidebar.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Accounts are free, so their code now lives in internal/team under
Apache-2.0. ee/team holds just AuditorAllowed, the Enterprise auditor
role's rules, which the accounts middleware consults for auditors. The
design doc becomes docs/guides/team-management.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Without accounts the menu now shows the role and a generic avatar.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ganisback
ganisback merged commit 4db8a0e into main Oct 22, 2026
4 checks passed
@ganisback
ganisback deleted the feat/ee-team-management branch October 22, 2026 09:15
ganisback added a commit that referenced this pull request Oct 22, 2026
This reverts commit 4db8a0e. The design
followed LiteLLM too closely; accounts will be redone in our own way.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ganisback ganisback mentioned this pull request Oct 22, 2026
5 tasks done
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