Kandev
Kandev Docs

Authentication & Users

Enable opt-in authentication, manage users and invites, and use personal access tokens on a shared Kandev server.

Kandev ships as a single-user local tool with authentication disabled: nothing changes for laptop installs. When several people share one Kandev server, enable authentication to give each person their own account and their own private workspaces.

Authentication is an experimental runtime feature toggle and is disabled in every shipped profile. It uses the same system as the other feature flags, so there is no separate "Authentication" configuration page.

Quick checklist

  1. Keep authentication off only for a single-user laptop bound to loopback or another private listener.
  2. For any server reachable by other users or networks, enable Authentication & users and restart.
  3. Create the first admin immediately after setup mode appears.
  4. Use personal access tokens for scripts and external MCP clients.
  5. Keep TLS, filesystem permissions, and agent credentials as separate security boundaries.

What changes when authentication is on

  • Everyone signs in with email + password. Browser sessions last 30 days (sliding) and can be revoked from Settings > Account. The signed-in user is shown in the bottom-left of the sidebar, with a log-out menu.
  • Workspaces become per-user by default. You only see workspaces you own, including their tasks, sessions, repositories, terminals, previews, and live updates. Existing data is assigned to the admin created during setup. A workspace can then be shared with colleagues on purpose: see Team Access.
  • Secrets are per-user. A Global secret is user-global across that user's workspaces; a Workspace secret belongs to one of their workspaces. With authentication disabled, Global is install-global. Executors and agent profiles remain shared across the instance, so they can reference Global secrets only; repositories may bind Global or same-workspace secrets.
  • With Organizations off, admins manage users and instance settings but do not see other users' private workspaces. With Organizations on, org admins manage organization-scoped resources and the separate instance operator manages organizations; neither role grants workspace visibility. The experimental multi-org path still has tenant-boundary gaps in user and system administration; see Limitations.
  • Programmatic clients (external MCP, scripts) authenticate with personal access tokens.

Sharing work with a team

Authentication makes each person's work private. To give a team a shared board without a shared login, see Team Access: workspace visibility, member roles, and scoped permissions.

Enabling authentication

From the UI: open Settings > System > Feature Toggles, turn on Authentication & users, and restart Kandev when prompted (it is a restart-required flag, like the other feature toggles). After the restart the instance is in setup mode: the setup wizard appears and the account you create becomes the admin. All existing workspaces, settings, and secrets carry over to that admin.

From the environment (fresh servers, Docker, Kubernetes): set

KANDEV_FEATURES_AUTH=true

The instance boots into setup mode and the first visitor creates the admin account. Complete the wizard immediately after deploying. When the flag is forced on by the environment variable, it cannot be turned off from the UI.

To turn authentication off again, an admin flips the same toggle off (and restarts), or the environment variable is unset. Ownership data is retained.

A server that listens on non-loopback interfaces with authentication disabled logs a startup warning.

Users and invites

Settings > Access Control > Users (admin only):

  • Invite links: mint a tokenized URL (/invite?token=…) and share it out of band. Optional pinned email, member or admin role, single use, 7-day default expiry. No email server needed.
  • Direct creation: create an account with a password yourself.
  • Disable / role changes: disabling a user immediately revokes their sessions and tokens. The last active admin cannot be demoted or disabled.

Roles are admin, member, and guest. With Organizations off, an admin manages users and system settings. With Organizations on, membership roles are associated with an organization and the instance operator tier is separate, but the current Users API does not consistently enforce the organization boundary; see Limitations. A guest reaches only explicitly shared workspaces. The current Users page changes accounts between admin and member; assign the API-only guest role as described in Team Access.

With Organizations off, install-wide data in Settings > System > Data & Logs and Settings > System > Storage follows the same admin/member split. Members can read the Database and Logs tabs, the backup listing, and the Host and Office retention tabs. Creating, downloading, restoring, and deleting a backup is admin only, because a backup is a copy of the whole database and downloading one would export every user's workspaces. Changing storage settings, adopting a Go cache, and running an analysis, cleanup, or quarantine restore/purge are admin only for the same reason database vacuum, optimize, and reset are: they act on the whole install. With Organizations enabled, org admins currently retain these install-wide capabilities; they are not yet restricted to the instance operator.

Personal access tokens

Settings > Account > API Tokens. Tokens look like kandev_pat_…, are shown once at creation, and are sent as a bearer header:

curl -H "Authorization: Bearer kandev_pat_..." https://kandev.example.com/api/v1/workspaces

