> ## Documentation Index
> Fetch the complete documentation index at: https://mcp-server-langgraph.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# GDPR Storage Backend Configuration

> The GDPR compliance endpoints (`/api/v1/users/me/*`) require persistent storage to meet data subject rights requirements under GDPR Articles 15, 16...

### Overview

The GDPR compliance endpoints (`/api/v1/users/me/*`) require persistent storage to meet data subject rights requirements under GDPR Articles 15, 16, 17, 20, and 21.

**CRITICAL**: In-memory storage is **NOT production-ready** and will cause data loss on server restart, violating GDPR compliance requirements.

<Info>
  PostgreSQL storage backend is **fully implemented** as of ADR-0041 (2025-11-02). See [ADR-0041: PostgreSQL GDPR Storage](/architecture/adr-0041-postgresql-gdpr-storage) for architecture details.
</Info>

### GDPR Data Subject Rights Flow

```mermaid theme={null}
%% ColorBrewer2 Set3 palette - each component type uniquely colored
sequenceDiagram
    %%{init: {'theme':'base', 'themeVariables': {'primaryColor':'#8dd3c7','primaryTextColor':'#1a202c','primaryBorderColor':'#2a9d8f','lineColor':'#fb8072','secondaryColor':'#fdb462','tertiaryColor':'#b3de69','actorBkg':'#8dd3c7','actorBorder':'#2a9d8f','actorTextColor':'#1a202c','actorLineColor':'#2a9d8f','signalColor':'#7cb342','signalTextColor':'#1a202c','labelBoxBkgColor':'#fdb462','labelBoxBorderColor':'#e67e22','labelTextColor':'#1a202c','noteBorderColor':'#e67e22','noteBkgColor':'#fdb462','noteTextColor':'#1a202c','activationBorderColor':'#7cb342','activationBkgColor':'#b3de69','sequenceNumberColor':'#4a5568'}}}%%
    participant User as Data Subject
    participant API as GDPR API
    participant Auth as Authentication
    participant Storage as Storage Backend
    participant DB as Conversation DB
    participant FGA as OpenFGA
    participant Logs as Log System
    participant Audit as Audit Trail

    Note over User,API: Article 15: Right to Access (GET /users/me)
    User->>API: GET /api/v1/users/me<br/>Authorization: Bearer JWT
    API->>Auth: Validate JWT
    Auth-->>API: Valid (user_id: alice)
    API->>Storage: SELECT * FROM user_profiles<br/>WHERE user_id = 'alice'
    Storage-->>API: User profile data
    API-->>User: 200 OK<br/>{user_id, name, email, preferences}

    Note over User,API: Article 20: Right to Data Portability (GET /users/me/data)
    User->>API: GET /api/v1/users/me/data<br/>Format: JSON
    API->>Auth: Validate JWT
    Auth-->>API: Valid (user_id: alice)
    API->>Storage: Get user profile
    Storage-->>API: Profile data
    API->>DB: SELECT conversations, messages<br/>WHERE user_id = 'alice'
    DB-->>API: Conversation history
    API->>FGA: List permissions<br/>user:alice
    FGA-->>API: Permission data
    API->>API: Compile portable data<br/>JSON format
    API-->>User: 200 OK<br/>{profile, conversations, permissions}
    API->>Audit: Log data export<br/>user:alice, timestamp, IP

    Note over User,API: Article 16: Right to Rectification (PATCH /users/me)
    User->>API: PATCH /api/v1/users/me<br/>{name: "Alice Updated", email: "new@example.com"}
    API->>Auth: Validate JWT
    Auth-->>API: Valid
    API->>Storage: UPDATE user_profiles<br/>SET name, email, updated_at<br/>WHERE user_id = 'alice'
    Storage-->>API: Updated
    API->>Audit: Log profile update<br/>Changed fields: name, email
    API-->>User: 200 OK<br/>{updated_profile}

    Note over User,API: Article 17: Right to Erasure (DELETE /users/me)
    User->>API: DELETE /api/v1/users/me<br/>Confirm: true
    API->>Auth: Validate JWT
    Auth-->>API: Valid

    Note over API,DB: Multi-step deletion process
    API->>DB: DELETE FROM conversations<br/>WHERE user_id = 'alice'
    DB-->>API: Deleted
    API->>DB: DELETE FROM messages<br/>WHERE user_id = 'alice'
    DB-->>API: Deleted
    API->>FGA: Delete all tuples<br/>WHERE user = 'user:alice'
    FGA-->>API: Removed
    API->>Storage: DELETE FROM user_profiles<br/>WHERE user_id = 'alice'
    Storage-->>API: Deleted
    API->>Storage: DELETE FROM consent_records<br/>WHERE user_id = 'alice'
    Storage-->>API: Deleted
    API->>Logs: Anonymize logs<br/>Replace user_id with <deleted>
    Logs-->>API: Anonymized
    API->>Audit: Log deletion request<br/>user:alice (final record)
    API-->>User: 204 No Content<br/>All data deleted

    Note over User,API: Article 21: Right to Object (POST /users/me/consent)
    User->>API: POST /api/v1/users/me/consent<br/>{analytics: false, marketing: false}
    API->>Auth: Validate JWT
    Auth-->>API: Valid
    API->>Storage: INSERT/UPDATE consent_records<br/>user_id, consent_type, granted, timestamp
    Storage-->>API: Recorded
    API->>Audit: Log consent change<br/>analytics: true→false, marketing: true→false
    API-->>User: 200 OK<br/>{consent_status}
```

