# Tool Governance

**Система керування інструментами DAARION.city**  
Версія: 2.0 | Нода: NODE2 (розробка) + NODA1 (production)

---

## Огляд

Tool Governance — єдина система контролю над усіма tool-викликами агентів.  
Складається з чотирьох компонентів:

| Компонент | Файл | Що робить |
|-----------|------|-----------|
| **Rollout Policy** | `config/tools_rollout.yml` | Визначає, які tools отримує кожен агент за роллю |
| **RBAC Matrix** | `config/rbac_tools_matrix.yml` | Матриця `tool → action → entitlement → role` |
| **Safety Middleware** | `services/router/tool_governance.py` | Limits, redaction, allowlist, audit |
| **Release Gate** | `ops/task_registry.yml` + `services/router/release_check_runner.py` | Єдиний release verdict |

---

## 1. Global Tools Rollout

### 1.1 Merge Policy

```
effective_tools = unique(DEFAULT_TOOLS_BY_ROLE ∪ FULL_STANDARD_STACK ∪ agent.specialized_tools)
```

Кожен агент **автоматично** отримує набір tools відповідно до ролі — без необхідності явно вказувати їх.

### 1.2 Конфіг (`config/tools_rollout.yml`)

```yaml
# Групи tools
default_tools_read:
  - repo_tool
  - kb_tool
  - oncall_tool
  - observability_tool
  ...

cto_tools:
  - pr_reviewer_tool
  - contract_tool
  - config_linter_tool
  - threatmodel_tool
  - job_orchestrator_tool

# Ролі → групи
role_map:
  agent_default:
    tools: ["@default_tools_read", "@content_tools"]
  agent_cto:
    tools: ["@default_tools_read", "@cto_tools", "@content_tools", "@media_tools"]
  agent_oncall:
    tools: ["@default_tools_read", "job_orchestrator_tool"]

# Агент → роль
agent_roles:
  sofiia: agent_cto
  helion: agent_oncall
  alateya: agent_media
```

### 1.3 Ролі

| Роль | Хто | Набір |
|------|-----|-------|
| `agent_cto` | sofiia, yaromir | Все: read + cto + content + media |
| `agent_oncall` | helion | Read + job_orchestrator |
| `agent_media` | alateya, nutra, agromatrix, greenfood... | Read + content + media |
| `agent_default` | всі інші / нові агенти | Read + content |

### 1.4 Розширення груп (`@group`)

`@group_name` у конфігу розгортається рекурсивно. Підтримуються вкладені групи:

```yaml
my_super_group:
  - "@cto_tools"
  - "@media_tools"
  - custom_tool
```

### 1.5 Як перевірити tools агента

```python
from agent_tools_config import get_agent_tools, get_agent_role

tools = get_agent_tools("sofiia")   # → список всіх tools
role  = get_agent_role("sofiia")    # → "agent_cto"
```

**Acceptance**: новий агент без явного `tools` отримує read-набір автоматично.

---

## 2. RBAC Matrix

### 2.1 Структура (`config/rbac_tools_matrix.yml`)

```yaml
tools:
  pr_reviewer_tool:
    actions:
      review:
        entitlements: ["tools.pr_review.use"]
      gate:
        entitlements: ["tools.pr_review.gate"]

role_entitlements:
  agent_cto:
    - tools.pr_review.use
    - tools.pr_review.gate
    ...
  agent_default:
    - tools.repo.read
    - tools.kb.read
    ...
```

### 2.2 Enforcement Flow

```
execute_tool(tool, action, agent_id)
  → get_agent_role(agent_id)          → "agent_cto"
  → get_role_entitlements(role)       → ["tools.pr_review.use", ...]
  → get_required_entitlements(tool, action)  → ["tools.pr_review.gate"]
  → missing = required - agent_ents
  → if missing: DENY
```

### 2.3 Entitlement схема

```
tools.<tool_short>.<scope>

Приклади:
  tools.repo.read
  tools.oncall.incident_write
  tools.pr_review.gate
  tools.jobs.run.deploy
```

### 2.4 Перевірка вручну

