Files
microdao-daarion/docs/PRIVACY_GATE.md
Apple ef3473db21 snapshot: NODE1 production state 2026-02-09
Complete snapshot of /opt/microdao-daarion/ from NODE1 (144.76.224.179).
This represents the actual running production code that has diverged
significantly from the previous main branch.

Key changes from old main:
- Gateway (http_api.py): expanded from ~40KB to 164KB with full agent support
- Router: new /v1/agents/{id}/infer endpoint with vision + DeepSeek routing
- Behavior Policy: SOWA v2.2 (3-level: FULL/ACK/SILENT)
- Agent Registry: config/agent_registry.yml as single source of truth
- 13 agents configured (was 3)
- Memory service integration
- CrewAI teams and roles

Excluded from snapshot: venv/, .env, data/, backups, .tgz archives

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-09 08:46:46 -08:00

6.6 KiB
Raw Blame History

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

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

Rule 2: Team Mode

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

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

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

Sanitization Rules

Context Sanitization

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

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

@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

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

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)

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
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

{
    "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

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