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>
6.6 KiB
6.6 KiB
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)
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
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