Nofax

Aprobaciones y notificaciones con intervención humana para agentes de codificación de IA a través de un servidor MCP local y hooks de agente.

Documentación

Nofax

CI License: MIT Node.js >=20 MCP

Aprobaciones y notificaciones con intervención humana para agentes de IA, sin ejecutar un SaaS de Nofax.

Nofax es un puente open-source pequeño entre un agente y un humano. El modo local puede pausar un flujo de trabajo de IA, notificar a tu teléfono y devolver una decisión explícita. Un Cloudflare Worker opcional auto-desplegado expone una superficie MCP remota deliberadamente más reducida para notificaciones unidireccionales e inspección segura de solicitudes.

Sin cuenta de Nofax. Sin API de modelo de pago. Sin puerto de entrada en tu máquina. Licencia MIT.

Estado actual: el Nofax local es 0.2.1. El Cloudflare Worker opcional es la próxima superficie remota 0.3.0 y se desarrolla junto con el paquete local.

Por qué Nofax

Los flujos de trabajo de agentes necesitan cada vez más una respuesta clara a una pregunta: cuando la automatización alcanza un límite de decisión humana, ¿cómo pregunta sin pretender que el silencio significa aprobación?

Nofax mantiene ese límite explícito:

  • pendiente nunca es aprobación;
  • el tiempo de espera y los fallos de transporte fallan en modo cerrado;
  • la primera respuesta terminal aceptada gana;
  • los esquemas de hooks específicos del agente permanecen aislados en adaptadores;
  • el acceso remoto es intencionalmente más reducido que el acceso local;
  • Nofax no otorga autoridad que el agente llamante no tuviera ya.

Dos modos de operación

CapacidadNofax local 0.2Worker remoto 0.3
Transportestdio / hooks CLIMCP Streamable HTTP
Notificación unidireccional
Permitir / DenegarNo
Opciones explícitasNo
Refinamiento de texto libreNo
Esperar respuesta humanaNo
Leer metadatos de solicitud
Estado duraderoArchivos localesFilas existentes de SQLite Durable Object
Alojado por NofaxNoNo — Worker auto-desplegado
Autenticación remotaLímite de proceso localClave bearer privada

El Worker remoto no es un servicio remoto de aprobación alojado. Puede enviar una notificación informativa e inspeccionar el estado de solicitudes existentes, pero no tiene callback de aprobación, opción, refinamiento, espera, webhook ni endpoint de escritura remota arbitraria.

Inicio rápido

1. Instalar

npm install -g nofax

Requiere Node.js 20 o superior.

2. Inicializar

nofax init

Nofax crea ~/.nofax/config.json y genera un tema de notificación de alta entropía. Con el transporte predeterminado, suscríbete al tema mostrado en la aplicación móvil ntfy.

3. Probar

nofax test

4. Usarlo

nofax notify --title "Build finished" "All tests passed"
nofax approve --title "Deploy?" "Release 1.4.0 is ready"
nofax refine --title "Refine draft" "Tell me what to change"

Una aprobación se resuelve en JSON terminal estable:

{"decision":"allow"}

o:

{"decision":"deny"}

Si la solicitud sigue pendiente, expira, se desconecta o encuentra un error de transporte, Nofax nunca convierte esa condición en aprobación.

MCP

Inicia el servidor MCP local stdio:

nofax mcp

El MCP local expone:

  • nofax_notify
  • nofax_request_approval
  • nofax_request_choice
  • nofax_request_refinement
  • nofax_wait_for_response
  • nofax_get_request
  • nofax_list_pending

Las solicitudes interactivas devuelven un ID de solicitud duradero. nofax_wait_for_response realiza una espera limitada; los llamantes deben repetir la espera mientras la solicitud permanezca pendiente en lugar de inferir aprobación.

Integraciones de agentes

Claude Code

Usa Nofax como hook local PermissionRequest en ~/.claude/settings.json:

{
  "hooks": {
    "PermissionRequest": [
      {
        "matcher": ".*",
        "hooks": [
          {
            "type": "command",
            "command": "nofax hook claude"
          }
        ]
      }
    ]
  }
}

Codex

Los hooks de Codex están habilitados por defecto. Configura ~/.codex/hooks.json:

{
  "hooks": {
    "PermissionRequest": [
      {
        "matcher": ".*",
        "hooks": [
          {
            "type": "command",
            "command": "nofax hook codex",
            "statusMessage": "Waiting for Nofax approval"
          }
        ]
      }
    ]
  }
}

Reinicia Codex, ejecuta /hooks y revisa/confía en la definición exacta del hook de Nofax antes de depender de él. Codex omite los hooks no gestionados hasta que se confíe en ellos, y una definición de hook modificada debe revisarse de nuevo. Si un administrador o una política local ha deshabilitado explícitamente los hooks, vuelve a habilitarlos con [features] hooks = true en ~/.codex/config.toml.

Gemini CLI

Las compilaciones actuales de Gemini CLI exponen un hook síncrono BeforeTool que puede permitir o denegar una llamada de herramienta. Enruta las herramientas seleccionadas a través de Nofax en ~/.gemini/settings.json:

