Sign-in

Sign-in built for the day someone tries.

Sessions for people, scoped API keys for servers, and passwords hashed with Argon2id and checked against known breaches. It's all in the binary.

A key for the website, shown once

POST /api/auth/keysAuthorization: Bearer tcs_…{ "name": "website",  "scopes": ["articles:read"] } { "key": "tcy_uhffr46z_…",  "scopes": ["articles:read"],  "expiresAt": null, … }

The key tries to do more

POST /api/articlesAuthorization: Bearer tcy_uhffr46z_…403 You don't have permission to do this. POST /api/auth/keysAuthorization: Bearer tcy_uhffr46z_…403 Manage API keys from a signed-in    session, not with another key.

A password from a breach

POST /api/users{ "password": "password1234", … } 422 { "errors": [{  "message": "This password has appeared    in a data breach. Choose another.",  "path": "password" }], … }
Real requests to TachyonCMS v0.1.0. Responses are trimmed, and the new key is cut short.

People get sessions, servers get keys

Sign in from a browser and you get a session cookie. Its name starts __Host-, which makes the browser insist on HTTPS, the whole site as its path and no other domain. From a script, ask for a bearer token instead. Either way the server keeps only a hash of it.

A browser sends cookies with requests that other sites start, so a write signed in by cookie also has to come from your own address. Tokens and keys aren’t sent automatically, so they don’t need that check. Sessions end after 7 days unused or 30 days at most, and you can list yours and end any of them.

Keys with the least they need

A key carries scopes: posts:read, posts:write, *:read, *:write, or admin. It acts as the person who made it, narrowed by those scopes and by every access rule.

You see the secret once, when it’s made; the server stores a hash. Give a key an expiry date if you like. Rotate it and the old secret stops working at that moment. And keys are only made from a signed-in session, never with another key, so a leaked key can’t mint more.

Passwords

Twelve characters at the least. Each is hashed with Argon2id at the OWASP baseline: 19 MiB of memory, two passes, one lane.

Before a new password is accepted, the server asks Have I Been Pwned whether it’s turned up in a breach. Only the first five characters of its SHA-1 hash leave the server, and the answer is padded so its size gives nothing away. A known password is refused. If the service can’t be reached, the password goes through and a warning goes in the log, so a network hiccup never blocks a password change.

Five wrong passwords in 15 minutes lock the account for 15 more.

The settings, and the one you can’t skip

The lockout, session lengths, Argon2 memory and the breach check all live under [auth] in tachyon.toml, or in the environment as TACHYON_AUTH__SETTING. The one you can’t leave out is public_url. The server won’t start on a public address without it, because browser sign-ins only accept changes from that address.

In the docs

Checked against v0.1.0

Every claim on this page comes from one of these. If one's out of date, tell me in an issue.