chore: sync from fn-registry agent
This commit is contained in:
@@ -0,0 +1,166 @@
|
||||
---
|
||||
name: device_agent
|
||||
lang: go
|
||||
domain: tools
|
||||
description: "Agente HTTP por dispositivo del mesh WireGuard. Recibe capability requests del Matrix bot agents_and_robots (via mesh 10.42.0.0/24), valida contra manifest YAML, ejecuta con sandbox (shell whitelist o docker exec whitelist), devuelve resultado con audit hash-chained."
|
||||
icon:
|
||||
phosphor: "robot"
|
||||
accent: "#06b6d4"
|
||||
tags: [agents, device-agent, mesh, wireguard, matrix, http, service, security]
|
||||
version: 0.2.0
|
||||
uses_functions:
|
||||
- shell_exec_whitelist_go_infra
|
||||
- docker_container_list_go_infra
|
||||
- docker_container_exec_go_infra
|
||||
- docker_container_logs_go_infra
|
||||
- http_logger_middleware_go_infra
|
||||
- logger_new_go_infra
|
||||
uses_types: []
|
||||
framework: "stdlib-http"
|
||||
entry_point: "main.go"
|
||||
dir_path: "projects/element_agents/apps/device_agent"
|
||||
repo_url: "https://gitea.organic-machine.com/dataforge/device_agent"
|
||||
e2e_checks:
|
||||
- id: build
|
||||
cmd: "cd /home/lucas/fn_registry/projects/element_agents/apps/device_agent && CGO_ENABLED=1 go build -o device_agent ."
|
||||
timeout_s: 60
|
||||
- id: self_test
|
||||
cmd: "/home/lucas/fn_registry/projects/element_agents/apps/device_agent/device_agent --self-test"
|
||||
timeout_s: 10
|
||||
- id: unit_tests
|
||||
cmd: "cd /home/lucas/fn_registry/projects/element_agents/apps/device_agent && CGO_ENABLED=1 go test -count=1 ./..."
|
||||
timeout_s: 60
|
||||
service:
|
||||
port: 7474
|
||||
health_endpoint: /health
|
||||
health_timeout_s: 3
|
||||
systemd_unit: device_agent.service
|
||||
systemd_scope: user
|
||||
restart_policy: always
|
||||
runtime: systemd-user
|
||||
pc_targets:
|
||||
- home-wsl
|
||||
- aurgi-pc
|
||||
is_local_only: false
|
||||
---
|
||||
|
||||
# device_agent
|
||||
|
||||
Agente HTTP que corre en cada dispositivo del mesh WireGuard mesh (10.42.0.0/24). Escucha en la IP del mesh asignada al peer (`10.42.0.10` en home-wsl, etc.) puerto 7474.
|
||||
|
||||
## Endpoints
|
||||
|
||||
| Metodo | Path | Descripcion |
|
||||
|---|---|---|
|
||||
| GET | `/health` | Liveness check, devuelve `{"ok":true,"device_id":"...","version":"..."}` |
|
||||
| GET | `/capabilities` | Lista capabilities declaradas en el manifest local |
|
||||
| POST | `/capability` | Despacha capability request. JSON envelope (ver flow 0009 spec issue 0134) |
|
||||
|
||||
## Flujo
|
||||
|
||||
```
|
||||
agents_and_robots (VPS, 10.42.0.1)
|
||||
↓ POST http://10.42.0.10:7474/capability
|
||||
device_agent (este binario)
|
||||
↓ validate manifest + nonce + (later) signature
|
||||
↓ route capability → shell.exec | docker.* | fs.read | ...
|
||||
↓ append audit hash-chain
|
||||
↓ return JSON {ok, result, audit_hash}
|
||||
agents_and_robots
|
||||
↓ Matrix message back to room
|
||||
Element user ve output
|
||||
```
|
||||
|
||||
## Manifest
|
||||
|
||||
`~/.config/device_agent/manifest.yaml` declara capabilities permitidas. POC inicial sin firma (issue 0134 introduce ed25519 sign). Formato:
|
||||
|
||||
```yaml
|
||||
device_id: home-wsl
|
||||
operator: egutierrez
|
||||
capabilities:
|
||||
- name: shell.exec
|
||||
binaries_allowed: [ls, cat, ps, df, git, echo]
|
||||
requires_approval: false
|
||||
- name: shell.exec.admin
|
||||
binaries_allowed: [systemctl, apt-get]
|
||||
requires_approval: true
|
||||
- name: shell.eval
|
||||
shell_mode: auto # bash en linux/darwin, powershell.exe en windows
|
||||
blocklist: [] # extension operador; hardcoded kill-list aplica siempre
|
||||
auto_approve: # regex pre-aprobados (override defaults si presente)
|
||||
- "^git\\s"
|
||||
- "^docker ps"
|
||||
max_output_bytes: 1048576 # 1MB
|
||||
timeout_seconds: 60
|
||||
requires_approval: false # true => cmd no-auto se cola en local_files/approval_queue.jsonl
|
||||
- name: docker.container.list
|
||||
requires_approval: false
|
||||
- name: docker.container.logs
|
||||
requires_approval: false
|
||||
- name: docker.container.exec
|
||||
binaries_allowed: [ls, ps, cat]
|
||||
requires_approval: true
|
||||
```
|
||||
|
||||
### Capabilities soportadas
|
||||
|
||||
| Capability | Estado | Que hace |
|
||||
|---|---|---|
|
||||
| `shell.exec` | v0.1.0 | Ejecuta argv estructurado, whitelist binaries |
|
||||
| `shell.eval` | v0.2.0 | Evalua cmd shell-libre (`bash -c <cmd>` o `powershell.exe -Command <cmd>`). Hardcoded blocklist + auto_approve regex + approval queue + audit verbose con cmd cleartext |
|
||||
| `docker.container.list` | stub | Lista contenedores via socket docker |
|
||||
| `docker.container.logs` | stub | Logs de un contenedor |
|
||||
| `docker.container.exec` | stub | exec en contenedor (whitelist) |
|
||||
|
||||
### shell.eval — detalle
|
||||
|
||||
`shell.eval` permite al agent LLM mandar comandos shell libres ("borra logs antiguos") en lugar de solo argv estructurado. Defensas:
|
||||
|
||||
1. **Hardcoded blocklist** no configurable: `rm -rf /`, `dd if=`, `mkfs.*`, `curl|sh`, `shutdown`, `reboot`, etc. Match case-insensitive. Cualquier match = rechazo, no aprobable.
|
||||
2. **Operator blocklist** (`capability.blocklist[]`) extiende el hardcoded.
|
||||
3. **OS detect**: `bash -c` en Linux/Mac, `powershell.exe -NoProfile -NonInteractive -Command` en Windows. Override via `shell_mode` o per-call `shell` arg.
|
||||
4. **auto_approve regex**: lista (override-able) de patrones pre-aprobados (`^git\s`, `^docker ps`, etc.). Match = ejecuta sin friccion.
|
||||
5. **Approval queue**: si `requires_approval: true` y el cmd NO matchea `auto_approve`, se anade entry a `local_files/approval_queue.jsonl` `{ts, request_id, cmd, cwd, capability, status: pending}` y devuelve error `approval_required`. v0.2.0 es placeholder; 0144f pasa a Matrix reactions.
|
||||
6. **Output cap**: `max_output_bytes` (default 1MB), `timeout_seconds` (default 60). Flag `truncated:true` cuando se aplica.
|
||||
7. **Audit verbose**: nueva tabla `audit_shell_eval(audit_id, cmd, cwd, shell, stdout_b64, stderr_b64)` con cmd en CLARO (no hash) para forense. stdout/stderr > 4KB se comprimen gzip+base64 (prefijo `gz:`); cortos van con prefijo `plain:`.
|
||||
|
||||
Args en la HTTP envelope (`POST /capability`):
|
||||
|
||||
```json
|
||||
{
|
||||
"request_id": "...",
|
||||
"capability": "shell.eval",
|
||||
"args": ["{\"cmd\":\"git status\",\"cwd\":\"/repo\"}"]
|
||||
}
|
||||
```
|
||||
|
||||
O en formato posicional `args: ["git status", "/repo", "bash"]`.
|
||||
|
||||
## Audit
|
||||
|
||||
`local_files/audit.db` con tabla `audit_log` hash-chained. Cada request: `{ts, request_id, capability, args_hash, exit_code, prev_hash, this_hash}`.
|
||||
|
||||
## Build
|
||||
|
||||
```bash
|
||||
cd projects/element_agents/apps/device_agent
|
||||
CGO_ENABLED=1 go build -o device_agent .
|
||||
./device_agent --listen 10.42.0.10:7474 --manifest ~/.config/device_agent/manifest.yaml
|
||||
```
|
||||
|
||||
Cross-compile a Windows (para aurgi-pc):
|
||||
|
||||
```bash
|
||||
GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build -o device_agent.exe .
|
||||
```
|
||||
|
||||
## Estado
|
||||
|
||||
- v0.1.0: POC sin firma de manifest. Solo shell.exec + docker.*. WIP.
|
||||
- v0.2.0 (issue 0140): firma ed25519, replay protection, approval flow.
|
||||
|
||||
## Capability growth log
|
||||
|
||||
- v0.1.0 (2026-05-24) — scaffold inicial POC para validar DoD gate Element→PC del flow 0009.
|
||||
- v0.2.0 (2026-05-24) — anade capability `shell.eval` (bash/powershell con hardcoded blocklist + regex auto_approve + approval queue + audit verbose con cmd cleartext). Nueva tabla `audit_shell_eval`. Manifest extendido con `blocklist`, `auto_approve`, `shell_mode`, `max_output_bytes`, `timeout_seconds`.
|
||||
Reference in New Issue
Block a user