Skip to main content
Authentication is always on and cannot be disabled. The same image runs on localhost and on a public host, so a disable switch would be a setting somebody forgets to change on the day the instance becomes reachable.

First run

A fresh installation redirects to registration. The account you create there becomes the owner. The first account always bootstraps as owner regardless of AUTH_ALLOW_REGISTRATION, so a locked down deployment can still create its first user. Set AUTH_SECRET before the first boot. The API refuses to start with fewer than 32 characters.

Registration

AUTH_ALLOW_REGISTRATION defaults to false, which closes registration the moment the owner account exists. That is the right default for a single operator deployment. Set it to true when more people need to sign themselves up. There is no invitation flow, so the choice is between closed and open.

Sessions

A session is a JWT in an httpOnly, SameSite=Lax cookie named asobeast_session, signed with AUTH_SECRET. Because the browser talks only to the web origin and the proxy forwards the cookie, there is nothing cross site to configure. A JSON only API plus SameSite=Lax is the CSRF story. Rotating AUTH_SECRET invalidates every existing session, so everyone is signed out. See Rotate secrets.

Rotate a password

Changing your password from the account menu bumps a session version, which signs out every other session while the one you are using keeps working. That is what you want after a laptop is lost. The auth endpoints, including register, login, password change and token creation, are rate limited per client IP address.

How is a request authorized?

Two guards run in order on every request. A small set of routes is marked public, and account plus paywall routes stay reachable even for an unentitled account so an expired user can sign in and pay. All users share the seeded default workspace. Every tenant owned row already carries workspaceId, so per user isolation is prepared, but team roles, invitations and multi workspace administration are not implemented.

Entitlements and the premium seam

BILLING_ENABLED defaults to false, which means every account is entitled and nothing is gated. Setting it to true turns on an entitlement check without wiring a payment provider. Registration stays open, each new account is stamped with a TRIAL_DAYS trial defaulting to 7, and an unentitled request answers 402 through the standard error envelope, which the web app catches and turns into the upgrade page. The seam writes only plan, planExpiresAt and billingCustomerId. Setting plan to premium in the database restores access with no code change.

Troubleshooting

  • You are signed out after a restart. AUTH_SECRET changed. Sessions are signed with it.
  • You cannot register a second account. Registration closed after the owner account. Set AUTH_ALLOW_REGISTRATION=true.
  • Sessions do not persist behind a proxy. Check AUTH_COOKIE_SECURE against whether the browser reaches you over HTTPS. See Hosting behind TLS and a reverse proxy.

Personal API tokens

Authenticate a script or an agent.

Hosting behind TLS and a reverse proxy

The checklist before exposing an instance.
Last modified on July 31, 2026