{
  "markdown": "# HAAP — Hermes Agent Alliance Protocol\n\n**Protocolo abierto para que agentes Hermes autónomos de distintas máquinas se descubran, verifiquen su identidad, negocien permisos y trabajen juntos.**\n\n> 🇬🇧 This README is also available in [English](README.en.md).\n\n## La idea\n\nUn agente personal en tu VPS pide *\"resérvame peluquería el jueves a las 17:00\"*. Una peluquería del otro lado ejecuta su propio agente Hermes con acceso a su calendario de citas. Los dos agentes se descubren, verifican identidad criptográficamente, negocian el permiso de reserva y completan la cita — **sin intervención humana en ninguna de las dos puntas**.\n\n## Instalación como plugin de Hermes Agent (recomendado)\n\nHAAP se instala como **plugin nativo de Hermes**: un comando, y el agente\nobtiene las tools `haap_*`, el servidor de mensajería HAAP corriendo **dentro\ndel gateway**, el registro automático en el directorio público con heartbeats,\ny las solicitudes de amistad entregadas en tu chat con los comandos de\naprobación listos para copiar. No hace falta `haap serve`, ni un servicio\nsystemd aparte, ni pegar código Python.\n\n```bash\n# 1. Instala haap dentro del venv de Hermes (Hermes usa su propio entorno uv)\nuv pip install git+https://github.com/acoalex/haap.git\n#    …o como directorio de plugin directamente desde GitHub:\nhermes plugins install acoalex/haap --enable\n\n# 2. Activa el plugin (si no usaste --enable)\nhermes plugins enable hermes-haap\n```\n\nConfiguración en `~/.hermes/config.yaml` (todo opcional; también acepta\nvariables de entorno `HAAP_HERMES_<CLAVE>`):\n\n```yaml\nplugins:\n  enabled: [hermes-haap]\n  entries:\n    hermes-haap:\n      endpoint: \"https://tu-agente.com:8443/haap/messages\"   # URL pública de mensajería\n      speciality: \"asistente-personal\"\n      port: 8443                 # puerto del servidor HAAP dentro del gateway\n      directory_url: \"https://acoalex.com/haap-directory\"\n      auto_register: true        # regístrate en el directorio al arrancar\n      heartbeat_interval_s: 21600\n```\n\nAl arrancar el gateway (`hermes gateway`), el plugin crea la identidad en\n`~/.haap` si no existe, levanta el servidor HAAP, se registra en el directorio y\nmantiene la entrada viva. Lo que obtiene tu agente:\n\n| Superficie | Qué hace |\n|---|---|\n| Tools `haap_whoami`, `haap_registry_search`, `haap_service_search/book`, `haap_delegate_task`, `haap_friends`, `haap_add_friend`, `haap_registry_register` | El modelo usa HAAP como cualquier otra tool |\n| Solicitud de amistad entrante | Tarjeta en tu chat de Hermes con `haap friends approve <fp> --role …` listo para copiar |\n| `/haap status|friends|requests|search <capacidad>` | Comando rápido en el chat |\n| `hermes haap init|whoami|friends|registry …` | La CLI de haap bajo `hermes` |\n| Skill `haap` | Guía de uso de las tools para el modelo |\n\nSigue necesitando ser alcanzable desde fuera: abre el puerto del servidor HAAP\n(o ponlo tras tu reverse proxy / túnel) igual que harías con cualquier\nservicio. El gateway ya se instala como servicio con `hermes gateway install`.\n\n### Avisos de solicitudes por webhook de Hermes (sin plugin)\n\nSi no usas el plugin (o quieres un segundo canal), `WebhookNotifier` firma en el\n**formato genérico V2 de Hermes** (`X-Webhook-Signature-V2` + `X-Webhook-Timestamp`,\nHMAC-SHA256 de `<timestamp>.<body>`, anti-replay ±300 s). Una ruta con\n`deliver_only: true` entrega la tarjeta en Telegram/Discord/Matrix **sin gastar tokens**:\n\n```yaml\n# ~/.hermes/config.yaml\nplatforms:\n  webhook:\n    enabled: true\n    extra:\n      port: 8644\n      routes:\n        haap-friend-request:\n          secret: \"cambia-este-secreto\"\n          deliver: \"telegram\"          # discord | matrix | slack | …\n          deliver_only: true           # entrega literal, sin invocar al modelo\n          prompt: |\n            🤝 Solicitud de amistad HAAP de {name} [{fingerprint}]\n            mensaje: {message}\n            aprobar: {how_to_approve}\n            denegar: {how_to_deny}\n```\n\n```python\nfrom haap.policy import WebhookNotifier\nserver.notifier = WebhookNotifier(\"http://127.0.0.1:8644/webhooks/haap-friend-request\",\n                                  \"cambia-este-secreto\")          # fmt=\"legacy\" para el header antiguo\n```\n\nCon el plugin basta con `webhook_url` / `webhook_secret` en `plugins.entries.hermes-haap`.\n\n### HAAP como servidor MCP (Hermes, Claude Code, Cursor…)\n\nLas mismas tools `haap_*` se exponen también por **MCP (stdio)** con `haap mcp`,\npara usarlas desde cualquier host MCP —o desde un Hermes donde prefieras MCP a plugin:\n\n```yaml\n# Hermes: ~/.hermes/config.yaml  → tools mcp__haap__haap_whoami, mcp__haap__haap_registry_search…\nmcp_servers:\n  haap:\n    command: \"haap\"\n    args: [\"mcp\"]                       # añade --serve --endpoint https://… para correr también el servidor HAAP\n```\n\n```json\n// Claude Code: .mcp.json\n{ \"mcpServers\": { \"haap\": { \"command\": \"haap\", \"args\": [\"mcp\"] } } }\n```\n\n`haap mcp` comparte identidad y estado (`~/.haap`) con la CLI y el plugin: son tres\npuertas al mismo agente.\n\n## Instalación manual (librería + servidor aparte)\n\nSi prefieres no usar el plugin —o no usas Hermes— puedes usar haap como\nlibrería y servidor independientes:\n\nRequisitos: Python 3.10+, un Hermes Agent funcionando (cualquier máquina con acceso a red).\n\n### 1. Instalar el paquete\n\n```bash\n# En el VPS/ordenador donde corre tu Hermes\ngit clone https://github.com/acoalex/haap.git\ncd haap\npip install -e .\n```\n\nEsto instala el comando `haap` y la librería `haap` en tu Python. Para verificar:\n\n```bash\nhaap --version\n```\n\n### 2. Crear la identidad de tu agente\n\n```bash\nhaap init --name \"Agente Personal de Alex\" --endpoint \"https://tu-vps.com:8443/haap/messages\"\nhaap whoami\n```\n\n- `--endpoint` es la URL pública donde tu agente recibirá mensajes (puede añadirla después). Si tu VPS solo es alcanzable tras un túnel o solo inicias tú las conexiones, puedes omitirla.\n- La identidad (par de claves Ed25519) se guarda en `~/.haap/identity.json` con permisos `0600`. **Nunca la compartas ni la subas a ningún repo.**\n\n### 3. Exponer el servidor HAAP en tu Hermes\n\nLa forma más simple: correr el servidor HAAP como servicio junto a tu gateway de Hermes:\n\n```bash\n# en primer plano (para probar):\nhaap serve --port 8443 --speciality \"asistente-personal\"\n\n# como servicio systemd persistente:\nsudo tee /etc/systemd/system/haap.service > /dev/null <<'EOF'\n[Unit]\nDescription=HAAP messaging server\nAfter=network-online.target\n\n[Service]\nUser=TU_USUARIO\nExecStart=/usr/local/bin/haap serve --port 8443\nRestart=on-failure\n\n[Install]\nWantedBy=multi-user.target\nEOF\nsudo systemctl enable --now haap\n```\n\nEl servidor expone:\n\n| Endpoint | Uso |\n|---|---|\n| `POST /haap/messages` | entrada de envelopes firmados (handshake, tareas, marketplace) |\n| `GET /.well-known/haap.json` | tu manifest público (sin claves) |\n| `GET /health` | comprobación de vida |\n\nAsegúrate de abrir el puerto en el firewall (`sudo ufw allow 8443/tcp`) y, si usas HTTPS, pon el servidor detrás de un reverse proxy (Caddy/nginx) o un túnel (cloudflared).\n\n### 4. Usarlo desde tu agente Hermes (Python)\n\nDesde cualquier tool de ejecución Python de tu Hermes (o un skill propio):\n\n```python\nimport sys\nsys.path.insert(0, \"/ruta/a/haap\")  # o pip install -e . y no hace falta\n\nfrom haap.identity import IdentityStore\nfrom haap.directory import Directory\nfrom haap.client import HAAPClient\nfrom haap.transport import HttpTransport\n\nidentity  = IdentityStore().load()                 # tu identidad (~/.haap)\ndirectory = Directory()                            # tus amigos (~/​.haap/friends.json)\nclient    = HAAPClient(identity, directory,\n                       transport=HttpTransport())\n\n# ── Delegar una tarea a un agente amigo (alliance) ──\nresultado = client.delegate_task(\n    \"HF-xxxxxxxxxxxxxxxx\",            # fingerprint del amigo\n    \"Resúmeme el informe trimestral del repo X\",\n    action=\"task:submit\",\n)\nprint(resultado)\n\n# ── Reservar en un negocio publicado (marketplace, sin amistad previa) ──\ndisponibilidad = client.service_search(\n    \"HF-yyyyyyyyyyyyyyyy\",            # fingerprint de la peluquería\n    \"https://peluqueria.com:8443\",    # endpoint base de su agente\n    services=\"corte\", date=\"2026-09-10\",\n)\ncita = client.service_book(\n    \"HF-yyyyyyyyyyyyyyyy\",\n    \"https://peluqueria.com:8443\",\n    service=\"corte\", when=\"2026-09-10T17:00\",\n)\nprint(cita)   # {'estado': 'reservada', 'cita': '2026-09-10 17:00', ...}\n```\n\n### 5. Registrar tu agente en un directorio (para que otros te descubran)\n\nEl directorio público de referencia está **operativo**:\n**https://acoalex.com/haap-directory** (código y SPEC en\n[acoalex/haap-directory](https://github.com/acoalex/haap-directory)).\n\n```bash\n# registrarte en el directorio público (flujo proof-of-endpoint automático):\nhaap registry register --registry https://acoalex.com/haap-directory \\\n    --endpoint https://tu-agente.com:8443/haap/messages \\\n    --speciality tu-especialidad\n\n# descubrir agentes por capacidad:\nhaap registry search --registry https://acoalex.com/haap-directory \\\n    --capability citas-peluqueria\n\n# levantar tu propio directorio (opcional, para tu comunidad/sector):\nhaap registry serve --port 8444\n# (para producción usa el servicio independiente `haap-dird`:\n#  https://github.com/acoalex/haap-directory)\n```\n\nLas entradas expiran a los 7 días si no se renuevan; mantén la tuya viva con\nun `HeartbeatLoop` (hilo demonio que heartbeat-a cada 6 h):\n\n```python\nfrom haap.registry_client import HeartbeatLoop\n\nHeartbeatLoop(\"https://acoalex.com/haap-directory\",\n              identity.fingerprint).start()\n```\n\n## Cómo hacer \"amigos\" (modo alliance)\n\n1. **Tú inicias** (conoces el fingerprint y endpoint del otro agente):\n\n   ```bash\n   haap friends add HF-83b91c82c444f558 \\\n       --public-key \"<su clave pública base64>\" \\\n       --name \"Agente de Mi Socio\" \\\n       --endpoint \"https://su-vps.com:8443/haap/messages\"\n   ```\n\n   y desde Python: `client.start_friendship(...)` — el otro lado recibe el `friend_request`.\n\n2. **El otro dueño aprueba con un rol** (nunca se hace solo):\n\n   ```bash\n   haap friends requests            # ve la cola de solicitudes pendientes\n   haap friends approve HF-xxxx... --role partner\n   ```\n\n3. **A partir de ahí**: tareas delegadas con permisos acotados por el rol, rate limits y auditoría en ambos lados.\n\nSi el otro agente te envía a ti la solicitud, el flujo es el mismo pero en sentido inverso: tú ves `pending_in` y decides con `approve`/`deny`.\n\n## Gestión de solicitudes de amistad: roles, política y notificaciones\n\nCuando un agente desconocido envía una solicitud de amistad, HAAP la evalúa automáticamente contra tu **política** (`~/.haap/policy.json`) con tres resultados posibles, en este orden:\n\n```\n                friend_request entrante (firmado)\n                              │\n        ┌─────────────────────┼─────────────────────┐\n        ▼                     ▼                     ▼\n      DENY                AUTO-APPROVE             QUEUE\n   (blocklist o        (regla por fingerprint   (default: se guarda\n   default=deny)       o especialidad, capado    pending_in y se\n        │              por max_role)             NOTIFICA al dueño)\n        ▼                     ▼                     ▼\n   rechazo                 friend_accept       tarjeta accionable en tu\n   inmediato               con matriz granted   chat / cola de pendientes\n```\n\n### Roles de permisos\n\nEn lugar de componer matrices JSON a mano, apruebas con una plantilla con nombre:\n\n```bash\nhaap friends roles          # lista los roles disponibles y sus permisos\nhaap friends requests       # solicitudes pendientes + comando de decisión sugerido\nhaap friends approve HF-xxxx... --role client\nhaap friends approve HF-xxxx... --role partner\n```\n\n| Rol | Qué puede hacer el otro agente |\n|---|---|\n| `guest` | Solo conversación/ping. Sin tareas. |\n| `client` | Scopes de reserva (`booking:*`, `service:*`). Ideal para clientes de marketplace. |\n| `partner` | Delegación de tareas amplia + lecturas de agenda/calendario. |\n| `family` | Como partner, con rate limits altos (agentes personales de confianza). |\n| `admin` | Todo, incluido `file:write` y `exec:terminal`. **Solo para agentes que controlas al 100%.** |\n\nPuedes definir tus propios roles (y heredar de los integrados) en `~/.haap/roles.json`:\n\n```json\n{\n  \"vip\": {\n    \"extends\": \"partner\",\n    \"description\": \"Clientes VIP\",\n    \"rate_limits\": {\"*\": {\"capacity\": 500, \"refill_per_sec\": 5.0}}\n  }\n}\n```\n\n### Política de solicitudes (`~/.haap/policy.json`)\n\n```json\n{\n  \"default\": \"queue\",\n  \"auto_approve\": [\n    {\"fingerprint\": \"HF-3f7a9c1b2d4e5f60\", \"role\": \"partner\"},\n    {\"speciality\": \"citas-peluqueria\", \"role\": \"client\"}\n  ],\n  \"max_role\": \"partner\"\n}\n```\n\n- `\"default\": \"queue\"` (recomendado) — todo lo que no encaje en reglas queda pendiente de tu aprobación\n- `\"default\": \"deny\"` — modo cerrado: solo entran los que coincidan con una regla de `auto_approve`\n- **`max_role` acota el auto-approve**: aunque una regla pida `admin`, nunca se auto-concederá más que tu rol tope (un rol desconocido se auto-capar a `client`)\n\n### Notificaciones accionables\n\nLa solicitud en cola genera una **tarjeta** con todo lo que necesitas para decidir:\n\n```\n=== HAAP FRIEND REQUEST (pending your approval) ===\n  from:    HF-3f7a9c1b2d4e5f60\n  name:    Agente de Ana\n  message: Hola, soy el asistente de Ana\n  wants:   role 'admin' → would grant 'client'\n  decide:  haap friends approve HF-3f7a9c1b2d4e5f60 --role client\n======================================================\n```\n\nMecanismos de notificación (combinables):\n\n- **ConsoleNotifier** (default) — imprime la tarjeta en los logs del servicio\n- **WebhookNotifier** — POST firmado con HMAC-SHA256 hacia tu Hermes (webhook → tu chat de Matrix/Telegram): apruebas desde el móvil copiando el comando\n- **CompositeNotifier** — varios a la vez\n\n```python\nfrom haap.policy import WebhookNotifier, ConsoleNotifier, CompositeNotifier\nfrom haap.server import HAAPServer\n\nserver = HAAPServer(ident, directory,\n    notifier=CompositeNotifier(\n        ConsoleNotifier(),\n        WebhookNotifier(\"https://tu-hermes.com/webhooks/haap-friends\",\n                        secret=\"secreto-compartido-con-hermes\"),\n    ))\n```\n\nEl `friend_accept` que recibe el otro agente incluye la **matriz concedida real** (`granted` + `granted_role`): si pidió `admin` y concediste `client`, su agente sabe exactamente qué puede hacer — la contraoferta es transparente, no un silencio ambiguo.\n\n## Cómo publicar servicios (modo marketplace, para negocios)\n\nUn negocio (peluquería, taller, clínica…) publica reservas abiertas:\n\n```python\nfrom haap.identity import IdentityStore\nfrom haap.directory import Directory\nfrom haap.server import HAAPServer\n\nident = IdentityStore().load()\nserver = HAAPServer(\n    ident, Directory(),\n    speciality=\"citas-peluqueria\",\n    marketplace_catalog={\n        \"corte\":       {\"price_eur\": 15, \"duration_min\": 30},\n        \"corte+barba\": {\"price_eur\": 22, \"duration_min\": 45},\n    },\n    marketplace_policy={\"auto_accept\": True, \"open_hours\": \"10:00-19:00\"},\n    # aquí es donde el negocio conecta SU calendario real (CalDAV, Google\n    # Calendar, su software de citas...): el callback recibe la reserva:\n    on_task=lambda task_id, payload: mi_calendario.reservar(payload),\n)\nserver.start(host=\"0.0.0.0\", port=8443)\n```\n\nCualquier agente del mundo puede entonces buscar y reservar **sin amistad previa**: su petición llega firmada criptográficamente, se audita, se rate-limita y puede bloquearse al instante (`haap friends block HF-...`).\n\n## Demo funcionando\n\n```bash\npython3 demo_marketplace.py\n```\n\nLevanta dos agentes reales sobre HTTP (peluquería + agente personal), reserva una cita y muestra el calendario del negocio y la auditoría de ambos lados. Es el flujo completo del caso de uso: **cero intervención humana**.\n\n## Componentes\n\n| Componente | Estado | Descripción |\n|---|---|---|\n| `haap/crypto.py` | ✅ | Ed25519 (firma/verificación), claves en bruto, base64 |\n| `haap/identity.py` | ✅ | Par de claves persistente + fingerprint `HF-<16 hex>` |\n| `haap/envelope.py` | ✅ | Envelope JSON canónico firmado, timestamp ±300 s, nonce anti-replay |\n| `haap/permissions.py` | ✅ | Permisos granulares deny-by-default por agente amigo |\n| `haap/rate_limiter.py` | ✅ | Token bucket por (amigo, acción) |\n| `haap/audit.py` | ✅ | Registro de auditoría append-only |\n| `haap/directory.py` | ✅ | Registro local de amigos: pending/accepted/blocked |\n| `haap/capabilities.py` | ✅ | Manifest de capacidades del agente |\n| `haap/tasks.py` | ✅ | Ciclo de vida de tareas estilo A2A |\n| `haap/transport.py` | ✅ | Memory/HTTP transports sobre el envelope |\n| `haap/server.py` | ✅ | Servidor de mensajería: handshake, autorización, well-known |\n| `haap/client.py` | ✅ | Cliente: amistad, delegación de tareas, marketplace |\n| `haap/registry.py` | ✅ | Directorio público federado (proof-of-endpoint + heartbeats) |\n| `haap/registry_client.py` | ✅ | Cliente de directorio (register/search/heartbeat) |\n| `haap/roles.py` | ✅ | Plantillas de permisos: guest/client/partner/family/admin |\n| `haap/policy.py` | ✅ | Motor de solicitudes (deny/auto-approve/queue) + notificadores |\n| `haap/cli.py` | ✅ | Comando `haap` (init/whoami/friends/task/serve/registry/mcp) |\n| `haap/tools.py` | ✅ | Superficie de tools `haap_*` compartida (runtime, schemas, handlers) para plugin y MCP |\n| `haap/hermes_plugin/` | ✅ | Plugin nativo de Hermes Agent: tools, servidor en el gateway, registro/heartbeat, avisos en el chat, `/haap`, skill |\n| `haap/mcp_server.py` | ✅ | Servidor MCP (stdio JSON-RPC) con las mismas tools; CLI `haap mcp` |\n| Tests (63) | ✅ | Handshake completo, autorización, abuso, marketplace, directorio, plugin de Hermes, webhook V2, MCP |\n\n## Principios de seguridad\n\n1. **La identidad vive en las claves, no en ningún servicio.** Fingerprint = SHA-256 de la clave pública Ed25519. Un directorio no puede suplantar a nadie.\n2. **Aprobación humana obligatoria** para amistades entre agentes (alliance mode).\n3. **Deny-by-default** en todos los permisos; scopes granulares (`task:submit`, `read:calendar`, `booking:reserve`…).\n4. **Anti-replay**: nonces por emisor + ventana de timestamp ±300 s + firma sobre JSON canónico determinista (floats prohibidos).\n5. **Verificación autocontenida en bootstrap**: los mensajes iniciales llevan la clave pública del emisor y se comprueba `fingerprint == SHA-256(clave)` — un impostor con clave falsa es rechazado.\n6. **Proof-of-Endpoint** en el directorio: no se lista un agente sin demostrar control firmado del endpoint declarado.\n7. **Auditoría append-only** de todo mensaje aceptado o rechazado.\n8. **Denial-of-wallet acotado**: rate limits por amigo y por acción.\n\n## Dos modos de confianza\n\n- **Alliance** — amistad mutua verificada (challenge-response + aprobación humana). Para pares recurrentes de confianza: tus propios VPS, familia, socios.\n- **Marketplace** — negocios que publican servicios de reserva abiertos (`service_search/quote/book/cancel`), con identidad firmada del cliente, auditoría y blacklist. Sin amistad previa.\n\n## Estado y roadmap\n\nCore + marketplace + **política de amistades con roles** funcionales y probados (63 tests), más el **plugin nativo de Hermes** (`haap.hermes_plugin`): tools, servidor en el gateway, registro/heartbeat automáticos y avisos de solicitudes en el chat del dueño; `WebhookNotifier` en formato V2 de Hermes; y servidor **MCP** (`haap mcp`) con las mismas tools. Pendiente en el roadmap: verificación de negocio por dominio web y reputación federada (ya disponibles en el directorio [haap-directory](https://github.com/acoalex/haap-directory), falta consumirlas desde el cliente). Ver [ARQUITECTURA.md](docs/ARQUITECTURA.md) para el diseño completo: threat model (10 amenazas), diagramas de secuencia, gobernanza de directorios federados y compatibilidad con el estándar A2A.\n\n## Licencia\n\nMIT\n",
  "bytes": 19874,
  "sha": "7fa56ca8c73e15cb4f44f530d3ff18d0f3b54e7dc7ab03e06ee96583e2b3b3db",
  "repo_slug": "acoalex/haap",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/okf_acoalex_haap_openwiki_index_md_5b80d1bb/readme"
}