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

267 lines
6.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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