Compliance Research Cache
TMDB API Terms of Use
Researched: 2026-04-05 (from existing knowledge, web search unavailable)
Key requirements from TMDB API Terms of Use:
- Attribution Required: Must display "This product uses the TMDB API but is not endorsed or certified by TMDB" or equivalent. The TMDB logo must be displayed with a link to tmdb.org.
- No Data Caching Beyond 6 Months: TMDB data cached locally should be refreshed. Long-term storage of TMDB metadata is permitted but images must be served from TMDB's CDN (image.tmdb.org).
- Image Serving: Poster images MUST be served from TMDB's image CDN. You cannot download and host TMDB images on your own servers.
- Rate Limits: Approximately 40 requests per 10 seconds per API key (free tier). No hard monthly cap but abuse triggers throttling.
- No Commercial Use Without Permission: Free tier is for personal/non-commercial projects. Commercial use requires separate agreement.
- Data Freshness: Apps should periodically refresh stored data to keep it current.
- API Key Security: API keys must not be exposed in client-side code. Use server-side proxying.
GDPR and Anonymous Users
Researched: 2026-04-05 (from existing knowledge, web search unavailable)
Key considerations:
- UUIDs as Personal Data: Under GDPR, any unique identifier that can single out an individual is personal data, including UUIDs stored in local storage.
- Local Storage/Cookies: Even without traditional cookies, local storage containing user identifiers triggers GDPR obligations.
- IP Logging: Server logs that capture IP addresses are personal data under GDPR.
- Lawful Basis: Even for anonymous-style auth, you need a lawful basis (likely legitimate interest or consent).
- Right to Deletion: Users must be able to delete their account and all associated data.
- Privacy Policy Required: Even minimal data collection requires a privacy policy.
- Data Minimization: Only collect what is necessary.
- Cookie/Storage Consent: Depending on jurisdiction (ePrivacy Directive in EU), storing identifiers in local storage may require consent banners.
WCAG 2.1 AA Requirements
Researched: 2026-04-05 (from existing knowledge, web search unavailable)
Key requirements for a PWA like MovieDice:
- Color Contrast: Minimum 4.5:1 for normal text, 3:1 for large text (18pt+ or 14pt+ bold).
- Touch Targets: Minimum 44x44 CSS pixels (mentioned in scope).
- Focus Management: All interactive elements must be keyboard-focusable with visible focus indicators.
- Screen Reader Support: aria-labels on icon buttons, semantic HTML, alt text on images.
- Motion/Animation: Respect
prefers-reduced-motion media query; provide option to disable animations.
- Text Resize: Content must be readable at 200% zoom.
- Form Labels: All inputs must have associated labels.
- Error Identification: Form errors must be clearly identified and described.
- Heading Hierarchy: Proper heading structure (h1-h6).
- Link Purpose: Link text must describe the destination.
PWA Standards
Researched: 2026-04-05 (from existing knowledge, web search unavailable)
- Web App Manifest: Required fields: name, short_name, icons (multiple sizes), start_url, display, theme_color, background_color.
- Service Worker: Required for installability. Must handle fetch events.
- HTTPS: Required for service workers and PWA installation.
- Offline Support: Service worker should cache critical assets for offline use.
- Icons: Multiple sizes required (192x192, 512x512 minimum). Maskable icons recommended.
- Splash Screen: Configured via manifest properties.
Docker + Next.js Best Practices
Researched: 2026-04-05 (from existing knowledge, web search unavailable)
- Multi-stage builds: Use separate build and runtime stages to minimize image size.
- Non-root user: Run the application as a non-root user in the container.
- Next.js standalone output: Use
output: 'standalone' in next.config.js for optimized Docker images.
- Health checks: Include HEALTHCHECK instruction in Dockerfile.
- Environment variables: Use ARG for build-time, ENV for runtime. Never bake secrets into images.
- Signal handling: Use
dumb-init or tini for proper signal forwarding (PID 1 problem).
- Layer caching: Copy package.json and install dependencies before copying source code.
- Security scanning: Scan images for vulnerabilities regularly.
- .dockerignore: Exclude node_modules, .git, .env files from build context.
TMDB Terms of Service — Deep Dive
Researched: 2026-04-05 (second pass, from existing knowledge)
Additional requirements beyond basic attribution:
- Image Hotlinking vs Caching: TMDB ToS Section 3(A) prohibits "storing" TMDB content except for reasonable caching. The distinction: serving images directly from
image.tmdb.org via <img> tags is compliant. Downloading images to your own server/CDN and re-serving them is a violation. Using next/image as a proxy (which downloads, optimizes, and re-serves) is a gray area — the scope correctly avoids this by using native TMDB URLs.
- Data Freshness Obligation: TMDB ToS require that cached/stored data be refreshed periodically. The scope's bi-weekly trailer refresh is a start, but the core movie metadata (title, genres, poster_path) stored in the
movies table has NO refresh mechanism. If TMDB updates a poster or corrects metadata, the app would serve stale data indefinitely.
- Rate Limits (Free Tier): ~40 requests per 10 seconds (~4/s average). No hard monthly cap. HTTP 429 responses include
Retry-After header. The scope does not specify handling of 429 responses in the server proxy.
- TMDB Logo Requirements: The logo must be used as provided (no modification). Minimum size requirements exist. The logo files are available at https://www.themoviedb.org/about/logos-attribution. The scope says "logo + link + disclaimer" but does not specify using the official logo assets.
- Content Filtering: TMDB includes adult content. The scope does not specify filtering
adult: true results from API responses.
GDPR and Supabase Anonymous Auth — Deep Dive
Researched: 2026-04-05 (second pass, from existing knowledge)
- Supabase Internal Logging: Self-hosted Supabase components (GoTrue, PostgREST, Kong) all produce their own logs containing IP addresses, user agent strings, and auth tokens. These are personal data under GDPR. The scope mentions no log management for the Supabase stack — only the Next.js app.
- JWT as Personal Data: The JWT issued by
signInAnonymously() contains the user's UUID in the sub claim, token expiry, and role. While the JWT itself is ephemeral, it is transmitted over the network and stored in the browser. Under GDPR, the UUID within is personal data (it singles out an individual). The JWT is not the concern — the UUID it carries is.
- Supabase Auth Tables: GoTrue stores its own
auth.users table with: id, created_at, updated_at, last_sign_in_at, raw_app_meta_data, raw_user_meta_data, is_anonymous flag, and more. This is separate from the app's public.users table. Account deletion must also clean up auth.users.
- Data Processor Agreement: Under GDPR, if using a third-party to process data, a Data Processing Agreement (DPA) is required. For self-hosted Supabase, the deployer IS the data controller and processor — no DPA needed with Supabase Inc. However, TMDB receives search queries (which could contain personal preferences) — TMDB's privacy policy should be referenced.
- Legitimate Interest Basis: For anonymous auth with no email, the lawful basis is likely "legitimate interest" (Art. 6(1)(f)) or "performance of a contract" (Art. 6(1)(b)). The privacy policy must state which basis is used.
WCAG 2.1 AA — Animation and Component Accessibility Deep Dive
Researched: 2026-04-05 (second pass, from existing knowledge)
- WCAG 2.2.2 Pause, Stop, Hide: Any auto-playing animation that (a) starts automatically, (b) lasts more than 5 seconds, and (c) is presented in parallel with other content MUST have a mechanism to pause, stop, or hide it. The landing page slot-machine reel (if it auto-plays or loops) would fall under this. The scope's
prefers-reduced-motion handling addresses motion but NOT the pause/stop requirement for users who want motion but need control over it.
- WCAG 2.4.7 Focus Visible: All interactive elements must have a visible focus indicator. The scope mentions focus management for the inline panel but does not mention visible focus styles for poster cards, buttons, or other interactive elements. Tailwind's default
outline-none on focus is a common violation.
- WCAG 1.1.1 Non-text Content: All poster images need meaningful
alt text (movie title + year minimum). The scope does not specify alt text strategy. Reel animation posters spinning at high speed should have aria-hidden="true" during spin and meaningful alt on the final result.
- WCAG 1.3.1 Info and Relationships: The poster grid should use semantic list markup (
<ul>/<li>) or role="list"/role="listitem". The watched/unwatched sections should have headings or aria-label.
- WCAG 4.1.3 Status Messages: The roll result, search results count, filter results, and "No matches" messages are status messages that should use
role="status" or aria-live="polite" to announce to screen readers without stealing focus.
- WCAG 2.4.3 Focus Order: The grid + inline panel expansion must maintain logical focus order. When a panel is open, Tab should move through panel controls, not skip to the next grid row behind it.
- Poster Grid Alt Text: Each poster
<img> must have alt="[Movie Title] ([Year]) poster". Decorative overlays (avatar, binoculars) should be aria-hidden="true" with their meaning conveyed via aria-label on the card or via screen-reader-only text.
PWA Compliance — Requirements Deep Dive
Researched: 2026-04-05 (second pass, from existing knowledge)
- Minimum Installability Criteria (Chrome): (a) Valid web app manifest with
name or short_name, icons (192x192 + 512x512 PNG), start_url, display (standalone/fullscreen/minimal-ui). (b) Served over HTTPS. (c) Registered service worker with a fetch handler. (d) Chrome removed the offline-page requirement for installability in 2022, but best practice still expects it.
- iOS/Safari PWA Limitations: Safari does not support the
beforeinstallprompt event. PWA on iOS uses <meta name="apple-mobile-web-app-capable"> and apple-touch-icon. Push notifications were added in iOS 16.4 but are limited. @serwist/next handles most of this.
- Maskable Icons: Required for adaptive icon display on Android. The scope does not mention maskable icons. The manifest should include
"purpose": "any maskable" icons.
- Splash Screen: Generated from manifest
name, background_color, theme_color, and the 512x512 icon. iOS requires separate apple-touch-startup-image meta tags for different screen sizes, which @serwist/next may not auto-generate.
display: standalone: The scope does not specify the display mode. This should be explicitly set.
- Service Worker Scope:
@serwist/next registers at / by default, which is correct. But the service worker must NOT cache API routes (/api/*) or Supabase WebSocket connections.
CIS Docker Benchmark — Key Requirements
Researched: 2026-04-05 (from existing knowledge)
CIS Docker Benchmark v1.6.0 key controls for container configuration:
- 4.1 — Create a user for the container: Do not run as root. The scope specifies non-root user — good.
- 4.2 — Use trusted base images:
node:22-slim is an official image — acceptable. Should pin to a specific digest or version tag, not latest.
- 4.5 — Enable Content Trust:
DOCKER_CONTENT_TRUST=1 ensures only signed images are pulled.
- 4.6 — Add HEALTHCHECK: The scope specifies
/api/health — good.
- 5.2 — Do not use host networking: docker-compose should use bridge networking (default).
- 5.4 — Restrict Linux capabilities: Drop all capabilities and add only what's needed:
cap_drop: [ALL].
- 5.10 — Limit memory: The scope should specify
mem_limit in docker-compose.
- 5.11 — Set CPU shares: Consider
cpus limit to prevent runaway containers.
- 5.12 — Mount root filesystem as read-only: Use
read_only: true with explicit tmpfs mounts for writable paths.
- 5.15 — Do not share host PID namespace: Default in docker-compose.
- 5.25 — Restrict container from gaining additional privileges:
security_opt: [no-new-privileges:true].
- Secrets Management: Env vars in docker-compose should use Docker secrets or
.env file with restricted permissions, not inline values.
Self-Hosted Supabase — Data Handling
Researched: 2026-04-05 (from existing knowledge)
- Backup Strategy: Self-hosted Supabase runs PostgreSQL in Docker. No automatic backups unless configured. Options: (a)
pg_dump cron job to a mounted volume, (b) WAL archiving for point-in-time recovery, (c) volume snapshots if using a cloud VM with snapshot support. The scope mentions this gap was flagged in review 1, but no specific solution is specified in the updated scope.
- Encryption at Rest: PostgreSQL in Docker does NOT encrypt data at rest by default. Options: (a) Use LUKS/dm-crypt on the host volume, (b) Use PostgreSQL's pgcrypto for column-level encryption (selective), (c) Use an encrypted filesystem for Docker volumes. For GDPR, encryption at rest is not strictly required but is a recommended safeguard (Art. 32).
- Log Retention: Self-hosted Supabase components (Kong, GoTrue, PostgREST, Realtime) all output logs to Docker's logging driver. Default: json-file with no rotation. This means logs grow unbounded. Must configure Docker log rotation (
max-size, max-file) or use a log driver with rotation (e.g., local).
- Supabase Studio: The self-hosted stack includes Supabase Studio (admin UI). This should NOT be publicly accessible — restrict to localhost or VPN only. If exposed, it provides full database access with the service role key.
Privacy Policy — Required Sections (EU/US)
Researched: 2026-04-05 (from existing knowledge)
For an app operating in the EU (GDPR) and US (CCPA/state laws):
- Identity of the controller: Who is responsible for the data (name, contact details).
- What data is collected: Exhaustive list (UUID, display name, avatar color, group membership, movie preferences, IP addresses in logs, device info from user agent).
- Purpose of processing: Why each data point is collected.
- Lawful basis (GDPR): Which Art. 6(1) basis applies to each processing activity.
- Data retention periods: How long each data type is kept (the 12-month inactive deletion is a start but all categories need periods).
- Third-party data sharing: Any third parties that receive data (TMDB receives search queries; Sentry receives error data including potentially user context).
- International data transfers: If data crosses borders (self-hosted means data stays on the server's jurisdiction, but TMDB API calls go to TMDB servers, Sentry data goes to Sentry's servers).
- User rights: Right to access, rectify, erase, restrict processing, data portability, object. For anonymous auth, some rights are limited (no email to verify identity).
- How to exercise rights: Contact method or self-service mechanism.
- Cookie/storage disclosure: What is stored in localStorage/cookies and why.
- Children's data (COPPA/GDPR): State whether the app is intended for users under 13/16. If not, disclaim it.
- Changes to the policy: How users will be notified of updates.
- CCPA-specific (California): Right to know, right to delete, right to opt-out of sale (even if no data is sold, must state this), categories of personal information.
Caddy HTTPS and TLS Compliance
Researched: 2026-04-05 (from existing knowledge)
- Caddy Auto-HTTPS: Caddy automatically obtains and renews Let's Encrypt (or ZeroSSL) certificates. TLS 1.2 is the minimum by default; TLS 1.3 is preferred. This meets PCI-DSS and general compliance requirements for TLS.
- HSTS: Caddy does NOT add HSTS headers by default. Must be configured explicitly in the Caddyfile. The scope mentions HSTS in security headers but at the Next.js/Caddy level — must ensure it's actually configured in Caddy, not just Next.js (since Caddy terminates TLS).
- OCSP Stapling: Caddy enables OCSP stapling by default — good for performance and security.
- Certificate Transparency: Let's Encrypt certificates are logged to CT logs by default.
- Internal Supabase Traffic: Traffic between Caddy and the Next.js container, and between Next.js and the Supabase stack, is internal Docker network traffic. This is unencrypted by default. For compliance-sensitive deployments, internal traffic should also be encrypted (mTLS), but for a personal/small-group app this is typically acceptable.
- Cipher Suite: Caddy's default cipher suite is modern and secure. No configuration needed unless targeting older clients.
- Certificate Renewal: Caddy renews certificates ~30 days before expiry. For self-hosted with a dynamic DNS or non-standard domain setup, ensure the ACME challenge can succeed (HTTP-01 requires port 80 access, TLS-ALPN-01 requires port 443).