### Environment Variables

#### Required Configuration

Set these environment variables in your production deployment:

```properties theme={null}
## Required: Set environment to production
ENVIRONMENT=production

## Required: Choose persistent backend (postgres or redis)
GDPR_STORAGE_BACKEND=postgres  # or "redis"
```

#### Development/Testing

For local development and testing only:

```bash theme={null}
ENVIRONMENT=development
GDPR_STORAGE_BACKEND=memory
```

### Storage Backend Options

#### Option 1: PostgreSQL (Recommended) ✅

PostgreSQL provides ACID compliance and is ideal for GDPR data subject rights. **Fully implemented as of ADR-0041.**

<Check>
  **Production Ready**: PostgreSQL storage backend is fully implemented with factory pattern, migrations, and comprehensive testing.
</Check>

**Configuration:**

```bash theme={null}
GDPR_STORAGE_BACKEND=postgres
GDPR_POSTGRES_URL=postgresql://user:pass@localhost:5432/gdpr
```

**Architecture Benefits** (see [ADR-0041](/architecture/adr-0041-postgresql-gdpr-storage)):

* **ACID Compliance**: Atomic GDPR Article 17 deletions across all data
* **Cost-Effective**: 14x cheaper than Redis for 7-year retention ($50/month vs $720/month)
* **Audit Trail**: Time-series queries for compliance reports
* **Already in Stack**: Keycloak and OpenFGA use PostgreSQL
* **5-10ms Latency**: Acceptable for user-initiated GDPR operations

**Database Schema:**

The PostgreSQL schema includes 5 tables optimized for GDPR compliance:

```sql theme={null}
-- User profiles (GDPR Article 15, 16, 17)
CREATE TABLE user_profiles (
    user_id VARCHAR(255) PRIMARY KEY,
    username VARCHAR(255) NOT NULL,
    name VARCHAR(255),
    email VARCHAR(255),
    preferences JSONB,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- User preferences (GDPR Article 16, 17)
CREATE TABLE user_preferences (
    user_id VARCHAR(255) PRIMARY KEY REFERENCES user_profiles(user_id) ON DELETE CASCADE,
    preferences JSONB NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- Consent records (GDPR Article 21, 7-year retention)
CREATE TABLE consent_records (
    consent_id VARCHAR(255) PRIMARY KEY,
    user_id VARCHAR(255) NOT NULL,
    consent_type VARCHAR(50) NOT NULL,
    granted BOOLEAN NOT NULL,
    timestamp TIMESTAMP NOT NULL,
    ip_address VARCHAR(45),
    user_agent TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_consent_user_id ON consent_records(user_id);
CREATE INDEX idx_consent_timestamp ON consent_records(timestamp);

-- Conversations (GDPR Article 15, 20, 90-day retention)
CREATE TABLE conversations (
    conversation_id VARCHAR(255) PRIMARY KEY,
    user_id VARCHAR(255) NOT NULL,
    thread_id VARCHAR(255),
    messages JSONB NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    last_message_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_conversations_user_id ON conversations(user_id);
CREATE INDEX idx_conversations_last_message_at ON conversations(last_message_at);

-- Audit logs (HIPAA 7-year retention, GDPR Article 5(2))
CREATE TABLE audit_logs (
    audit_id VARCHAR(255) PRIMARY KEY,
    user_id VARCHAR(255),
    action VARCHAR(100) NOT NULL,
    resource_type VARCHAR(100),
    resource_id VARCHAR(255),
    metadata JSONB,
    timestamp TIMESTAMP NOT NULL,
    ip_address VARCHAR(45),
    user_agent TEXT
);

CREATE INDEX idx_audit_user_id ON audit_logs(user_id);
CREATE INDEX idx_audit_timestamp ON audit_logs(timestamp);
CREATE INDEX idx_audit_action ON audit_logs(action);
```

**Kubernetes Deployment:**

The schema is deployed via ConfigMap in Kubernetes:

```bash theme={null}
# View the schema ConfigMap
kubectl get configmap postgres-gdpr-schema -o yaml
```

