paygate-mcp 5.0.0 → 5.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -77,6 +77,8 @@ Agent → PayGate (auth + billing) → Your MCP Server (stdio or HTTP)
77
77
  - **Webhook Delivery Log** — `GET /webhooks/log` returns a queryable log of all webhook delivery attempts with timestamps, HTTP status codes, response times, success/failure, retry attempts, event counts, and event types — filter by success status, time range, and limit
78
78
  - **Webhook Pause/Resume** — `POST /webhooks/pause` and `POST /webhooks/resume` temporarily halt webhook delivery during maintenance — events are buffered (not lost) and flushed on resume, with pause state visible in `/webhooks/stats`
79
79
  - **Key Aliases** — `POST /keys/alias` assigns human-readable aliases (e.g. `my-service`, `prod-backend`) to API keys — use aliases in any admin endpoint (topup, revoke, suspend, resume, clone, transfer, usage) instead of opaque key IDs, with uniqueness enforcement, format validation, state file persistence, and audit trail
80
+ - **Key Expiry Scanner** — Proactive background scanner that detects expiring API keys before they expire — configurable scan interval and notification thresholds (default: 7d, 24h, 1h), de-duplicated `key.expiry_warning` webhook events, audit trail, `GET /keys/expiring?within=86400` query endpoint, and graceful shutdown
81
+ - **Key Templates** — Named templates for API key creation — define reusable presets (credits, ACL, quotas, IP, tags, namespace, expiry TTL, spending limit, auto-topup) and create keys with `template: "free-tier"` — explicit params override template defaults, CRUD admin API, Prometheus gauge, file persistence, max 100 templates
80
82
  - **Config Hot Reload** — `POST /config/reload` reloads pricing, rate limits, webhooks, quotas, and behavior flags from config file without server restart
81
83
  - **Webhook Events** — POST batched usage events to any URL for external billing/alerting
82
84
  - **Config File Mode** — Load all settings from a JSON file (`--config`)
@@ -359,6 +361,10 @@ A real-time admin UI for managing keys, viewing usage, and monitoring tool calls
359
361
  | `/webhooks/pause` | POST | `X-Admin-Key` | Pause webhook delivery (events buffered until resumed) |
360
362
  | `/webhooks/resume` | POST | `X-Admin-Key` | Resume webhook delivery and flush buffered events |
361
363
  | `/keys/alias` | POST | `X-Admin-Key` | Set or clear a human-readable alias for an API key |
364
+ | `/keys/expiring` | GET | `X-Admin-Key` | List keys expiring within a time window (`?within=86400` seconds) |
365
+ | `/keys/templates` | GET | `X-Admin-Key` | List all key templates |
366
+ | `/keys/templates` | POST | `X-Admin-Key` | Create or update a key template |
367
+ | `/keys/templates/delete` | POST | `X-Admin-Key` | Delete a key template |
362
368
  | `/config/reload` | POST | `X-Admin-Key` | Hot-reload config file (pricing, rate limits, webhooks, quotas) |
363
369
  | `/health` | GET | None | Health check (status, uptime, version, in-flight, Redis/webhook status) |
364
370
  | `/` | GET | None | Root endpoint (endpoint list) |
@@ -1237,6 +1243,94 @@ curl -X POST http://localhost:3402/keys/alias \
1237
1243
  | Clone | Cloned keys do **not** inherit the source key's alias |
1238
1244
  | Audit | `key.alias_set` event logged for every set/clear operation |
1239
1245
 
