Docs · private trial

Quickstart

Gate0x puts a deterministic policy between an AI agent and its MCP tools. It runs locally; nothing is uploaded and no account is needed. Requires macOS and Node 24+.

Security notice: local approvals are not a security boundary. REQUIRE_APPROVAL asks you before a call runs, but approvals are files that any process running as your OS user can write. If an agent has unrestricted shell access as you (a built-in terminal tool, or any shell-capable MCP server), it can approve its own requests. Block or approval-gate shell execution, and treat approvals as a safeguard against mistakes, not against a hostile agent. node gate0x.js scan flags the obvious cases (GX-APR-001, GX-APR-002).

The short path (about five minutes)

Use a test setup, not production, and no real secrets. You need Node 24+ and nothing else: no npm install, no sudo, no PATH or shell-profile changes. Gate0x is one file, gate0x.js, that you run with node from the folder you received.

Open the folder in Terminal

You received a ZIP that holds one folder, gate0x-private-trial-0.1.0-rc.5.

  • If that folder already exists (macOS usually unpacks a ZIP for you, often in Downloads), use it. Do not unzip it again.
  • If you only see the .zip file, double-click it once in Finder. The folder appears next to it.

Open Terminal, type cd and a space, drag the folder from Finder into the Terminal window, and press Enter. Then check you are in the right place:

check
$ node gate0x.js --version

It prints 0.1.0-rc.5. If it says it cannot find the file, you are not in the folder yet: repeat the drag. Optional: check that the files arrived intact.

optional
$ shasum -a 256 -c SHA256SUMS

1. Scan

scan
$ node gate0x.js scan

Scan lists the MCP servers it found and ends with a ready-made line for each risky one, with that server's real name already filled in.

2. Protect one server

Copy the line Scan printed for the server you want to protect. It looks like the line below, which is only an example: if Scan found a server called filesystem, the line is

example, use the line Scan printed
$ node gate0x.js wrap filesystem --apply

If your server has a different name, do not paste the line above; paste the one Scan printed. The command shows exactly what it will change, asks you to confirm, and writes a backup first. When it finishes it prints what to do next. The first step is to restart your MCP client.

3. Keep Approval Watch open

Open a second Terminal window, type cd and a space, drag the same folder into it, press Enter, then run:

second terminal
$ node gate0x.js approvals watch

Leave that window open while you use your agent. When a call needs approval it shows up there (see Approvals in detail). Press Ctrl-C only when you want to stop watching.

4. Look at the audit, and undo

Back in the first window:

afterwards
$ node gate0x.js audit

To undo everything wrap --apply did, then restart your MCP client again:

undo
$ node gate0x.js wrap --rollback

That is the whole loop. Everything Gate0x prints for a next step uses the same node gate0x.js ... form, so you can copy it as it is.

How Gate0x is installed (and why there is no install step)

  • Nothing is installed globally. node gate0x.js runs the file in place. It does not use npm, your npm configuration or cache, or a writable /usr/local.
  • Protect needs a stable file. A desktop MCP client starts your server long after your terminal command has finished, so wrap --apply copies this one file to ~/.gate0x/bin/gate0x-<version>-<hash>.js (owner-only, never overwritten) and points the config at the copy. It shows you that path first. You can delete the folder you received afterwards.
  • No sudo, no root, no shell-profile edits. Everything Gate0x writes is under ~/.gate0x and the one config file you approve, plus a backup next to it. If a command ever asks for sudo, stop and tell us.

Scan in detail

scan
$ node gate0x.js scan
$ node gate0x.js scan --verbose
$ node gate0x.js scan --json

The first shows what was found, what is dangerous, why and what to do. --verbose adds every finding, launch commands and which config files were checked. --json is stable output for scripts.

Scan checks the default config locations of Claude Desktop, Claude Code, Cursor, Windsurf, VS Code, Gemini CLI and Zed. Those locations are not yet verified against every client version, and --path scans any file. It does not start any server and never prints secret values. The capabilities it reports are inferred from names and packages in the config, so treat them as strong hints, not proof.

Protect in detail

--apply changes one server's entry in one config file:

  • it prints the before and after (secrets hidden) and asks you to confirm;
  • it writes a timestamped backup next to the file first, and never overwrites a backup;
  • it replaces only that entry; every other byte of the file stays identical (comments, formatting and other servers included);
  • it validates the result before replacing the file, then replaces it atomically;
  • it refuses symbolic links, malformed or oversized files, remote (URL) servers, and anything it cannot edit safely. Then wrap without --apply prints the entry so you can edit it yourself.

To see the change without making it, add --dry-run instead of --apply: nothing is written.

If a server is configured in several clients, Gate0x tells you and you choose with --client cursor (or claude-desktop, claude-code, windsurf, vscode, gemini-cli) or --path followed by the config file. Restart the MCP client afterwards. The entry uses absolute paths to node and the stable copy of gate0x.js, because desktop clients do not inherit your shell PATH; if you later change Node versions, roll back and apply again.

Policy