The ConfigMap is defined in `deployments/base/postgres-gdpr-schema-configmap.yaml` and automatically applied during PostgreSQL initialization.

**Factory Pattern Usage:**

```python theme={null}
from mcp_server_langgraph.compliance.gdpr.factory import get_gdpr_storage

# Dependency injection in FastAPI
async def my_endpoint(
    gdpr_storage: GDPRStorage = Depends(get_gdpr_storage)
):
    # Access user profiles
    profile = await gdpr_storage.user_profiles.get(user_id)

    # Access consents
    consents = await gdpr_storage.consents.get_user_consents(user_id)

    # Access conversations
    conversations = await gdpr_storage.conversations.list_by_user(user_id)

    # Log audit event
    await gdpr_storage.audit_logs.log(audit_entry)
```

#### Option 2: Redis

Redis provides fast, persistent key-value storage suitable for consent management.

**Configuration:**

```bash theme={null}
GDPR_STORAGE_BACKEND=redis
GDPR_REDIS_URL=redis://localhost:6379/2  # Use separate DB from sessions
```

**Implementation Required:**

1. Create `RedisConsentStore` class
2. Use Redis hashes for user profiles: `user:profile:{user_id}`
3. Use Redis hashes for consents: `user:consents:{user_id}`
4. Configure TTL if retention policies apply

**Example Implementation:**

```python theme={null}
import redis
from typing import Dict, Any, Optional

class RedisConsentStore:
    def __init__(self, redis_url: str):
        self.redis = redis.from_url(redis_url)

    def set_consent(self, user_id: str, consent_type: str, data: Dict[str, Any]):
        key = f"user:consents:{user_id}"
        self.redis.hset(key, consent_type, json.dumps(data))

    def get_consents(self, user_id: str) -> Dict[str, dict]:
        key = f"user:consents:{user_id}"
        consents = self.redis.hgetall(key)
        return {k.decode(): json.loads(v) for k, v in consents.items()}
```

### Production Guard

The application includes a runtime guard that **prevents startup** if:

```text theme={null}
ENVIRONMENT=production AND GDPR_STORAGE_BACKEND=memory
```

**Error Message:**

```text theme={null}
RuntimeError: CRITICAL: GDPR endpoints cannot use in-memory storage in production.
Set GDPR_STORAGE_BACKEND=postgres or GDPR_STORAGE_BACKEND=redis,
or set ENVIRONMENT=development for testing.
Data subject rights (GDPR compliance) require persistent storage.
```

### Migration Checklist

Before deploying GDPR endpoints to production:

