10 KiB
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/seal/crypto.gowith:DeriveKey(masterKey []byte, salt string) ([32]byte, error)- HKDF-SHA256 key derivationEncrypt(key [32]byte, plaintext []byte) (string, error)- NaCl secretbox encrypt, returns base64urlDecrypt(key [32]byte, ciphertext string) ([]byte, error)- NaCl secretbox decrypt from base64url
- Create
internal/seal/crypto_test.gowith 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/securecookiedependency - Create
internal/session/session.gowith:Datastruct:Authenticated bool,CreatedAt time.Time,ExpiresAt time.TimeManagerstruct using gorilla/securecookie with keys derived via HKDFNewManager(signingKey string, secure bool) (*Manager, error)CreateSession(w http.ResponseWriter) error- creates 30-day encrypted cookieValidateSession(r *http.Request) (*Data, error)- decrypts and validates cookieClearSession(w http.ResponseWriter)- clears cookie (logout)IsAuthenticated(r *http.Request) bool- convenience wrapper- Cookie settings:
HttpOnly,Secure(prod),SameSite=Strict, namepixa_session
- Create
internal/session/session_test.gowith 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/v2dependency for compact binary encoding - Create
internal/encurl/encurl.gowith:Payloadstruct (short CBOR field names, omitempty for fields with sane defaults):SourceHost string(cbor:"h") - requiredSourcePath string(cbor:"p") - requiredSourceQuery string(cbor:"q,omitempty") - optionalWidth int(cbor:"w,omitempty") - 0 = originalHeight int(cbor:"ht,omitempty") - 0 = originalFormat ImageFormat(cbor:"f,omitempty") - default: origQuality int(cbor:"ql,omitempty") - default: 85FitMode FitMode(cbor:"fm,omitempty") - default: coverExpiresAt int64(cbor:"e") - required, Unix timestamp
Generatorstruct with URL key derived via HKDF (salt:"pixa-urlenc-v1")NewGenerator(signingKey string) (*Generator, error)Generate(p *Payload) (string, error)- CBOR encode, encrypt, base64urlParse(token string) (*Payload, error)- base64url decode, decrypt, CBOR decode, validate expiration(p *Payload) ToImageRequest() *imgcache.ImageRequest
- Create
internal/encurl/encurl_test.gowith 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
internal/templates/templates.gowith://go:embed *.htmlfor embedded templatesGet() *template.Templatefunction
- Create
internal/templates/login.html:- Simple form with password input for signing key
- POST to
/ - Error message display area
- Tailwind CSS styling
- Create
internal/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 functionality for generated URL
Phase 5: Auth Handlers
- Create
internal/handlers/auth.gowith:HandleRoot() http.HandlerFunc- serves login form (GET) or authenticates (POST) if not logged in; serves generator form if logged inhandleLoginPost(w, r)- validates key with constant-time comparison, creates session on successHandleLogout() 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.gowith:HandleImageEnc() http.HandlerFunc- handles/v1/e/{token}- Extract token from chi URL param
- Decrypt and validate via
encGen.Parse(token) - Convert to
ImageRequestviapayload.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.Managerfield toHandlersstruct - Add
encGen *encurl.Generatorfield toHandlersstruct - Initialize session manager and URL generator in
initImageService()
- Add
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 - Add static file serving for Tailwind CSS
- Add
Phase 9: Testing & Verification
- Run
make checkto 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