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

@countersig/policy-client

v0.1.1

Published

Countersig policy client — fetches signed policy bundles and enforces destination allow-lists for AI agents. Client-side enforcement only; for hard security boundaries use the Countersig Gateway.

Readme

@countersig/policy-client

Client-side policy enforcement for AI agents registered with Countersig. Fetches signed policy bundles from the Countersig backend and enforces destination allow-lists before outbound HTTP calls.

⚠️ Read this first

This is a client-side hint, not a security boundary.

Any code path that does not route through PolicyClient.fetch() is not enforced. A compromised or malicious agent that imports node-fetch (or any other HTTP client) directly will bypass this library entirely.

For real security boundaries — where enforcement cannot be bypassed by the calling code — deploy the Countersig Gateway at the network layer in front of your agents.

Use this SDK to:

  • Catch honest mistakes (a developer hardcoding a destination they shouldn't)
  • Get fast in-process decisions during development
  • Emit policy-violation telemetry from agent runtimes

Do not use this SDK as the only enforcement mechanism for production deployments handling sensitive data.

Install

npm install @countersig/policy-client

Requires Node.js 18+.

Quick start

import { PolicyClient, PolicyDeniedError } from '@countersig/policy-client';

const client = new PolicyClient({
  apiBase: 'https://api.countersig.com',
  apiKey: process.env.COUNTERSIG_API_KEY!,
  agentId: process.env.COUNTERSIG_AGENT_ID!,
});

await client.init();

try {
  const res = await client.fetch('https://api.openai.com/v1/chat/completions', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ /* ... */ }),
  });
  const data = await res.json();
} catch (err) {
  if (err instanceof PolicyDeniedError) {
    console.error(`Blocked: ${err.destination} (${err.decision.reason})`);
  } else {
    throw err;
  }
}

// Cleanup on shutdown
process.on('SIGTERM', () => client.close());

Configuration

new PolicyClient({
  // Required
  apiBase: 'https://api.countersig.com',
  apiKey: '...',          // JWT or API key with read scope
  agentId: 'uuid-here',

  // Optional — JWKS source (one of)
  jwksUrl: '...',         // Defaults to {apiBase}/.well-known/jwks.json
  jwks: { keys: [...] },  // Inline key set (skips remote fetch entirely)

  // Optional — refresh and timing
  refreshIntervalMs: 0,   // Defaults to 90% of bundle TTL
  failClosed: true,       // Default: deny all on stale bundle
  clockSkewSeconds: 30,   // Default: 30s tolerance on issued_at
  maxRefreshAttempts: 0,  // Default: 0 (unlimited, exp backoff capped at 5 min)
  fetchImpl: fetch,       // For tests
});

Policy modes

The bundle's policy_mode (set by your org admin) determines behavior:

  • enforced — Default-deny. Calls to non-whitelisted destinations throw PolicyDeniedError.
  • audit_only — Calls go through, but blocked-in-enforced-mode calls emit a policy_violation event with wouldBlock: true. Use this for safe rollout.
  • permissive — Whitelist is ignored. Only the deny list blocks calls. Dev/staging only.

Events

The client extends EventEmitter. Listen for:

client.on('bundle_loaded', ({ bundle }) => {
  console.log('policy mode:', bundle.policy_mode);
});

client.on('bundle_refresh_failed', ({ error, attempt }) => {
  console.warn(`refresh attempt ${attempt} failed:`, error.message);
});

client.on('policy_violation', ({ destination, reason, wouldBlock }) => {
  // wouldBlock=true means audit_only mode allowed it through
  metricsClient.increment('policy.violation', { reason, wouldBlock });
});

client.on('signature_invalid', ({ reason }) => {
  // Bundle signature did not verify. Treat as a critical alert.
  alertClient.fire('countersig.signature_invalid', { reason });
});

client.on('stale_bundle', ({ expiredAt, failClosed }) => {
  // Bundle expired and refresh hasn't recovered yet
});

Decision API (no network)

If you make calls through your own HTTP layer, use check() to get a decision without making a request:

const decision = client.check('https://api.openai.com/v1/chat');
// { allowed: true, reason: 'whitelisted', scope: 'org', mode: 'enforced' }

if (!decision.allowed) {
  // Handle deny case yourself
}

Agent-to-agent calls

For Countersig-internal calls between two registered agents, use fetchAgent. The client requests a short-lived A2A token from the backend and attaches it as a Bearer token on the outbound call.

const res = await client.fetchAgent(
  'agent:550e8400-e29b-41d4-a716-446655440000',
  JSON.stringify({ task: 'process-this' }),
  {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      // Required: where to actually dispatch the call
      'x-countersig-target-url': 'https://other-agent.internal/api/dispatch',
    },
  }
);

The target agent verifies the token using @countersig/verify against the JWKS endpoint.

Stale bundles and fail-closed mode

When failClosed: true (default):

  • A stale bundle (past expires_at) causes every call to deny with reason: 'stale_bundle_fail_closed'.
  • Background refresh attempts continue with exponential backoff (5s → 10s → 20s → ... capped at 5 min).
  • A successful refresh recovers all functionality immediately.

When failClosed: false:

  • A stale bundle is used until refreshed. Calls evaluate against the last-known policy.
  • Use this in development if you want to keep working when the backend is unreachable.

Bundle verification

Every bundle is signed with Ed25519 (JWS Compact Serialization, alg: EdDSA). The client:

  1. Fetches JWKS from {apiBase}/.well-known/jwks.json (cached 10 minutes)
  2. Verifies the signature against the key with kid: 'a2a-ed25519-1'
  3. Compares signed payload identity fields against the response envelope (defense against signing one bundle and shipping a different one)
  4. Validates timing (issued_at not in the future, expires_at after issued_at)
  5. Validates agent_id matches the configured agent

Any failure throws and emits signature_invalid. The bundle is rejected.

Comparison with the gateway

| | @countersig/policy-client | Countersig Gateway | |---|---|---| | Where it runs | Inside the agent process | Network layer in front of the agent | | Latency | Microseconds (in-memory) | Single round-trip per cold-cache call | | Bypassable | Yes — agent can import fetch directly | No — gateway sees all egress | | Enforces blocks on | Calls routed through this SDK | All HTTP egress | | Setup | npm install + client.init() | Deploy proxy/sidecar |

For serious deployments, run both: SDK catches honest mistakes early, gateway provides the actual security boundary.

License

MIT

Links

  • Documentation: https://github.com/RunTimeAdmin/Countersig-Public/blob/main/docs/POLICY_SDK.md
  • Backend repo: https://github.com/RunTimeAdmin/Countersig-Public
  • Gateway: https://github.com/RunTimeAdmin/countersig-gateway
  • Issue tracker: https://github.com/RunTimeAdmin/Countersig-Public/issues