README.md
June 7, 2026 ยท View on GitHub
Anonymous runtime analytics for npm packages.
Real usage โ not download noise.
๐ฏ 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 downloads | pinglet | |
|---|---|---|
| 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
| Collected | Not collected |
|---|---|
| Random hashed client id | Hardware id, hostname, username |
| Event name | File paths, source code, logs |
| Package name + version | Environment variables, secrets |
| Node.js version | User-generated content |
| Platform (darwin/linux/win32) | IP address |
| CI flag | Client 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=1shows 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
| Method | Path | Auth | Description |
|---|---|---|---|
POST | /ping | public | Receive a runtime event |
POST | /auth/login | Basic Auth | Create 30-day admin token |
GET | /auth/check | Bearer token | Verify saved login |
GET | /packages | admin | List tracked packages |
GET | /stats?pkg=<name> | admin | Aggregated analytics |
GET | /health | public | Health check |
๐ฆ Deployment
| Platform | Guide |
|---|---|
| Railway | docs/deploy-railway.md โ step by step |
| Docker | docker build -t pinglet . โ single command |
| Fly.io | fly launch --dockerfile Dockerfile |
| Self-host | npx -p @black-knight.dev/pinglet pinglet-server |
| Overview | docs/deployment.md โ all options |
All deployments need one env var: PINGLET_ADMIN_PASSWORD.
๐ API
new Pinglet(options)
| Option | Required | Description |
|---|---|---|
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
| Doc | Topic |
|---|---|
deployment.md | Deploy (Railway, Docker, Fly) |
deploy-railway.md | Railway guide |
security.md | Privacy, GDPR, hardening |
maintainer-guide.md | Add pinglet to your package |
agent-quickstart.md | For AI agents |
market-research.md | Why pinglet |
examples/basic-cli.mjs | Working example |
โ Best practices
- Add a visible
Telemetrysection 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=1so users can inspect the payload before trusting - Telemetry failures are always silent and non-blocking
๐ License
MIT