# SSR Full Page Cache (FPC) Implementation Plan ## Overview Server-side rendered static page caching system using Playwright + Redis. Routes marked with `#[Static_Page]` auto-generate and serve pre-rendered HTML to unauthenticated users. ## Implementation Order ### Phase 1: Foundation & Research 1. Study `rsx:debug` command and Playwright script structure 2. Understand current Dispatch.php flow and route attribute processing ### Phase 2: Core Commands 3. Create `rsx:ssr_fpc:create` command (copy from `rsx:debug`) 4. Create Playwright script `generate-static-cache.js` in command's resource directory 5. Implement exclusive lock `GENERATE_STATIC_CACHE` in Playwright script 6. Strip GET parameters from URL before rendering 7. Handle redirect interception (manual redirect mode) 8. Capture response: DOM + headers OR redirect location 9. Generate cache structure with 30-char ETag (SHA1 of build_key + URL + content) ### Phase 3: Storage Layer 10. Implement Redis cache with key format: `ssr_fpc:{build_key}:{sha1(url)}` 11. Store JSON: `{url, code, page_dom/redirect, build_key, etag, generated_at}` 12. Add comprehensive error logging to `storage/logs/ssr-fpc-errors.log` ### Phase 4: Runtime Integration 13. Update `Session::__is_fpc_client()` with header check: `X-RSpade-FPC-Client: 1` 14. Add header to Playwright script 15. Create `#[Static_Page]` attribute stub in `.vscode/attribute-stubs.php` 16. Modify Dispatch.php to: - Check for `#[Static_Page]` attribute - Verify `!Session::is_active()` (unauthenticated only) - Skip FPC if `Session::__is_fpc_client()` is true - Check Redis cache, generate if missing (fatal on failure) - Validate ETag for 304 responses - Serve with proper cache headers (0s dev, 5min prod) ### Phase 5: Management & Config 17. Create `rsx:ssr_fpc:reset` command (flush Redis `ssr_fpc:*` keys) 18. Add config to `config/rsx.php`: ```php 'ssr_fpc' => [ 'enabled' => env('SSR_FPC_ENABLED', false), 'generation_timeout' => 30000, // ms ] ``` ### Phase 6: Documentation 19. Create `app/RSpade/man/ssr_fpc.txt` with: - Purpose and usage - Attribute syntax - Cache lifecycle - Bypass headers - Future roadmap (sitemap, parallelization, external service, shared secret) - Security considerations ### Phase 7: Testing 20. Test simple static page generation and serving 21. Test redirect caching and serving 22. Test ETag validation (304 responses) 23. Test authenticated user bypass 24. Test cache invalidation on build_key change ## Key Technical Decisions ### Redis Cache Key Format: `ssr_fpc:{build_key}:{sha1(request_path)}` - Auto-invalidates on deployment (build_key changes) - URL hash prevents key collisions - GET params stripped before hashing ### ETag First 30 chars of SHA1(build_key + url + content) ### FPC Client Header `X-RSpade-FPC-Client: 1` ### Session Check `Session::is_active()` (not `RsxAuth::check()`) ### Redirect Handling Cache first 302, don't follow ### Cache TTL Indefinite (Redis LRU handles eviction) ## Future Roadmap (Not in Initial Implementation) - Option for `rsx:ssr_fpc:create --from-sitemap` rather than a specific url - Shared, private, automatic key for the fpc runner, so the fpc headers are only recognized by the server itself - The same should be done for `rsx:debug` - External service to generate the fpc renderings - Parallelization - Programmatic cache reset for things like updating cms or blog posts