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

countersign-mcp

v0.1.4

Published

Transaction firewall and intent-verification layer for AI agents transacting onchain. Countersign firewalls the MCP execution path of the OKX OnchainOS stack.

Readme

Countersign

A transaction firewall for AI agents that transact onchain.

Countersign sits between an agent's reasoning and its execution tools. It firewalls the OKX OnchainOS MCP execution path: every transactional tool call is simulated, scanned, policy-checked, and compared against what the human actually asked for — before it can execute. Every decision lands in a tamper-evident audit log.

It is not a wallet, a custody solution, or a key manager. Countersign never holds keys.

Agent  ──►  Countersign  ──►  onchainos  ──►  chain
              │
              ├── does this match what the human asked for?
              ├── is it within policy?
              ├── do the scanners flag it?
              └── allow · escalate · block  →  audit log

Why

Wallet-layer policy engines — spend caps, allowlists — are becoming a commodity, and they solve a real problem. But they only see the signed payload. They cannot see that the agent was talked into signing it.

A prompt-injected agent produces transactions that are individually unremarkable: right size, clean token, valid signature, allowed chain. They are wrong only relative to what the user asked for. Catching that requires seeing both the instruction and the decoded transaction, which is what Countersign does.

Here is the case that motivates the whole product. The user asks for USDC. The agent is induced to buy a token whose ticker is uSDC — a real one, which every risk scanner rates LOW with zero risk labels:

COUNTERSIGN ESCALATE — human approval required

You asked for:   "Buy 5 dollars of USDC on solana"
The transaction: sells WSOL (So111111…111112): risk LOW
                 buys unverified token EguMF3v7…mapump: risk LOW

Mismatch with your instruction:
  ! you asked to acquire USDC, but this transaction acquires unverified token
    EguMF3v7…mapump — not the canonical USDC address on solana

The scanners are working correctly. They are just not answering the right question.

Don't take our word for it

One command, against the live endpoint:

npx -y countersign-mcp verify-endpoint

It runs four real prompt-injection scenarios and two honest transactions against https://mcp.countersignai.cloud, using tokens that exist on-chain and scanners running live, then prints what the firewall did at each step:

ATTACK · counterfeit token
  ! you asked to acquire USDC, but this transaction acquires unverified token
    EguMF3v7…mapump — not the canonical USDC address on solana
  ✓ CAUGHT
...
  attacks caught:   4/4
  honest allowed:   2/2
  false positives:  0

The honest cases matter as much as the attacks: a firewall that refuses everything would score 4/4 and be useless.

Prerequisites: none beyond Node. No wallet, no API key, no OnchainOS identity — the endpoint is public and free, and the command speaks plain MCP over HTTPS. Point it elsewhere with verify-endpoint <url> to check your own deployment.

Or check a case of your own

verify-endpoint runs a fixed script. To judge a transaction you choose, give it the instruction and the token being acquired:

# The real USDC — matches the instruction, so it passes.
npx -y countersign-mcp verify "buy 5 dollars of USDC on solana" \
  --to EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v

# The look-alike — same instruction, one address different.
npx -y countersign-mcp verify "buy 5 dollars of USDC on solana" \
  --to EguMF3v7KgSvQ5iXkF498DtfJQRJZ84ebhnoyymapump

That is the whole argument in two commands: identical request, one field changed, opposite verdicts. --chain, --from, --amount and --wallet all have defaults; run countersign verify with no arguments for the full list. Exit codes are 0 passed, 2 refused, 1 no verdict — a refusal is a successful run.

Tokens are addresses, never tickers. --to USDC is not accepted, deliberately: a ticker is chosen by whoever deploys the contract, which is exactly what the look-alike above exploits.

When something misbehaves

countersign info        # build, config, deny lists, audit chain, upstream CLI — and what's missing
countersign --help      # every command
countersign --version   # which build is actually running

info answers the questions that otherwise cost an afternoon: which version is running, which policy file is in effect, whether the deny lists were ever fetched (an unfetched required list blocks everything, with no obvious cause), and whether the audit chain is intact. It is read-only, starts no server, and reports credentials as set/not-set without ever printing a value — so its output is safe to paste into an issue.

If you install via npx, pin the version when it matters — npx -y [email protected] — because npx will otherwise reuse a cached copy, and a command missing from an older build falls through to a confusing error rather than the thing you asked for.

Read-only throughout: it asks for verdicts. Nothing is signed and nothing is broadcast — that public endpoint verifies transactions and never executes them, holds nothing spendable, and refuses every tool that could sign or broadcast. See docs/DEPLOY.md for how that is enforced.