```python
from tool_governance import check_rbac

ok, reason = check_rbac("sofiia", "pr_reviewer_tool", "gate")
# → (True, "")

ok, reason = check_rbac("helion", "pr_reviewer_tool", "gate")
# → (False, "Missing entitlements: ['tools.pr_review.gate']")
```

**Acceptance**: всі tool handlers використовують матрицю — жодного хардкоду прав у коді.

---

## 3. Tool Safety Middleware

Реалізовано у `services/router/tool_governance.py`.  
Застосовується автоматично до **кожного** `execute_tool(...)` виклику.

### 3.1 Limits (`config/tool_limits.yml`)

| Параметр | Опис |
|----------|------|
| `timeout_ms` | Максимальний час виконання |
| `max_chars_in` | Максимальна довжина вхідного тексту |
| `max_bytes_out` | Максимальний розмір відповіді |
| `rate_limit_rpm` | Запитів на хвилину |
| `concurrency` | Паралельних викликів |

Приклад:
```yaml
pr_reviewer_tool:
  timeout_ms: 60000      # 60s
  max_chars_in: 409600   # 400KB
  rate_limit_rpm: 10
```

### 3.2 Redaction

Модуль `redact(text)` у `tool_governance.py` маскує:
- API ключі (`api_key=***REDACTED***`)
- Токени (`token=***REDACTED***`)
- Паролі (`password=***REDACTED***`)
- Bearer tokens, JWT, OAuth secrets, private keys

Застосовується до:
- Evidence/snippets у результатах pr_reviewer_tool
- Evidence у config_linter_tool
- Log lines у observability_tool

**Включено за замовчуванням.** Вимкнути: `ToolGovernance(enable_redaction=False)`.

### 3.3 Network Allowlist (`config/network_allowlist.yml`)

Tools, що роблять HTTP-запити, обмежені allowlist:

```python
from tool_governance import check_url_allowed

ok, reason = check_url_allowed("oncall_tool", "http://localhost:9102/health")
# → (True, "")

ok, reason = check_url_allowed("oncall_tool", "http://evil.com/steal")
# → (False, "Host 'evil.com' not in allowlist for tool 'oncall_tool'")
```

`web_extract` та `crawl4ai_scrape` мають `allow_any_public: true` але блокують private IPs (RFC1918/loopback).

### 3.4 Audit Events

На кожен tool-виклик емітується structured event у log:

```json
{
  "ts": "2026-02-23T12:00:00Z",
  "req_id": "abc123def456",
  "tool": "pr_reviewer_tool",
  "action": "review",
  "workspace_id": "default",
  "user_id": "user_123",
  "agent_id": "sofiia",
  "status": "pass",
  "duration_ms": 234.5,
  "limits_applied": {"timeout_ms": 60000, "max_chars_in": 409600},
  "input_hash": "a1b2c3d4e5f6",
  "input_chars": 1024,
  "output_size_bytes": 2048
}
```

**Payload не логується** — тільки hash та розміри.  
Log prefix: `TOOL_AUDIT`.

### 3.5 Integration у `execute_tool`

```python
# В tool_manager.py, автоматично:
governance = get_governance()
pre = governance.pre_call(tool, action, agent_id, user_id, workspace_id, input_text)
if not pre.allowed:
    return ToolResult(success=False, error=pre.reason)

result = await _handler(args)  # actual tool execution

governance.post_call(pre.call_ctx, result.result, error=result.error)
```

---

## 4. Налаштування та Hot-Reload

```python
# Force reload конфігів (без перезапуску)
from agent_tools_config import reload_rollout_config
from tool_governance import _reload_yaml_cache

reload_rollout_config()
_reload_yaml_cache()
```

---

## 5. Acceptance Criteria

- ✅ Новий агент без явного `tools` отримує read-набір автоматично
- ✅ Sofiia/CTO має повний набір через роль `agent_cto`
- ✅ Будь-який tool call проходить через middleware (limits/redaction/audit)
- ✅ RBAC денить без entitlement, без хардкоду в коді
- ✅ Allowlist блокує довільні URL для HTTP-tools
- ✅ 31/31 тест проходить
