What it promises
- Nobody signs in as someone else.
- Nobody learns a password, from it or from a copy of its database.
- A session works only in the browser it was given to, and ends when it should.
- Signing in doesn't reveal who has an account.
- It stays usable under ordinary attack.
Each is kept by the measures below, and each measure has a test that fails if it stops working.
How
| Against | What it does |
|---|---|
| Guessing passwords | scrypt at OWASP's cost; 15 characters or more; 5,500 common passwords refused; limits per account, per address, and per IPv6 block, counted before any password is checked |
| A copy of the database | Passwords only as salted scrypt hashes; sessions only as hashes of 256-bit tokens |
| Stealing a session | A cookie scripts can't read, sent only to this exact site over HTTPS; a session ends after 7 days unused, and 30 at most |
| Other sites acting for someone | Every change must come from the site's own pages, as JSON |
| Planting a session | A fresh session at every sign-in; two session cookies in one request mean nobody |
| Racing a password change | A session starts only if the password just checked is still the account's |
| Finding who has an account | A wrong email and a wrong password get the same answer, after the same work |
| Swamping the server | Two passwords checked at once, with a short queue; small bodies; malformed requests answered, never crashing |
| Lookalike or invisible characters | Emails normalised, and invisible characters refused |
The threat model has every threat, its measure, and where in the code it lives.
What it doesn't do yet
- Sign-up says when an email already has an account. Hiding it needs email confirmation, which is on the roadmap.
- No password reset yet. That needs email too.
- Someone with many addresses can pause sign-in to an account for up to an hour. Its owner, already signed in, can still change the password.
- Letters from other alphabets that look alike (a Cyrillic "а") make a different account that looks the same.
- One factor only: NIST's Authenticator Assurance Level 1. Two-step sign-in is on the roadmap.
How the project stays secure
- No runtime dependencies, and CI fails if one is added, or if anything but Node's own modules is imported.
- CI refuses invisible or direction-changing characters in code ("Trojan Source").
- CI fails if the source goes over 1,000 lines.
- CodeQL checks every change, OpenSSF Scorecard grades the project weekly, and GitHub Actions are pinned to exact commits.
- Releases are to be published only by CI, from a tag on main, with provenance linking each to its source.
Reporting a vulnerability
Please don't describe it in public. Use the repository's Security tab, then Report a vulnerability, with what you found, how to make it happen, and what an attacker could do with it. You'll hear back within 5 working days.