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/server

v0.2.1

Published

Servidor de controle do Herald Protocol — gestão de Outposts, usuários e histórico de métricas, backed by Postgres. Sem dashboard/UI de dados (ver @herald/dashboard, congelado em ICA-34) — só a tela mínima de autorização do device flow (herald login).

Downloads

579

Readme

@heraldserver/server

Servidor de controle do Herald Protocol — gestão de Outposts, usuários e histórico de métricas, backed by Postgres. Sem dashboard/UI de dados (ver @herald/dashboard, congelado desde a revisão que decidiu deixar a parte visual de lado por agora — continua existindo, separado, pro caso de uso original de fazer poll de /metrics de Gateways individuais) — mas serve uma tela HTML mínima de autorização (GET /auth/device, ver seção "Autenticação" abaixo), categoria diferente de um dashboard de métricas: um formulário funcional de login, sem framework/build step.

Três formas de falar com este pacote, cada uma pra um consumidor diferente:

  • HTTP autenticado (/api/outposts/*, /api/users/*) — @heraldserver/cli fala com isso via Bearer <token> (ver "Autenticação" abaixo). Nenhum operador humano tem credencial de Postgres — só o processo server fala com o banco.
  • HTTP de push (POST /api/outposts/reports) — o Gateway/app monitorada usa isso pra empurrar métricas (@heraldserver/outpost's createOutpostReporter, HERALD_SERVER_URL), autenticado só pela Outpost key (não por usuário).
  • Biblioteca (@heraldserver/server, import direto) — PgOutpostStore, PgReportsStore, createPool, migrate, SCHEMA_SQL (src/lib.ts). Uso interno (o próprio server.ts) e pra quem quiser embutir o control plane noutro processo.

Autenticação

Multi-usuário — substitui o acesso direto a Postgres que @heraldserver/cli fazia antes (ver ARCHITECTURE.md §4.7). Dois jeitos de autenticar:

  1. Bypass de loopback — requests com origem 127.0.0.1/::1/::ffff:127.0.0.1 (IP real da conexão TCP, nunca um header) são liberadas sem Authorization, com um usuário sintético de role admin. Mesmo raciocínio de confiança do Postgres (pg_hba.conf peer/trust local) e do daemon do Docker: quem tem acesso de shell na máquina que roda server já está confiado. Resultado prático: operador sozinho, herald rodando na MESMA máquina que herald-server, nunca precisa de herald login. Cuidado: só é seguro se server não estiver atrás de reverse proxy também em loopback (nesse caso todo tráfego proxied chegaria como 127.0.0.1, inclusive de fora). Nesse cenário, desligue com HERALD_DISABLE_LOOPBACK_TRUST=1 ou configure trust proxy + valide X-Forwarded-For no proxy.
  2. herald login (device flow, tipo gh auth login) — pra acesso remoto (--server-url apontando pra um host que não é a própria máquina). CLI pede um código em POST /api/auth/device/code, abre (ou imprime) uma URL — humano autentica numa tela HTML servida por este processo (GET /auth/device, POST /auth/device/verify, sem framework/JS obrigatório, form HTML puro), CLI faz poll em POST /api/auth/device/token até virar autorizado. RFC 8628 (Device Authorization Grant) adaptado pra autenticação própria em vez de OAuth de terceiro — sem IdP externo, é o próprio server que autentica contra a tabela users.

Bootstrap do primeiro admin: no boot, se users estiver vazia E HERALD_ADMIN_EMAIL/HERALD_ADMIN_PASSWORD estiverem setadas, cria esse admin automaticamente (idempotente — só roda enquanto a tabela tá vazia). Sem isso, o primeiro admin teria que ser inserido manualmente no banco.

Papéis: admin (usuários + Outposts) e member (só Outposts) — sem ACL por Outpost individual (decisão consciente de simplicidade, não lacuna escondida).

Convite de usuário por link (herald user invite, admin-only) — alternativa a herald user create (que exige o admin escolher e relayar uma senha temporária de algum jeito inseguro). POST /api/users/invite gera um link de uso único, válido por 7 dias (GET /invite/:token) — o convidado define a PRÓPRIA senha numa tela HTML, e a própria página já mostra o comando herald login --server-url ... pronto (resolve também a descoberta da URL do server, embutida no link). Herald nunca manda o link por email sozinho — quem convida compartilha por qualquer canal (Slack, WhatsApp, email próprio).

Por que Postgres, e por que isso agora exige Docker

Antes, tudo era self-host "zero infra": Outpost persistido em arquivo JSON, métricas em memória. Isso resolvia identidade (sobrevive a restart) mas não histórico (métricas zeravam a cada restart do Dashboard). Trocar pra Postgres resolve os dois de verdade — o trade-off consciente é que agora rodar isso exige uma instância Postgres de verdade, mais fácil via container.

Instalação

npm install
npm run build

Banco de dados

docker compose up -d          # sobe Postgres local (postgres:16-alpine)

DATABASE_URL esperado (já é o default do docker-compose.yml deste pacote):

postgres://herald:herald@localhost:5432/herald_server

Sem DATABASE_URL, o processo recusa subir (erro claro, sem fallback silencioso — diferente do resto da config, que sempre teve default local).

Schema é aplicado automaticamente no startup (CREATE TABLE/INDEX IF NOT EXISTS, idempotente — sem framework de migração, ver src/schema.ts).

Rodando

Instalado do npm (uso real, fora deste monorepo):

npm install -g @heraldserver/server
DATABASE_URL=postgres://herald:herald@localhost:5432/herald_server \
[email protected] HERALD_ADMIN_PASSWORD=troque-isso \
  herald-server
# Herald Server rodando em http://localhost:4810

HERALD_ADMIN_EMAIL/HERALD_ADMIN_PASSWORD são opcionais — sem elas, ninguém consegue herald login até um admin existir (mas o bypass de loopback continua funcionando pra quem estiver na mesma máquina, ver "Autenticação" acima).

Ou sem instalar global: npx @heraldserver/server. Dentro deste monorepo (contribuindo):

DATABASE_URL=postgres://herald:herald@localhost:5432/herald_server npm start

Porta default 4810 — faixa 48xx reservada pros apps ativos do Herald (server=4810, poc=4811), incomum o bastante pra não colidir com outra ferramenta rodando na mesma máquina. @herald/dashboard (congelado) fica em 4000, fora dessa faixa.

Observabilidade (Prometheus)

docker compose up -d prometheus   # sobe junto com o Postgres, porta 9090

prometheus.yml já vem configurado pra fazer scrape de http://host.docker.internal:4811/metrics (o /metrics de um app usando @heraldserver/gateway, ex: poc/) — endereço portável, funciona em Docker Desktop (Mac/Windows) e em Linux com dockerd nativo (extra_hosts: host.docker.internal:host-gateway no docker-compose.yml).

Status atual: MetricsCollector baseado em prom-client implementado — @heraldserver/prometheus. /metrics só devolve texto Prometheus de verdade se a app configurar PrometheusMetricsCollector explicitamente (default continua InMemoryMetricsCollector/JSON — ver README do pacote). Na PoC, isso é HERALD_METRICS=prometheus.

Gotcha conhecido: em WSL2 + Docker Desktop rodando o app monitorado fora de container (fluxo de dev atual — node dist/server.js direto na distro), o container do Prometheus não alcança host.docker.internal (resolve pro gateway da VM do Docker Desktop, não pra rede da distro WSL2). Não é bug deste repo — é limitação dessa topologia específica de máquina. Funciona normal em servidor Linux real ou Docker Desktop sem WSL2 no meio.

Testes

Testes rodam contra Postgres real (não pg-mem/mock), cada arquivo de teste cria seu próprio banco efêmero (herald_test_<random>) e derruba no final — precisa do docker compose up -d deste pacote rodando antes:

docker compose up -d
DATABASE_URL=postgres://herald:herald@localhost:5432/herald_server npm run build && npm test

Endpoints

| Rota | Auth | Descrição | |---|---|---| | POST /api/outposts/reports | Outpost key (Bearer) | Push de métricas. 401 (key errada/desconhecida) ou 403 {error: "outpost_stopped"} (key válida, Outpost pausado) | | POST /api/auth/login | — | Login direto (email/senha) — usado pela tela HTML do device flow, não pelo CLI | | POST /api/auth/device/code | — | CLI inicia o device flow — retorna deviceCode/userCode/verificationUriComplete | | GET /auth/device | — | Tela HTML — humano autoriza o user_code aqui | | POST /auth/device/verify | — | Form POST da tela acima (email/senha) | | POST /api/auth/device/token | — | CLI faz poll aqui até o deviceCode virar autorizado | | POST /api/outposts · GET /api/outposts · GET/DELETE /api/outposts/:id · POST /api/outposts/:id/{stop,start} · POST /api/outposts/prune | usuário (Bearer) ou loopback | CRUD de Outpost — admin e member | | POST /api/users · GET /api/users · DELETE /api/users/:id | usuário admin (Bearer) ou loopback | Gestão de usuários — admin-only | | POST /api/users/invite | usuário admin (Bearer) ou loopback | Gera link de convite — retorna {inviteUrl, expiresAt} | | GET /invite/:token | — | Tela HTML — convidado define a própria senha | | POST /invite/:token/accept | — | Form POST da tela acima (password+confirmPassword) — cria o usuário |

Biblioteca (@heraldserver/server)

import { createPool, migrate, PgOutpostStore, PgReportsStore } from "@heraldserver/server";

const pool = createPool(databaseUrl);
await migrate(pool); // idempotente, seguro de rodar toda vez
const outposts = new PgOutpostStore(pool);
const reports = new PgReportsStore(pool);

src/index.ts (o processo HTTP, npm start) não faz parte desse main/types — só é invocado via node dist/index.js, tem efeito colateral (app.listen).

Limitações conhecidas

  • Sem retenção automática — outpost_reports é append-only por padrão. herald outpost prune [<id>] --older-than-days <n> poda manualmente (tipo docker system prune, sem cron/job rodando sozinho — decisão consciente, ver TESTPLAN.md §5). Fica com o operador lembrar de rodar; não acontece sozinho.
  • Geração de id/nome/chave é duplicada verbatim de dashboard/src/outposts.ts (dashboard está congelado, não deve virar dependência de ninguém) — mudança de segurança nessa lógica precisa ser replicada nos dois lugares à mão. Auditado em 2026-08-08 (diff byte-a-byte, ver src/outposts.ts): idênticos hoje — risco aceito e registrado, não eliminado.