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

@kya-brasil/agent-tools

v0.11.0

Published

KYA Brasil for AI agents: verify that an agent acts for a Brazilian company (e-CNPJ-signed credential, revocation and company registration checked daily against the Receita's open data, no third-party service), check an action against its mandate, or give

Readme

@kya-brasil/agent-tools

Know-your-agent for Brazil, as code. Verify that an AI agent really acts for a Brazilian company (credential signed by the company's e-CNPJ, checked against its AC's revocation list, company active at the Receita Federal), check an action against the agent's mandate, or give your own agent a credential.

Paid per call with x402 (USDC on Base, no signup). Free verification works without a wallet (50/day per IP). Errors are never charged. MCP endpoint for agent frameworks: https://kya-br-agents.marcelfons.workers.dev/mcp.

npm i @kya-brasil/agent-tools

The same build is mirrored to GitHub Packages as @celfons/kya-agent-tools (listed on the repository page; installing from there needs a GitHub token). npmjs.com is the channel to install from.

Node 18+ (global fetch, WebCrypto). ESM.

Verifier: you receive an action from someone else's agent

import { createKyaClient, verifyAttestation, verifyCredentialOffline, companyText, signWithECnpj, hashPaymentRef } from "@kya-brasil/agent-tools";

const kya = createKyaClient({ privateKey: process.env.KYA_PAYER_PK as `0x${string}` }); // wallet optional

const v = await kya.verify(credentialId);                 // free
if (v.status !== "active") reject();

const verdict = await kya.allowed(credentialId, {         // $0.01, signed
  action: "order", amount: 4200, currency: "BRL", counterparty: "12345678000190"
});
if (!verdict.allowed) reject(verdict.reason);

await verifyAttestation(verdict);                          // local Ed25519 check of the issuer's signature

// max_per_day: pass your own spent_today; the issuer does not record acts.
// Payment instrument (card networks, acquirers, D-25): pass the hash of the token reference or PAR you received,
// never the card number: { payment_network: "visa", payment_ref_sha256: await hashPaymentRef(tokenRef) }

const offline = await verifyCredentialOffline(credential);   // issuer down: signature + expires_at only,
                                                             // live_status "unknown" (your risk until expires_at)

Also check that the key that signed the agent's request is v.agent.id; the credential_id alone proves nothing.

Company side, after issuance: see every agent in your name and get signed events (issued, renewed, suspended, reinstated, revoked) at your webhook. Both requests are signed with the e-CNPJ:

const text = companyText("webhook", cnpj, { url: "https://acme.com.br/kya-events" });
await kya.setCompanyWebhook(cnpj, { signed_text: text, ecnpj_signature: await signWithECnpj(material, text) });
const inv = await kya.companyAgents(cnpj, { signed_text: t2, ecnpj_signature: s2 });   // t2 = companyText("agents", cnpj)
// on your webhook: (await verifyAttestation(body)).valid before trusting the event

Agent frameworks (verifier tools kya_verify, kya_check_action, kya_verify_paid):

import { toOpenAI, toVercelAI, toLangChain, toAnthropic } from "@kya-brasil/agent-tools";
toVercelAI(kya, { tool, jsonSchema });   // from "ai"
toLangChain(kya, tool);                  // from "@langchain/core/tools"
toOpenAI(kya); toAnthropic(kya);         // then kya.execute(name, args)

Sign in with KYA: accept signed requests from agents

import { createKyaAuth } from "@kya-brasil/agent-tools";
const auth = createKyaAuth();                     // options: privateKey (for kya.allowed), cacheTtlMs, maxSkewSeconds

app.use("/api/*", auth.hono());                    // Hono: c.get("kya")
app.use(auth.express());                           // Express: req.kya
export default { fetch: auth.fetchHandler(async (req, kya) => Response.json({ hello: kya.principal.legal_name })) }; // Workers
const r = await auth.authenticate(request);        // anywhere: { ok, kya } or { ok: false, status, error, message }

kya carries credential_id, agent, principal, mandate, check(q) (local mandate evaluation, no daily ceilings) and allowed(q) (the issuer's signed verdict, $0.01). The agent signs with signRequest(...):

import { signRequest } from "@kya-brasil/agent-tools";
const headers = await signRequest({ credentialId, key: { kind: "evm", privateKey: agentPk }, method: "POST", url, body });
await fetch(url, { method: "POST", headers: { ...headers, "content-type": "application/json" }, body });

Keys: { kind: "evm" | "erc8004", privateKey } or { kind: "web-bot-auth", privateKey: CryptoKey | pkcs8 }.

Agent to agent (mutual)

The callee signs its response, bound to the request; the caller verifies who answered.

// callee (inside a handler, after auth.authenticate succeeded)
const headers = await signResponse({ credentialId: myCredentialId, key: myKey, requestSignature: request.headers.get("kya-signature")!, body });
return new Response(body, { headers: { ...headers, "content-type": "application/json" } });

// caller
const me = createKyaAgent({ credentialId: myCredentialId, key: myKey });
const { response, callee } = await me.fetch("https://beta.com.br/orders", { method: "POST", body });
if (!callee?.ok) throw new Error("unsigned or invalid callee");       // callee.kya: principal, mandate, status

Sub-agents (delegation, D-30): one certificate signature for the whole tree

When the mandate carries delegation: { max_depth, max_ttl_seconds }, the agent hands a narrower mandate to another key with its own signature, no new e-CNPJ or e-CPF signature. The sub-agent signs its requests with its own key and sends the chain; your middleware verifies it offline and check() / allowed() evaluate every mandate in it.

import { signDelegation, signRequest } from "@kya-brasil/agent-tools";
const chain = await signDelegation({ credential, key: agentKey, delegate: { kind: "web-bot-auth", id: subPublicKey },
  mandate: { actions: ["quote"], max_per_operation: { amount: 500, currency: "BRL" } }, ttlSeconds: 600 });
const headers = await signRequest({ credentialId, key: subKey, delegation: chain, method: "GET", url });
// server side: c.get("kya").delegation → { depth, delegate, mandates, expires_at, chain }

Short-lived (at most max_ttl_seconds, never past the credential), contained in the parent mandate, at most 3 levels; revoking the credential ends the whole tree. The issuer stores nothing. SPEC section 17.

Person: an agent that acts for you (e-CPF)

const reg = await kya.register({ agent: { kind: "evm", id: agentAddress }, principal: "person", mandate }); // $0.25
// agent signs reg.challenge.text; you sign it with your e-CPF (reg.sign_url, kya-sign or signWithECnpjPfx: same code)
const { credential } = await kya.prove(reg.credential_id, { challenge_id: reg.challenge.id, agent_signature, ecnpj_signature: ecpfSignature });
// credential.principal = { kind: "person", verified_by: "icp-brasil-e-cpf" }: nothing personal

// A verifier that already has the person's CPF:
const m = await kya.principalMatch(credentialId, "529.982.247-25");   // $0.01, signed: { match, subject_ref? }

// The person, to see their agents and every check made on them:
const text = personAgentsText();                                        // sign it with the e-CPF
const mine = await kya.personAgents({ signed_text: text, ecpf_signature });

Company: give your own agent a credential

import { createKyaClient, signChallengeWithEvmKey, signingLink, signWithECnpjPfx } from "@kya-brasil/agent-tools";
import { readFileSync } from "node:fs";

const kya = createKyaClient({ privateKey: payerPk });
const reg = await kya.register({                                           // $0.25
  agent: { kind: "evm", id: agentAddress },
  cnpj: "11.222.333/0001-81",
  mandate: {
    actions: ["quote", "order", "pay"],
    max_per_operation: { amount: 5000, currency: "BRL" },
    max_per_day: { amount: 20000, currency: "BRL" },
    currencies: ["BRL"],
    counterparties: ["12345678000190", "*.beta.com.br"]
  }
});

const agent_signature = await signChallengeWithEvmKey(agentPk, reg.challenge.text);

// Usual case: a person holds the e-CNPJ (owner, accountant). Send them the link; they sign in the browser
// (.pfx, or a .p7s from any ICP-Brasil signer for A3 tokens and cloud certificates) and the credential is issued.
const link = signingLink({ credentialId: reg.credential_id, challenge: reg.challenge, agentSignature: agent_signature });
// (reg.sign_url is the same link without the agent signature)

// Or in code, when you hold the A1 file yourself:
const ecnpj_signature = signWithECnpjPfx(readFileSync("empresa.pfx"), process.env.PFX_PASSWORD!, reg.challenge.text);
const { credential, warnings } = await kya.prove(reg.credential_id, { challenge_id: reg.challenge.id, agent_signature, ecnpj_signature }); // free

signWithECnpjPfx reads the .pfx/.p12 directly, including the legacy 3DES/RC2 encryption many Brazilian A1 files use; signWithECnpj still takes PEM. On a terminal, the kya-sign command does the same without code:

npx -p @kya-brasil/agent-tools kya-sign --pfx empresa.pfx --link "<sign_url>&agent_signature=…"   # signs and issues
npx -p @kya-brasil/agent-tools kya-sign --pfx empresa.pfx --text-file desafio.txt               # prints the signature

The password comes from KYA_PFX_PASSWORD or is asked for without echo. Everything runs locally.

A3 (token or smart card). Through PKCS#11, the interface every ICP-Brasil token driver ships. Install the optional pkcs11js and point to the vendor's library (e.g. SafeNet eToken libeTPkcs11.so / eTPKCS11.dll):

npm install pkcs11js
npx -p @kya-brasil/agent-tools kya-sign --pkcs11 /usr/lib/libeTPkcs11.so --list                       # certificates on the token
npx -p @kya-brasil/agent-tools kya-sign --pkcs11 /usr/lib/libeTPkcs11.so --link "<sign_url>&agent_signature=…"
const token = await openToken({ library: "/usr/lib/libeTPkcs11.so", pin: process.env.TOKEN_PIN! });
try { ecnpj_signature = await token.sign(reg.challenge.text); } finally { token.close(); }

The PIN comes from KYA_TOKEN_PIN or is asked for. The key never leaves the token. Tested against SoftHSM2 (a software PKCS#11 token) in CI. The signing code is in @kya-brasil/core (openPfx, ecnpjSigner, buildDetachedCms), re-exported here; any other token or cloud signer plugs into ecnpjSigner.

Cloud certificate (PSC). kya-sign --cloud ./my-psc.mjs --link "<sign_url>" with a module that implements CloudSignatureProvider for your PSC (template); the browser opens for the approval and the terminal receives the redirect on 127.0.0.1.

From the agent's side, kya.signLink(credentialId, { challenge_id, agent_signature }) (free) returns the sign_url to send, the same as signingLink(...) offline and the MCP tool kya_sign_link.

An agent should not hold the company's e-CNPJ to sign for itself: that signature is the company's consent. Let the agent prepare and send the link, and a person sign.

Renew every 90 days (kya.renew then kya.renewProve), revoke at once (kya.revoke with revocationText(id) signed by the e-CNPJ).

Prices (USDC)

| Call | Price | |---|---| | verify, verifyByKey, prove, renewProve, revoke, revocations, companyAgents, setCompanyWebhook | free | | verified (signed receipt, no limit), allowed (signed verdict) | 0.01 | | renew | 0.10 | | register | 0.25 |

Schemas and keys: /.well-known/kya-mandate.schema.json, /.well-known/kya.json, /openapi.json, /llms.txt. Legal entities only: no CPF is stored; the e-CNPJ signer is recorded by certificate hash.