From 02de534cc2b056388b51bb2b9723fa3f2632d5d6 Mon Sep 17 00:00:00 2001 From: sneak Date: Thu, 8 Jan 2026 10:41:00 -0800 Subject: [PATCH] Reorganize TODO.md: remove completed, prioritize for 1.0 P0 Critical: Manual testing, cache eviction, config validation P1 Production: Blocked networks, rate limiting, EXIF stripping P2 Nice to have: Everything else --- TODO.md | 198 +++++++------------------------------------------------- 1 file changed, 23 insertions(+), 175 deletions(-) diff --git a/TODO.md b/TODO.md index 3354983..9132f94 100644 --- a/TODO.md +++ b/TODO.md @@ -1,111 +1,10 @@ -# Pixa Implementation TODO +# Pixa 1.0 TODO -A single linear checklist of tasks to implement the complete pixa caching image reverse proxy server. +Remaining tasks sorted by priority for a working 1.0 release. -## Login, Sessions & Encrypted URLs Feature +## P0: Critical for 1.0 -### Phase 1: Crypto Foundation -- [x] Create `internal/seal/crypto.go` with: - - [x] `DeriveKey(masterKey []byte, salt string) ([32]byte, error)` - HKDF-SHA256 key derivation - - [x] `Encrypt(key [32]byte, plaintext []byte) (string, error)` - NaCl secretbox encrypt, returns base64url - - [x] `Decrypt(key [32]byte, ciphertext string) ([]byte, error)` - NaCl secretbox decrypt from base64url -- [x] Create `internal/seal/crypto_test.go` with tests for: - - [x] Key derivation produces consistent keys for same input - - [x] Encrypt/decrypt round-trip - - [x] Decryption fails with wrong key - - [x] Decryption fails with tampered ciphertext - -### Phase 2: Session Management -- [x] Add `github.com/gorilla/securecookie` dependency -- [x] Create `internal/session/session.go` with: - - [x] `Data` struct: `Authenticated bool`, `CreatedAt time.Time`, `ExpiresAt time.Time` - - [x] `Manager` struct using gorilla/securecookie with keys derived via HKDF - - [x] `NewManager(signingKey string, secure bool) (*Manager, error)` - - [x] `CreateSession(w http.ResponseWriter) error` - creates 30-day encrypted cookie - - [x] `ValidateSession(r *http.Request) (*Data, error)` - decrypts and validates cookie - - [x] `ClearSession(w http.ResponseWriter)` - clears cookie (logout) - - [x] `IsAuthenticated(r *http.Request) bool` - convenience wrapper - - [x] Cookie settings: `HttpOnly`, `Secure` (prod), `SameSite=Strict`, name `pixa_session` -- [x] Create `internal/session/session_test.go` with tests for: - - [x] Session creation and validation round-trip - - [x] Expired session rejection - - [x] Tampered cookie rejection - - [x] Missing cookie returns unauthenticated - -### Phase 3: Encrypted URL Generation -- [x] Add `github.com/fxamacker/cbor/v2` dependency for compact binary encoding -- [x] Create `internal/encurl/encurl.go` with: - - [x] `Payload` 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 - - [x] `Generator` struct with URL key derived via HKDF (salt: `"pixa-urlenc-v1"`) - - [x] `NewGenerator(signingKey string) (*Generator, error)` - - [x] `Generate(p *Payload) (string, error)` - CBOR encode, encrypt, base64url - - [x] `Parse(token string) (*Payload, error)` - base64url decode, decrypt, CBOR decode, validate expiration - - [x] `(p *Payload) ToImageRequest() *imgcache.ImageRequest` -- [x] Create `internal/encurl/encurl_test.go` with tests for: - - [x] Generate/parse round-trip preserves all fields - - [x] Expired URL returns `ErrExpired` - - [x] Malformed token returns error - - [x] Tampered token fails decryption - -### Phase 4: HTML Templates -- [x] Create `internal/templates/templates.go` with: - - [x] `//go:embed *.html` for embedded templates - - [x] `Get() *template.Template` function -- [x] Create `internal/templates/login.html`: - - [x] Simple form with password input for signing key - - [x] POST to `/` - - [x] Error message display area - - [x] Tailwind CSS styling -- [x] Create `internal/templates/generator.html`: - - [x] Logout link at top - - [x] Form with fields: Source URL, Width, Height, Format (dropdown), Quality, Fit Mode (dropdown), Expiration TTL (dropdown) - - [x] POST to `/generate` - - [x] Result display area showing generated URL and expiration - - [x] Click-to-copy functionality for generated URL - -### Phase 5: Auth Handlers -- [x] Create `internal/handlers/auth.go` with: - - [x] `HandleRoot() http.HandlerFunc` - serves login form (GET) or authenticates (POST) if not logged in; serves generator form if logged in - - [x] `handleLoginPost(w, r)` - validates key with constant-time comparison, creates session on success - - [x] `HandleLogout() http.HandlerFunc` - clears session, redirects to `/` - - [x] `HandleGenerateURL() http.HandlerFunc` - parses form, generates encrypted URL, renders generator.html with result - -### Phase 6: Encrypted Image Handler -- [x] Create `internal/handlers/imageenc.go` with: - - [x] `HandleImageEnc() http.HandlerFunc` - handles `/v1/e/{token}` - - [x] Extract token from chi URL param - - [x] Decrypt and validate via `encGen.Parse(token)` - - [x] Convert to `ImageRequest` via `payload.ToImageRequest()` - - [x] Serve via `imgSvc.Get()` (bypass signature validation - encrypted URL is trusted) - - [x] Set same response headers as regular image handler (Cache-Control, Content-Type, etc.) - - [x] Handle errors: expired → 410 Gone, decrypt fail → 400 Bad Request - -### Phase 7: Handler Integration -- [x] Modify `internal/handlers/handlers.go`: - - [x] Add `sessMgr *session.Manager` field to `Handlers` struct - - [x] Add `encGen *encurl.Generator` field to `Handlers` struct - - [x] Initialize session manager and URL generator in `initImageService()` - -### Phase 8: Route Registration -- [x] Modify `internal/server/routes.go`: - - [x] Add `s.router.Get("/", s.h.HandleRoot())` - login or generator page - - [x] Add `s.router.Post("/", s.h.HandleRoot())` - login form submission - - [x] Add `s.router.Get("/logout", s.h.HandleLogout())` - logout - - [x] Add `s.router.Post("/generate", s.h.HandleGenerateURL())` - URL generation - - [x] Add `s.router.Get("/v1/e/{token}", s.h.HandleImageEnc())` - encrypted image serving - - [x] Add static file serving for Tailwind CSS - -### Phase 9: Testing & Verification -- [x] Run `make check` to verify lint and tests pass +### Manual Testing (verify auth/encrypted URLs work) - [ ] Manual test: visit `/`, see login form - [ ] Manual test: enter wrong key, see error - [ ] Manual test: enter correct signing key, see generator form @@ -113,100 +12,49 @@ A single linear checklist of tasks to implement the complete pixa caching image - [ ] 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 +### Cache Management +- [ ] Implement cache size management/eviction (prevent disk from filling up) -## 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 +### Configuration +- [ ] Validate configuration on startup (fail fast on bad config) -## 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 +## P1: Important for Production -## 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 -- [x] Implement max input dimensions validation -- [x] Implement max output dimensions validation -- [ ] Implement EXIF/metadata stripping -- [x] Implement fit modes (cover, contain, fill, inside, outside) +### Security +- [ ] Implement blocked networks configuration (extend SSRF protection) +- [ ] Add rate limiting global concurrent fetches (prevent resource exhaustion) -## Security -- [x] Implement path traversal prevention -- [x] Implement request sanitization -- [x] Implement response header sanitization +### Image Processing +- [ ] Implement EXIF/metadata stripping (privacy) + +## P2: Nice to Have + +### Security - [ ] 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 -- [x] Implement ETag generation and validation +### HTTP Response Handling - [ ] Implement Last-Modified headers -- [x] Implement conditional requests (If-None-Match, If-Modified-Since) -- [x] 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 -- [x] Implement metrics endpoint with auth +### Additional Endpoints - [ ] Implement auto-format selection (format=auto based on Accept header) -## Configuration +### Configuration - [ ] Add all configuration options from README - [ ] Implement environment variable overrides - [ ] Implement YAML config file support -- [ ] Validate configuration on startup -## Operational -- [x] Implement graceful shutdown +### Operational - [ ] 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 +### Documentation - [ ] Document configuration options - [ ] Document API endpoints - [ ] Document deployment guide