Reference
Endpoints
Section titled “Endpoints”All request and response bodies are JSON. Authenticated calls accept either an
Authorization: Bearer <token> header or the session cookie.
| Method | Path | Auth | Success | Notable failures |
|---|---|---|---|---|
POST | /api/v1/auth/signup | — | 201 user | 400 invalid email or password, 409 email taken |
POST | /api/v1/auth/login | — | 200 token + expiry, sets cookie | 401 invalid credentials, 429 rate limited |
POST | /api/v1/auth/logout | ✓ | 204 | 401 unauthenticated |
GET | /api/v1/me | ✓ | 200 user | 401 unauthenticated |
GET | /api/v1/version | — | 200 build tag | — |
GET | /healthz | — | 200 process alive | — |
GET | /readyz | — | 200 ready | 503 database unreachable |
Request bodies are capped at 1 MiB and reject unknown fields. The server sets
read, write, and idle timeouts, and returns an X-Request-Id on every response
that also appears in the log line for that request.
Error codes
Section titled “Error codes”Every error shares one envelope, so clients can switch on a stable string rather than parse prose:
{"error": "invalid_credentials", "message": "email or password is incorrect"}| Code | Status | Meaning |
|---|---|---|
bad_request | 400 | Body was not valid JSON, or contained unknown fields |
invalid_email | 400 | Address failed validation |
invalid_password | 400 | Outside the 8–512 byte policy |
email_taken | 409 | An account with that address exists |
invalid_credentials | 401 | Wrong email, wrong password, or disabled account — deliberately indistinguishable |
unauthenticated | 401 | Missing, unknown, or expired session token |
rate_limited | 429 | Login budget exhausted; see Retry-After |
unready | 503 | Database unreachable — readiness only |
internal | 500 | Something failed; the cause is in the logs, not the response |
Configuration
Section titled “Configuration”| Variable | Default | Description |
|---|---|---|
LABAPP_DB_URL | (required) | PostgreSQL connection string; the service refuses to start without it |
LABAPP_ADDR | :8080 | Listen address |
LABAPP_ENV | dev | dev gives human-readable logs; anything else emits JSON |
LABAPP_LOG_LEVEL | info | debug, info, warn, or error |
Schema
Section titled “Schema”Applied by embedded migrations at startup, under an advisory lock so concurrent replicas cannot race.
create table users ( id uuid primary key default gen_random_uuid(), email text not null unique, -- stored lowercased password_hash text not null, -- PHC string, self-describing status text not null default 'active', -- active | disabled created_at timestamptz not null default now(), updated_at timestamptz not null default now());
create table sessions ( id uuid primary key default gen_random_uuid(), user_id uuid not null references users(id) on delete cascade, token_hash bytea not null unique, -- sha256 of the opaque token created_at timestamptz not null default now(), expires_at timestamptz not null, user_agent text not null default '', ip inet);on delete cascade means deleting a user revokes their sessions in the same
statement. Expired rows are currently invisible to queries but not yet swept —
a background job is the obvious next chore.
Make targets
Section titled “Make targets”| Target | What it does |
|---|---|
check | Formatting, go vet, linter, race-detector tests — the commit gate |
run | Starts a development database and runs the service locally |
test / test-race | Unit tests, optionally under the race detector |
test-integration | Store tests against a disposable PostgreSQL container |
acceptance | End-to-end auth flow against a live deployment |
deploy | Build, push, upgrade, then poll until that exact build answers |
rollback | Return to the previous release |
db-up | Create or refresh the database and its secret |
status / logs / url | Inspect the running deployment |