Files
pixa/TODO.md

10 KiB

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

  • Create Makefile with check, lint, test, fmt targets
  • Create project structure (cmd/pixad, internal/*)
  • Implement globals package
  • Implement logger package
  • Implement config package
  • Implement database package (SQLite)
  • Implement healthcheck service
  • Implement middleware package
  • Implement handlers package with placeholder routes
  • Implement server package (lifecycle, routing, HTTP)
  • Wire up fx dependency injection in main.go
  • Verify basic server starts and healthcheck works

Core Image Proxy Features

  • Implement URL parsing for /v1/image/<host>/<path>/<size>.<format>
  • Implement upstream HTTP client with TLS verification
  • Implement SSRF protection (block private/internal IPs)
  • Implement source host whitelist checking
  • Implement HMAC-SHA256 signature generation
  • Implement HMAC-SHA256 signature verification
  • Implement signature expiration checking
  • Implement upstream fetch with timeout and size limits
  • Implement Content-Type validation (whitelist MIME types)
  • Implement magic byte verification

Caching Layer

  • Design and create SQLite schema for cache metadata
  • Implement source content storage (cache/src-content/<hash>)
  • Implement source metadata storage (cache/src-metadata/<host>/<hash>.json)
  • Implement output content storage (cache/dst-content/<hash>)
  • Implement cache key generation
  • Implement cache lookup (in-memory hot path)
  • Implement cache write
  • Implement negative caching (404s)
  • Implement cache TTL and expiration
  • Implement cache size management/eviction

Image Processing

  • Select and integrate image processing library (libvips bindings or pure Go)
  • Implement image decoding (JPEG, PNG, WebP, GIF, AVIF)
  • Implement image resizing with size options (WxH, 0x0, orig)
  • Implement format conversion (JPEG, PNG, WebP, AVIF)
  • Implement quality parameter support
  • Implement max input dimensions validation
  • Implement max output dimensions validation
  • Implement EXIF/metadata stripping
  • 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

  • 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
  • Implement X-Pixa-Cache debug header (HIT/MISS/STALE)
  • Implement X-Request-ID propagation
  • Implement proper error response format (JSON)

Additional Endpoints

  • 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)
  • Write unit tests for URL parsing
  • Write unit tests for signature generation/verification
  • Write unit tests for cache operations
  • 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