README.md

June 7, 2026 ยท View on GitHub

pinglet

Anonymous runtime analytics for npm packages.
Real usage โ€” not download noise.

version tests deps license

Watch Video


๐ŸŽฏ The problem

npm download counts are useless for understanding if anyone actually uses your package.

They count CI installs. Mirror caches. Bots. npm install in pipelines that never execute your code. A package with 100k weekly downloads might have zero real users.

pinglet tells you the truth: who's running your package, how often, and which parts.

npm downloadspinglet
Someone actually ran my code?โŒโœ…
How many active anonymous users?โŒโœ…
Which commands/features?โŒโœ…
Which versions still active?โŒโœ…
CI or real person?โŒโœ…
macOS, Linux, Windows?โŒโœ…

โšก Quick Start (3 steps)

1. Deploy your server

Railway (easiest): push this repo โ†’ create project โ†’ add volume โ†’ done.
Full guide: docs/deploy-railway.md.

Docker: docker build -t pinglet . && docker run -p 3456:3456 -e PINGLET_ADMIN_PASSWORD=... pinglet

Self-host: npx -p @black-knight.dev/pinglet pinglet-server

2. Login once

# Install the CLI (globally or via npx)
npm install -g @black-knight.dev/pinglet
# or: npx -p @black-knight.dev/pinglet pinglet login ...

pinglet login --url https://your-server.example.com --user admin

Stores a 30-day token โ€” not your password.

3. Add 5 lines to your package

npm install @black-knight.dev/pinglet
import { Pinglet } from '@black-knight.dev/pinglet';

const analytics = new Pinglet({
  packageName: 'my-package',
  packageVersion: '1.0.0',
  endpoint: 'https://your-server.example.com/ping',
});

await analytics.track('run');
await analytics.track('command:build');

That's it. Your package now sends anonymous runtime pings.


๐Ÿ“Š What you see

pinglet                        # quick overview
pinglet my-package             # detailed stats
{
  "pkg": "my-package",
  "totalPings": 1420,
  "uniqueUsers": 312,
  "events":      { "run": 980, "command:build": 310 },
  "versions":    { "1.4.0": 900, "1.3.0": 520 },
  "platforms":   { "darwin": 800, "linux": 500, "win32": 120 },
  "ci":          { "true": 40, "false": 1380 },
  "days":        { "2026-06-03": 1420 }
}

CLI cheat sheet

# Install: npm install -g @black-knight.dev/pinglet
# Or via npx: npx -p @black-knight.dev/pinglet pinglet <command>

pinglet                         # status overview
pinglet <pkg>                   # stats for package
pinglet ls                      # list all tracked packages
pinglet show <pkg>              # same as pinglet <pkg>
pinglet snippet <pkg>           # print copy-paste SDK code
pinglet health                  # server health check
pinglet login --url <url>       # login once
pinglet logout                  # remove local login

๐Ÿ›ก๏ธ Privacy

CollectedNot collected
Random hashed client idHardware id, hostname, username
Event nameFile paths, source code, logs
Package name + versionEnvironment variables, secrets
Node.js versionUser-generated content
Platform (darwin/linux/win32)IP address
CI flagClient timezone

Full privacy model: docs/security.md.

Telemetry model

pinglet uses the industry-standard opt-out model (like Next.js, VS Code, Homebrew):

  • Tracking is ON by default at level 1 (basic: all event names, no properties)
  • No prompts during npm install โ€” no postinstall script
  • Documented in every README โ€” open source transparency
  • Inspect before sending: PINGLET_DEBUG=1 shows the JSON payload without transmitting
  • Opt out anytime: PINGLET_OPT_OUT=1, DO_NOT_TRACK=1, --no-telemetry

For more detail: docs/telemetry.md.


๐ŸŒ Server endpoints

MethodPathAuthDescription
POST/pingpublicReceive a runtime event
POST/auth/loginBasic AuthCreate 30-day admin token
GET/auth/checkBearer tokenVerify saved login
GET/packagesadminList tracked packages
GET/stats?pkg=<name>adminAggregated analytics
GET/healthpublicHealth check

๐Ÿ“ฆ Deployment

PlatformGuide
Railwaydocs/deploy-railway.md โ€” step by step
Dockerdocker build -t pinglet . โ€” single command
Fly.iofly launch --dockerfile Dockerfile
Self-hostnpx -p @black-knight.dev/pinglet pinglet-server
Overviewdocs/deployment.md โ€” all options

All deployments need one env var: PINGLET_ADMIN_PASSWORD.


๐Ÿ”Œ API

new Pinglet(options)

OptionRequiredDescription
packageNameโœ…Your package name
packageVersionโœ…Current version
endpointโœ…URL receiving POST /ping
saltโ€”Stable salt for anonymous client id
silentโ€”Suppress console output
timeoutMsโ€”Network timeout (default 1500ms)
ingestTokenโ€”Write token for private endpoints
metaโ€”Non-PII properties on every event

Methods

await analytics.init()         // prepare client
await analytics.track('event') // send event โ€” never throws
analytics.optOut()             // persist disable
analytics.optIn()              // re-enable
analytics.isOptedOut           // current state

๐Ÿ“„ Documentation

DocTopic
deployment.mdDeploy (Railway, Docker, Fly)
deploy-railway.mdRailway guide
security.mdPrivacy, GDPR, hardening
maintainer-guide.mdAdd pinglet to your package
agent-quickstart.mdFor AI agents
market-research.mdWhy pinglet
examples/basic-cli.mjsWorking example

โœ… Best practices

  • Add a visible Telemetry section to your README
  • Events go low-cardinality: command:build, not raw input
  • Never track: paths, source code, logs, stack traces, secrets
  • Support DO_NOT_TRACK=1 + PINGLET_OPT_OUT + --no-telemetry
  • Offer PINGLET_DEBUG=1 so users can inspect the payload before trusting
  • Telemetry failures are always silent and non-blocking

๐Ÿ“ License

MIT