@littlebearapps/platform-admin-sdk 1.2.0 → 1.4.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 +140 -45
- package/dist/templates.d.ts +1 -1
- package/dist/templates.js +4 -1
- package/package.json +13 -3
- package/templates/full/workers/lib/pattern-discovery/index.ts +13 -0
- package/templates/full/workers/lib/pattern-discovery/storage.ts +1 -0
- package/templates/shared/docs/kv-key-patterns.md +101 -0
- package/templates/shared/migrations/002_usage_warehouse.sql +1 -0
- package/templates/shared/migrations/seed.sql.hbs +2 -2
- package/templates/shared/scripts/sync-config.ts +59 -5
- package/templates/shared/workers/lib/platform-settings.ts +16 -1
- package/templates/shared/workers/lib/usage/queue/budget-enforcement.ts +11 -5
- package/templates/shared/workers/lib/usage/scheduled/anomaly-detection.ts +23 -8
- package/templates/shared/workers/lib/usage/scheduled/rollups.ts +8 -5
- package/templates/standard/workers/error-collector.ts +179 -362
- package/templates/standard/workers/lib/sentinel/gap-detection.ts +48 -8
- package/templates/standard/workers/platform-sentinel.ts +4 -4
package/README.md
CHANGED
|
@@ -4,6 +4,16 @@
|
|
|
4
4
|
|
|
5
5
|
Generates workers, D1 migrations, and config files. Run once, then you own the code.
|
|
6
6
|
|
|
7
|
+
> **Using Claude Code?** Install the [Platform SDK Plugin](https://github.com/littlebearapps/platform-sdk-plugin) for automated SDK convention enforcement — it validates wrangler bindings, budget wrappers, and cost safety patterns in real time.
|
|
8
|
+
|
|
9
|
+
## Prerequisites
|
|
10
|
+
|
|
11
|
+
- **Node.js 20+** and npm
|
|
12
|
+
- **wrangler CLI** installed and authenticated (`npx wrangler whoami`)
|
|
13
|
+
- **Cloudflare Workers Paid plan** (required for KV, Queues, and D1)
|
|
14
|
+
- **GitHub organisation** (Standard/Full tiers — for error collection GitHub issues)
|
|
15
|
+
- **Gatus instance** (optional — for heartbeat monitoring)
|
|
16
|
+
|
|
7
17
|
## Usage
|
|
8
18
|
|
|
9
19
|
```bash
|
|
@@ -17,9 +27,12 @@ The CLI prompts for:
|
|
|
17
27
|
- **Gatus URL** (optional) — for heartbeat monitoring
|
|
18
28
|
- **Default assignee** — GitHub username for error issues
|
|
19
29
|
|
|
20
|
-
### CLI
|
|
30
|
+
### CLI Commands
|
|
31
|
+
|
|
32
|
+
#### `scaffold` (default)
|
|
21
33
|
|
|
22
34
|
```bash
|
|
35
|
+
npx @littlebearapps/platform-admin-sdk my-platform
|
|
23
36
|
npx @littlebearapps/platform-admin-sdk my-platform --tier full --github-org myorg --skip-prompts
|
|
24
37
|
```
|
|
25
38
|
|
|
@@ -31,6 +44,27 @@ npx @littlebearapps/platform-admin-sdk my-platform --tier full --github-org myor
|
|
|
31
44
|
| `--default-assignee <user>` | Default GitHub issue assignee |
|
|
32
45
|
| `--skip-prompts` | Fail if required flags missing (for CI/automation) |
|
|
33
46
|
|
|
47
|
+
#### `upgrade`
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
npx @littlebearapps/platform-admin-sdk upgrade
|
|
51
|
+
npx @littlebearapps/platform-admin-sdk upgrade --dry-run
|
|
52
|
+
npx @littlebearapps/platform-admin-sdk upgrade --tier standard
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
| Flag | Description |
|
|
56
|
+
|------|------------|
|
|
57
|
+
| `--dry-run` | Preview changes without writing files |
|
|
58
|
+
| `--tier <tier>` | Upgrade to a higher tier |
|
|
59
|
+
|
|
60
|
+
#### `adopt`
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
npx @littlebearapps/platform-admin-sdk adopt . --tier minimal --project-name my-platform --skip-prompts
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Creates a `.platform-scaffold.json` manifest for projects scaffolded before v1.1.0. See [Upgrade vs Adopt](#upgrade-vs-adopt).
|
|
67
|
+
|
|
34
68
|
## Tiers
|
|
35
69
|
|
|
36
70
|
| Tier | Workers | What You Get | Est. Cost |
|
|
@@ -39,45 +73,63 @@ npx @littlebearapps/platform-admin-sdk my-platform --tier full --github-org myor
|
|
|
39
73
|
| **Standard** | 3 | + Error collector (auto GitHub issues), gap detection sentinel | ~$0/mo |
|
|
40
74
|
| **Full** | 8 | + AI pattern discovery, alert router, notifications, search, settings | ~$5/mo |
|
|
41
75
|
|
|
76
|
+
See [Tier Comparison](../../docs/admin-sdk/tiers.md) for a detailed breakdown of what each tier generates.
|
|
77
|
+
|
|
42
78
|
## What Gets Generated
|
|
43
79
|
|
|
44
80
|
### All tiers
|
|
45
81
|
|
|
46
82
|
```
|
|
47
83
|
my-platform/
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
84
|
+
├── platform/config/
|
|
85
|
+
│ ├── services.yaml # Project registry, feature definitions
|
|
86
|
+
│ └── budgets.yaml # Daily limits, circuit breaker thresholds
|
|
87
|
+
├── storage/d1/migrations/ # D1 schema (4 core migrations + seed)
|
|
88
|
+
├── workers/
|
|
89
|
+
│ ├── platform-usage.ts # Data warehouse worker (cron + queue consumer)
|
|
90
|
+
│ └── lib/ # Shared libraries (billing, analytics, budgets)
|
|
91
|
+
├── docs/
|
|
92
|
+
│ └── kv-key-patterns.md # KV key prefix reference
|
|
93
|
+
├── scripts/
|
|
94
|
+
│ └── sync-config.ts # Sync YAML config to D1/KV
|
|
95
|
+
├── wrangler.*.jsonc # Worker configs with binding placeholders
|
|
96
|
+
├── package.json
|
|
97
|
+
├── tsconfig.json
|
|
98
|
+
└── README.md
|
|
61
99
|
```
|
|
62
100
|
|
|
101
|
+
### Key generated files
|
|
102
|
+
|
|
103
|
+
| File | Purpose |
|
|
104
|
+
|------|---------|
|
|
105
|
+
| `platform/config/services.yaml` | Project registry — feature definitions, connections, metadata |
|
|
106
|
+
| `platform/config/budgets.yaml` | Daily limits, circuit breaker thresholds, warning percentages |
|
|
107
|
+
| `storage/d1/migrations/001_*.sql` | Core tables: project registry, resource snapshots, daily rollups |
|
|
108
|
+
| `workers/platform-usage.ts` | Central data warehouse — receives queue telemetry, stores in D1 |
|
|
109
|
+
| `scripts/sync-config.ts` | Syncs YAML config to D1 + KV (run after config changes) |
|
|
110
|
+
|
|
63
111
|
### Standard tier adds
|
|
64
112
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
113
|
+
| File | Purpose |
|
|
114
|
+
|------|---------|
|
|
115
|
+
| `workers/error-collector.ts` | Tail worker — creates GitHub issues from worker errors |
|
|
116
|
+
| `workers/platform-sentinel.ts` | Gap detection, cost monitoring, alerts |
|
|
117
|
+
| `workers/lib/error-collector/` | Fingerprinting, deduplication, digest generation |
|
|
118
|
+
| `workers/lib/sentinel/` | Per-project gap detection |
|
|
119
|
+
| `storage/d1/migrations/005_error_collection.sql` | Error tables: occurrences, fingerprint decisions, digests |
|
|
70
120
|
|
|
71
121
|
### Full tier adds
|
|
72
122
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
123
|
+
| File | Purpose |
|
|
124
|
+
|------|---------|
|
|
125
|
+
| `workers/pattern-discovery.ts` | AI-assisted transient error pattern discovery |
|
|
126
|
+
| `workers/platform-alert-router.ts` | Unified alert normalisation and routing |
|
|
127
|
+
| `workers/platform-notifications.ts` | In-app notification API |
|
|
128
|
+
| `workers/platform-search.ts` | Full-text search (FTS5) |
|
|
129
|
+
| `workers/platform-settings.ts` | Settings management API |
|
|
130
|
+
| `workers/lib/pattern-discovery/` | Types, clustering, AI prompts, validation, shadow evaluation |
|
|
131
|
+
| `storage/d1/migrations/006_pattern_discovery.sql` | Pattern tables |
|
|
132
|
+
| `storage/d1/migrations/007_notifications_search.sql` | Notification and search tables |
|
|
81
133
|
|
|
82
134
|
## Post-Scaffold Steps
|
|
83
135
|
|
|
@@ -156,11 +208,24 @@ npx wrangler deploy -c wrangler.my-platform-pattern-discovery.jsonc
|
|
|
156
208
|
npx wrangler deploy -c wrangler.my-platform-alert-router.jsonc
|
|
157
209
|
```
|
|
158
210
|
|
|
159
|
-
|
|
211
|
+
See [Quickstart Guide](../../docs/admin-sdk/quickstart.md) for a detailed walkthrough.
|
|
160
212
|
|
|
161
|
-
|
|
213
|
+
## The Manifest File
|
|
214
|
+
|
|
215
|
+
When you scaffold or upgrade, the SDK writes a `.platform-scaffold.json` file. **Commit this to git** — it's how `upgrade` knows what to update.
|
|
216
|
+
|
|
217
|
+
The manifest contains:
|
|
218
|
+
- **`sdkVersion`** — Version of the Admin SDK that generated the files
|
|
219
|
+
- **`tier`** — Infrastructure tier (minimal/standard/full)
|
|
220
|
+
- **`context`** — Project name, slug, organisation, and other scaffold-time settings
|
|
221
|
+
- **`files`** — SHA-256 hash of each generated file as originally written
|
|
222
|
+
- **`highestScaffoldMigration`** — Highest D1 migration number generated
|
|
223
|
+
|
|
224
|
+
The `files` hash map is the basis for the three-way merge during `upgrade`: if your current disk content matches the original hash, the file is "unmodified" and can be safely updated. If the disk content differs, you've customised it and `upgrade` skips it with a warning.
|
|
225
|
+
|
|
226
|
+
## Updating Your Platform
|
|
162
227
|
|
|
163
|
-
### Upgrade
|
|
228
|
+
### Upgrade (v1.1.0+)
|
|
164
229
|
|
|
165
230
|
```bash
|
|
166
231
|
cd my-platform
|
|
@@ -169,9 +234,9 @@ npx @littlebearapps/platform-admin-sdk upgrade
|
|
|
169
234
|
|
|
170
235
|
The upgrade command:
|
|
171
236
|
- **Creates** new files added in the SDK update
|
|
172
|
-
- **Updates** files you haven't modified (compares content hashes)
|
|
173
|
-
- **Skips** files you've customised (with a warning)
|
|
174
|
-
- **Renumbers** new migrations
|
|
237
|
+
- **Updates** files you haven't modified (compares content hashes from manifest)
|
|
238
|
+
- **Skips** files you've customised (with a warning showing which files were skipped)
|
|
239
|
+
- **Renumbers** new D1 migrations above your highest existing migration
|
|
175
240
|
|
|
176
241
|
Preview changes without writing:
|
|
177
242
|
|
|
@@ -185,15 +250,22 @@ Upgrade to a higher tier:
|
|
|
185
250
|
npx @littlebearapps/platform-admin-sdk upgrade --tier standard
|
|
186
251
|
```
|
|
187
252
|
|
|
188
|
-
###
|
|
253
|
+
### Upgrade vs Adopt
|
|
189
254
|
|
|
190
|
-
Projects scaffolded before v1.1.0 don't have a manifest. Run `adopt` first:
|
|
191
|
-
|
|
192
|
-
```bash
|
|
193
|
-
npx @littlebearapps/platform-admin-sdk adopt . --tier minimal --project-name my-platform --skip-prompts
|
|
194
255
|
```
|
|
256
|
+
Do you have .platform-scaffold.json in your project?
|
|
257
|
+
│
|
|
258
|
+
├── YES → Run: npx @littlebearapps/platform-admin-sdk upgrade
|
|
259
|
+
│ (three-way merge using manifest hashes)
|
|
260
|
+
│
|
|
261
|
+
└── NO → Run: npx @littlebearapps/platform-admin-sdk adopt . --tier <your-tier>
|
|
262
|
+
(creates manifest by hashing existing files as baseline)
|
|
263
|
+
Then run: npx @littlebearapps/platform-admin-sdk upgrade
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
Projects scaffolded before v1.1.0 don't have a manifest. The `adopt` command hashes your existing SDK-generated files as the "original" state, creating a baseline for future upgrades.
|
|
195
267
|
|
|
196
|
-
|
|
268
|
+
See [Upgrade Guide](../../docs/admin-sdk/upgrade-guide.md) for detailed instructions.
|
|
197
269
|
|
|
198
270
|
## Data Safety
|
|
199
271
|
|
|
@@ -210,20 +282,43 @@ The scaffolder generates core infrastructure only. It does **not** create:
|
|
|
210
282
|
- Email workers or notification templates
|
|
211
283
|
- Data connectors (Stripe, GA4, Plausible, etc.)
|
|
212
284
|
- Test suites
|
|
213
|
-
- CI/CD workflows
|
|
285
|
+
- CI/CD workflows (use the [consumer-check.yml](../../docs/admin-sdk/ci-workflow.md) reusable workflow)
|
|
214
286
|
|
|
215
287
|
These are project-specific — build them as you need them.
|
|
216
288
|
|
|
217
|
-
## Consumer SDK
|
|
289
|
+
## Consumer SDK Integration
|
|
218
290
|
|
|
219
|
-
The generated workers use `@littlebearapps/platform-consumer-sdk`
|
|
291
|
+
The generated backend workers use `@littlebearapps/platform-consumer-sdk` internally. To connect your application workers, install the Consumer SDK and add the required bindings:
|
|
220
292
|
|
|
221
293
|
```bash
|
|
222
294
|
npm install @littlebearapps/platform-consumer-sdk
|
|
223
295
|
```
|
|
224
296
|
|
|
225
|
-
|
|
297
|
+
Add to each application worker's `wrangler.jsonc`:
|
|
298
|
+
|
|
299
|
+
```jsonc
|
|
300
|
+
{
|
|
301
|
+
"kv_namespaces": [
|
|
302
|
+
{ "binding": "PLATFORM_CACHE", "id": "YOUR_KV_NAMESPACE_ID" }
|
|
303
|
+
],
|
|
304
|
+
"queues": {
|
|
305
|
+
"producers": [
|
|
306
|
+
{ "binding": "TELEMETRY_QUEUE", "queue": "my-platform-telemetry" }
|
|
307
|
+
]
|
|
308
|
+
},
|
|
309
|
+
// Standard/Full tier — route errors to error-collector
|
|
310
|
+
"tail_consumers": [
|
|
311
|
+
{ "service": "my-platform-error-collector" }
|
|
312
|
+
]
|
|
313
|
+
}
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
See the [Consumer SDK README](../consumer-sdk/README.md) for integration details and the [First Worker tutorial](../../docs/guides/first-worker.md) for a complete walkthrough.
|
|
317
|
+
|
|
318
|
+
## Multi-Account Support
|
|
319
|
+
|
|
320
|
+
The Admin SDK's programmatic API accepts `accountId` and `apiToken` per call, enabling cross-account monitoring and emergency control from a single script or worker. See the [Multi-Account Setup guide](../../docs/guides/multi-account.md) for architecture patterns.
|
|
226
321
|
|
|
227
|
-
##
|
|
322
|
+
## Licence
|
|
228
323
|
|
|
229
|
-
MIT
|
|
324
|
+
MIT — Made with ❤️ by [Little Bear Apps](https://littlebearapps.com) 🐶
|
package/dist/templates.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import type { Tier } from './prompts.js';
|
|
8
8
|
/** Single source of truth for the SDK version. */
|
|
9
|
-
export declare const SDK_VERSION = "1.
|
|
9
|
+
export declare const SDK_VERSION = "1.4.0";
|
|
10
10
|
/** Returns true if `to` is the same or higher tier than `from`. */
|
|
11
11
|
export declare function isTierUpgradeOrSame(from: Tier, to: Tier): boolean;
|
|
12
12
|
/** Check if a template file is a numbered migration (not seed.sql). */
|
package/dist/templates.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* All other files are copied verbatim.
|
|
6
6
|
*/
|
|
7
7
|
/** Single source of truth for the SDK version. */
|
|
8
|
-
export const SDK_VERSION = '1.
|
|
8
|
+
export const SDK_VERSION = '1.4.0';
|
|
9
9
|
/** Tier ordering for upgrade validation. */
|
|
10
10
|
const TIER_ORDER = { minimal: 0, standard: 1, full: 2 };
|
|
11
11
|
/** Returns true if `to` is the same or higher tier than `from`. */
|
|
@@ -82,6 +82,8 @@ const SHARED_FILES = [
|
|
|
82
82
|
// Workers — lib/usage/collectors (pluggable interface + example)
|
|
83
83
|
{ src: 'shared/workers/lib/usage/collectors/index.ts', dest: 'workers/lib/usage/collectors/index.ts', template: false },
|
|
84
84
|
{ src: 'shared/workers/lib/usage/collectors/example.ts', dest: 'workers/lib/usage/collectors/example.ts', template: false },
|
|
85
|
+
// Documentation
|
|
86
|
+
{ src: 'shared/docs/kv-key-patterns.md', dest: 'docs/kv-key-patterns.md', template: false },
|
|
85
87
|
];
|
|
86
88
|
const STANDARD_FILES = [
|
|
87
89
|
// Additional migrations
|
|
@@ -116,6 +118,7 @@ const FULL_FILES = [
|
|
|
116
118
|
{ src: 'full/wrangler.settings.jsonc.hbs', dest: 'wrangler.{{projectSlug}}-settings.jsonc', template: true },
|
|
117
119
|
// Workers — pattern-discovery (AI-assisted transient error patterns)
|
|
118
120
|
{ src: 'full/workers/pattern-discovery.ts', dest: 'workers/pattern-discovery.ts', template: false },
|
|
121
|
+
{ src: 'full/workers/lib/pattern-discovery/index.ts', dest: 'workers/lib/pattern-discovery/index.ts', template: false },
|
|
119
122
|
{ src: 'full/workers/lib/pattern-discovery/types.ts', dest: 'workers/lib/pattern-discovery/types.ts', template: false },
|
|
120
123
|
{ src: 'full/workers/lib/pattern-discovery/clustering.ts', dest: 'workers/lib/pattern-discovery/clustering.ts', template: false },
|
|
121
124
|
{ src: 'full/workers/lib/pattern-discovery/ai-prompt.ts', dest: 'workers/lib/pattern-discovery/ai-prompt.ts', template: false },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@littlebearapps/platform-admin-sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Platform Admin SDK — scaffold backend infrastructure with workers, circuit breakers, and cost protection for Cloudflare",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -35,12 +35,22 @@
|
|
|
35
35
|
"directory": "packages/admin-sdk"
|
|
36
36
|
},
|
|
37
37
|
"keywords": [
|
|
38
|
+
"cloudflare",
|
|
38
39
|
"cloudflare-workers",
|
|
39
|
-
"platform-admin-sdk",
|
|
40
40
|
"scaffold",
|
|
41
41
|
"cost-protection",
|
|
42
|
-
"circuit-breaker"
|
|
42
|
+
"circuit-breaker",
|
|
43
|
+
"billing-safety",
|
|
44
|
+
"observability",
|
|
45
|
+
"telemetry",
|
|
46
|
+
"budget-enforcement",
|
|
47
|
+
"error-collection",
|
|
48
|
+
"usage-monitoring"
|
|
43
49
|
],
|
|
50
|
+
"homepage": "https://github.com/littlebearapps/platform-sdks#readme",
|
|
51
|
+
"bugs": {
|
|
52
|
+
"url": "https://github.com/littlebearapps/platform-sdks/issues"
|
|
53
|
+
},
|
|
44
54
|
"author": "Little Bear Apps",
|
|
45
55
|
"license": "MIT"
|
|
46
56
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pattern Discovery Module
|
|
3
|
+
*
|
|
4
|
+
* AI-assisted discovery of transient error patterns.
|
|
5
|
+
*
|
|
6
|
+
* @module workers/lib/pattern-discovery
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export * from './types';
|
|
10
|
+
export * from './clustering';
|
|
11
|
+
export * from './ai-prompt';
|
|
12
|
+
export * from './validation';
|
|
13
|
+
export * from './storage';
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# KV Key Patterns Reference
|
|
2
|
+
|
|
3
|
+
All keys stored in the `PLATFORM_CACHE` KV namespace, grouped by subsystem.
|
|
4
|
+
|
|
5
|
+
> Generated by `@littlebearapps/platform-admin-sdk`. Keep this up to date as you add features.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Circuit Breaker State
|
|
10
|
+
|
|
11
|
+
| Key Pattern | Example | TTL | Worker | Purpose |
|
|
12
|
+
|---|---|---|---|---|
|
|
13
|
+
| `GLOBAL_STOP_ALL` | `GLOBAL_STOP_ALL` | None | platform-usage | Emergency kill switch — pauses all operations |
|
|
14
|
+
| `PROJECT:{slug}:STATUS` | `PROJECT:MY-APP:STATUS` | None | platform-usage | Per-project circuit breaker state (`active`/`warning`/`paused`) |
|
|
15
|
+
| `FEATURE:{key}:enabled` | `FEATURE:my-app:scanner:enabled` | None | platform-usage | Feature-level on/off flag |
|
|
16
|
+
| `FEATURE:{key}:disabled_reason` | `FEATURE:my-app:scanner:disabled_reason` | None | platform-usage | Why feature was disabled |
|
|
17
|
+
| `FEATURE:{key}:disabled_at` | `FEATURE:my-app:scanner:disabled_at` | None | platform-usage | Timestamp of disable |
|
|
18
|
+
| `FEATURE:{key}:auto_reset_at` | `FEATURE:my-app:scanner:auto_reset_at` | None | platform-usage | Auto-reset timestamp |
|
|
19
|
+
|
|
20
|
+
## Budget Enforcement
|
|
21
|
+
|
|
22
|
+
| Key Pattern | Example | TTL | Worker | Purpose |
|
|
23
|
+
|---|---|---|---|---|
|
|
24
|
+
| `CONFIG:BUDGETS` | `CONFIG:BUDGETS` | None (synced) | sync-config | Budget JSON from budgets.yaml |
|
|
25
|
+
| `CONFIG:FEATURE:{key}:BUDGET_MONTHLY` | `CONFIG:FEATURE:my-app:scanner:BUDGET_MONTHLY` | None | sync-config | Monthly budget limit for feature |
|
|
26
|
+
| `BUDGET_WARN:{feature}:{metric}:{threshold}` | `BUDGET_WARN:my-app:scanner:d1_writes:70` | 1hr | platform-usage | Budget warning dedup (prevents duplicate Slack alerts) |
|
|
27
|
+
| `BUDGET_WARN_MONTHLY:{feature}:{limit}:exceeded` | `BUDGET_WARN_MONTHLY:my-app:scanner:d1_writes:exceeded` | 24hr | platform-usage | Monthly budget exceeded dedup |
|
|
28
|
+
| `BUDGET_WARN_MONTHLY:{feature}:{limit}:{threshold}` | `BUDGET_WARN_MONTHLY:my-app:scanner:d1_writes:70` | 24hr | platform-usage | Monthly warning threshold dedup |
|
|
29
|
+
|
|
30
|
+
## Usage & Sampling State
|
|
31
|
+
|
|
32
|
+
| Key Pattern | Example | TTL | Worker | Purpose |
|
|
33
|
+
|---|---|---|---|---|
|
|
34
|
+
| `platform-usage:sampling-mode` | `platform-usage:sampling-mode` | None | platform-usage | Current sampling mode enum |
|
|
35
|
+
| `platform-usage:d1-writes-24h` | `platform-usage:d1-writes-24h` | None | platform-usage | Rolling 24h D1 write counter |
|
|
36
|
+
| `platform-usage:d1-writes-timestamp` | `platform-usage:d1-writes-timestamp` | None | platform-usage | Timestamp for write counter reset |
|
|
37
|
+
| `platform-usage:do-gb-seconds-24h:{project}` | `platform-usage:do-gb-seconds-24h:my-app` | None | platform-usage | DO GB-seconds per project |
|
|
38
|
+
| `platform-usage:pricing:v1` | `platform-usage:pricing:v1` | None | platform-usage | Cached pricing config |
|
|
39
|
+
| `platform-usage:prev-hour:account` | `platform-usage:prev-hour:account` | None | platform-usage | Delta calculation state |
|
|
40
|
+
| `platform-usage:prev-hour:timestamp` | `platform-usage:prev-hour:timestamp` | None | platform-usage | Last collection hour |
|
|
41
|
+
|
|
42
|
+
## Settings Cache
|
|
43
|
+
|
|
44
|
+
| Key Pattern | Example | TTL | Worker | Purpose |
|
|
45
|
+
|---|---|---|---|---|
|
|
46
|
+
| `CONFIG:SETTINGS:ALL` | `CONFIG:SETTINGS:ALL` | 1hr | platform-usage | Full PlatformSettings JSON cache |
|
|
47
|
+
| `CONFIG:SETTINGS:{key}` | `CONFIG:SETTINGS:budget_soft_limit` | 1hr | platform-usage | Individual setting value cache |
|
|
48
|
+
|
|
49
|
+
## Error Collection (Standard+ tier)
|
|
50
|
+
|
|
51
|
+
| Key Pattern | Example | TTL | Worker | Purpose |
|
|
52
|
+
|---|---|---|---|---|
|
|
53
|
+
| `SCRIPT_MAP:{scriptName}` | `SCRIPT_MAP:my-worker` | None | error-collector | Worker name to project mapping |
|
|
54
|
+
| `ERROR_FINGERPRINT:{hash}` | `ERROR_FINGERPRINT:a1b2c3` | None | error-collector | Cached known error state |
|
|
55
|
+
| `ISSUE_LOCK:{hash}` | `ISSUE_LOCK:a1b2c3` | 60s | error-collector | Race-condition dedup for GitHub issue creation |
|
|
56
|
+
| `TRANSIENT:{script}:{category}:{date}` | `TRANSIENT:my-worker:quota-exhausted:2026-03-02` | 24hr | error-collector | Transient error dedup window (1 issue/category/day) |
|
|
57
|
+
| `ERROR_RATE:{script}:{hour}` | `ERROR_RATE:my-worker:2026-03-02T14` | 2hr | error-collector | Hourly error rate counter |
|
|
58
|
+
|
|
59
|
+
## Sentinel (Standard+ tier)
|
|
60
|
+
|
|
61
|
+
| Key Pattern | Example | TTL | Worker | Purpose |
|
|
62
|
+
|---|---|---|---|---|
|
|
63
|
+
| `sentinel:project-gaps:result` | `sentinel:project-gaps:result` | 1hr | platform-sentinel | Cached gap detection results |
|
|
64
|
+
| `alert-thresholds:config` | `alert-thresholds:config` | None | platform-sentinel | Alert threshold configuration |
|
|
65
|
+
|
|
66
|
+
## Gap Alerts (Standard+ tier)
|
|
67
|
+
|
|
68
|
+
| Key Pattern | Example | TTL | Worker | Purpose |
|
|
69
|
+
|---|---|---|---|---|
|
|
70
|
+
| `GAP_ALERT:{project}:{date}` | `GAP_ALERT:my-app:2026-03-02` | 24hr | error-collector | Gap alert dedup (1 issue/project/day) |
|
|
71
|
+
|
|
72
|
+
## Pattern Discovery (Full tier)
|
|
73
|
+
|
|
74
|
+
| Key Pattern | Example | TTL | Worker | Purpose |
|
|
75
|
+
|---|---|---|---|---|
|
|
76
|
+
| `PATTERNS:DYNAMIC:APPROVED` | `PATTERNS:DYNAMIC:APPROVED` | None (refreshed) | pattern-discovery | Approved patterns JSON cache for error-collector runtime |
|
|
77
|
+
|
|
78
|
+
## Notifications (Full tier)
|
|
79
|
+
|
|
80
|
+
| Key Pattern | Example | TTL | Worker | Purpose |
|
|
81
|
+
|---|---|---|---|---|
|
|
82
|
+
| `NOTIFICATION_COUNT:{userId}` | `NOTIFICATION_COUNT:admin` | None | platform-notifications | Unread notification count |
|
|
83
|
+
| `NOTIFICATION_PREFS:{userId}` | `NOTIFICATION_PREFS:admin` | None | platform-notifications | User notification preferences |
|
|
84
|
+
| `NOTIFICATION_READ:{userId}:{notifId}` | `NOTIFICATION_READ:admin:abc123` | None | platform-notifications | Per-notification read state |
|
|
85
|
+
|
|
86
|
+
## Alert Dedup
|
|
87
|
+
|
|
88
|
+
| Key Pattern | Example | TTL | Worker | Purpose |
|
|
89
|
+
|---|---|---|---|---|
|
|
90
|
+
| `ALERT:{key}:last_sent` | `ALERT:my-app:scanner:last_sent` | None | platform-usage | Alert send dedup |
|
|
91
|
+
| `SLACK_ALERT:{type}:{key}` | `SLACK_ALERT:budget:my-app:scanner` | 1hr | shared/slack-alerts | Slack message dedup |
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Key Conventions
|
|
96
|
+
|
|
97
|
+
- **Uppercase prefixes** (`CONFIG:`, `FEATURE:`, `BUDGET_WARN:`) = system state
|
|
98
|
+
- **Lowercase prefixes** (`platform-usage:`, `sentinel:`) = operational cache
|
|
99
|
+
- **TTL = None** means the key persists until explicitly deleted or overwritten
|
|
100
|
+
- **Dedup keys** use short TTLs (60s-24hr) to prevent duplicate actions
|
|
101
|
+
- All keys use `:` as separator (never `/` or `.`)
|
|
@@ -193,6 +193,7 @@ CREATE TABLE IF NOT EXISTS daily_usage_rollups (
|
|
|
193
193
|
|
|
194
194
|
-- Vectorize metrics
|
|
195
195
|
vectorize_queries INTEGER DEFAULT 0,
|
|
196
|
+
vectorize_inserts INTEGER DEFAULT 0,
|
|
196
197
|
vectorize_vectors_stored_max INTEGER DEFAULT 0,
|
|
197
198
|
vectorize_cost_usd REAL DEFAULT 0,
|
|
198
199
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
-- Seed data: register your project in the platform
|
|
2
|
-
INSERT INTO project_registry (project_id, display_name, status,
|
|
3
|
-
VALUES ('{{projectSlug}}', '{{projectName}}', 'active', '
|
|
2
|
+
INSERT INTO project_registry (project_id, display_name, status, repo_path)
|
|
3
|
+
VALUES ('{{projectSlug}}', '{{projectName}}', 'active', '{{githubOrg}}/{{projectSlug}}')
|
|
4
4
|
ON CONFLICT (project_id) DO NOTHING;
|
|
@@ -36,10 +36,10 @@ const D1_DATABASE_NAME = 'YOUR_D1_DATABASE_NAME';
|
|
|
36
36
|
|
|
37
37
|
interface FeatureDefinition {
|
|
38
38
|
display_name: string;
|
|
39
|
-
feature_id?: string;
|
|
40
39
|
circuit_breaker: boolean;
|
|
41
40
|
description?: string;
|
|
42
41
|
cost_tier: string;
|
|
42
|
+
sources?: string[];
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
interface FeatureCategory {
|
|
@@ -49,36 +49,90 @@ interface FeatureCategory {
|
|
|
49
49
|
interface Project {
|
|
50
50
|
display_name: string;
|
|
51
51
|
status: string;
|
|
52
|
-
tier:
|
|
52
|
+
tier: number;
|
|
53
|
+
primary_resource?: string;
|
|
53
54
|
repository?: string;
|
|
55
|
+
runbook?: string;
|
|
56
|
+
infrastructure?: unknown;
|
|
54
57
|
features?: Record<string, FeatureCategory>;
|
|
55
58
|
}
|
|
56
59
|
|
|
57
60
|
interface Services {
|
|
58
|
-
metadata: {
|
|
61
|
+
metadata: {
|
|
62
|
+
version: string;
|
|
63
|
+
lastUpdated: string;
|
|
64
|
+
};
|
|
59
65
|
projects: Record<string, Project>;
|
|
60
66
|
}
|
|
61
67
|
|
|
62
68
|
interface BudgetLimit {
|
|
63
69
|
d1_writes?: number;
|
|
64
70
|
d1_reads?: number;
|
|
71
|
+
d1_rows_read?: number;
|
|
72
|
+
d1_rows_written?: number;
|
|
65
73
|
kv_reads?: number;
|
|
66
74
|
kv_writes?: number;
|
|
75
|
+
kv_deletes?: number;
|
|
76
|
+
kv_lists?: number;
|
|
77
|
+
r2_class_a?: number;
|
|
78
|
+
r2_class_b?: number;
|
|
79
|
+
ai_requests?: number;
|
|
80
|
+
ai_neurons?: number;
|
|
81
|
+
vectorize_queries?: number;
|
|
82
|
+
vectorize_inserts?: number;
|
|
67
83
|
queue_messages?: number;
|
|
84
|
+
do_requests?: number;
|
|
85
|
+
workflow_invocations?: number;
|
|
68
86
|
requests?: number;
|
|
69
87
|
cpu_ms?: number;
|
|
70
88
|
}
|
|
71
89
|
|
|
90
|
+
interface CircuitBreakerConfig {
|
|
91
|
+
enabled?: boolean;
|
|
92
|
+
auto_reset_seconds?: number;
|
|
93
|
+
cooldown_seconds?: number;
|
|
94
|
+
max_consecutive_trips?: number;
|
|
95
|
+
notes?: string;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
interface FeatureOverride {
|
|
99
|
+
hourly?: BudgetLimit;
|
|
100
|
+
daily?: BudgetLimit;
|
|
101
|
+
monthly?: BudgetLimit;
|
|
102
|
+
circuit_breaker?: CircuitBreakerConfig;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
interface GlobalLimits {
|
|
106
|
+
d1_write_limit?: number;
|
|
107
|
+
do_gb_seconds_daily_limit?: number;
|
|
108
|
+
}
|
|
109
|
+
|
|
72
110
|
interface Budgets {
|
|
111
|
+
metadata: {
|
|
112
|
+
version: string;
|
|
113
|
+
lastUpdated: string;
|
|
114
|
+
};
|
|
73
115
|
defaults: {
|
|
116
|
+
global_limits?: GlobalLimits;
|
|
74
117
|
daily: BudgetLimit;
|
|
75
118
|
circuit_breaker: {
|
|
76
119
|
auto_reset_seconds: number;
|
|
77
120
|
cooldown_seconds: number;
|
|
121
|
+
max_consecutive_trips: number;
|
|
122
|
+
};
|
|
123
|
+
error_budget?: {
|
|
124
|
+
error_rate_threshold: number;
|
|
125
|
+
window_minutes: number;
|
|
126
|
+
min_requests: number;
|
|
127
|
+
};
|
|
128
|
+
thresholds: {
|
|
129
|
+
warning: number;
|
|
130
|
+
critical: number;
|
|
78
131
|
};
|
|
79
|
-
thresholds: { warning: number; critical: number };
|
|
80
132
|
};
|
|
81
|
-
|
|
133
|
+
cost_tiers: Record<string, { multiplier: number; description: string }>;
|
|
134
|
+
project_budgets: Record<string, { allocation_percent: number; notes: string }>;
|
|
135
|
+
feature_overrides: Record<string, FeatureOverride>;
|
|
82
136
|
}
|
|
83
137
|
|
|
84
138
|
// =============================================================================
|
|
@@ -56,6 +56,9 @@ export interface PlatformSettings {
|
|
|
56
56
|
// Resource limits (daily)
|
|
57
57
|
d1WriteLimit: number;
|
|
58
58
|
doGbSecondsDailyLimit: number;
|
|
59
|
+
|
|
60
|
+
// Gap detection
|
|
61
|
+
gapCoverageThresholdPct: number;
|
|
59
62
|
}
|
|
60
63
|
|
|
61
64
|
/**
|
|
@@ -104,6 +107,9 @@ export const DEFAULT_PLATFORM_SETTINGS: PlatformSettings = {
|
|
|
104
107
|
// Resource limits (daily)
|
|
105
108
|
d1WriteLimit: 1_000_000, // 1M writes per 24h (adaptive sampling trigger)
|
|
106
109
|
doGbSecondsDailyLimit: 200_000, // 200K GB-seconds per 24h (~$2.50/day)
|
|
110
|
+
|
|
111
|
+
// Gap detection
|
|
112
|
+
gapCoverageThresholdPct: 90, // Per-project resource coverage threshold (percentage)
|
|
107
113
|
};
|
|
108
114
|
|
|
109
115
|
/**
|
|
@@ -128,6 +134,7 @@ export const SETTING_KEY_MAP: Record<string, keyof PlatformSettings> = {
|
|
|
128
134
|
error_rate_min_requests: 'errorRateMinRequests',
|
|
129
135
|
d1_write_limit: 'd1WriteLimit',
|
|
130
136
|
do_gb_seconds_daily_limit: 'doGbSecondsDailyLimit',
|
|
137
|
+
gap_coverage_threshold_pct: 'gapCoverageThresholdPct',
|
|
131
138
|
};
|
|
132
139
|
|
|
133
140
|
/**
|
|
@@ -244,7 +251,13 @@ export async function getPlatformSettings(env: SettingsEnv): Promise<PlatformSet
|
|
|
244
251
|
try {
|
|
245
252
|
const parsed = JSON.parse(cached) as Partial<PlatformSettings>;
|
|
246
253
|
// Merge with defaults to handle any missing keys
|
|
247
|
-
|
|
254
|
+
const merged = { ...DEFAULT_PLATFORM_SETTINGS, ...parsed };
|
|
255
|
+
// Defense-in-depth: floor critical limits to prevent stale/poisoned cache values
|
|
256
|
+
if (merged.d1WriteLimit < 1000)
|
|
257
|
+
merged.d1WriteLimit = DEFAULT_PLATFORM_SETTINGS.d1WriteLimit;
|
|
258
|
+
if (merged.doGbSecondsDailyLimit < 1000)
|
|
259
|
+
merged.doGbSecondsDailyLimit = DEFAULT_PLATFORM_SETTINGS.doGbSecondsDailyLimit;
|
|
260
|
+
return merged;
|
|
248
261
|
} catch {
|
|
249
262
|
// Invalid JSON, fall through to D1
|
|
250
263
|
}
|
|
@@ -404,4 +417,6 @@ export const EXPECTED_SETTINGS_KEYS = [
|
|
|
404
417
|
// Resource limits
|
|
405
418
|
'd1_write_limit',
|
|
406
419
|
'do_gb_seconds_daily_limit',
|
|
420
|
+
// Gap detection
|
|
421
|
+
'gap_coverage_threshold_pct',
|
|
407
422
|
];
|