npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

agentgovern-guard

v0.1.2

Published

Local proxy in front of AgentGovern that blocks when AgentGovern is slow or unreachable

Readme

agentgovern-guard

CI

A small local proxy that sits between AgentGovern's coding-agent hook (~/.agentgovern/hook.sh) or your own code and AgentGovern. It runs on your machine.

Upstream: by default the guard forwards to https://agentgovern.io. To use a different AgentGovern (a staging or self-run instance), set AGG_UPSTREAM, for example AGG_UPSTREAM=https://staging.example.com.

tool hook ── hook.sh ──POST──▶ guard 127.0.0.1:8787 ──▶ AgentGovern /api/v1/...
                                     │
   timeout · network error · 5xx · 3xx · any answer it doesn't recognise  ⇒  answers "blocked"

What it adds — and what it doesn't

| Situation | Without the guard | With the guard | |---|---|---| | AgentGovern unreachable or erroring | hook.sh blocks | Guard blocks with a clearer reason | | AgentGovern slow | hook.sh waits up to 8 s, then blocks | Guard blocks at AGG_UPSTREAM_TIMEOUT_MS | | Guard itself not running | — | hook.sh can't connect and blocks | | Hook never runs (script deleted, hook not trusted in Codex /hooks, not accepted in Hermes) | Tool runs ungoverned | Still runs ungoverned. Nothing reaches the guard. Only the tool's own fail-closed setting covers this: Cursor failClosed, Hermes fail_closed. |

The guard does not make allowed calls faster. Every allowed action has to reach AgentGovern so it is logged, counted against the agent's spending cap, and checked against pause/revoke. So allows are never cached.

Run

npx [email protected]                                   # forwards to https://agentgovern.io
AGG_UPSTREAM=https://staging.example.com npx [email protected]   # or anywhere else

Requires Node 20+. No dependencies. It listens on 127.0.0.1:8787. Keep it running with your process manager of choice (launchd, systemd, pm2).

Then point hook.sh at it. Add this to the environment the coding tool runs in, for example your shell profile:

export AGENTGOVERN_URL=http://127.0.0.1:8787

Your tool's hook config doesn't change. hook.sh still sends the agent key from ~/.agentgovern/<format>.auth, and the guard forwards it untouched.

Example: Claude Code

  1. Set up the hook exactly as AgentGovern's Integrations → Claude Code card shows. The card's install command downloads ~/.agentgovern/hook.sh and saves your key. Then put this in ~/.claude/settings.json:

    {
      "hooks": {
        "PreToolUse": [
          {
            "matcher": "Bash|Read|Edit|Write|MultiEdit|NotebookEdit|WebFetch|WebSearch|mcp__.*",
            "hooks": [
              { "type": "command", "command": "sh \"$HOME/.agentgovern/hook.sh\" claude-code", "timeout": 30 }
            ]
          }
        ]
      }
    }
  2. Point the hook at the guard. Claude Code's hooks inherit this from the environment it starts in:

    export AGENTGOVERN_URL=http://127.0.0.1:8787

    Each tool call now goes hook.sh → guard → AGG_UPSTREAM/api/v1/hooks/claude-code. AgentGovern checks the whole call: the tool and its full input, not just the tool's name.

Config

Settings come from environment variables first, then guard.config.json (or $AGG_CONFIG), then the defaults below.

| Env | Default | Meaning | |---|---|---| | AGG_PORT / AGG_HOST | 8787 / 127.0.0.1 | Listen address | | AGG_UPSTREAM | https://agentgovern.io | AgentGovern base URL. Set this to use any other instance. | | AGG_UPSTREAM_BY_TOOL | — | JSON map of per-tool upstreams, keyed by hook format or X-AG-Tool. Example: {"codex":"https://staging…"} | | AGG_UPSTREAM_TIMEOUT_MS | 8000 | Upstream answers slower than this are blocked | | AGG_CACHE_TTL_SEC | 0 (off) | Block cache. See below | | AGG_LOG | on | Set 0 to silence stderr logs. One line per answer: the path, the status, and AgentGovern's decision and reason. Logs never include keys or request bodies. |

Block cache (opt-in)

When AGG_CACHE_TTL_SEC is above 0, the guard caches only explicit "decision":"blocked" answers from /api/v1/evaluate:

  • The cache key is the method, path, Authorization header and full body. Two agents, or two different calls, never share an entry.
  • Allowed, pending-approval and hook answers are never cached. An approval must be able to flip a hook answer straight away.
  • A repeat of a cached block isn't written to AgentGovern's audit log, and a rule you loosen takes up to one TTL to apply.

Behaviour