{
  "hooks": {
    "BeforeTool": [
      {
        "matcher": "run_shell_command|write_file|replace",
        "hooks": [
          {
            "name": "nofax-approval",
            "type": "command",
            "command": "nofax hook gemini",
            "timeout": 305000
          }
        ]
      }
    ],
    "Notification": [
      {
        "matcher": "ToolPermission",
        "hooks": [
          {
            "name": "nofax-notification",
            "type": "command",
            "command": "nofax hook gemini"
          }
        ]
      }
    ]
  }
}

BeforeTool espera un resultado explícito de Permitir/Denegar de Nofax. Un tiempo de espera o fallo de transporte de Nofax emite JSON válido sin decisión y deja el flujo de política/confirmación propio de Gemini CLI en control, en lugar de convertir el fallo en aprobación. El hook Notification sigue siendo consultivo y solo se reenvía como notificación telefónica.

Ajusta el matcher a las herramientas que quieres que Nofax controle. Mantén el tiempo de espera del hook más largo que el tiempo de espera de aprobación configurado de Nofax (timeoutSeconds, 300 segundos por defecto).

Cloudflare Worker remoto opcional

El paquete worker/ proporciona un endpoint MCP privado auto-desplegado:

remote MCP client
       |
       | authenticated Streamable HTTP
       v
Cloudflare Worker
       |
       +--> nofax_notify ------> ntfy ------> phone
       |
       +--> SQLite Durable Object
              |
              +--> get request metadata
              +--> list pending requests

Expone exactamente tres herramientas:

  • nofax_notify — solo notificación unidireccional;
  • nofax_get_request — lee una proyección segura de solicitud;
  • nofax_list_pending — lee proyecciones de solicitudes no resueltas y no expiradas.

Despliega desde worker/:

npm ci
npx wrangler login
npx wrangler secret put NOFAX_REMOTE_KEY
npx wrangler secret put NTFY_TOPIC
npm run check
npm run deploy

Conexión MCP preferida:

https://<worker>.workers.dev/mcp
Authorization: Bearer <NOFAX_REMOTE_KEY>

Los clientes que no pueden adjuntar un encabezado de autorización estático pueden usar la ruta de capacidad de compatibilidad:

https://<worker>.workers.dev/mcp/<NOFAX_REMOTE_KEY>

Trata la URL de capacidad completa como una contraseña.

Consulta docs/remote-mcp.md para detalles de despliegue, límites de amenazas y calificación.

Importante: ntfy público + salida serverless

El servicio público predeterminado ntfy.sh aplica cuotas de publicador. Las plataformas serverless como Cloudflare Workers pueden usar espacio de IP de salida compartido, por lo que un Worker puede recibir una respuesta de cuota diaria de ntfy 42908 incluso cuando ese Worker individual ha enviado muy poco tráfico. Ese límite lo impone ntfy, no la cuota de solicitudes de Cloudflare Workers.

Para despliegues sensibles a la fiabilidad, usa un proveedor de notificaciones cuya cuota esté vinculada a tu propia cuenta/identidad autenticada, u opera un transporte auto-alojado de confianza. No construyas un flujo de trabajo crítico en torno a suposiciones de cuota de temas públicos anónimos.

Modelo de seguridad

Nofax es un componente de transporte e interacción humana, no un motor de políticas de autorización.

Modo local:

  • pendiente, tiempo de espera, desconexión, estado malformado y fallo de red nunca significan aprobación;
  • la primera respuesta terminal válida gana;
  • los temas de notificación y los temas de respuesta de un solo uso son capacidades;
  • ntfy público no está cifrado de extremo a extremo desde el proveedor;
  • la redacción es de mejor esfuerzo y no puede identificar de manera fiable secretos incrustados en texto libre arbitrario.

Modo remoto:

  • solo nofax_notify explícito realiza un efecto secundario de mensajería externa;
  • las operaciones de inspección de solicitudes son de solo lectura y no realizan escrituras de limpieza ocultas;
  • las superficies de aprobación remota, callback, webhook, refinamiento, opción y espera están ausentes;
  • NOFAX_REMOTE_KEY es una credencial bearer;
  • las proyecciones remotas omiten capacidades de callback, texto de prompt/mensaje y listas internas de decisiones permitidas.

Lee SECURITY.md antes de usar Nofax con información sensible.

Configuración

La configuración local predeterminada se encuentra en ~/.nofax/config.json:

{
  "version": 1,
  "server": "https://ntfy.sh",
  "topic": "nofax_<random>",
  "timeoutSeconds": 300
}

Anula el directorio de inicio con NOFAX_HOME:

NOFAX_HOME=/path/to/nofax-home nofax config

Usa otro servidor compatible con ntfy con:

nofax init --server https://ntfy.example.com --force

Desarrollo

Paquete local:

npm ci
npm run check
npm test
npm pack --dry-run

Worker remoto:

cd worker
npm ci
npm run check

CI califica Node.js 20, 22 y 24 para el paquete local. La puerta del Worker ejecuta TypeScript, Vitest, una auditoría de dependencias de producción y un ensayo de despliegue de Wrangler.

Documentación del proyecto

No objetivos

Nofax deliberadamente no proporciona:

  • un SaaS de aprobación operado por Nofax;
  • una dependencia de API de modelo de pago;
  • política persistente de always approve;
  • un endpoint de shell remoto arbitrario;
  • un Worker multiusuario público detrás de una clave de despliegue compartida;
  • una afirmación de que las anotaciones MCP en sí mismas son un límite de seguridad.

Licencia

MIT © Tomi Šeregi. Consulta LICENSE.