1246
+ ### Key Expiry Scanner
1247
+
1248
+ Proactive background scanner that detects API keys approaching expiration and sends webhook notifications before they expire — even if the keys are not actively being used:
1249
+
1250
+ ```bash
1251
+ # Query keys expiring within 24 hours (default)
1252
+ curl http://localhost:3402/keys/expiring \
1253
+ -H "X-Admin-Key: YOUR_ADMIN_KEY"
1254
+ # → { "within": 86400, "count": 2, "scanner": { ... }, "keys": [ ... ] }
1255
+
1256
+ # Query keys expiring within 7 days
1257
+ curl http://localhost:3402/keys/expiring?within=604800 \
1258
+ -H "X-Admin-Key: YOUR_ADMIN_KEY"
1259
+ ```
1260
+
1261
+ Configure the scanner in your config file:
1262
+
1263
+ ```json
1264
+ {
1265
+ "expiryScanner": {
1266
+ "enabled": true,
1267
+ "intervalSeconds": 3600,
1268
+ "thresholds": [604800, 86400, 3600]
1269
+ }
1270
+ }
1271
+ ```
1272
+
1273
+ | Field | Description |
1274
+ |-------|-------------|
1275
+ | `enabled` | Enable/disable the background scanner. Default: `true` |
1276
+ | `intervalSeconds` | How often to scan (seconds). Default: `3600` (1 hour). Min: 60 |
1277
+ | `thresholds` | Seconds before expiry to notify. Default: `[604800, 86400, 3600]` (7d, 24h, 1h) |
1278
+ | Webhook | Fires `key.expiry_warning` events with key name, alias, namespace, expiry time, and remaining seconds |
1279
+ | De-duplication | Each key+threshold pair is only notified once (no duplicate alerts) |
1280
+ | Progressive | Largest threshold fires first, then progressively smaller thresholds on subsequent scans |
1281
+ | Audit | `key.expiry_warning` event logged for every notification |
1282
+ | Endpoint | `GET /keys/expiring?within=N` lists keys expiring within N seconds (default: 86400) |
1283
+
1284
+ ### Key Templates
1285
+
1286
+ Named templates for API key creation. Define reusable presets and create keys with `template: "free-tier"`:
1287
+
1288
+ ```bash
1289
+ # Create a template
1290
+ curl -X POST http://localhost:3402/keys/templates \
1291
+ -H "X-Admin-Key: YOUR_ADMIN_KEY" \
1292
+ -d '{
1293
+ "name": "free-tier",
1294
+ "description": "Free plan with basic access",
1295
+ "credits": 50,
1296
+ "allowedTools": ["search", "read"],
1297
+ "deniedTools": ["admin"],
1298
+ "tags": {"plan": "free"},
1299
+ "namespace": "public",
1300
+ "expiryTtlSeconds": 2592000,
1301
+ "spendingLimit": 200
1302
+ }'
1303
+
1304
+ # List all templates
1305
+ curl http://localhost:3402/keys/templates \
1306
+ -H "X-Admin-Key: YOUR_ADMIN_KEY"
1307
+
1308
+ # Create a key from template (inherits all defaults)
1309
+ curl -X POST http://localhost:3402/keys \
1310
+ -H "X-Admin-Key: YOUR_ADMIN_KEY" \
1311
+ -d '{"name": "new-user", "template": "free-tier"}'
1312
+
1313
+ # Create a key from template with overrides
1314
+ curl -X POST http://localhost:3402/keys \
1315
+ -H "X-Admin-Key: YOUR_ADMIN_KEY" \
1316
+ -d '{"name": "vip-user", "template": "free-tier", "credits": 500, "tags": {"plan": "vip"}}'
1317
+
1318
+ # Delete a template
1319
+ curl -X POST http://localhost:3402/keys/templates/delete \
1320
+ -H "X-Admin-Key: YOUR_ADMIN_KEY" \
1321
+ -d '{"name": "free-tier"}'
1322
+ ```
1323
+
1324
+ | Feature | Details |
1325
+ |---------|---------|
1326
+ | Fields | credits, allowedTools, deniedTools, quota, ipAllowlist, spendingLimit, tags, namespace, expiryTtlSeconds, autoTopup |
1327
+ | Override | Explicit params in `POST /keys` always override template defaults |
1328
+ | TTL | `expiryTtlSeconds` sets expiry relative to key creation time (0 = never) |
1329
+ | Limit | Max 100 templates per server |
1330
+ | Persistence | `-templates.json` alongside state file, survives restarts |
1331
+ | Audit | `template.created`, `template.updated`, `template.deleted` events |
1332
+ | Prometheus | `paygate_templates_total` gauge tracks template count |
1333
+
1240
1334
  ### IP Allowlisting