Quickstart (5 minutes)

npm install -g countersign-mcp     # or use npx directly, below
cp policy.example.yaml policy.yaml

Point your MCP client at Countersign instead of onchainos mcp. There are two ways, and which you want depends on whether you are firewalling your own execution or just want verdicts.

Hosted — nothing to configure. Verdicts only; it verifies and never executes, so you keep signing and broadcasting through your own wallet. No keys, no credentials, no onchainos CLI:

{
  "mcpServers": {
    "countersign": { "url": "https://mcp.countersignai.cloud/mcp" }
  }
}

Local — the full firewall. Countersign runs on your machine and proxies the whole OnchainOS tool surface, so transactions are checked and executed through it. Requires the onchainos CLI on PATH and your OKX credentials in its environment — without them every transactional call fails closed:

{
  "mcpServers": {
    "countersign": { "command": "npx", "args": ["-y", "countersign-mcp"] }
  }
}

Remove any direct onchainos entry — two entries means the agent can choose the unfirewalled one. Optionally fetch deny lists:

npx -y countersign-mcp refresh-lists

Then in your agent session: call set_intent with the user's instruction verbatim, and use the OnchainOS tools as usual. npx -y countersign-mcp verify-audit checks the log's hash chain.

| Variable | Default | Purpose | |---|---|---| | COUNTERSIGN_POLICY | ./policy.yaml | Policy file (schema defaults if absent) | | COUNTERSIGN_AUDIT | ./countersign.audit.jsonl | Audit log | | COUNTERSIGN_LISTS | ./.countersign/lists | Deny-list cache | | COUNTERSIGN_APPROVALS | ~/.countersign/approvals | Out-of-band approval drop | | ONCHAINOS_BIN | onchainos | Path to the CLI | | ANTHROPIC_API_KEY | (unset) | Enables the optional LLM second signal |

OKX credentials are read by the onchainos CLI from its own environment. Countersign never reads, stores, or logs them.

What it checks

