Skip to main content
Glama

Nofax

CI License: MIT Node.js >=20 MCP

Human-in-the-loop approvals and notifications for AI coding agents, without running a Nofax SaaS.

Nofax is a small open-source bridge between an agent and a human. Local mode can pause an AI workflow, notify your phone, and return an explicit decision. An optional self-deployed Cloudflare Worker exposes a deliberately narrower remote MCP surface for one-way notifications and safe request inspection.

No Nofax account. No paid model API. No inbound port on your machine. MIT licensed.

Current status: local Nofax is 0.2.0. The optional Cloudflare Worker is the upcoming 0.3.0 remote surface and is developed alongside the local package.

Why Nofax

Agent workflows increasingly need a clean answer to one question: when automation reaches a human decision boundary, how does it ask without pretending that silence means approval?

Nofax keeps that boundary explicit:

  • pending is never approval;

  • timeout and transport failure fail closed;

  • the first accepted terminal response wins;

  • agent-specific hook schemas stay isolated in adapters;

  • remote access is intentionally narrower than local access;

  • Nofax does not grant authority the calling agent did not already have.

Related MCP server: Relay

Two operating modes

Capability

Local Nofax 0.2

Remote Worker 0.3

Transport

stdio / CLI hooks

MCP Streamable HTTP

One-way notification

Yes

Yes

Allow / Deny

Yes

No

Explicit choices

Yes

No

Free-text refinement

Yes

No

Wait for human response

Yes

No

Read request metadata

Yes

Yes

Durable state

Local files

Existing SQLite Durable Object rows

Hosted by Nofax

No

No — self-deployed Worker

Remote authentication

Local process boundary

Private bearer key

The remote Worker is not a hosted remote-approval service. It can send an informational notification and inspect existing request state, but it has no approval callback, choice, refinement, wait, webhook, or arbitrary remote-write endpoint.

Quick start

1. Install

Until a registry release is published:

npm install -g https://github.com/AKzar1el/nofax.git

Requires Node.js 20 or newer.

2. Initialize

nofax init

Nofax creates ~/.nofax/config.json and generates a high-entropy notification topic. With the default transport, subscribe to the displayed topic in the ntfy mobile app.

3. Test

nofax test

4. Use it

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"

An approval resolves to stable terminal JSON:

{"decision":"allow"}

or:

{"decision":"deny"}

If the request is still pending, times out, disconnects, or hits a transport error, Nofax never converts that condition into approval.

MCP

Start the local stdio MCP server:

nofax mcp

Local MCP exposes:

  • nofax_notify

  • nofax_request_approval

  • nofax_request_choice

  • nofax_request_refinement

  • nofax_wait_for_response

  • nofax_get_request

  • nofax_list_pending

Interactive requests return a durable request ID. nofax_wait_for_response performs a bounded wait; callers must repeat the wait while the request remains pending rather than infer approval.

Agent integrations

Claude Code

Use Nofax as a local PermissionRequest hook in ~/.claude/settings.json:

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

Codex

Enable hooks in ~/.codex/config.toml:

[features]
hooks = true

Then configure ~/.codex/hooks.json:

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

Gemini CLI

Gemini integration is notification-only where the upstream hook contract is advisory:

nofax hook gemini

Nofax does not claim bidirectional permission control where the host does not expose a suitable decision contract.

Optional remote Cloudflare Worker

The worker/ package provides a private, self-deployed MCP endpoint:

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

It exposes exactly three tools:

  • nofax_notify — one-way notification only;

  • nofax_get_request — read one safe request projection;

  • nofax_list_pending — read unresolved, unexpired request projections.

Deploy from 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

Preferred MCP connection:

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

Clients that cannot attach a static authorization header can use the compatibility capability path:

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

Treat the complete capability URL like a password.

See docs/remote-mcp.md for deployment, threat boundaries, and qualification details.

Important: public ntfy + serverless egress

The default public ntfy.sh service applies publisher quotas. Serverless platforms such as Cloudflare Workers may use shared outbound IP space, so a Worker can receive an ntfy 42908 daily-quota response even when that individual Worker has sent very little traffic. That limit is imposed by ntfy, not by the Cloudflare Workers request quota.

For reliability-sensitive deployments, use a notification provider whose quota is tied to your own authenticated account/identity, or operate a trusted self-hosted transport. Do not build a critical workflow around anonymous public-topic quota assumptions.

Security model

Nofax is a transport and human-interaction component, not an authorization policy engine.

Local mode:

  • pending, timeout, disconnect, malformed state, and network failure never mean approval;

  • the first valid terminal response wins;

  • notification topics and one-time response topics are capabilities;

  • public ntfy is not end-to-end encrypted from the provider;

  • redaction is best-effort and cannot reliably identify secrets embedded in arbitrary free-form text.

Remote mode:

  • only explicit nofax_notify performs an external messaging side effect;

  • request-inspection operations are read-only and do not perform hidden cleanup writes;

  • remote approval, callback, webhook, refinement, choice, and wait surfaces are absent;

  • NOFAX_REMOTE_KEY is a bearer credential;

  • remote projections omit callback capabilities, prompt/message text, and internal allowed-decision lists.

Read SECURITY.md before using Nofax with sensitive information.

Configuration

Default local config lives at ~/.nofax/config.json:

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

Override the home directory with NOFAX_HOME:

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

Use another ntfy-compatible server with:

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

Development

Local package:

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

Remote Worker:

cd worker
npm ci
npm run check

CI qualifies Node.js 20, 22, and 24 for the local package. The Worker gate runs TypeScript, Vitest, a production-dependency audit, and a Wrangler deployment dry-run.

Project docs

Non-goals

Nofax deliberately does not provide:

  • a Nofax-operated approval SaaS;

  • a paid model API dependency;

  • persistent always approve policy;

  • an arbitrary remote shell endpoint;

  • a public multi-user Worker behind one shared deployment key;

  • a claim that MCP annotations themselves are a security boundary.

License

MIT © Tomi Šeregi. See LICENSE.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Human-in-the-Loop authorization gateway for AI Agents. Securely pause MCP workflows and route high-risk actions to human approvers via Slack or Email.
    85
    1
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Adds a human-in-the-loop checkpoint to MCP-capable AI coding agents, enabling them to pause and request user feedback before executing actions.
    10
    71
    MIT
  • F
    license
    Not graded
    quality
    D
    maintenance
    Enables AI agents to request human decisions for subjective or high-stakes choices through MCP tools like ask_human and provision_api_key.
    -
  • A
    license
    Not graded
    quality
    C
    maintenance
    MCP server that provides human-in-the-loop approval for risky AI agent actions, with durable state and audit logs.
    MIT