wrap --apply creates a starter policy at ~/.gate0x/policy.json if you do not have one; add --strict to create the strict starter instead.

  • balanced (default): blocks destructive database statements and asks for approval for shell commands, deleting data, payments, credential access, privileged administration, deploys, outbound messages, tools whose definition changed, and calls Gate0x could not fully inspect. Everything else runs.
  • strict: asks for approval for anything that is not clearly read-only.

How well does the classification hold up? On a hand-made set of realistic tool names it had never seen, the balanced policy stopped about 7 in 10 dangerous calls and interrupted about 1 in 14 harmless ones. The strict policy stopped all of them but also asked about roughly half of the harmless ones. Expect misses with balanced and some approval fatigue with strict.

policy
$ node gate0x.js policy test ~/.gate0x/policy.json --tool delete_note
$ node gate0x.js policy check ~/.gate0x/policy.json

Approvals in detail

When a call needs approval, the proxy holds it (up to 120 seconds) and says so on stderr. Approval Watch (short path, step 3) shows it like this:

approval watch
ACTION REQUIRES APPROVAL

Tool:
notes.delete_note

Arguments:
{"id":"n1"}
(secret values are redacted)

Reason:
Deleting or destroying data requires approval.

Expires:
in 120 seconds, at 14:03:09. No answer means denied.

[A] Approve once
[D] Deny

Choice:

Type A to approve exactly this one call, or D to deny it, then press Enter.

  • Only A (or a) approves and only D (or d) denies. Enter alone does nothing, and anything else is not accepted: it explains the choices and asks again.
  • If nobody answers, the call is denied when the time is up and the agent is told so.
  • If the arguments are too long to show in full, A is not enough: you are asked to type APPROVE as well, because you cannot see everything the call would do.

Other ways to answer, if you prefer:

approvals
$ node gate0x.js approvals
$ node gate0x.js approve
$ node gate0x.js deny

These list what is waiting, approve the single waiting request (asks you to confirm), and deny it. Nothing is approved automatically: Approval Watch needs an interactive terminal. An approval is bound to the exact call and can be used once. At most 25 requests can wait at a time. Remember the notice above: this is a safeguard against mistakes, not a security boundary.

Audit in detail

audit
$ node gate0x.js audit
$ node gate0x.js audit --verify

The first shows recent decisions and approvals (an answer of D is recorded as outcome=denied, an unanswered request as outcome=timeout). The second checks the tamper-evident hash chain.

State lives in ~/.gate0x (owner-only): the audit log, pending approvals, pinned tool fingerprints and your policy. Arguments are redacted before they are written (best effort).

If the audit log cannot be written, Gate0x does not run what it cannot record:

  • BLOCK and REQUIRE_APPROVAL calls never run without their record, and high-risk calls never run without a record.
  • Low-risk allowed calls are also blocked by default (--on-audit-failure block). To keep them flowing during an outage, opt in with --on-audit-failure allow-low-risk; the gap is counted and written to the log as an audit_gap entry once writing resumes.

Policy reference

policy.json
{
  "id": "my-policy",
  "version": 1,
  "defaults": { "noMatch": "ALLOW" },
  "rules": [
    {
      "id": "block-big-transfers",
      "decision": "BLOCK",
      "match": {
        "category": "payments",
        "when": [ { "field": "arguments.amount", "op": "gt", "value": 10000 } ]
      },
      "reasonCode": "TRANSFER_TOO_LARGE",
      "reason": "Transfers over 10,000 are blocked."
    }
  ]
}

Decisions are ALLOW, BLOCK or REQUIRE_APPROVAL. When several rules match, the most restrictive wins; ties go to the lowest rule id; rule order never matters. If no rule matches, defaults.noMatch applies (BLOCK when unset). Rules match on source, action (server.tool), tool, resource and category (a string or list; exact or ending in one *; case and invisible characters ignored). Categories: filesystem_write, filesystem_delete, shell_exec, network_egress, messaging_send, credential_access, payments, db_write, db_destructive, data_delete, privileged_admin, deploy_publish.

match.when is a list of conditions that must all hold, each with a field (arguments.<path> or context.<path>), an op (eq, ne, in, prefix, contains, gt, gte, lt, lte, within, exists) and a value. A missing field or wrong type makes the condition false. For paths use within, which resolves ..; prefix does not.

Known limits

Read these before relying on Gate0x for anything.

  • Local approvals are not a security boundary (see the notice at the top).
  • Coverage is narrow. Protect checks tools/call for local stdio MCP servers only. It does not see remote (URL) servers, MCP resources or prompts, or an agent's built-in tools (for example a built-in terminal).
  • Classification can miss dangerous tools. It is a keyword heuristic over names, descriptions, argument names and SQL or shell text. It misses tools with unusual names and does not decode encoded commands.
  • It only sees what goes through it. node gate0x.js scan shows which servers are not protected.
  • The audit log is tamper-evident, not tamper-proof. Edits and middle deletions are detected; removing the newest entries is not.
  • macOS first. Linux has automated test coverage only; Windows is not supported.
  • Single machine. No central management, sync or hosted dashboard.