| Stage | What happens | |---|---| | Intent gate | A verbatim set_intent must be active, unexpired, within its use cap | | Pre-policy | Chain rules, deny lists (owner's and maintained), token risk on the requested pair | | Intent check | The requested transaction is compared to the instruction — before upstream builds anything | | Forward | The upstream tool runs only if the pre-phase allowed it | | Post-scan | The produced calldata is scanned and simulated | | Post-policy | Slippage, per-transaction and daily limits, counterparty rules | | Intent recheck | The decoded outcome is compared to the instruction again | | Decide | allow · escalate · block, with a plain-language diff | | Log | Hash-chained JSONL; tampering with any past decision is detectable |

Independent scans run concurrently. Every merge path is fail-closed: a scanner that times out, a simulation that reverts, a broadcast that cannot be traced to scanned calldata — all escalate. A failed scan is never a pass.

One call, one verdict

If you only want an opinion on a single transaction, verify_transaction takes everything at once and returns the verdict — no session to set up:

{
  "instruction": "buy 5 dollars of USDC on solana",
  "chain": "solana",
  "from_token": "So11111111111111111111111111111111111111112",
  "to_token": "EguMF3v7KgSvQ5iXkF498DtfJQRJZ84ebhnoyymapump",
  "amount": "5000000",
  "wallet": "<your address>"
}

Nothing is signed or broadcast — a verdict is an opinion about the transaction, not permission. Use set_intent plus the OnchainOS tools instead when you want a whole session firewalled rather than one transaction checked.

Intent verification

Token identity comes from a committed registry of canonical addresses, never from a token's self-declared name. That is what makes the counterfeit above catchable: a fresh mint can call itself uSDC, but it cannot occupy the canonical USDC address. Unverified tokens always render by address — their chosen ticker is never printed as identity.

An optional LLM judge (ANTHROPIC_API_KEY) runs as a second signal only. It can escalate what the deterministic layer passed; it can never clear a deterministic failure, and it has no way to express "allow". If unavailable, the deterministic result stands and the rationale records that the judge did not run.

Measured on the test corpora: 8/8 adversarial attack classes caught (7/8 without the judge), and 36/36 ordinary instructions allowed without a false escalation. Both numbers are regenerated on every test run, because a firewall that escalates normal requests gets switched off, and one that is switched off protects nothing.

Default posture

  • Unclassified tools are refused, never forwarded. Countersign ships a reviewed classification of the upstream surface; anything outside it is escalated. After upgrading onchainos, new read-only tools escalate until the snapshot is refreshed. That cost is deliberate — a new execution path must never inherit read-only treatment by default.
  • Buy is stricter than sell. You can always exit a position the buy policy would refuse to enter.
  • Calldata is withheld from the agent when post-forward scans fail.
  • Unwitnessed signed payloads escalate. gateway_broadcast takes an already-signed blob; Countersign does not decode it (shipping an RLP decoder into the trust path means trusting our own parser about what a transaction does). It checks whether the payload embeds calldata it already scanned. No match means unknown provenance — broadcast.unknown_payload chooses escalate (default) or block. There is deliberately no allow.

Approvals

An escalation issues an approval_id. A human reviews the diff and approves; the agent may then re-issue the identical call. The approval is bound to the exact arguments and the active intent, single-use, and expiring — approve a small transfer and the agent cannot execute a large one. Approval clears an escalation; it never clears a block.

Two channels:

  • In-channel (approve_transaction): convenient, and it proves what was approved — but the MCP channel cannot distinguish a human from the agent, so it does not prove who.
  • Out-of-band (a file drop the agent has no write access to): create ~/.countersign/approvals, owned by you and not writable by the agent's account. Countersign checks this at startup and warns if the directory is writable by its own account, rather than claiming protection it does not have.

Deny-list screening

Countersign can screen counterparties against maintained deny lists, refreshed explicitly and never fetched at decision time. Its value is verifiable provenance: every decision that consults a list records the list name, version, sha256, and fetch date — on passes as well as matches. That is what lets an audit answer what did you know, from which list version, at the moment you allowed this?

A required list that is missing, stale, or fails its integrity check is a failure, not a skip. A screening control that degrades quietly is worse than none.

This is not sanctions compliance, and Countersign does not claim better coverage than any other tool — only that its coverage is dated, versioned, and auditable. See docs/sanctions-feed-design.md.

Limitations worth knowing before you rely on it

  • Token-denominated amounts are not checked by the intent layer. An instruction phrased in token units — "swap 0.5 ETH for USDC" — has its tokens, chain, and direction verified, but not its magnitude: comparing token units needs price data the pipeline does not fetch. An instruction phrased in USD — "swap 50 dollars of ETH" — is amount-checked. The compensating control is limits.per_tx_usd, which bounds every transaction regardless of phrasing. Treat these two as a pair: if your instructions are usually token-denominated, set per_tx_usd deliberately, because it is doing the work the intent layer cannot. Tracked by a failing-by-design test in the adversarial corpus; the fix is a month-one item.
  • The token registry is deliberately small — 11 tokens across 3 chains, each hand-verified against live chain data. Anything outside it renders as unverified token 0x… and cannot be confirmed against an intent that names a symbol. That noise is by design, not an oversight: the registry is the trust anchor for the counterfeit check, so entries are a reviewed change rather than an automated import. Broadening it is a tracked growth area.
  • 95 installed packages from 3 direct dependencies, via the MCP SDK's unused web-server stack. Accounted for honestly in HARDENING.md §4.1; replacing the SDK with direct stdio JSON-RPC is the first post-release milestone.

What Countersign does not do

  • No sanctions compliance. See above — screening with provenance is an input to compliance, not compliance itself.
  • No key handling. It never sees, stores, or logs keys or credentials.
  • No governance of non-MCP routes. An agent with shell access to the onchainos binary, its own signing key, or any other execution path is outside what this software governs. Closing those routes is a deployment task, documented as a first-class checklist in HARDENING.md. Read it before production use.

Status

Phase 4 of a phased build; pre-release. The block path is verified against real scanner data rather than mocks (test/blockTier.test.ts), and the adversarial and honest corpora (test/adversarial.test.ts, test/honestCorpus.test.ts) are the acceptance tests.

Not implemented, and every decision record says so rather than implying otherwise: live scanning for the DeFi, payment, gas-station, and competition tool families, and deterministic protocol-substitution checking (the judge covers it). Token-denominated amount checking is also absent — see Limitations, and set limits.per_tx_usd accordingly, because it bounds what an unchecked token-denominated amount can move.

Engineering notes, including the bugs worth reading about, are in docs/engineering-notes.md.

Reporting a security issue

Email [email protected] — please don't open a public issue for a security report. Non-critical findings are welcome at the same address, and we prefer coordinated disclosure. Scope and details: SECURITY.md.

For anything that isn't security-sensitive, use GitHub issues.

Links

License

MIT. See LICENSE.