Files
rfc-app/backend/migrations/013_cookie_consent.sql
T
Ben Stull 8aa65014b4 Release 0.13.0: cookie/privacy consent banner + policy pages (instrumentation prep)
Roadmap item #11. Ships the non-modal bottom-of-page cookie consent
banner, the default /privacy and /cookies policy pages, the
`cookie_consent` table + two §17 endpoints for server-side persistence,
the localStorage fallback for anonymous viewers, the /settings
"Privacy & cookies" tab for revisiting the choice, and the
`frontend/src/lib/consent.js` helper that roadmap item #13's analytics
SDK (v0.15.0) will gate against. No analytics SDK ships in this release
— the consent infrastructure goes in first so the gate is already in
place. Adds SPEC §14.5 / §14.6, lists two new endpoints in §17, names
the new table in §5, and surfaces four §19.2 candidates (content-repo
file vs env-var policy, GPC / DNT headers, i18n, item-#13 dependency).
Two new optional env vars (`VITE_PRIVACY_POLICY_URL`,
`VITE_COOKIES_POLICY_URL`) — defaults render the framework's stub
pages.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 01:08:23 -07:00

36 lines
1.8 KiB
SQL

-- v0.13.0 / roadmap item #11 — cookie consent.
--
-- The framework now ships a non-modal cookie consent banner per the
-- privacy-and-cookies UX (SPEC §14.5 / §14.6). Authenticated viewers
-- get their choice persisted server-side so it survives sign-out /
-- sign-in across devices; anonymous viewers persist their choice in
-- localStorage only.
--
-- Shape: a single row per user, three flags, plus a recorded-at stamp.
-- The flags are:
-- - essential: the framework's strictly-necessary cookies (session,
-- itsdangerous-signed payloads, CSRF if any). Permanently
-- true at the API surface — included in the row for
-- symmetry with the analytics / other flags rather than
-- because the user can switch it off.
-- - analytics: reserved for the §13 analytics SDK gating that lands
-- in v0.15.0. Off by default; opt-in via the banner.
-- - other: everything else (third-party embeds, social widgets).
-- Off by default; opt-in via the banner.
--
-- A NULL recorded_at means "no choice yet" — the banner should re-prompt
-- the next time the user signs in on a fresh device. Once recorded_at is
-- set, the banner is hidden until the user re-opens it from the
-- /settings/notifications "Privacy & cookies" tab.
--
-- The row is created lazily on first PUT. Absence of a row is equivalent
-- to NULL recorded_at — the banner shows.
CREATE TABLE cookie_consent (
user_id INTEGER PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE,
essential INTEGER NOT NULL DEFAULT 1 CHECK (essential IN (0, 1)),
analytics INTEGER NOT NULL DEFAULT 0 CHECK (analytics IN (0, 1)),
other_cookies INTEGER NOT NULL DEFAULT 0 CHECK (other_cookies IN (0, 1)),
recorded_at TEXT
);