{"_id":"@boole/token-guard","name":"@boole/token-guard","dist-tags":{"latest":"0.3.0"},"versions":{"0.3.0":{"name":"@boole/token-guard","version":"0.3.0","description":"Detect and throttle bots/scripts that abuse free-tier AI API token quotas","main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"},"./express":{"types":"./dist/middleware/express.d.ts","import":"./dist/middleware/express.mjs","require":"./dist/middleware/express.js"}},"scripts":{"build":"tsup","test":"vitest run","test:watch":"vitest","typecheck":"tsc --noEmit"},"keywords":["rate-limit","token","ai","llm","anti-abuse","bot-detection","middleware"],"license":"MIT","devDependencies":{"@types/express":"^5.0.0","@types/node":"^22.0.0","express":"^5.0.0","tsup":"^8.0.0","typescript":"^5.7.0","vitest":"^3.0.0"},"peerDependencies":{"ioredis":">=5.0.0"},"peerDependenciesMeta":{"ioredis":{"optional":true}},"engines":{"node":">=18"},"_id":"@boole/token-guard@0.3.0","gitHead":"87eba199016aee17dce5ce1740c7b7cceea4a1ec","_nodeVersion":"22.23.2","_npmVersion":"10.9.8","dist":{"integrity":"sha512-pV+/20VBvcDCwIe41YaqrqT1Qhcb2JPR5XAM57AbYYWy398meftg/MrtmTqgqIQXpaeCAAPTv0mBOJqqzroZGw==","shasum":"be35b1a544f525d47e639837a0190b2817f78651","tarball":"https://registry.npmjs.org/@boole/token-guard/-/token-guard-0.3.0.tgz","fileCount":2,"unpackedSize":5140,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQC3hOiecSiL7E3+RBsUN7YJi6lOYEHpEtcDVV8N+ziRgAIhAMNT7/TSHdhY/8IrUdy5eGLKnQRd1D6dfINZWbbmFkgw"}]},"_npmUser":{"name":"jordan.plows","email":"plowstjordan@gmail.com"},"directories":{},"maintainers":[{"name":"jordan.plows","email":"plowstjordan@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/token-guard_0.3.0_1787682477001_0.5660427934724808"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-25T18:27:56.868Z","0.3.0":"2026-08-25T18:27:57.128Z","modified":"2026-08-25T18:27:57.351Z"},"maintainers":[{"name":"jordan.plows","email":"plowstjordan@gmail.com"}],"description":"Detect and throttle bots/scripts that abuse free-tier AI API token quotas","keywords":["rate-limit","token","ai","llm","anti-abuse","bot-detection","middleware"],"license":"MIT","readme":"# token-guard\n\nDetect and throttle bots and scripts that abuse free-tier AI API token quotas. Combines sliding-window rate limiting, token-based quota tracking, and behavioral risk scoring into a single middleware-ready package.\n\n## Install\n\n```bash\nnpm install token-guard\n```\n\n## Quick Start\n\n```ts\nimport { TokenGuard } from \"token-guard\";\n\nconst guard = new TokenGuard({\n  identityResolver: (req) => req.headers[\"x-api-key\"],\n  defaultTier: \"free\",\n  tiers: {\n    free: {\n      maxTokensPerWindow: 100_000,\n      maxRequestsPerWindow: 60,\n      windowMs: 60 * 60 * 1000, // 1 hour\n      riskThresholds: { throttle: 50, block: 80 },\n    },\n  },\n});\n\nconst decision = await guard.check({\n  identity: \"user-123\",\n  tokenCount: 500,\n  headers: req.headers,\n  email: \"user@example.com\",\n  accountCreatedAt: Date.now() - 86_400_000,\n});\n\nif (!decision.allowed) {\n  // decision.reason, decision.retryAfterMs, decision.quota\n}\n```\n\n## Express Middleware\n\n```ts\nimport { tokenGuard } from \"token-guard/express\";\n\napp.use(\n  tokenGuard({\n    identityResolver: (req) => req.headers[\"x-api-key\"],\n    defaultTier: \"free\",\n    tiers: {\n      free: {\n        maxTokensPerWindow: 100_000,\n        maxRequestsPerWindow: 60,\n        windowMs: 60 * 60 * 1000,\n        riskThresholds: { throttle: 50, block: 80 },\n      },\n    },\n    onBlocked: (req, res, decision) => {\n      res.status(429).json({ error: \"blocked\", retryAfterMs: decision.retryAfterMs });\n    },\n  })\n);\n```\n\nThe middleware automatically sets response headers:\n\n| Header | Description |\n|--------|-------------|\n| `X-TokenGuard-Score` | Current risk score (0-100) |\n| `X-TokenGuard-Action` | `allow`, `throttle`, or `block` |\n| `X-RateLimit-Remaining` | Tokens remaining in the current window |\n| `X-RateLimit-Reset` | Seconds until the window resets |\n\n## Risk Scoring\n\nThe `RiskScorer` evaluates five behavioral signals to detect automated abuse:\n\n| Signal | Weight | What it detects |\n|--------|--------|-----------------|\n| Burst velocity | 30 | Sudden spikes above the per-minute baseline |\n| Timing regularity | 25 | Machine-like fixed intervals between requests |\n| Account age | 15 | Newly created accounts |\n| Client signals | 15 | Missing or scripted User-Agent/Accept headers |\n| Disposable email | 15 | Known throwaway email domains |\n\nWeights are configurable via `riskScorer` in the config. The composite score is checked against per-tier thresholds to produce an action: `allow`, `throttle`, or `block`.\n\n## Custom Store (Redis)\n\nThe default `MemoryStore` works for single-process deployments. For multi-instance setups, implement the `Store` interface with Redis or any KV backend:\n\n```ts\nimport { TokenGuard, type Store } from \"token-guard\";\n\nconst redisStore: Store = {\n  get: (key) => redis.get(key),\n  set: (key, value, ttlMs) => redis.set(key, value, \"PX\", ttlMs),\n  increment: (key, amount, ttlMs) => redis.incrby(key, amount),\n  getList: (key) => redis.lrange(key, 0, -1),\n  appendToList: (key, value, ttlMs) => redis.rpush(key, value),\n  trimList: (key, minTimestamp) => { /* filter by timestamp */ },\n  delete: (key) => redis.del(key),\n};\n\nconst guard = new TokenGuard({ store: redisStore, /* ... */ });\n```\n\n## API\n\n### `TokenGuard`\n\n| Method | Returns | Description |\n|--------|---------|-------------|\n| `check(ctx: RequestContext)` | `Promise<GuardDecision>` | Evaluate a request against rate limits, quota, and risk |\n| `getRateLimiter()` | `RateLimiter` | Access the rate limiter directly |\n| `getRiskScorer()` | `RiskScorer` | Access the risk scorer directly |\n| `getQuotaLedger()` | `QuotaLedger` | Access the quota ledger directly |\n\n### `GuardDecision`\n\n```ts\n{\n  allowed: boolean;\n  reason?: string;\n  riskScore: number;\n  action: \"allow\" | \"throttle\" | \"block\";\n  quota: QuotaStatus;\n  retryAfterMs?: number;\n}\n```\n\n## Development\n\n```bash\nnpm test          # run tests\nnpm run build     # build with tsup\nnpm run typecheck # tsc --noEmit\n```\n\n## License\n\nMIT\n","readmeFilename":"README.md","_rev":"1-d47c4cc10d84342c1ecdb3e95224d453"}