-
Notifications
You must be signed in to change notification settings - Fork 0
Authentication and Users
Auth guards /api/* only. /health, the SPA shell, and /api/setup/status stay
public (browsers cannot attach a bearer to asset requests, so the shell must load
before the user is known). The implementation is packages/api/src/auth.ts.
Three mechanisms, freely combined. The bearer token always works for automation independently of the interactive options.
| Mode | How | Role | Notes |
|---|---|---|---|
| Password login | Setup wizard creates the first admin; users then sign in with email plus password | per-user (admin / member) |
scrypt-hashed; session cookie signed with TACHY_SESSION_SECRET
|
token |
TACHY_API_TOKEN bearer on /api/*
|
admin | automation, scripts, CI; timing-safe comparison |
sso |
OIDC_ISSUER / OIDC_CLIENT_ID / OIDC_CLIENT_SECRET (plus TACHY_SESSION_SECRET) |
per-user, member by default |
Microsoft Entra or any OIDC; coexists with password login |
open |
nothing configured, wizard skipped | admin | no auth, binds to 127.0.0.1 only; laptop dev |
authMode is auto-derived (sso if OIDC is set, else token if a bearer is
set, else open) unless TACHY_AUTH_MODE overrides it. The public
GET /auth/config tells the SPA which of wizard / password form / SSO button to
show.
resolveIdentity tries, in order:
-
Bearer token (
Authorization: Bearer ..., timing-safe) → admin,via: token. - Password session cookie → looks up the user; rejected if disabled.
-
SSO session → looks up the user (or defaults to
member); rejected if disabled. -
Open fallback → only when no token, no OIDC, and the instance is not yet
bootstrapped. Returns admin,
via: open.
A null result is a 401. The resolved identity is stashed on the request and
read by requireAdmin.
"Bootstrapped" means at least one active admin exists. On first boot with an empty database:
-
GET /api/setup/statusreturns{ bootstrapped: false }, so the SPA shows the wizard instead of a login form. -
POST /api/setup(public, but only usable while no admin exists) creates the admin account (email, scrypt-hashed password, optional display name), writes optional org name plus runtime settings, optionally seeds a first team and product, and signs the caller in. - It takes an exclusive table lock on
usersso two concurrent first-run POSTs cannot both become admin, and it self-disables permanently once an admin exists (a second call gets409).
Once bootstrapped it can never revert (demoting or disabling the last admin is rejected in core), so the positive result is cached for the process lifetime.
packages/api/src/index.ts binds to all interfaces only when some auth stands
guard: a token, SSO, or a bootstrapped password instance. Otherwise it stays on
127.0.0.1 and logs a warning, so an unauthenticated instance is never exposed.
Run the wizard locally first (or set TACHY_API_TOKEN / OIDC_*), then restart
to accept remote requests. Under Docker with no token, "loopback inside the
container" means the published port is unreachable from the host until an admin
exists, which is the intended safe default.
Password and SSO sessions are signed cookies (tachy_session for password login,
the OIDC middleware's own for SSO), 7-day lifetime. TACHY_SESSION_SECRET
(32+ chars) signs them. If unset, a per-process random secret is generated with a
loud warning and sessions reset on every restart. Set it in any real deployment.
A naive in-memory throttle limits password attempts to 5 failures per minute per email.
Each user has a global role: admin or member.
-
Admins manage users, org structure, and settings. Every mutation under the
admin API and the whole
/api/userssurface requires the admin role (requireAdminreturns403otherwise). The bearer token and open mode act as admin. - Members use the app: reads plus the agent Chat.
Users are managed in Admin › Users (/api/users): create a user (with an
optional initial password and role), patch role / password / disabled, and manage
team membership (team_members). SSO users are upserted on first sign-in
(attribution and role default member). TACHY_USER_EMAIL attributes the
standalone MCP server; web and SSO users are attributed from their login instead.
tachý - self-hosted knowledge engine for work items. README · AGPL-3.0-or-later
Design
Subsystems
Platform
Security