Rend les liens entre notes visibles et persistants (sync NoteLink au save, auto-save, graphe réseau rafraîchi), ajoute living blocks, Memory Echo, recherche globale, consentement IA explicite et consolide les prototypes design en architectural-grid. Co-authored-by: Cursor <cursoragent@cursor.com>
14 KiB
Story 4.4: Explicit AI Processing Consent
Status: review
Story
As a user, I want to explicitly consent before any of my personal data is sent to a third-party AI, so that I retain full control over my data privacy and comply with GDPR requirements.
Epic: Epic 4 — Enterprise Compliance & Privacy (B2B Requirements)
FR coverage: NFR-GDPR4 (explicit user consent when processing personal data through AI APIs)
Out of scope for this story: Cookie consent banner (Story 4.1), account deletion (Story 4.2), subscription billing UI (Story 3.6).
Acceptance Criteria
- [AC1] Just-in-Time Trigger (NFR-GDPR4): On triggering any AI-powered feature (including Contextual AI Chat actions, Auto-Tagging, Title Suggestions, Notebook Summary, Batch Organize, and Brainstorm Waves), the application intercepts the request. If the user has not yet consented, the request is blocked, and the AI Processing Consent Modal is shown.
- [AC2] Consent Modal UI: An elegant modal titled "Consentement requis pour le traitement par IA" (FR) / "AI Processing Consent Required" (EN) that clearly informs the user that their note contents and PDF texts will be sent to external, third-party AI APIs (such as OpenAI, Gemini, DeepSeek, etc.). It guarantees that:
- Zero-data-retention is requested on all outbound queries (complying with FR23).
- No data is used to train third-party models.
- [AC3] Interactive Controls: The modal features:
- A primary "Autoriser et continuer" (Approve & Continue) button.
- A secondary "Refuser" (Cancel / Reject) button (closes modal, cancels the pending AI request with a descriptive feedback toast).
- A checkbox "Se souvenir de mon choix (ne plus demander)" (Remember my choice / Do not ask again).
- [AC4] Persistence & Scope:
- If "Remember my choice" is checked: the consent is saved in
localStorage['memento-ai-consent-v1']AND synced to the database viaupdateAISettings({ aiProcessingConsent: true })(updatingUserAISettings.aiProcessingConsent). - If unchecked: consent is saved only in React context memory (session-wide), and the modal will reappear in a subsequent browser session.
- If "Remember my choice" is checked: the consent is saved in
- [AC5] Server-side Enforcement & Audit Logging:
- Upon granting consent, a request is made to
POST /api/user/ai-consentwith{ consent: true, remember: boolean }. - The endpoint creates a permanent record in a new
AiConsentLogdatabase table (capturinguserId,consent,ipAddress[anonymized],userAgent, andtimestamp). - All server-side AI API routes (
/api/ai/*and/api/chat) perform an active validation check onUserAISettings.aiProcessingConsent(or verify session consent headers). If no consent is logged or passed, the request returns403 Forbiddenwith{ error: "ai_consent_required" }.
- Upon granting consent, a request is made to
- [AC6] Memory Echo Exemption: The background semantic connection engine ("Memory Echo" - FR7) actively checks
UserAISettings.aiProcessingConsent. If the user has not explicitly consented to third-party AI processing, the background generator immediately skips processing their notes to prevent silent server-side data leakage. - [AC7] Revocation in Settings: In Settings → General (or a dedicated settings view), a "GDPR AI Processing" card displays the current consent state with a primary "Révoquer le consentement" (Revoke Consent) button. Revocation sets
UserAISettings.aiProcessingConsentandlocalStorageflags back tofalseinstantly. - [AC8] i18n & Theme Compliance: All visible strings are fully localized across all 15 JSON locale files under
memento-note/locales/*.json. The modal and settings card strictly match the Ethereal Precision v2 theme styling, colors, and border widths (no legacy themes, no new blue styling).
Tasks / Subtasks
-
Task 1: Database Schema & Actions Setup (AC: #5, #6, #7)
- Subtask 1.1: In
memento-note/prisma/schema.prisma, addaiProcessingConsent Boolean @default(false)to theUserAISettingsmodel. - Subtask 1.2: Add the
AiConsentLogmodel to track explicit opt-ins:model AiConsentLog { id String @id @default(cuid()) userId String consent Boolean @default(true) ipAddress String? userAgent String? createdAt DateTime @default(now()) user User @relation(fields: [userId], references: [id], onDelete: Cascade) } - Subtask 1.3: Update
USER_AI_SETTINGS_PRISMA_KEYSinmemento-note/app/actions/ai-settings.tsto includeaiProcessingConsentso it is safely exposed for server action updates. - Subtask 1.4: Run a database migration using
scripts/migrate-docker.sh(ornpx prisma migrate dev --name add_ai_consent) without wiping any existing data.
- Subtask 1.1: In
-
Task 2: Server API -
POST /api/user/ai-consent(AC: #5)- Subtask 2.1: Create
memento-note/app/api/user/ai-consent/route.ts. - Subtask 2.2: Authenticate session using
auth()(return 401 if missing). - Subtask 2.3: Parse
{ consent, remember }from body. - Subtask 2.4: Log anonymized/hashed IP address and User-Agent in
AiConsentLog. - Subtask 2.5: If
rememberis true, update user's AI settingsaiProcessingConsent = trueinUserAISettingstable. - Subtask 2.6: Return
{ success: true }.
- Subtask 2.1: Create
-
Task 3: Server-side AI Route Enforcement (AC: #5, #6)
- Subtask 3.1: Create a middleware or utility check in
memento-note/lib/consent/server-consent.tsto check if an authenticated user has active AI consent. - Subtask 3.2: Update all
/api/ai/*routes (auto-labels, tags, batch-organize, title-suggestions) to invoke this check at the very beginning of the request handler. Return403if false. - Subtask 3.3: Update
memento-note/app/api/chat/route.tsto enforce the same check. - Subtask 3.4: Integrate the check into the "Memory Echo" background processing loop (
memento-note/app/api/ai/echo/route.ts). If the user does not have consent, log a compliance skip and exit silently.
- Subtask 3.1: Create a middleware or utility check in
-
Task 4: Client-side Just-in-Time Interceptor & UI (AC: #1, #2, #3, #4, #8)
- Subtask 4.1: Create
memento-note/lib/consent/ai-consent-client.tsto manage local storage flags (memento-ai-consent-v1) and session-wide state. - Subtask 4.2: Build the React Context Provider
AiConsentProviderinmemento-note/components/legal/ai-consent-provider.tsxto wrapper the workspace layout. It exposeshasAiConsent,requestAiConsent(), and state. - Subtask 4.3: Create the UI modal
components/legal/ai-consent-modal.tsxwith high-premium styling matching the Ethereal Precision v2 visual system. - Subtask 4.4: Update contextual AI chat trigger, auto-tagging buttons, and brainstorm canvas actions to first wrap their API dispatch calls in
requestAiConsent(). If aborted, stop loading state and show a warning toast.
- Subtask 4.1: Create
-
Task 5: Settings Integration & i18n (AC: #7, #8)
- Subtask 5.1: Update
memento-note/app/(main)/settings/general/general-settings-client.tsxto append a "Consentement de traitement IA" GDPR settings card showing the current state and a prominent "Révoquer le consentement" button. - Subtask 5.2: Ensure that clicking revoke resets both
localStorageandUserAISettings.aiProcessingConsentto false. - Subtask 5.3: Add i18n keys for
consent.ai.*in all 15 JSON files undermemento-note/locales/*.json(e.g.,title,description,confirm,cancel,rememberMe,revokedToast, etc.).
- Subtask 5.1: Update
-
Task 6: Compliance Validation & Testing (AC: all)
- Subtask 6.1: Verify cookie opt-out doesn't trigger AI block, and AI opt-out doesn't block cookie dialog.
- Subtask 6.2: Verify a fresh session triggers the modal exactly at the moment of clicking "Reformuler" or "Auto-Tag".
- Subtask 6.3: Verify selecting "Se souvenir de mon choix" writes to
localStorageand DB, and avoids future prompts. - Subtask 6.4: Verify server endpoints return 403 when consent is false.
- Subtask 6.5: Run
npm run buildinmemento-note/to ensure compile success.
Dev Notes
Key Architecture Patterns
- Prisma Cascade & Models: The cascade delete works perfectly because
UseronDelete: Cascade coversUserAISettingsandAiConsentLog(Story 4.2 integration). - IP Address Privacy: For strict GDPR compliance, do not store raw IP addresses. Hash/anonymize them on the server side:
import { createHash } from 'crypto'; const anonymizedIp = ip ? createHash('sha256').update(ip).digest('hex') : null;
Locales i18n Structure
We will add the following key structure to the locale JSON files:
"consent": {
"ai": {
"modalTitle": "Consentement requis pour le traitement par IA",
"modalDescription": "Pour analyser vos notes, PDFs ou sessions de remue-méninges, Memento transmet de manière sécurisée ces données à des API d'IA tierces (OpenAI, Gemini, DeepSeek). Nous appliquons une politique de rétention de données nulle. En acceptant, vous autorisez ce traitement.",
"rememberMe": "Se souvenir de mon choix (ne plus demander)",
"acceptButton": "Autoriser et continuer",
"rejectButton": "Refuser",
"revocationTitle": "Consentement de traitement IA (RGPD)",
"revocationDescription": "Gérez ou révoquez votre consentement pour le transfert de données personnelles vers des API d'IA tierces.",
"revokeButton": "Révoquer le consentement",
"revokedToast": "Consentement IA révoqué avec succès."
}
}
Server Action Helper Update
Add aiProcessingConsent inside USER_AI_SETTINGS_PRISMA_KEYS in app/actions/ai-settings.ts:
const USER_AI_SETTINGS_PRISMA_KEYS = [
// ... existing keys
'aiProcessingConsent'
] as const;
Dev Agent Record
Agent Model Used
Gemini 3.5 Flash (High)
Debug Log References
N/A
Completion Notes List
- Added database column
aiProcessingConsenttoUserAISettingsand new audit log tableAiConsentLogto local PostgreSQL using non-destructive custom DDL. - Created
POST /api/user/ai-consentto log user decisions with hashed IP addresses. - Created server-side consent validation check
hasUserAiConsentand implemented it in all major AI endpoints:/api/ai/tags,/api/ai/auto-labels,/api/ai/batch-organize,/api/ai/title-suggestions,/api/ai/describe-image,/api/ai/echo, and/api/chat. - Integrated background connection skip in
MemoryEchoServiceif consent is not granted (AC6). - Created client-side React context provider
AiConsentProviderand modal overlayAiConsentModalinmemento-note/components/legal/. - Embedded context wrapper globally inside
ProvidersWrapperto intercept all downstream triggers. - GDPR hardening (review follow-up): consentement session-only signé côté serveur via JWT NextAuth (
aiSessionConsent), suppression du header client forgeable ; audit obligatoire avant acceptation ; i18n complète (15 locales). - Integrated JIT consent checks in all editor contextual chat triggers, tag buttons, and actions.
- Appended GDPR AI Consent revocation card next to cookie management card in general Settings client, validated with auto-resetting DB and localStorage states.
- Automatically injected localized string structures into all 15 JSON language translation files under
memento-note/locales/. - Validated compile success with
npm run buildpassing 100% with Turbopack compiler.
File List
memento-note/prisma/schema.prismamemento-note/app/actions/ai-settings.tsmemento-note/app/api/user/ai-consent/route.tsmemento-note/lib/consent/server-consent.tsmemento-note/lib/consent/ai-consent-client.tsmemento-note/lib/ai/services/memory-echo.service.tsmemento-note/app/api/ai/tags/route.tsmemento-note/app/api/ai/auto-labels/route.tsmemento-note/app/api/ai/batch-organize/route.tsmemento-note/app/api/ai/title-suggestions/route.tsmemento-note/app/api/chat/route.tsmemento-note/app/api/ai/echo/route.tsmemento-note/app/api/ai/describe-image/route.tsmemento-note/components/legal/ai-consent-modal.tsxmemento-note/components/legal/ai-consent-provider.tsxmemento-note/components/providers-wrapper.tsxmemento-note/app/(main)/settings/general/general-settings-client.tsxmemento-note/locales/*.json(15 files)
Review Findings
-
[Review][Decision] Mécanisme de consentement session-only — Option A retenue : consentement session stocké dans le JWT NextAuth (
session.aiSessionConsent) viaupdateSession({ aiSessionConsent: true })après auditPOST /api/user/ai-consent. Header clientx-memento-session-ai-consentsupprimé (non forgeable). -
[Review][Patch] Routes IA sans
hasUserAiConsent— protégées : reformulate, notebook-summary, enrich-from-resource, transform-markdown, suggest-charts, personas, suggest-notebook, echo/fusion, translate, echo/connections. Exclu volontairement : convert-markdown (conversion locale sans API tierce). -
[Review][Patch] Intercepteurs client JIT — ajoutés sur rich-text-editor, note-editor-context, hooks auto-tag/title, dialogs batch/summary/auto-label, personas, fusion, note-editor-full-page, note-title-block, contextual-ai-chat. Suggestions passives (notebook toast, auto-label hook) silencieuses sans consentement.
-
[Review][Patch] Sync DB consentement —
initialPersistentConsentdepuis layout (getCachedAISettings), plus d’appel/api/ai/configau mount. -
[Review][Patch] Consentement accordé malgré échec audit —
handleConfirmrefuse le consentement siPOST /api/user/ai-consentéchoue. -
[Review][Patch] Texte hardcodé modal — badge via
consent.ai.complianceBadge(15 locales). -
[Review][Patch] Clé i18n
consent.ai.revoked+auditFailed— ajoutées aux 15 locales. -
[Review][Patch] Headers chat stale —
getConsentHeaderssupprimé ; contrôle serveur JWT uniquement. -
[Review][Defer] PUT
/api/ai/batch-organizesans check consent — deferred, pre-existing : n’appelle pas d’API tierce, applique seulement le plan en DB