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

@heraldserver/gateway

v0.1.2

Published

Gateway de referência (middleware Express) para o Herald Protocol.

Readme

@heraldserver/gateway

Gateway de referência para o Herald Protocol — um middleware Express que aplica identificação de agentes, negociação de capacidades, Policy Engine e rate limiting a qualquer aplicação existente, sem exigir reescrita de rotas.

Depende de @heraldserver/sdk para toda a lógica de protocolo; o Gateway apenas orquestra o pipeline por requisição e expõe os endpoints padrão (/.well-known/herald, /metrics).

Instalação

# a partir da raiz do repositório
cd sdk && npm install && npm run build   # o Gateway depende do build do SDK
cd ../gateway && npm install && npm run build

@heraldserver/sdk é referenciado via file:../sdk no package.json — não precisa estar publicado no npm para desenvolvimento local.

Pipeline por requisição

1. identifyAgent(headers)                            → AgentContext
2. cliente sem identificação (source="none")          → next() imediato, comportamento inalterado
3. policyEngine.evaluate({ agent, resource, read })   → PolicyDecision
4. decision.result === "deny"                         → 403 + Herald-Policy-Decision
5. decision.result === "ask"                          → 402 + Herald-Policy-Decision
                                                          (com `monetization` configurado: fluxo x402, ver abaixo)
6. decision.rateLimit definido e excedido             → 429 + Retry-After
7. negotiateFormat(Herald-Accept-Capabilities, ...)   → { format, matched }
8. formatter registrado para (recurso, formato)?      → responde direto (200 + JSON)
   caso contrário                                     → next(), app lê getHeraldContext(req)
9. em res.on("finish")                                → métricas (latência, formato, erros)

Este pipeline implementa diretamente a sequência descrita em ARCHITECTURE.md §3.1 e os casos de borda normativos de HEADERS.md §6.

Configuração rápida via npx (gera o .env)

Pra quem já tem um Outpost criado (herald outpost create, via @heraldserver/cli) e só precisa configurar HERALD_SERVER_URL/ HERALD_OUTPOST_KEY na aplicação — sem instalar @heraldserver/cli só pra isso:

npx @heraldserver/gateway
# URL do Herald Server (ex: https://herald.suaempresa.com): ...
# Outpost key (gerada via `herald outpost create`): ...
# .env atualizado em ./.env

Grava o .env, não mexe em nenhum código da aplicação — a única linha que sobra é app.use(gateway.router) (ver "Uso básico" abaixo). Valida a URL igual herald outpost init (assertSecureServerUrl — exige HTTPS fora de localhost).

Uso básico

import express from "express";
import { createHeraldGateway } from "@heraldserver/gateway";

const app = express();

const gateway = createHeraldGateway({
  discovery: {
    origin: "https://example.com",
    capabilities: ["structured-json", "html"],
    defaultPolicy: { read: "allow", train: "deny", redistribute: "deny" },
    byAgentType: {
      crawler: { read: "allow", train: "ask", rateLimit: { requests: 10, windowSeconds: 60 } },
      assistant: { read: "allow", train: "deny" },
    },
    analytics: { reporting: "aggregate", contact: "mailto:[email protected]" },
  },
  policy: {
    default: { read: "allow", train: "deny" },
    byAgentType: {
      crawler: { read: "allow", train: "ask", rateLimit: { requests: 10, windowSeconds: 60 } },
    },
    // Agentes não verificados nunca herdam "ask" para treino — vira "deny" direto
    // (decisão de design da revisão da Fase 1, registrada em ICA-27).
    unverifiedOverride: { train: "deny" },
  },
});

// Formatter opcional: responde direto em JSON estruturado para /artigos/*
gateway.formatters.register("/artigos/*", "structured-json", async (req) => {
  const artigo = await getArtigoPorPath(req.path); // mesma fonte de dados do HTML
  return { title: artigo.title, body: artigo.body, publishedAt: artigo.publishedAt };
});

app.use(gateway.router);

// Rotas normais da aplicação — humanos e agentes sem formatter registrado passam por aqui
app.get("/artigos/*", (req, res) => {
  res.send(renderHtml(req.path));
});

app.listen(3000);

/.well-known/herald e /metrics já ficam disponíveis automaticamente ao montar gateway.router.

Lendo o contexto Herald em rotas downstream

Quando não há formatter registrado, a aplicação decide como responder — mas pode consultar o que o Gateway já resolveu:

import { getHeraldContext } from "@heraldserver/gateway";

app.get("/artigos/:slug", (req, res) => {
  const ctx = getHeraldContext(req);
  if (ctx?.format === "structured-json") {
    return res.json(buildStructuredArticle(req.params.slug));
  }
  res.send(renderHtml(req.params.slug));
});

Verificação de identidade (assinatura de requisições)

