# 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