External MCP clients (Claude Code, Cursor connecting to /mcp) must be configured with a PAT once authentication is enabled. WebSocket clients that cannot send headers can pass ?token=<PAT> on the connection URL.

Endpoints that stay public

/health (liveness probes) and /ready (readiness probes), the login/setup/invite pages, GET /api/v1/features, and self-authenticating webhook receivers (automation webhooks with X-Webhook-Secret, office channel HMAC webhooks). Everything else requires a session or token.

Plugin webhooks (/api/plugins/{id}/webhooks/{key}) are not public by default: a plugin's manifest must explicitly declare webhooks[].public: true for that specific webhook to accept anonymous requests. Unflagged webhooks require a session or PAT like any other endpoint. See Plugin manifest reference.

Multiple instances on one host

Browsers match cookies by host and ignore the port. Kandev adds the port to its instance-identity cookie names when the host uses a non-default port:

Cookie nameName on a non-default port
kandev_sessionkandev_session_<port>
kandev-active-workspacekandev-active-workspace_<port>
office-active-workspaceoffice-active-workspace_<port>

Default ports follow the URL scheme. HTTP :80 and HTTPS :443 use plain cookie names. HTTP :443 and HTTPS :80 keep the port suffix. This keeps sessions and workspace selections separate across instances on different ports.

Reverse proxies must preserve the browser hostname. The CORS and WebSocket origin gate compares the browser's Origin hostname with the request Host. It ignores X-Forwarded-Host and ports.

  • Preserve the full public Host, or rewrite only its port and send the correct X-Forwarded-Host.
  • Kandev reads that forwarded header only from peers in KANDEV_TRUSTED_PROXIES, the same list used for X-Forwarded-For.
  • Rewriting a non-loopback hostname returns 403 before authentication. A loopback alias such as localhost to 127.0.0.1 passes this gate.

Session-cookie migration. Old auth-enabled builds conflict with each other via the shared unprefixed kandev_session; an upgraded instance ignores the legacy session token and requires one re-login (the new build never reads the unprefixed session cookie, so this holds on rollback and re-upgrade too). Workspace selections keep their validated legacy read fallback, so a pre-upgrade selection survives.

A custom auth.cookieName disables automatic port isolation. It must be unique per cookie host. See configuration.

  • An explicit auth.cookieName: kandev_session keeps the old shared-name behavior. Remove it to use port isolation.
  • A custom name does not change the origin gate, which checks hostnames.
  • HTTP :80 and HTTPS :443 both use plain names. This mechanism does not isolate those two default-port instances.

What is isolated

When authentication is on, guarded workspace reads and actions are available only to its owner and users who can reach it through Team Access. Other users receive "not found" even if they know the ID. This covers workspaces, tasks, workflows, sessions, plans, walkthroughs, terminals, VS Code, port previews, git snapshots, Workspace secrets, repository bindings, and the workspace's third-party integration settings (GitHub/GitLab/Jira/Linear/Sentry/Azure) and automations. A user's Global secrets are also private to that user. Admin and operator management authority does not reveal workspaces or secrets they cannot otherwise reach.

Shared across the instance (by design): executors, agent profiles, environments, editors, prompts, and system pages.

Limitations

  • Multi-org user administration is not fully tenant-scoped. With Organizations enabled, the current Users API lists accounts across organizations and accepts role or status changes for accounts outside the caller's organization. Treat org-admin access to user management as install-wide until this gap is closed.
  • Multi-org system administration is not operator-exclusive. Backup, database, and storage mutations, runtime feature-toggle changes, and shared configuration mutations (including saved prompts) currently use org-admin scopes, not the instance-operator tier. An org admin can therefore affect install-wide behavior, including prompt instructions used by other organizations, and can download backups that contain their data.
  • WebSocket notification routing is not a strict access boundary. Workspace-aware fan-out normally narrows recipients by reach, but the regular path falls back to global delivery if reach and owner resolution both fail. See WebSocket API.
  • Filesystem and agent credentials are not isolated. Worktrees and repositories live under one ~/.kandev tree owned by the OS user running the backend, and agent CLI logins (gh auth, claude login, provider API keys) authenticate as that OS user, so all app-users share the same on-disk agent credentials, and anyone with shell access to the server can read all files. Authentication isolates users' kandev data at the application layer, not the filesystem or per-user agent auth. For hard isolation of agent credentials, run a separate kandev instance per user (or use OS-level access control / sandboxed executors).
  • Local accounts only for now; the account model is ready for OIDC/SSO later.
  • Authentication does not replace TLS. Terminate HTTPS in front of Kandev (the session cookie is marked Secure when the request arrives over TLS or X-Forwarded-Proto: https).