labapp
labapp is a Go web service I build against my own Kubernetes lab. Its first feature is authentication, written by hand rather than delegated to an identity provider — not because rolling your own auth is good advice, but because the point of the project is to understand the parts you normally outsource: how a password is stored, what a session actually is, and which of the defences around them matter.
These pages are the design notes. They cover what the service does, how it is put together, and — more usefully — why each security decision went the way it did and what it costs.
At a glance
Section titled “At a glance”| Language | Go 1.27, standard library first |
| Status | active — credentials, sessions, and the HTTP surface are complete and deployed |
| Storage | PostgreSQL 17, schema applied by embedded migrations at startup |
| Runtime | a three-node k3s cluster, behind an ingress that terminates TLS |
| Dependencies | golang.org/x/crypto (argon2), jackc/pgx, pressly/goose — and nothing else |
What it does today
Section titled “What it does today”Signup, login, logout, and a “who am I” endpoint. Passwords are hashed with
argon2id. Sessions are opaque random tokens stored server-side as hashes, and
work as either an HttpOnly cookie or an Authorization: Bearer header, so a
browser and a curl script are equally first-class. Login is rate limited per
client address and per account.
curl -s -X POST https://labapp.example/api/v1/auth/login \ -H 'Content-Type: application/json' \ -d '{"email":"you@example.com","password":"a-long-password"}'# {"token":"J4h2…","expires_at":"2026-09-09T14:27:03Z"}Why build authentication by hand
Section titled “Why build authentication by hand”Deploying Keycloak or Ory would have been the correct engineering call and the wrong learning one. Every interesting decision — how expensive to make a hash, whether a failed login should admit that the account exists, what happens to a session when a laptop is stolen — lives inside the thing an identity provider hands you finished. Writing it means those decisions have to be made explicitly, which is what these notes record.
The trade is real and worth stating plainly: this code has one reviewer and no production users. It is a study, not a recommendation.
Where to go next
Section titled “Where to go next”Roughly in order of altitude — how to run it, how it is shaped, then the parts worth reading closely.
- Getting started — toolchain, the local development loop, and the checks that gate a commit.
- Architecture — the four packages, the path a request takes, and why the domain logic is kept away from HTTP and SQL.
- Authentication — the substance: password hashing, session lifecycle, and the attacks each choice is meant to frustrate.
- Reference — endpoints, error codes, configuration, and the schema.
- Design decisions — the record of what was chosen over what, and the conditions that would reverse each call.