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

14 KiB
Raw Permalink Blame History

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

  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

  • Task 1: Database Schema & Actions Setup (AC: #5, #6, #7)

    • Subtask 1.1: In memento-note/prisma/schema.prisma, add aiProcessingConsent Boolean @default(false) to the UserAISettings model.
    • Subtask 1.2: Add the AiConsentLog model 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_KEYS in memento-note/app/actions/ai-settings.ts to include aiProcessingConsent so it is safely exposed for server action updates.
    • 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.
  • 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 remember is true, update user's AI settings aiProcessingConsent = true in UserAISettings table.
    • Subtask 2.6: Return { success: true }.
  • 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.ts to 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. Return 403 if false.
    • Subtask 3.3: Update memento-note/app/api/chat/route.ts to 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.
  • 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.ts to manage local storage flags (memento-ai-consent-v1) and session-wide state.
    • 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.
    • Subtask 4.3: Create the UI modal components/legal/ai-consent-modal.tsx with 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.
  • Task 5: Settings Integration & i18n (AC: #7, #8)

    • 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.
    • Subtask 5.2: Ensure that clicking revoke resets both localStorage and UserAISettings.aiProcessingConsent to false.
    • 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.).
  • 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 localStorage and DB, and avoids future prompts.
    • Subtask 6.4: Verify server endpoints return 403 when consent is false.
    • 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:
    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 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

  • [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).

  • [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 — initialPersistentConsent depuis layout (getCachedAISettings), plus dappel /api/ai/config au mount.

  • [Review][Patch] Consentement accordé malgré échec audit — handleConfirm refuse le consentement si POST /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 — getConsentHeaders supprimé ; contrôle serveur JWT uniquement.

  • [Review][Defer] PUT /api/ai/batch-organize sans check consent — deferred, pre-existing : nappelle pas dAPI tierce, applique seulement le plan en DB