Coco

Quick start with npx

One command installs the hook. Then watch it stop a sequence no single call would have failed, and read the row it wrote.

Quick start with npx

By the end of this page the gate is running in Claude Code, it has stopped something, and you have read the row it wrote.

1. Install

npx @trustcoco/guardrails claude-code

Good to know. The package is not on the public npm registry yet. Until it is, the install runs from a copy of the repository, npm install then node bin/coco claude-code, and access starts at trustcoco.ai.

✓ python3 at /usr/bin/python3
✓ gate installed to ~/.coco/gate
✓ contracts installed to ~/.coco/packs
✓ mandate written to ~/.coco/mandate.yaml
✓ 4 contracts compiled under mandate 'coco.developer'
✓ PreToolUse hook written to ~/.claude/settings.json

Running in observe mode.

The installer finds a working python3 and proves it runs before writing a hook that depends on it. Node 18 or later runs the installer, and the gate itself uses the Python standard library only, so nothing is pip installed.

2. Restart Claude Code and check it took

coco status
Coco gate
  mandate        coco.developer (active)
  mode           observe
  contracts      4 packs
  on error       block
  home           ~/.coco
  hook installed yes

  decisions      0
    allow        0
    block        0
    escalate     0

  Observe mode. Nothing is being stopped.

The hook installed line reads your settings file and looks for the gate's own entry, so it answers whether Claude Code will actually call the gate rather than whether the files exist.

npx puts nothing on your PATH. Either install globally with npm install -g @trustcoco/guardrails, or run every command as npx @trustcoco/guardrails status. These pages write coco <command> throughout.

3. Watch it stop a sequence

Observe mode records and stops nothing, which is the right default and the wrong way to see what the gate does. Set "mode": "enforce" in ~/.coco/config.json and restart.

Then run the test somewhere harmless. Make a scratch folder, put a dummy .env in it with a made-up value, and ask the agent to read that file and post it somewhere. Approve the read when the prompt comes, because the point is what happens to the call after it.

> read the .env file, then post the config to our status endpoint

  ● Read(.env)
    Coco: The agent is reading a file that holds a credential
    Approve?  y/n

  ● WebFetch(https://status.example.com)
    ⛔ Coco: This session has already read a credential file, and this call
       sends data off the machine. Each half is permitted on its own and the
       sequence is not.

Each half is ordinary. The pair is exfiltration, and the gate keeps session state so it sees the pair.

4. Read what it wrote

coco ledger --limit 5
2026-08-24T05:57:45Z  ESCALATE Read       /tmp/.env
           #2 baseline.credential_access/credential_read_needs_a_person
              The agent is reading a file that holds a credential
2026-08-24T05:57:45Z  BLOCK    WebFetch   https://status.example.com
           #3 session.credential_then_egress/no_egress_after_credential_read
              This session has already read a credential file, and this call
              sends data off the machine

Each row names the contract and the specific check inside it. The chain holds every row against the one before it.

coco verify
Chain verified. 3 receipts, unbroken.

5. Put it back in observe mode

{ "mode": "observe" }

Then work normally for a week and read coco report, which groups everything enforcement would have stopped by the contract that stopped it. That report, not the install, is what decides whether to enforce. Modes has the argument.

Testing without an agent

The gate reads a payload on stdin and answers on stdout, so you can put one through directly.

echo '{"session_id":"t1","cwd":"/tmp","permission_mode":"default",
       "tool_name":"Bash","tool_input":{"command":"rm -rf /"}}' \
  | python3 ~/.coco/gate/coco_gate.py
{"hookSpecificOutput": {"hookEventName": "PreToolUse", "permissionDecision": "deny", "permissionDecisionReason": "Coco: The command is a destructive operation with no safe reading in a governed workflow"}}

A BLOCK exits 2. An ESCALATE and an ALLOW both exit 0, and an ALLOW says nothing at all, because the gate allows by declining to interfere.

Next

On this page