The guard decides for itself whether an answer is one the caller can act on. It doesn't rely on hook.sh's parser to catch anything odd.

  • Paths. Only /api/v1/* is proxied, checked after resolving dot segments, so /api/v1/../../x and /api/v1/%2e%2e/… can't escape it. Encoded / or \ (%2f, %5c) and absolute-form targets (http://host/…, //host/…) are refused.
    • A POST anywhere else, including /healthz and the 0.1.0 README's /tool-use/evaluate, is answered with a block: {"decision":"block","permission":"deny",…}. It's never a 404, which an old curl … || echo hook would read as "go ahead".
    • Other methods outside /api/v1/* get a 404.
    • GET /healthz reports status, with any credentials in the upstream URL removed.
  • Hook paths (/api/v1/hooks/:format). Only a 2xx whose first line is exactly 0 or 2 is passed on. Everything else is answered with 2\n<reason> (exit 2, which blocks in every supported tool): every 3xx, every 4xx, an empty body, an HTML page, or any other first line.
  • /api/v1/evaluate. A 2xx JSON object with a decision is passed on. So is a 4xx JSON object, which is AgentGovern's own "not allowed" answer, for example 400 invalid_cost. Anything else is answered with {"decision":"blocked","allowed":false,"reason":…}: a 3xx, a 2xx without decision, or non-JSON.
  • Other /api/v1/* (me, approvals, receipts): a 2xx or 4xx JSON body is passed on. Anything else is blocked.
  • Wrong, revoked or deleted agent key (401/403). The answer says it's a key problem, not a policy decision, and the log line reads KEY REJECTED ….
    • On /evaluate, the status is kept and the JSON gets key_rejected: true plus that reason.
    • On hook paths, the answer is 2\n<that reason>.
  • 5xx, timeout, network error or oversized answer are blocked in the same shapes.
  • Logs. One line per answer:
    • allow only when the answer really allows: hook exit 0 with no output, or the tool's own JSON saying so. A Claude Code deny or ask is exit 0 plus permissionDecision, and it's logged as deny or ask.
    • Anything the guard doesn't recognise is logged as unrecognised … => block.
    • Logs never include keys, request bodies, or credentials from the upstream URL.
  • Upstream warning. At startup the guard warns if AGG_UPSTREAM or any AGG_UPSTREAM_BY_TOOL entry isn't https://. Plain http:// is only expected for localhost.

Test

npm test        # node --test test/*.test.js — works on Node 20, 22 and 24

CI runs the suite on Node 20, 22 and 24 on every push to main and every PR. It also checks that the package still has zero dependencies and ships only src/, README.md and package.json.

30 offline tests. They cover:

  • pass-through, blocking on outage, and the TTL;
  • the cache-bypass cases: a look-alike call (same agent and resource, different body), a bigger cost, another agent's key, allows never cached;
  • a paused agent blocked on the very next call;
  • key-rejected wording and logs;
  • the 0.1.0 legacy-path block;
  • 0.1.2's answer validation: a 3xx or 2xx HTML/empty answer on a hook path, a 2xx without decision on /evaluate;
  • dot-segment and encoded-slash paths;
  • log labels, GET-only /healthz, and credential redaction.

Upgrading from 0.1.0

0.1.0 cached every answer for 60 s, keyed on agent + resource only. A different call to the same resource could be answered from memory, with no audit row and no spend charge, and pause or revoke took up to a minute to apply. Since 0.1.1, the guard:

  • turns caching off by default;
  • never caches allowed or pending answers;
  • keys any opt-in block cache on the Authorization header and the full body;
  • defaults the upstream to https://agentgovern.io.

Hook setups from the 0.1.0 README (/tool-use/evaluate, a constant {"resource":"Bash"} body) no longer check anything, so 0.1.1 and later block them with a message. Switch to AgentGovern's hook script and AGENTGOVERN_URL=http://127.0.0.1:8787 as described above.

Releasing

Releases go out from CI, never from someone's machine. 0.1.0 was published by hand from a stale clone, a week behind main, and that's what this process prevents.

  1. Bump the version in a PR. Change "version" in package.json, and merge once CI is green.
  2. Run the release. Go to Actions → Release → Run workflow on main, and enter the same version. The workflow:
    1. runs the full test matrix;
    2. checks that the version matches package.json and isn't already on npm or tagged;
    3. runs npm publish from that exact commit;
    4. tags the commit v<version>.
  3. Check it landed. npm view agentgovern-guard version should show the new version, and the v<version> tag should point at the commit you merged.

Auth. The release workflow uses npm trusted publishing (OIDC), so no long-lived token is needed. It must be set up once on npmjs.com: agentgovern-guard → Settings → Trusted publishing → GitHub Actions, with organization or user dank0296, repository agentgovern-guard, workflow release.yml, environment npm. Until that's done, the workflow falls back to an NPM_TOKEN repository secret (a granular automation token). Delete the secret once trusted publishing works.

Provenance. None, while this repo is private. npm only attaches provenance for public repositories.

Don't run npm publish locally. If you must, publish from a clean, up-to-date checkout of main and tag it yourself.