Sem configuração adicional, todo agente é verified: false (identidade autodeclarada). Para exigir e validar assinaturas RFC 9421 (RFC-0001 §4.4), passe signatureVerification:

const gateway = createHeraldGateway({
  discovery: { /* ... */ },
  policy: {
    default: { read: "allow" },
    // só concede leitura de recursos sensíveis a agentes com assinatura válida
    byResource: [{ pattern: "/interno/*", policies: { read: "ask" } }],
    unverifiedOverride: { read: "ask" },
  },
  signatureVerification: {
    resolvePublicKey: (keyId) => keyRegistry.get(keyId) ?? null, // seu registro de chaves
    maxAgeSeconds: 300, // default
  },
});

Requisições de agentes (Herald-Agent-Id presente) com headers Signature-Input/Signature válidos (assinados com signRequest do @heraldserver/sdk) têm agent.verified promovido para true antes da avaliação de política — o que faz unverifiedOverride diferenciar de fato os dois casos. Toda resposta a um agente identificado também ganha o header de diagnóstico X-Herald-Debug-Agent-Verified: true|false (não normativo, só para observabilidade).

Monetização do intent ask (x402)

Sem configuração adicional, ask responde 402 só com a decisão de política (como sempre). Para habilitar o fluxo de referência x402 (ver MONETIZATION.md), passe monetization:

import { createDemoPaymentVerifier } from "@heraldserver/sdk";

const gateway = createHeraldGateway({
  discovery: { /* ... */ },
  policy: {
    default: { read: "allow" },
    byResource: [{ pattern: "/artigos/relatorio-premium", policies: { read: "ask" } }],
  },
  monetization: {
    resolveRequirements: ({ resource }) =>
      resource === "/artigos/relatorio-premium"
        ? { scheme: "exact", network: "base-sepolia", maxAmountRequired: "1000", asset: "0xUSDC", payTo: "0x...", resource }
        : null, // null preserva o 402 "puro" — ask continua sendo aprovação fora de banda
    verifier: createDemoPaymentVerifier(), // trocar por um PaymentVerifier real em produção
  },
});

Na primeira tentativa (sem Payment-Signature), o Gateway responde 402 com o header Payment-Required. O agente decodifica, monta um pagamento, e reenvia com Payment-Signature; se verifier.verify() confirmar liquidação, a requisição segue o pipeline normalmente a partir dali (rate limit, negociação de formato, formatter/next()) — como se a decisão fosse allow. createDemoPaymentVerifier() não faz liquidação real, serve só para a PoC/testes locais.

Rate limiting

O FixedWindowRateLimiter é in-memory, por processo — adequado para a PoC e para uma única instância. Para múltiplas instâncias, substitua por um backend compartilhado (ex: Redis) implementando a mesma interface check(key, limit): number | null.

Métricas

gateway.metrics é um InMemoryMetricsCollector do @heraldserver/sdk por padrão. A rota GET /metrics expõe metrics.snapshot() como JSON — consumido pelo Dashboard Agent Analytics (Fase 4). Para produção, passe seu próprio MetricsCollector (ex: adaptador prom-client) via createHeraldGateway({ metrics: meuColetor, ... }).

Testes

npm run build
npm test   # node --test dist/**/*.test.js

Testes de integração (gateway.test.ts) com node:test + supertest, montando createHeraldGateway() num Express mínimo (sem depender do poc/): respostas 403/402/429, headers Vary/Herald-Content-Format/Herald-Policy-Decision, formatter dispatch (incluindo padrões conflitantes e erro propagado via next(err)), rate limit sob concorrência real (requisições em paralelo, não sequenciais), verificação de assinatura (válida/ausente/ forjada), e o fluxo de monetização x402 (sem monetization, resolveRequirements nulo, primeira tentativa sem pagamento, pagamento válido/insuficiente/malformado). Ver ../TESTPLAN.md §2 e §5.

Estrutura

gateway/
├── src/
│   ├── gateway.ts       # createHeraldGateway() — pipeline principal
│   ├── gateway.test.ts   # testes de integração (supertest)
│   ├── formatters.ts      # FormatterRegistry
│   ├── rate-limiter.ts     # FixedWindowRateLimiter
│   ├── context.ts           # getHeraldContext/setHeraldContext
│   ├── index.ts               # exports públicos
│   ├── init.ts                 # runGatewayInit() — wizard do `npx @heraldserver/gateway`
│   ├── bin.ts                    # shebang, chama runGatewayInit()
│   ├── prompt.ts                  # readline compartilhado (duplicado de cli/src/prompt.ts)
│   └── env-file.ts                 # upsertEnvVars (duplicado de cli/src/env-file.ts)
├── package.json
├── tsconfig.json
└── README.md

Status

Implementação de referência da Fase 3 do roadmap do Herald Protocol. Publicado no npm como @heraldserver/gateway.