Files
Momento/docs/4-4-explicit-ai-consent.md
Antigravity e2672cd2c2
Some checks failed
CI / Lint, Test & Build (push) Failing after 1m19s
CI / Deploy production (on server) (push) Has been skipped
feat(notes): liens internes, onglet Réseau, living blocks et consentement IA
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>
2026-05-24 14:27:29 +00:00

201 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Story 4.4: Explicit AI Processing Consent
Status: review
<!-- Ultimate context engine analysis completed - comprehensive developer guide created -->
## 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
1. [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.
2. [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.
3. [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).
4. [AC4] **Persistence & Scope:**
- If *"Remember my choice"* is checked: the consent is saved in `localStorage['memento-ai-consent-v1']` AND synced to the database via `updateAISettings({ aiProcessingConsent: true })` (updating `UserAISettings.aiProcessingConsent`).
- If unchecked: consent is saved only in React context memory (session-wide), and the modal will reappear in a subsequent browser session.
5. [AC5] **Server-side Enforcement & Audit Logging:**
- Upon granting consent, a request is made to `POST /api/user/ai-consent` with `{ consent: true, remember: boolean }`.
- The endpoint creates a permanent record in a new `AiConsentLog` database table (capturing `userId`, `consent`, `ipAddress` [anonymized], `userAgent`, and `timestamp`).
- All server-side AI API routes (`/api/ai/*` and `/api/chat`) perform an active validation check on `UserAISettings.aiProcessingConsent` (or verify session consent headers). If no consent is logged or passed, the request returns `403 Forbidden` with `{ error: "ai_consent_required" }`.
6. [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.
7. [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.aiProcessingConsent` and `localStorage` flags back to `false` instantly.
8. [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
- [x] **Task 1: Database Schema & Actions Setup (AC: #5, #6, #7)**
- [x] Subtask 1.1: In `memento-note/prisma/schema.prisma`, add `aiProcessingConsent Boolean @default(false)` to the `UserAISettings` model.
- [x] Subtask 1.2: Add the `AiConsentLog` model to track explicit opt-ins:
```prisma
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)
}
```
- [x] Subtask 1.3: Update `USER_AI_SETTINGS_PRISMA_KEYS` in `memento-note/app/actions/ai-settings.ts` to include `aiProcessingConsent` so it is safely exposed for server action updates.
- [x] Subtask 1.4: Run a database migration using `scripts/migrate-docker.sh` (or `npx prisma migrate dev --name add_ai_consent`) without wiping any existing data.
- [x] **Task 2: Server API - `POST /api/user/ai-consent` (AC: #5)**
- [x] Subtask 2.1: Create `memento-note/app/api/user/ai-consent/route.ts`.
- [x] Subtask 2.2: Authenticate session using `auth()` (return 401 if missing).
- [x] Subtask 2.3: Parse `{ consent, remember }` from body.
- [x] Subtask 2.4: Log anonymized/hashed IP address and User-Agent in `AiConsentLog`.
- [x] Subtask 2.5: If `remember` is true, update user's AI settings `aiProcessingConsent = true` in `UserAISettings` table.
- [x] Subtask 2.6: Return `{ success: true }`.
- [x] **Task 3: Server-side AI Route Enforcement (AC: #5, #6)**
- [x] Subtask 3.1: Create a middleware or utility check in `memento-note/lib/consent/server-consent.ts` to check if an authenticated user has active AI consent.
- [x] 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. Return `403` if false.
- [x] Subtask 3.3: Update `memento-note/app/api/chat/route.ts` to enforce the same check.
- [x] 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.
- [x] **Task 4: Client-side Just-in-Time Interceptor & UI (AC: #1, #2, #3, #4, #8)**
- [x] Subtask 4.1: Create `memento-note/lib/consent/ai-consent-client.ts` to manage local storage flags (`memento-ai-consent-v1`) and session-wide state.
- [x] Subtask 4.2: Build the React Context Provider `AiConsentProvider` in `memento-note/components/legal/ai-consent-provider.tsx` to wrapper the workspace layout. It exposes `hasAiConsent`, `requestAiConsent()`, and state.
- [x] Subtask 4.3: Create the UI modal `components/legal/ai-consent-modal.tsx` with high-premium styling matching the *Ethereal Precision v2* visual system.
- [x] 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.
- [x] **Task 5: Settings Integration & i18n (AC: #7, #8)**
- [x] Subtask 5.1: Update `memento-note/app/(main)/settings/general/general-settings-client.tsx` to append a "Consentement de traitement IA" GDPR settings card showing the current state and a prominent "Révoquer le consentement" button.
- [x] Subtask 5.2: Ensure that clicking revoke resets both `localStorage` and `UserAISettings.aiProcessingConsent` to false.
- [x] Subtask 5.3: Add i18n keys for `consent.ai.*` in all 15 JSON files under `memento-note/locales/*.json` (e.g., `title`, `description`, `confirm`, `cancel`, `rememberMe`, `revokedToast`, etc.).
- [x] **Task 6: Compliance Validation & Testing (AC: all)**
- [x] Subtask 6.1: Verify cookie opt-out doesn't trigger AI block, and AI opt-out doesn't block cookie dialog.
- [x] Subtask 6.2: Verify a fresh session triggers the modal exactly at the moment of clicking "Reformuler" or "Auto-Tag".
- [x] Subtask 6.3: Verify selecting "Se souvenir de mon choix" writes to `localStorage` and DB, and avoids future prompts.
- [x] Subtask 6.4: Verify server endpoints return 403 when consent is false.
- [x] Subtask 6.5: Run `npm run build` in `memento-note/` to ensure compile success.
---
## Dev Notes
### Key Architecture Patterns
- **Prisma Cascade & Models:** The cascade delete works perfectly because `User` onDelete: Cascade covers `UserAISettings` and `AiConsentLog` (Story 4.2 integration).
- **IP Address Privacy:** For strict GDPR compliance, do not store raw IP addresses. Hash/anonymize them on the server side:
```typescript
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:
```json
"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`:
```typescript
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 `aiProcessingConsent` to `UserAISettings` and new audit log table `AiConsentLog` to local PostgreSQL using non-destructive custom DDL.
- Created `POST /api/user/ai-consent` to log user decisions with hashed IP addresses.
- Created server-side consent validation check `hasUserAiConsent` and 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 `MemoryEchoService` if consent is not granted (AC6).
- Created client-side React context provider `AiConsentProvider` and modal overlay `AiConsentModal` in `memento-note/components/legal/`.
- Embedded context wrapper globally inside `ProvidersWrapper` to 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 build` passing 100% with Turbopack compiler.
### File List
- `memento-note/prisma/schema.prisma`
- `memento-note/app/actions/ai-settings.ts`
- `memento-note/app/api/user/ai-consent/route.ts`
- `memento-note/lib/consent/server-consent.ts`
- `memento-note/lib/consent/ai-consent-client.ts`
- `memento-note/lib/ai/services/memory-echo.service.ts`
- `memento-note/app/api/ai/tags/route.ts`
- `memento-note/app/api/ai/auto-labels/route.ts`
- `memento-note/app/api/ai/batch-organize/route.ts`
- `memento-note/app/api/ai/title-suggestions/route.ts`
- `memento-note/app/api/chat/route.ts`
- `memento-note/app/api/ai/echo/route.ts`
- `memento-note/app/api/ai/describe-image/route.ts`
- `memento-note/components/legal/ai-consent-modal.tsx`
- `memento-note/components/legal/ai-consent-provider.tsx`
- `memento-note/components/providers-wrapper.tsx`
- `memento-note/app/(main)/settings/general/general-settings-client.tsx`
- `memento-note/locales/*.json` (15 files)
### Review Findings
- [x] [Review][Decision] Mécanisme de consentement session-only — **Option A retenue** : consentement session stocké dans le JWT NextAuth (`session.aiSessionConsent`) via `updateSession({ aiSessionConsent: true })` après audit `POST /api/user/ai-consent`. Header client `x-memento-session-ai-consent` **supprimé** (non forgeable).
- [x] [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).
- [x] [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.
- [x] [Review][Patch] Sync DB consentement — `initialPersistentConsent` depuis layout (`getCachedAISettings`), plus dappel `/api/ai/config` au mount.
- [x] [Review][Patch] Consentement accordé malgré échec audit — `handleConfirm` refuse le consentement si `POST /api/user/ai-consent` échoue.
- [x] [Review][Patch] Texte hardcodé modal — badge via `consent.ai.complianceBadge` (15 locales).
- [x] [Review][Patch] Clé i18n `consent.ai.revoked` + `auditFailed` — ajoutées aux 15 locales.
- [x] [Review][Patch] Headers chat stale — `getConsentHeaders` supprimé ; contrôle serveur JWT uniquement.
- [x] [Review][Defer] PUT `/api/ai/batch-organize` sans check consent — deferred, pre-existing : nappelle pas dAPI tierce, applique seulement le plan en DB