1241
1335
 
1242
1336
  Restrict API keys to specific IP addresses or CIDR ranges:
package/dist/audit.d.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  * session lifecycle, and admin operations. Ring buffer with configurable
6
6
  * max size and age-based retention. Zero external dependencies.
7
7
  */
8
- export type AuditEventType = 'key.created' | 'key.revoked' | 'key.suspended' | 'key.resumed' | 'key.cloned' | 'key.rotated' | 'key.topup' | 'key.acl_updated' | 'key.expiry_updated' | 'key.quota_updated' | 'key.tags_updated' | 'key.ip_updated' | 'key.limit_updated' | 'gate.allow' | 'gate.deny' | 'session.created' | 'session.destroyed' | 'oauth.client_registered' | 'oauth.token_issued' | 'oauth.token_revoked' | 'team.created' | 'team.updated' | 'team.deleted' | 'team.key_assigned' | 'team.key_removed' | 'admin.auth_failed' | 'admin.alerts_configured' | 'webhook.dead_letter_cleared' | 'webhook.replayed' | 'webhook.test' | 'webhook.pause' | 'webhook.resume' | 'key.alias_set' | 'token.created' | 'token.revoked' | 'billing.refund' | 'key.auto_topup_configured' | 'key.auto_topped_up' | 'admin_key.created' | 'admin_key.revoked' | 'group.created' | 'group.updated' | 'group.deleted' | 'group.key_assigned' | 'group.key_removed' | 'key.credits_transferred' | 'keys.exported' | 'keys.imported' | 'webhook_filter.created' | 'webhook_filter.updated' | 'webhook_filter.deleted' | 'config.reloaded';
8
+ export type AuditEventType = 'key.created' | 'key.revoked' | 'key.suspended' | 'key.resumed' | 'key.cloned' | 'key.rotated' | 'key.topup' | 'key.acl_updated' | 'key.expiry_updated' | 'key.quota_updated' | 'key.tags_updated' | 'key.ip_updated' | 'key.limit_updated' | 'gate.allow' | 'gate.deny' | 'session.created' | 'session.destroyed' | 'oauth.client_registered' | 'oauth.token_issued' | 'oauth.token_revoked' | 'team.created' | 'team.updated' | 'team.deleted' | 'team.key_assigned' | 'team.key_removed' | 'admin.auth_failed' | 'admin.alerts_configured' | 'webhook.dead_letter_cleared' | 'webhook.replayed' | 'webhook.test' | 'webhook.pause' | 'webhook.resume' | 'key.alias_set' | 'key.expiry_warning' | 'template.created' | 'template.updated' | 'template.deleted' | 'token.created' | 'token.revoked' | 'billing.refund' | 'key.auto_topup_configured' | 'key.auto_topped_up' | 'admin_key.created' | 'admin_key.revoked' | 'group.created' | 'group.updated' | 'group.deleted' | 'group.key_assigned' | 'group.key_removed' | 'key.credits_transferred' | 'keys.exported' | 'keys.imported' | 'webhook_filter.created' | 'webhook_filter.updated' | 'webhook_filter.deleted' | 'config.reloaded';
9
9
  export interface AuditEvent {
10
10
  /** Monotonically increasing ID */
11
11
  id: number;
@@ -1 +1 @@
1
- {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"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"}
package/dist/audit.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"audit.js","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"audit.js","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,99 @@
1
+ /**
2
+ * ExpiryScanner — Proactive background scanner for expiring API keys.
3
+ *
4
+ * Unlike the reactive key_expiry_soon alert (which only fires during gate evaluation),
5
+ * this scanner runs on a configurable interval and catches expiring keys even when idle.
6
+ *
7
+ * Features:
8
+ * - Configurable scan interval (default: 1 hour)
9
+ * - Multiple notification thresholds (e.g., 7d, 24h, 1h before expiry)
10
+ * - De-duplication: same key+threshold pair is only notified once
11
+ * - Fires key.expiry_warning webhook events
12
+ * - Audit trail for all notifications
13
+ * - Graceful shutdown (clears interval timer)
14
+ */
15
+ import { ApiKeyRecord } from './types';
16
+ export interface ExpiryScannerConfig {
17
+ /** Whether the scanner is enabled. Default: true when thresholds are configured. */
18
+ enabled: boolean;
19
+ /** How often to scan, in seconds. Default: 3600 (1 hour). Min: 60. */
20
+ intervalSeconds: number;
21
+ /** Seconds before expiry to send notifications. Default: [604800, 86400, 3600] (7d, 24h, 1h). */
22
+ thresholds: number[];
23
+ }
24
+ export interface ExpiryWarning {
25
+ /** The API key (full, for internal use — mask before exposing) */
26
+ key: string;
27
+ /** Key name */
28
+ name: string;
29
+ /** Key alias (if set) */
30
+ alias?: string;
31
+ /** Key namespace */
32
+ namespace: string;
33
+ /** ISO string when the key expires */
34
+ expiresAt: string;
35
+ /** Seconds remaining until expiry */
36
+ remainingSeconds: number;
37
+ /** Human-readable time remaining */
38
+ remainingHuman: string;
39
+ /** Which threshold triggered this warning (seconds) */
40
+ thresholdSeconds: number;
41
+ }
42
+ export declare const DEFAULT_EXPIRY_SCANNER_CONFIG: ExpiryScannerConfig;
43
+ export declare class ExpiryScanner {
44
+ private config;
45
+ private timer;
46
+ /** De-duplication: "keyPrefix:threshold" → timestamp of last notification */
47
+ private readonly notified;
48
+ /** Callback for each warning — wired by the server to emit webhooks/audit */
49
+ onWarning: ((warning: ExpiryWarning) => void) | null;
50
+ /** Key provider — returns all key records for scanning */
51
+ private getKeys;
52
+ constructor(config?: Partial<ExpiryScannerConfig>);
53
+ /**
54
+ * Start the background scanner.
55
+ * @param getKeys Function that returns all key records to scan
56
+ */
57
+ start(getKeys: () => ApiKeyRecord[]): void;
58
+ /**
59
+ * Stop the scanner and clear all state.
60
+ */
61
+ destroy(): void;
62
+ /**
63
+ * Run a scan now (also called by the interval timer).
64
+ * Returns warnings found in this scan.
65
+ */
66
+ scan(): ExpiryWarning[];
67
+ /**
68
+ * Query keys expiring within a time window (for the admin endpoint).
69
+ * Does NOT trigger notifications — this is a read-only query.
70
+ */
71
+ static queryExpiring(keys: ApiKeyRecord[], withinSeconds: number): Array<{
72
+ keyPrefix: string;
73
+ name: string;
74
+ alias?: string;
75
+ namespace: string;
76
+ expiresAt: string;
77
+ remainingSeconds: number;
78
+ remainingHuman: string;
79
+ suspended: boolean;
80
+ }>;
81
+ /**
82
+ * Get scanner status (for /health or diagnostics).
83
+ */
84
+ get status(): {
85
+ enabled: boolean;
86
+ intervalSeconds: number;
87
+ thresholds: number[];
88
+ notifiedCount: number;
89
+ };
90
+ /**
91
+ * Clear de-duplication state (for testing).
92
+ */
93
+ clearNotified(): void;
94
+ /**
95
+ * Update config at runtime (for config hot-reload).
96
+ */
97
+ updateConfig(config: Partial<ExpiryScannerConfig>): void;
98
+ }
99
+ //# sourceMappingURL=expiry-scanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expiry-scanner.d.ts","sourceRoot":"","sources":["../src/expiry-scanner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAIvC,MAAM,WAAW,mBAAmB;IAClC,oFAAoF;IACpF,OAAO,EAAE,OAAO,CAAC;IACjB,sEAAsE;IACtE,eAAe,EAAE,MAAM,CAAC;IACxB,iGAAiG;IACjG,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,kEAAkE;IAClE,GAAG,EAAE,MAAM,CAAC;IACZ,eAAe;IACf,IAAI,EAAE,MAAM,CAAC;IACb,yBAAyB;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oBAAoB;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,sCAAsC;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,qCAAqC;IACrC,gBAAgB,EAAE,MAAM,CAAC;IACzB,oCAAoC;IACpC,cAAc,EAAE,MAAM,CAAC;IACvB,uDAAuD;IACvD,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,eAAO,MAAM,6BAA6B,EAAE,mBAI3C,CAAC;AAIF,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAsB;IACpC,OAAO,CAAC,KAAK,CAA+C;IAC5D,6EAA6E;IAC7E,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA6B;IACtD,6EAA6E;IAC7E,SAAS,EAAE,CAAC,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI,CAAC,GAAG,IAAI,CAAQ;IAC5D,0DAA0D;IAC1D,OAAO,CAAC,OAAO,CAAuC;gBAE1C,MAAM,CAAC,EAAE,OAAO,CAAC,mBAAmB,CAAC;IAQjD;;;OAGG;IACH,KAAK,CAAC,OAAO,EAAE,MAAM,YAAY,EAAE,GAAG,IAAI;IAU1C;;OAEG;IACH,OAAO,IAAI,IAAI;IASf;;;OAGG;IACH,IAAI,IAAI,aAAa,EAAE;IAiEvB;;;OAGG;IACH,MAAM,CAAC,aAAa,CAAC,IAAI,EAAE,YAAY,EAAE,EAAE,aAAa,EAAE,MAAM,GAAG,KAAK,CAAC;QACvE,SAAS,EAAE,MAAM,CAAC;QAClB,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;QAClB,gBAAgB,EAAE,MAAM,CAAC;QACzB,cAAc,EAAE,MAAM,CAAC;QACvB,SAAS,EAAE,OAAO,CAAC;KACpB,CAAC;IA0CF;;OAEG;IACH,IAAI,MAAM,IAAI;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,eAAe,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,EAAE,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE,CAOvG;IAED;;OAEG;IACH,aAAa,IAAI,IAAI;IAIrB;;OAEG;IACH,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,mBAAmB,CAAC,GAAG,IAAI;CAWzD"}
@@ -0,0 +1,216 @@
1
+ "use strict";
2
+ /**
3
+ * ExpiryScanner — Proactive background scanner for expiring API keys.
4
+ *
5
+ * Unlike the reactive key_expiry_soon alert (which only fires during gate evaluation),
6
+ * this scanner runs on a configurable interval and catches expiring keys even when idle.
7
+ *
8
+ * Features:
9
+ * - Configurable scan interval (default: 1 hour)
10
+ * - Multiple notification thresholds (e.g., 7d, 24h, 1h before expiry)
11
+ * - De-duplication: same key+threshold pair is only notified once
12
+ * - Fires key.expiry_warning webhook events
13
+ * - Audit trail for all notifications
14
+ * - Graceful shutdown (clears interval timer)
15
+ */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.ExpiryScanner = exports.DEFAULT_EXPIRY_SCANNER_CONFIG = void 0;
18
+ exports.DEFAULT_EXPIRY_SCANNER_CONFIG = {
19
+ enabled: true,
20
+ intervalSeconds: 3600,
21
+ thresholds: [604800, 86400, 3600], // 7 days, 24 hours, 1 hour
22
+ };
23
+ // ─── Scanner Class ────────────────────────────────────────────────────────────
24
+ class ExpiryScanner {
25
+ config;
26
+ timer = null;
27
+ /** De-duplication: "keyPrefix:threshold" → timestamp of last notification */
28
+ notified = new Map();
29
+ /** Callback for each warning — wired by the server to emit webhooks/audit */
30
+ onWarning = null;
31
+ /** Key provider — returns all key records for scanning */
32
+ getKeys = null;
33
+ constructor(config) {
34
+ this.config = { ...exports.DEFAULT_EXPIRY_SCANNER_CONFIG, ...config };
35
+ // Enforce minimum interval
36
+ if (this.config.intervalSeconds < 60)
37
+ this.config.intervalSeconds = 60;
38
+ // Sort thresholds descending (largest first) for consistent scanning
39
+ this.config.thresholds = [...this.config.thresholds].sort((a, b) => b - a);
40
+ }
41
+ /**
42
+ * Start the background scanner.
43
+ * @param getKeys Function that returns all key records to scan
44
+ */
45
+ start(getKeys) {
46
+ if (!this.config.enabled)
47
+ return;
48
+ if (this.config.thresholds.length === 0)
49
+ return;
50
+ this.getKeys = getKeys;
51
+ // Run immediately on start, then on interval
52
+ this.scan();
53
+ this.timer = setInterval(() => this.scan(), this.config.intervalSeconds * 1000);
54
+ this.timer.unref(); // Don't prevent process exit
55
+ }
56
+ /**
57
+ * Stop the scanner and clear all state.
58
+ */
59
+ destroy() {
60
+ if (this.timer) {
61
+ clearInterval(this.timer);
62
+ this.timer = null;
63
+ }
64
+ this.notified.clear();
65
+ this.getKeys = null;
66
+ }
67
+ /**
68
+ * Run a scan now (also called by the interval timer).
69
+ * Returns warnings found in this scan.
70
+ */
71
+ scan() {
72
+ if (!this.getKeys)
73
+ return [];
74
+ const keys = this.getKeys();
75
+ const now = Date.now();
76
+ const warnings = [];
77
+ for (const record of keys) {
78
+ // Skip keys without expiry, revoked, or already expired
79
+ if (!record.expiresAt)
80
+ continue;
81
+ if (!record.active)
82
+ continue;
83
+ const expiresMs = new Date(record.expiresAt).getTime();
84
+ if (isNaN(expiresMs))
85
+ continue;
86
+ const remainingMs = expiresMs - now;
87
+ if (remainingMs <= 0)
88
+ continue; // Already expired
89
+ const remainingSeconds = Math.round(remainingMs / 1000);
90
+ // Check each threshold (sorted descending)
91
+ for (const threshold of this.config.thresholds) {
92
+ if (remainingSeconds <= threshold) {
93
+ const dedupeKey = `${record.key.slice(0, 10)}:${threshold}`;
94
+ // Skip if already notified for this key+threshold
95
+ if (this.notified.has(dedupeKey))
96
+ continue;
97
+ const warning = {
98
+ key: record.key,
99
+ name: record.name,
100
+ alias: record.alias,
101
+ namespace: record.namespace,
102
+ expiresAt: record.expiresAt,
103
+ remainingSeconds,
104
+ remainingHuman: formatDuration(remainingSeconds),
105
+ thresholdSeconds: threshold,
106
+ };
107
+ warnings.push(warning);
108
+ this.notified.set(dedupeKey, now);
109
+ // Emit callback
110
+ if (this.onWarning) {
111
+ try {
112
+ this.onWarning(warning);
113
+ }
114
+ catch {
115
+ // Swallow callback errors — scanner must not crash
116
+ }
117
+ }
118
+ // Only fire the most specific (smallest) threshold per key per scan
119
+ break;
120
+ }
121
+ }
122
+ }
123
+ // Cleanup old de-duplication entries (older than 2x the largest threshold)
124
+ const maxThreshold = this.config.thresholds[0] || 0;
125
+ const cleanupCutoff = now - maxThreshold * 2 * 1000;
126
+ for (const [k, ts] of this.notified) {
127
+ if (ts < cleanupCutoff)
128
+ this.notified.delete(k);
129
+ }
130
+ return warnings;
131
+ }
132
+ /**
133
+ * Query keys expiring within a time window (for the admin endpoint).
134
+ * Does NOT trigger notifications — this is a read-only query.
135
+ */
136
+ static queryExpiring(keys, withinSeconds) {
137
+ const now = Date.now();
138
+ const results = [];
139
+ for (const record of keys) {
140
+ if (!record.expiresAt)
141
+ continue;
142
+ if (!record.active)
143
+ continue;
144
+ const expiresMs = new Date(record.expiresAt).getTime();
145
+ if (isNaN(expiresMs))
146
+ continue;
147
+ const remainingMs = expiresMs - now;
148
+ if (remainingMs <= 0)
149
+ continue; // Already expired
150
+ const remainingSeconds = Math.round(remainingMs / 1000);
151
+ if (remainingSeconds > withinSeconds)
152
+ continue;
153
+ results.push({
154
+ keyPrefix: record.key.slice(0, 10) + '...',
155
+ name: record.name,
156
+ alias: record.alias,
157
+ namespace: record.namespace,
158
+ expiresAt: record.expiresAt,
159
+ remainingSeconds,
160
+ remainingHuman: formatDuration(remainingSeconds),
161
+ suspended: record.suspended || false,
162
+ });
163
+ }
164
+ // Sort by remaining time ascending (most urgent first)
165
+ results.sort((a, b) => a.remainingSeconds - b.remainingSeconds);
166
+ return results;
167
+ }
168
+ /**
169
+ * Get scanner status (for /health or diagnostics).
170
+ */
171
+ get status() {
172
+ return {
173
+ enabled: this.config.enabled,
174
+ intervalSeconds: this.config.intervalSeconds,
175
+ thresholds: this.config.thresholds,
176
+ notifiedCount: this.notified.size,
177
+ };
178
+ }
179
+ /**
180
+ * Clear de-duplication state (for testing).
181
+ */
182
+ clearNotified() {
183
+ this.notified.clear();
184
+ }
185
+ /**
186
+ * Update config at runtime (for config hot-reload).
187
+ */
188
+ updateConfig(config) {
189
+ if (config.intervalSeconds !== undefined) {
190
+ this.config.intervalSeconds = Math.max(60, config.intervalSeconds);
191
+ }
192
+ if (config.thresholds !== undefined) {
193
+ this.config.thresholds = [...config.thresholds].sort((a, b) => b - a);
194
+ }
195
+ if (config.enabled !== undefined) {
196
+ this.config.enabled = config.enabled;
197
+ }
198
+ }
199
+ }
200
+ exports.ExpiryScanner = ExpiryScanner;
201
+ // ─── Helpers ──────────────────────────────────────────────────────────────────
202
+ function formatDuration(seconds) {
203
+ if (seconds < 60)
204
+ return `${seconds}s`;
205
+ if (seconds < 3600)
206
+ return `${Math.round(seconds / 60)}m`;
207
+ if (seconds < 86400) {
208
+ const hours = Math.floor(seconds / 3600);
209
+ const mins = Math.round((seconds % 3600) / 60);
210
+ return mins > 0 ? `${hours}h ${mins}m` : `${hours}h`;
211
+ }
212
+ const days = Math.floor(seconds / 86400);
213
+ const hours = Math.round((seconds % 86400) / 3600);
214
+ return hours > 0 ? `${days}d ${hours}h` : `${days}d`;
215
+ }
216
+ //# sourceMappingURL=expiry-scanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expiry-scanner.js","sourceRoot":"","sources":["../src/expiry-scanner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * KeyTemplateManager — Named templates for API key creation.
3
+ *
4
+ * Templates define reusable presets for key creation (credits, ACL, quotas, etc.).
5
+ * Instead of passing all options every time, use `template: "free-tier"` in POST /keys.
6
+ *
7
+ * Features:
8
+ * - CRUD: create, update, list, get, delete templates
9
+ * - File persistence (-templates.json alongside state file)
10
+ * - Templates define: credits, allowedTools, deniedTools, quota, ipAllowlist,
11
+ * spendingLimit, tags, namespace, expiryTtlSeconds, autoTopup
12
+ * - Max 100 templates
13
+ */
14
+ import { QuotaConfig } from './types';
15
+ export interface KeyTemplate {
16
+ /** Unique template name (alphanumeric, hyphens, underscores, 1-50 chars) */
17
+ name: string;
18
+ /** Human-readable description */
19
+ description: string;
20
+ /** Initial credits for keys created from this template */
21
+ credits: number;
22
+ /** Whitelist: only these tools are accessible. Empty = all tools allowed. */
23
+ allowedTools: string[];
24
+ /** Blacklist: these tools are always denied. */
25
+ deniedTools: string[];
26
+ /** Per-key quota overrides. Undefined = use global defaults. */
27
+ quota?: QuotaConfig;
28
+ /** IP allowlist. Empty = all IPs allowed. */
29
+ ipAllowlist: string[];
30
+ /** Max total credits this key can spend. 0 = unlimited. */
31
+ spendingLimit: number;
32
+ /** Arbitrary key-value metadata tags. */
33
+ tags: Record<string, string>;
34
+ /** Namespace for multi-tenant isolation. */
35
+ namespace: string;
36
+ /** TTL in seconds from creation. 0 = never expires. */
37
+ expiryTtlSeconds: number;
38
+ /** Auto-topup configuration. Undefined = disabled. */
39
+ autoTopup?: {
40
+ threshold: number;
41
+ amount: number;
42
+ maxDaily: number;
43
+ };
44
+ /** ISO timestamp when template was created */
45
+ createdAt: string;
46
+ /** ISO timestamp of last update */
47
+ updatedAt: string;
48
+ }
49
+ export declare class KeyTemplateManager {
50
+ private templates;
51
+ private readonly filePath;
52
+ constructor(filePath?: string);
53
+ /**
54
+ * Create or update a template.
55
+ */
56
+ set(name: string, data: Partial<Omit<KeyTemplate, 'name' | 'createdAt' | 'updatedAt'>>): {
57
+ success: boolean;
58
+ error?: string;
59
+ template?: KeyTemplate;
60
+ };
61
+ /**
62
+ * Get a template by name.
63
+ */
64
+ get(name: string): KeyTemplate | null;
65
+ /**
66
+ * Delete a template.
67
+ */
68
+ delete(name: string): boolean;
69
+ /**
70
+ * List all templates.
71
+ */
72
+ list(): KeyTemplate[];
73
+ /**
74
+ * Get template count.
75
+ */
76
+ get count(): number;
77
+ private saveToFile;
78
+ private loadFromFile;
79
+ }
80
+ //# sourceMappingURL=key-templates.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"key-templates.d.ts","sourceRoot":"","sources":["../src/key-templates.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAItC,MAAM,WAAW,WAAW;IAC1B,4EAA4E;IAC5E,IAAI,EAAE,MAAM,CAAC;IACb,iCAAiC;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,0DAA0D;IAC1D,OAAO,EAAE,MAAM,CAAC;IAChB,6EAA6E;IAC7E,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,gDAAgD;IAChD,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,gEAAgE;IAChE,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,6CAA6C;IAC7C,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,2DAA2D;IAC3D,aAAa,EAAE,MAAM,CAAC;IACtB,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,4CAA4C;IAC5C,SAAS,EAAE,MAAM,CAAC;IAClB,uDAAuD;IACvD,gBAAgB,EAAE,MAAM,CAAC;IACzB,sDAAsD;IACtD,SAAS,CAAC,EAAE;QACV,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;IACF,8CAA8C;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,mCAAmC;IACnC,SAAS,EAAE,MAAM,CAAC;CACnB;AAMD,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,SAAS,CAAkC;IACnD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgB;gBAE7B,QAAQ,CAAC,EAAE,MAAM;IAO7B;;OAEG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,WAAW,GAAG,WAAW,CAAC,CAAC,GAAG;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,WAAW,CAAA;KAAE;IAqCrJ;;OAEG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI;IAIrC;;OAEG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAM7B;;OAEG;IACH,IAAI,IAAI,WAAW,EAAE;IAIrB;;OAEG;IACH,IAAI,KAAK,IAAI,MAAM,CAElB;IAID,OAAO,CAAC,UAAU;IAclB,OAAO,CAAC,YAAY;CAgBrB"}