# Pixa Implementation TODO A single linear checklist of tasks to implement the complete pixa caching image reverse proxy server. ## Login, Sessions & Encrypted URLs Feature ### Phase 1: Crypto Foundation - [ ] Create `internal/crypto/crypto.go` with: - [ ] `DeriveKey(masterKey []byte, salt string) ([32]byte, error)` - HKDF-SHA256 key derivation - [ ] `Encrypt(key [32]byte, plaintext []byte) (string, error)` - NaCl secretbox encrypt, returns base64url - [ ] `Decrypt(key [32]byte, ciphertext string) ([]byte, error)` - NaCl secretbox decrypt from base64url - [ ] Create `internal/crypto/crypto_test.go` with tests for: - [ ] Key derivation produces consistent keys for same input - [ ] Encrypt/decrypt round-trip - [ ] Decryption fails with wrong key - [ ] Decryption fails with tampered ciphertext ### Phase 2: Session Management - [ ] Add `github.com/gorilla/securecookie` dependency - [ ] Create `internal/session/session.go` with: - [ ] `SessionData` struct: `Authenticated bool`, `CreatedAt time.Time`, `ExpiresAt time.Time` - [ ] `Manager` struct using gorilla/securecookie with keys derived via HKDF - [ ] `NewManager(signingKey string, secure bool) (*Manager, error)` - [ ] `CreateSession(w http.ResponseWriter) error` - creates 30-day encrypted cookie - [ ] `ValidateSession(r *http.Request) (*SessionData, error)` - decrypts and validates cookie - [ ] `ClearSession(w http.ResponseWriter)` - clears cookie (logout) - [ ] `IsAuthenticated(r *http.Request) bool` - convenience wrapper - [ ] Cookie settings: `HttpOnly`, `Secure` (prod), `SameSite=Strict`, name `pixa_session` - [ ] Create `internal/session/session_test.go` with tests for: - [ ] Session creation and validation round-trip - [ ] Expired session rejection - [ ] Tampered cookie rejection - [ ] Missing cookie returns unauthenticated ### Phase 3: Encrypted URL Generation - [ ] Add `github.com/fxamacker/cbor/v2` dependency for compact binary encoding - [ ] Create `internal/encurl/encurl.go` with: - [ ] `EncryptedPayload` struct (short CBOR field names, omitempty for fields with sane defaults): - `SourceHost string` (`cbor:"h"`) - required - `SourcePath string` (`cbor:"p"`) - required - `SourceQuery string` (`cbor:"q,omitempty"`) - optional - `Width int` (`cbor:"w,omitempty"`) - 0 = original - `Height int` (`cbor:"ht,omitempty"`) - 0 = original - `Format ImageFormat` (`cbor:"f,omitempty"`) - default: orig - `Quality int` (`cbor:"ql,omitempty"`) - default: 85 - `FitMode FitMode` (`cbor:"fm,omitempty"`) - default: cover - `ExpiresAt int64` (`cbor:"e"`) - required, Unix timestamp - [ ] `Generator` struct with URL key derived via HKDF (salt: `"pixa-urlenc-v1"`) - [ ] `NewGenerator(signingKey string) (*Generator, error)` - [ ] `Generate(p *EncryptedPayload) (string, error)` - CBOR encode, encrypt, base64url - [ ] `Parse(token string) (*EncryptedPayload, error)` - base64url decode, decrypt, CBOR decode, validate expiration - [ ] `(p *EncryptedPayload) ToImageRequest() *imgcache.ImageRequest` - [ ] Create `internal/encurl/encurl_test.go` with tests for: - [ ] Generate/parse round-trip preserves all fields - [ ] Expired URL returns `ErrExpired` - [ ] Malformed token returns error - [ ] Tampered token fails decryption ### Phase 4: HTML Templates - [ ] Create `templates/templates.go` with: - [ ] `//go:embed *.html` for embedded templates - [ ] `GetParsed() *template.Template` function - [ ] Create `templates/login.html`: - [ ] Simple form with password input for signing key - [ ] POST to `/` - [ ] Error message display area - [ ] Minimal inline CSS - [ ] Create `templates/generator.html`: - [ ] Logout link at top - [ ] Form with fields: Source URL, Width, Height, Format (dropdown), Quality, Fit Mode (dropdown), Expiration TTL (dropdown) - [ ] POST to `/generate` - [ ] Result display area showing generated URL and expiration - [ ] Click-to-copy input field for generated URL ### Phase 5: Auth Handlers - [ ] Create `internal/handlers/auth.go` with: - [ ] `HandleRoot() http.HandlerFunc` - serves login form (GET) or authenticates (POST) if not logged in; serves generator form if logged in - [ ] `handleLoginGet(w, r)` - renders login.html template - [ ] `handleLoginPost(w, r)` - validates key with constant-time comparison, creates session on success - [ ] `HandleLogout() http.HandlerFunc` - clears session, redirects to `/` - [ ] `HandleGenerateURL() http.HandlerFunc` - parses form, generates encrypted URL, renders generator.html with result ### Phase 6: Encrypted Image Handler - [ ] Create `internal/handlers/imageenc.go` with: - [ ] `HandleImageEnc() http.HandlerFunc` - handles `/v1/e/{token}` - [ ] Extract token from chi URL param - [ ] Decrypt and validate via `encGen.Parse(token)` - [ ] Convert to `ImageRequest` via `payload.ToImageRequest()` - [ ] Serve via `imgSvc.Get()` (bypass signature validation - encrypted URL is trusted) - [ ] Set same response headers as regular image handler (Cache-Control, Content-Type, etc.) - [ ] Handle errors: expired → 410 Gone, decrypt fail → 400 Bad Request ### Phase 7: Handler Integration - [ ] Modify `internal/handlers/handlers.go`: - [ ] Add `sessMgr *session.Manager` field to `Handlers` struct - [ ] Add `encGen *encurl.Generator` field to `Handlers` struct - [ ] Add `templates *template.Template` field to `Handlers` struct - [ ] Initialize session manager and URL generator in `initImageService()` or new init method - [ ] Add import for `internal/session`, `internal/encurl`, `templates` ### Phase 8: Route Registration - [ ] Modify `internal/server/routes.go`: - [ ] Add `s.router.Get("/", s.h.HandleRoot())` - login or generator page - [ ] Add `s.router.Post("/", s.h.HandleRoot())` - login form submission - [ ] Add `s.router.Get("/logout", s.h.HandleLogout())` - logout - [ ] Add `s.router.Post("/generate", s.h.HandleGenerateURL())` - URL generation - [ ] Add `s.router.Get("/v1/e/{token}", s.h.HandleImageEnc())` - encrypted image serving ### Phase 9: Testing & Verification - [ ] Run `make check` to verify lint and tests pass - [ ] Manual test: visit `/`, see login form - [ ] Manual test: enter wrong key, see error - [ ] Manual test: enter correct signing key, see generator form - [ ] Manual test: generate encrypted URL, verify it works - [ ] Manual test: wait for expiration or use short TTL, verify expired URL returns 410 - [ ] Manual test: logout, verify redirected to login ## Project Setup - [x] Create Makefile with check, lint, test, fmt targets - [x] Create project structure (cmd/pixad, internal/*) - [x] Implement globals package - [x] Implement logger package - [x] Implement config package - [x] Implement database package (SQLite) - [x] Implement healthcheck service - [x] Implement middleware package - [x] Implement handlers package with placeholder routes - [x] Implement server package (lifecycle, routing, HTTP) - [x] Wire up fx dependency injection in main.go - [x] Verify basic server starts and healthcheck works ## Core Image Proxy Features - [x] Implement URL parsing for `/v1/image///.` - [x] Implement upstream HTTP client with TLS verification - [x] Implement SSRF protection (block private/internal IPs) - [x] Implement source host whitelist checking - [x] Implement HMAC-SHA256 signature generation - [x] Implement HMAC-SHA256 signature verification - [x] Implement signature expiration checking - [x] Implement upstream fetch with timeout and size limits - [x] Implement Content-Type validation (whitelist MIME types) - [x] Implement magic byte verification ## Caching Layer - [x] Design and create SQLite schema for cache metadata - [x] Implement source content storage (`cache/src-content/`) - [x] Implement source metadata storage (`cache/src-metadata//.json`) - [x] Implement output content storage (`cache/dst-content/`) - [x] Implement cache key generation - [x] Implement cache lookup (in-memory hot path) - [x] Implement cache write - [x] Implement negative caching (404s) - [x] Implement cache TTL and expiration - [ ] Implement cache size management/eviction ## Image Processing - [x] Select and integrate image processing library (libvips bindings or pure Go) - [x] Implement image decoding (JPEG, PNG, WebP, GIF, AVIF) - [x] Implement image resizing with size options (WxH, 0x0, orig) - [x] Implement format conversion (JPEG, PNG, WebP, AVIF) - [x] Implement quality parameter support - [ ] Implement max input dimensions validation - [ ] Implement max output dimensions validation - [ ] Implement EXIF/metadata stripping - [x] Implement fit modes (cover, contain, fill, inside, outside) ## Security - [ ] Implement path traversal prevention - [ ] Implement request sanitization - [ ] Implement response header sanitization - [ ] Implement referer blacklist - [ ] Implement blocked networks configuration - [ ] Add rate limiting per-IP - [ ] Add rate limiting per-origin - [ ] Add rate limiting global concurrent fetches ## HTTP Response Handling - [x] Implement proper Cache-Control headers - [ ] Implement ETag generation and validation - [ ] Implement Last-Modified headers - [ ] Implement conditional requests (If-None-Match, If-Modified-Since) - [ ] Implement HEAD request support - [ ] Implement Vary header for content negotiation - [x] Implement X-Pixa-Cache debug header (HIT/MISS/STALE) - [ ] Implement X-Request-ID propagation - [x] Implement proper error response format (JSON) ## Additional Endpoints - [x] Implement robots.txt endpoint - [ ] Implement metrics endpoint with auth - [ ] Implement auto-format selection (format=auto based on Accept header) ## Configuration - [ ] Add all configuration options from README - [ ] Implement environment variable overrides - [ ] Implement YAML config file support - [ ] Validate configuration on startup ## Operational - [ ] Implement graceful shutdown - [ ] Implement Sentry error reporting (optional) - [ ] Add comprehensive request logging - [ ] Add performance metrics (Prometheus) - [x] Write unit tests for URL parsing - [x] Write unit tests for signature generation/verification - [x] Write unit tests for cache operations - [x] Write unit tests for image processing - [ ] Write integration tests for image proxy flow - [ ] Write load tests to verify 1-5k req/s target ## Documentation - [ ] Document configuration options - [ ] Document API endpoints - [ ] Document deployment guide - [ ] Add example nginx/caddy reverse proxy config