* [ ] Set `ENVIRONMENT=production`
* [ ] Set `GDPR_STORAGE_BACKEND=postgres`
* [ ] Configure `GDPR_POSTGRES_URL` connection string
* [ ] Deploy PostgreSQL GDPR schema (see [Kubernetes deployment](#kubernetes-deployment) section)
* [ ] Apply `postgres-gdpr-schema-configmap.yaml` to create database tables
* [ ] Run database migrations from `migrations/` directory (if any schema updates)
* [ ] Test data subject rights workflows (see [Testing](#testing) section)
* [ ] Verify data is persisted across pod/container restarts
* [ ] Configure automated backups (daily PostgreSQL dumps recommended)
* [ ] Configure retention policies (see [Data Retention](#data-retention) section)
* [ ] Document data retention periods in privacy policy
* [ ] Update privacy policy with GDPR data subject rights
* [ ] Test GDPR API endpoints (see [GDPR API Reference](/api-reference/gdpr-endpoints))
* [ ] Verify audit logging for all GDPR operations

**New in v2.8.0**: PostgreSQL storage is fully implemented and production-ready. The factory pattern automatically initializes the correct storage backend based on `GDPR_STORAGE_BACKEND` environment variable.

### Database Migrations

The `migrations/` directory contains schema migrations for PostgreSQL GDPR storage:

```bash theme={null}
migrations/
├── 001_initial_schema.sql       # Initial GDPR tables
├── 002_add_audit_indexes.sql    # Performance optimization indexes
└── README.md                     # Migration instructions
```

**Applying Migrations Manually:**

```bash theme={null}
# Connect to GDPR database
psql $GDPR_POSTGRES_URL

# Run migrations in order
\i migrations/001_initial_schema.sql
\i migrations/002_add_audit_indexes.sql
```

**Automated Migration (Kubernetes):**

Migrations are automatically applied via the `postgres-gdpr-schema` ConfigMap during PostgreSQL StatefulSet initialization. See `deployments/base/postgres-statefulset.yaml` for implementation details.

<Info>
  Future schema changes will be added as numbered migrations. Always apply migrations in sequential order.
</Info>

### GDPR Compliance Requirements

#### Data Retention

Configure retention policies based on your legal requirements:

```md theme={null}

## Example: 90-day retention for consent records
CONSENT_RETENTION_DAYS=90
PROFILE_RETENTION_DAYS=365
```

#### Audit Trail

All GDPR operations are logged with:

* User ID
* Operation type (access, rectification, erasure, etc.)
* Timestamp
* GDPR article (15, 16, 17, 20, 21)

**Log Example:**

```json theme={null}
{
  "message": "User consent updated",
  "user_id": "user:alice",
  "consent_type": "analytics",
  "granted": true,
  "gdpr_article": "21",
  "timestamp": "2025-10-18T12:34:56Z"
}
```

#### Data Deletion (Article 17)

When users exercise right to erasure:

1. User profile and preferences are **deleted**
2. Consent records are **deleted**
3. Audit logs are **anonymized** (user\_id replaced with hash)
4. Sessions are **revoked**
5. Conversations are **deleted**

**Retention for Compliance:**

Some data may be retained for legal/compliance reasons:

* Anonymized audit logs (for GDPR compliance proof)
* Aggregated analytics (no PII)
* Financial records (tax law requirements)

### Testing

#### Unit Tests

```bash theme={null}
pytest tests/integration/test_gdpr_endpoints.py -v
```

#### Integration Tests

Test with real database:

```properties theme={null}
## Start PostgreSQL
docker-compose up -d postgres

## Run integration tests
GDPR_STORAGE_BACKEND=postgres \
GDPR_POSTGRES_URL=postgresql://test:test@localhost:5432/test_db \
pytest tests/integration/test_gdpr_endpoints.py
```

#### Production Guard Test

Verify production guard:

```properties theme={null}
## Should fail
ENVIRONMENT=production GDPR_STORAGE_BACKEND=memory python -c "import mcp_server_langgraph.api.gdpr"

## Should succeed
ENVIRONMENT=production GDPR_STORAGE_BACKEND=postgres python -c "import mcp_server_langgraph.api.gdpr"
```

### Deployment Examples

#### Docker Compose

```yaml theme={null}
services:
  app:
    image: mcp-server-langgraph:latest
    environment:
      - ENVIRONMENT=production
      - GDPR_STORAGE_BACKEND=postgres
      - GDPR_POSTGRES_URL=postgresql://gdpr:${DB_PASSWORD}@postgres:5432/gdpr_db
    depends_on:
      - postgres

  postgres:
    image: postgres:15
    environment:
      - POSTGRES_DB=gdpr_db
      - POSTGRES_USER=gdpr
      - POSTGRES_PASSWORD=${DB_PASSWORD}
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:
```

#### Kubernetes

```yaml theme={null}
apiVersion: v1
kind: ConfigMap
metadata:
  name: gdpr-config
data:
  ENVIRONMENT: "production"
  GDPR_STORAGE_BACKEND: "postgres"

---
apiVersion: v1
kind: Secret
metadata:
  name: gdpr-secrets
type: Opaque
stringData:
  GDPR_POSTGRES_URL: "postgresql://gdpr:password@postgres-service:5432/gdpr_db"
```

### Troubleshooting

#### Error: "GDPR endpoints use in-memory storage"

**Cause:** GDPR\_STORAGE\_BACKEND not set or set to "memory"

**Solution:**

```bash theme={null}
export GDPR_STORAGE_BACKEND=postgres
export GDPR_POSTGRES_URL=postgresql://...
```

#### Error: "RuntimeError: CRITICAL: GDPR endpoints cannot use in-memory storage"

**Cause:** Running in production with memory backend

**Solution:** Change backend or environment:

```bash theme={null}
## Option 1: Use persistent backend
export GDPR_STORAGE_BACKEND=postgres

## Option 2: Switch to development (NOT for production!)
export ENVIRONMENT=development
```

#### Data Loss on Restart

**Cause:** Using in-memory storage in non-development environment

**Solution:** Migrate to PostgreSQL or Redis immediately

### References

* [GDPR Article 15: Right to Access](https://gdpr-info.eu/art-15-gdpr/)
* [GDPR Article 16: Right to Rectification](https://gdpr-info.eu/art-16-gdpr/)
* [GDPR Article 17: Right to Erasure](https://gdpr-info.eu/art-17-gdpr/)
* [GDPR Article 20: Right to Data Portability](https://gdpr-info.eu/art-20-gdpr/)
* [GDPR Article 21: Right to Object](https://gdpr-info.eu/art-21-gdpr/)

### Support

For implementation assistance:

1. Check existing issues: [https://github.com/vishnu2kmohan/mcp-server-langgraph/issues](https://github.com/vishnu2kmohan/mcp-server-langgraph/issues)
2. Create new issue with `gdpr` and `compliance` labels
3. Include environment details and error messages
