# Workflow * branch (from `main`) * do the work in Next Step * move Next Step to the top of Completed Steps * move the top item of Future Steps into Next Step * commit (`TODO.md` changes in the same commit as the work) * merge to `main` if the branch is not protected, otherwise open a PR * push # Status pre-1.0. No git tags exist. Recent work extracted the internal/magic, internal/allowlist, internal/httpfetcher, and internal/signature packages. The gosec findings from the 2026-07-06 survey are resolved: the last two open findings (G124, session cookie attributes in internal/session) are fixed as of this change, so `make check` is green on main. # Next Step P0: implement cache size management and eviction so the disk cannot fill up # Completed Steps - 2026-08-07 validate configuration on startup, fail fast on bad config (closes #52): a config value that is set but unparseable or invalid aborts startup naming the key and value (defaults apply only to omitted keys), unknown config keys abort startup, a malformed config file aborts instead of being skipped, and `state_dir` is verified creatable and writable before the listener binds - 2026-08-07 manual test pass of the auth and encrypted URL flows against a locally built and running `pixad` (built from `main` at `6573b9d`, port 18099, local throwaway config); all six checks passed, plus all nine tests in `scripts/manual-test.sh` (closes #49): - [x] visit `/` and see the login form: HTTP 200, `Pixa - Login` page with `name="key"` password form - [x] wrong key shows an error: POST `/` with `key=wrong-key` returned HTTP 200 login page containing "Invalid signing key" - [x] correct signing key shows the generator form: POST `/` returned HTTP 303 to `/` with `Set-Cookie: pixa_session=...; HttpOnly; Secure; SameSite=Strict`; GET `/` with that cookie rendered `Pixa - URL Generator` with the `/generate` form and logout link - [x] a generated encrypted URL serves the image: POST `/generate` (ttl=3600) produced a `/v1/e//img.jpeg` URL that returned HTTP 200, `Content-Type: image/jpeg`, an 800x600 baseline JPEG of 61706 bytes - [x] an expired URL (short TTL) returns 410: a ttl=1 URL fetched after 3 s returned HTTP 410 Gone with `{"error":"URL has expired","status":410,...}` - [x] logout redirects back to login: GET `/logout` returned HTTP 303 to `/` with `Set-Cookie: pixa_session=; Max-Age=0`; subsequent GET `/` rendered the login form again - 2026-08-07 fix the two remaining gosec findings (G124 in internal/session): session cookies now always carry Secure/HttpOnly/SameSite=Strict on both the set and clear paths; `make check` green (closes #47) - 2026-07-07 Adopted scripts-to-rule-them-all: `script/` entrypoints, Makefile shims, README Entrypoints section - 2026-04-07 extract magic byte detection into internal/magic (#42) - 2026-03-25 extract allowlist package from internal/imgcache (#41) - 2026-03-25 move schema_migrations table creation into 000.sql (#36) - 2026-03-20 enforce and document exact-match-only signature verification (#40) - 2026-03-20 bound imageprocessor.Process input read to prevent unbounded memory use (#37); consolidate appname into an internal/globals constant (#34) - 2026-03-18 parse version prefix from migration filenames (#33) - 2026-03-15 QA audit fixes for 1.0/MVP readiness (#25) - 2026-03-02 split Dockerfile with pre-built golangci-lint stage for faster CI (#23) - 2026-02-25 repo policy compliance: CI workflow, hash-pinned images, golangci-lint and gosec fixes of that date (#14); arm64 Docker build fix (#16) - 2026-01-08 WebP and AVIF encoding support via govips (both former P0 image processing items, now done) # Future Steps - P1: implement blocked networks configuration to extend SSRF protection - P1: rate limit global concurrent upstream fetches to prevent resource exhaustion - P1: strip EXIF and other metadata from processed images (privacy) - P2: security - referer blacklist - per-IP rate limiting - per-origin rate limiting - P2: HTTP response handling - Last-Modified headers - Vary header for content negotiation - X-Request-ID propagation - P2: auto format selection (format=auto based on Accept header) - P2: configuration - add all configuration options from README - environment variable overrides - YAML config file support - P2: operational - optional Sentry error reporting - comprehensive request logging - Prometheus performance metrics - integration tests for the image proxy flow - load tests to verify the 1k to 5k req/s target - P2: documentation - configuration options - API endpoints - deployment guide - example nginx or caddy reverse proxy config