# Privacy Gate Specification

**Версія:** 1.0  
**Статус:** Mandatory Middleware

---

## Принцип

Privacy Gate — обов'язковий middleware на Router/Tool Manager рівні, який контролює передачу контенту між сервісами на основі `mode` та `consent`.

---

## Modes

| Mode | Description | Content Access | Logging |
|------|-------------|----------------|---------|
| `public` | Публічний контент | Full access | Full logging |
| `team` | Командний контент | Team members only | Metadata only |
| `confidential` | Конфіденційний | Sanitized or with consent | No content |
| `e2ee` | End-to-end encrypted | Never plaintext | No content |

---

## Privacy Gate Rules

### Rule 1: Public Mode
```python
if request.mode == "public":
    # Повний доступ
    allow_content = True
    log_content = True
    transform = None
```

### Rule 2: Team Mode
```python
if request.mode == "team":
    # Перевірка membership
    if user in request.team.members:
        allow_content = True
        log_content = False  # Тільки metadata
        transform = None
    else:
        allow_content = False
        raise AccessDenied("Not a team member")
```

### Rule 3: Confidential Mode
```python
if request.mode == "confidential":
    # Тільки sanitized або з consent
    if request.user_consent:
        allow_content = True
        log_content = False
        transform = None
    else:
        allow_content = True
        log_content = False
        transform = sanitize_context  # Тільки summary
```

### Rule 4: E2EE Mode
```python
if request.mode == "e2ee":
    # НІКОЛИ plaintext
    allow_content = False
    log_content = False
    # Обробка тільки на клієнті
    raise E2EEViolation("Cannot process E2EE content server-side")
```

---

## Sanitization Rules

### Context Sanitization

```python
def sanitize_context(context: str, max_tokens: int = 100) -> str:
    """
    Перетворює детальний контекст на узагальнений summary.
    
    Правила:
    1. Видалити PII (імена, телефони, email)
    2. Видалити конкретні числа/дати
    3. Узагальнити до категорії
    4. Обмежити довжину
    """
    # Step 1: Remove PII
    context = remove_pii(context)
    
    # Step 2: Summarize
    summary = llm_summarize(context, max_tokens=max_tokens)
    
    # Step 3: Validate no sensitive data
    if contains_sensitive(summary):
        return "[Confidential content]"
    
    return summary
```

### PII Patterns

```python
PII_PATTERNS = [
    r'\b\d{10,}\b',                    # Phone numbers
    r'\b[\w.-]+@[\w.-]+\.\w+\b',       # Emails
    r'\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}\b',  # Credit cards
    r'\b\d{2,3}-\d{2,3}-\d{2,3}\b',    # IDs
    # Додати специфічні для домену
]
```

---

## Integration Points

### 1. Router Level

```python
@router.middleware
async def privacy_gate_middleware(request: Request, call_next):
    gate = PrivacyGate()
    result = gate.check(request)
    
    if not result.allow:
        raise AccessDenied(result.reason)
    
    if result.transform:
        request.context = result.transform(request.context)
    
    response = await call_next(request)
    
    if result.log_content:
        await audit_log(request, response)
    else:
        await audit_log_metadata_only(request, response)
    
    return response
```

### 2. Tool Manager Level

```python
class ToolManager:
    def execute(self, tool: Tool, context: Context) -> Result:
        # Check privacy before tool execution
        gate = PrivacyGate()
        if context.mode in ["confidential", "e2ee"]:
            if tool.requires_plaintext:
                raise PrivacyViolation(
                    f"Tool {tool.name} cannot be used in {context.mode} mode"
                )
        
        return tool.execute(context)
```

### 3. Memory Service Level

```python
class MemoryService:
    async def store(self, content: str, metadata: dict) -> str:
        mode = metadata.get("mode", "public")
        
        if mode == "confidential":
            # Зберігаємо тільки embedding, не plaintext
            embedding = await self.embed(content)
            return await self.store_embedding_only(embedding, metadata)
        
        if mode == "e2ee":
            # Зберігаємо тільки encrypted blob
            return await self.store_encrypted(content, metadata)
        
        # Public/team - зберігаємо все
        return await self.store_full(content, metadata)
```

---

## Consent Flow

### User Consent for Handoff

```
User → DAARWIZZ: "my health data question"

DAARWIZZ detects: mode=confidential, target=Nutra

DAARWIZZ → User: "Це питання потребує передачі в Nutra. 
                  Дозволиш передати узагальнений контекст?"

User → DAARWIZZ: "так"

DAARWIZZ sets: request.user_consent = True

DAARWIZZ → Nutra: sanitized_context (with consent)

Nutra → DAARWIZZ: response

DAARWIZZ → User: response
```

### Consent Storage

```sql
CREATE TABLE user_consents (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id VARCHAR(255) NOT NULL,
    consent_type VARCHAR(100) NOT NULL,  -- 'handoff', 'indexing', etc.
    target_agent VARCHAR(100),
    granted_at TIMESTAMPTZ DEFAULT NOW(),
    expires_at TIMESTAMPTZ,
    revoked_at TIMESTAMPTZ,
    context_hash VARCHAR(64)  -- Для аудиту, без контенту
);
```

---

## Audit Trail

### Privacy Events

```json
{
    "event_type": "privacy.check",
    "timestamp": "2026-01-19T10:00:00Z",
    "request_id": "uuid",
    "user_id": "user_123",
    "mode": "confidential",
    "action": "sanitize",
    "target_service": "nutra",
    "consent_given": true,
    "content_hash": "sha256:...",  // Без контенту
    "result": "allowed"
}
```

---

## Testing

### Test Cases

```python
def test_public_mode_allows_full_content():
    request = Request(mode="public", content="Hello")
    result = gate.check(request)
    assert result.allow == True
    assert result.transform is None

def test_confidential_mode_requires_consent():
    request = Request(mode="confidential", content="Secret")
    result = gate.check(request)
    assert result.transform == sanitize_context

def test_e2ee_mode_blocks_server_processing():
    request = Request(mode="e2ee", content="Encrypted")
    with pytest.raises(E2EEViolation):
        gate.check(request)
```

---

**Останнє оновлення:** 2026-01-19
