Repository navigation
v1.5.2
Accounts replace the access token
Contrack used to be gated by a single shared secret you copied out of the server logs. It now has real accounts.
- First run shows a one-time setup screen that creates the owner's account. Everything already in the database — contacts, notes, lists — is assigned to it, so an existing install keeps its data.
- Sign-in takes a username or email, plus a password.
- Settings → Account holds your profile, your password, and the list of devices you're signed in on, with "sign out other devices". A sign-out button now sits in the sidebar.
Sessions are server-side rows rather than JWTs, because revocation is the feature that matters for self-hosted: signing out actually ends the session. The cookie carries a random secret and the database stores only its SHA-256, so neither the database nor one of the rotating backups yields a live session. Changing your password ends every other session.
Passwords use scrypt from node:crypto — no second native module on top of better-sqlite3. Cost parameters are stored inside each hash, so raising them later upgrades passwords silently on next sign-in rather than locking anyone out.
There is no password-reset email (self-hosted, no mail server). The recovery procedure is documented in Configuration.
Configuration changes
AUTH_TOKEN→API_TOKEN, and it now means something narrower: the credential for scripts, cron jobs and MCP clients, sent asAuthorization: Bearer. The old name still works with a deprecation warning at startup.AUTH_REQUIREDnow defaults tofalseeverywhere, Docker included. The common deployment is a container reached only from its host. The server logs a warning at startup whenever it binds a non-loopback address with auth off — setAUTH_REQUIRED=trueif the port is reachable by anything else.
Groundwork for multi-tenancy
ownerId now exists on contacts, lists, ai_invocations and dedupe_merge_log — every table not reachable from contacts through a foreign key. Nothing filters on it yet; this is so that adding multi-tenancy later is "scope the queries" rather than "scope the queries and migrate live data".
NULL means "belongs to whoever owns this instance". The foreign key is ON DELETE RESTRICT, not CASCADE — cascade is what a mature multi-tenant app wants, but inheriting it before account deletion has been designed turns a stray DELETE FROM users into "silently destroy every contact".
Housekeeping
- Removed 27 exported symbols nothing referenced, 47 unused imports, and several parameters that were accepted and silently ignored — one of which had a call-site comment claiming it did something.
no-unused-varsis now an error rather than a warning. - Fixed a test flake this work exposed: integration test files shared one process under the default worker-thread pool, so
AUTH_REQUIREDset by the auth tests landed on every file running beside them and roughly one run in five failed somewhere unrelated. Integration tests now get one process per file.
Full Changelog: v1.4.0...v1.5.2