Accounts
Accounts belong to your Threadshift instance and have nothing to do with Reddit. They let you keep an instance to yourself and give admins a place to change its settings. Sign-in is off for everyone except admins until you turn it on. There's no email delivery or password recovery; admins reset passwords.
First-run setup
While the account database has no users, every page redirects to /setup, even with accounts off. The installer asks for a database, then the first admin's username and password (at least 12 characters), and signs you in. The Quick start walks through it. There are no default accounts, and setup closes for good once any account exists, so finish it before the instance is public.
Where accounts are stored:
| Database | Where | How it's chosen |
|---|---|---|
| SQLite | threadshift-auth.sqlite, next to overrides.toml (REDLIB_OVERRIDES_FILE) | Pick it in the installer; nothing to configure |
| PostgreSQL | An existing database you provide | Paste its URL in the installer, which checks it and creates the tables |
| Cloudflare D1 | The AUTH_DB binding | Always used on Workers; see Cloudflare D1 |
The installer saves its choice to auth-database.json beside the settings file (owner-only permissions on Unix; move it with REDLIB_AUTH_CONFIG_FILE). Keep that file private, because a PostgreSQL URL contains its password. REDLIB_AUTH_DATABASE_URL overrides the saved choice, and when it's set the installer only asks for the admin. In Docker, all of this lives in the mounted ./data directory.
Settings
Each one is an environment variable, a redlib.toml value, or a switch admins flip at /settings/instance:
| Setting | Default | What it does |
|---|---|---|
REDLIB_AUTH_ENABLED | off | Turns on sign-in and the account pages |
REDLIB_AUTH_REQUIRED | off | With accounts on, requires sign-in for all browsing and media |
REDLIB_AUTH_REGISTRATION | off | Lets visitors create their own regular accounts at /auth/register |
REDLIB_AUTH_ALLOWED_USERS | empty | Comma-separated usernames allowed to sign in. Empty allows every active account; admins can always sign in |
REDLIB_ADMIN_PASSWORD | unset | Creates an admin without the installer (see Admins). A secret: set it as an environment variable or wrangler secret, never on the instance page |
REDLIB_ADMIN_USERNAME | admin | That admin's username |
Usernames are case-insensitive and may use letters, numbers, ., _, - and @. On Workers, changes made at /settings/instance take up to two minutes to reach every copy of the Worker.
Who can open what
While accounts are on:
| Pages | Who |
|---|---|
| Reddit browsing and media | Everyone, or only signed-in visitors with REDLIB_AUTH_REQUIRED=on |
Preferences (/settings) and /info | Signed-in visitors. The header's light and dark switch still works for everyone |
Instance settings (/settings/instance), Users (/auth/users) and Database (/auth/database) | Admins |
Signed-out visitors see only "sign in" in the header. When sign-in is required, the sign-in page still loads the site's styles and logo, but themes that need instance settings apply after sign-in.
Admins
The admin pages are for signed-in admins only, whether accounts are on or off. Admins can always sign in at /auth/login, even while accounts are off for everyone else.
- Users (
/auth/users) creates accounts, resets passwords, disables accounts, and makes or removes admins. Disabling an account or resetting its password signs it out everywhere. - Database (
/auth/database) shows which database holds the accounts and how many there are, and tests the connection. - Registration: with
REDLIB_AUTH_REGISTRATION=on, the sign-in page links to/auth/register, which creates a regular account and signs it in. It respects the allowlist, and new accounts share a limit of ten per 15 minutes.
To provision an admin without the installer, or to get back in when no admin can sign in, set REDLIB_ADMIN_PASSWORD (and REDLIB_ADMIN_USERNAME to a name that isn't taken), then restart or redeploy. On start, if no account has that name, Threadshift creates an admin with that password. An existing account with the name is left alone, so a password changed on the Users page stays changed. If accounts exist but no admin can sign in, pages show a recovery warning, and this is the way back in; setup doesn't reopen.
Preferences follow the account
While someone is signed in, their preferences are their account's, on every device. That covers Redlib's settings (theme, sorts, NSFW, subscriptions and filters) and the extensions' (look, media options, favorites and the layout editor). The account always wins: a new account starts from the instance defaults, not from the browser it signed in on. Signing out brings back that browser's own preferences, untouched.
They're stored in the account database under prefs:<username> and stores:<username>. How it works is in src/auth/prefs.rs.
Security
- Passwords are hashed with Argon2id, and session tokens are stored only as SHA-256 hashes. Sessions last seven days, and signing out deletes them.
- Sign-in allows ten attempts per username every 15 minutes, counted in the database. For a public instance, also rate-limit at the edge to slow attacks across many usernames.
- Cookies are Secure, HttpOnly and SameSite=Strict, so setup and sign-in need HTTPS.
make localsetsREDLIB_AUTH_INSECURE_LOCAL=onto allow plain HTTP; never set it on a server others can reach. - With
REDLIB_AUTH_INSECURE_LOCAL=on, and only then, the sign-in page offers one-click "Login as admin" and "Login as regular user" for development. They createdev-adminanddev-useron first use with random passwords; the regular one needs accounts on. The Workers build never shows them. - Forms check their origin against the
Hostheader, so a reverse proxy must pass the original one through. - If the account storage is unreachable, nothing can create an account, and pages that require sign-in stay closed.
PostgreSQL
Set the URL as a secret in the server's environment, or paste it into the installer:
REDLIB_AUTH_DATABASE_URL='postgres://user:password@host/threadshift?sslmode=require'
The database user needs permission to create two tables and an index. SQLite and PostgreSQL share one schema, migrations/0001_auth.sql, which the native server applies when it first opens the database. PostgreSQL is for the native server and Docker; Workers uses D1.
Cloudflare D1
D1 is Cloudflare's SQLite database. From workers/, create one and put its ID in the AUTH_DB binding in workers/wrangler.toml:
wrangler d1 create threadshift-auth
wrangler d1 migrations apply AUTH_DB --local
wrangler d1 migrations apply AUTH_DB --remote
Then run make worker-deploy and open /setup to create the first admin. A Deploy to Cloudflare install creates the database for you, but you still apply the migration and finish setup before sharing the URL. Saving instance settings on Workers needs the SETTINGS KV binding too. Never put PostgreSQL credentials in instance settings or a committed Wrangler file.
Testing
cargo test --lib auth:: -- --skip oauth::
This runs the real SQLite adapter, password checks, session revocation, CSRF checks and concurrent throttling.
Alternative: Cloudflare Access
Cloudflare Access can put a hosted sign-in in front of the whole site instead, with one-time PINs by email, identity providers and email allowlists, managed through its API. It replaces Threadshift's accounts rather than adding to them.