@bongos/core 1.19.953 → 1.19.955
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/.bongos-core.json +49 -24
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +8 -0
- package/clients/bongos-client/index.cjs +8 -0
- package/clients/bongos-client/index.d.ts +13 -0
- package/clients/bongos-client/index.mjs +8 -0
- package/docs/adr/0325-the-managed-fleet-control-plane-is-the-hub-we-already-run.md +80 -0
- package/docs/adr/README.md +1 -0
- package/docs/api/openapi.json +334 -3
- package/docs/api-reference.md +6 -2
- package/docs/copy-inventory.md +156 -151
- package/docs/copy-registry.json +238 -192
- package/docs/module-api-changelog.md +4 -0
- package/modules/provisioning/env-manifest.js +215 -0
- package/modules/provisioning/migrations/provisioning_023_env_manifest.sql +67 -0
- package/modules/provisioning/module.json +20 -5
- package/modules/provisioning/routes/env-manifest.js +201 -0
- package/modules/public-landing/public/projects.html +157 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/src/module-api.js +1 -1
- package/tests/provisioning_env_manifest.mjs +435 -0
package/.bongos-core.json
CHANGED
|
@@ -2,22 +2,22 @@
|
|
|
2
2
|
"artifact": "bongos-core",
|
|
3
3
|
"manifest_schema": 1,
|
|
4
4
|
"generator": "scripts/gds/package-core.js",
|
|
5
|
-
"core_version": "1.19.
|
|
6
|
-
"core_contract": "1.19.
|
|
7
|
-
"source_commit": "
|
|
5
|
+
"core_version": "1.19.955",
|
|
6
|
+
"core_contract": "1.19.955",
|
|
7
|
+
"source_commit": "3595ab322a25adbfb15980ecfbf3277c095e0d4b",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-09-
|
|
9
|
+
"built_at": "2026-09-22T05:17:25.737Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
|
-
"docs_redacted":
|
|
12
|
+
"docs_redacted": 514,
|
|
13
13
|
"agent_docs_stubbed": 26,
|
|
14
|
-
"functional_verbatim":
|
|
14
|
+
"functional_verbatim": 2355,
|
|
15
15
|
"rules": 3,
|
|
16
16
|
"gate_literals": 3,
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
|
-
"file_count":
|
|
20
|
-
"tree_sha256": "
|
|
19
|
+
"file_count": 2895,
|
|
20
|
+
"tree_sha256": "cb656bb0b99f15d870988930f28520127a84bbc30f2c897df885ef8139504e4a",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/ask-for-help/SKILL.md",
|
|
@@ -347,12 +347,12 @@
|
|
|
347
347
|
{
|
|
348
348
|
"path": "clients/bongos-client/README.md",
|
|
349
349
|
"mode": "0000644",
|
|
350
|
-
"sha256": "
|
|
350
|
+
"sha256": "7d0b5d24baaf5f06e7fafe32a7c7cd1ea6c69746fb92c95a0a611ad776432cd8"
|
|
351
351
|
},
|
|
352
352
|
{
|
|
353
353
|
"path": "clients/bongos-client/bongos-client.global.js",
|
|
354
354
|
"mode": "0000644",
|
|
355
|
-
"sha256": "
|
|
355
|
+
"sha256": "71e5d4495bd293695bf42331bc20a6a3283fad8703663718209bd257953014f1"
|
|
356
356
|
},
|
|
357
357
|
{
|
|
358
358
|
"path": "clients/bongos-client/examples/hello-world.mjs",
|
|
@@ -362,17 +362,17 @@
|
|
|
362
362
|
{
|
|
363
363
|
"path": "clients/bongos-client/index.cjs",
|
|
364
364
|
"mode": "0000644",
|
|
365
|
-
"sha256": "
|
|
365
|
+
"sha256": "525d496a716e92467593447bcbb71369d6bdb817605631023d9b081cd75ce4c1"
|
|
366
366
|
},
|
|
367
367
|
{
|
|
368
368
|
"path": "clients/bongos-client/index.d.ts",
|
|
369
369
|
"mode": "0000644",
|
|
370
|
-
"sha256": "
|
|
370
|
+
"sha256": "f134e3ba8380879dae51531cd0867bbda06c5229a5d0dd8b29fa2e0f026490e5"
|
|
371
371
|
},
|
|
372
372
|
{
|
|
373
373
|
"path": "clients/bongos-client/index.mjs",
|
|
374
374
|
"mode": "0000644",
|
|
375
|
-
"sha256": "
|
|
375
|
+
"sha256": "2fe6fa6938205dce4bd1f48a3adc21b0898b78d3f125a9ee8c743497eaae6d22"
|
|
376
376
|
},
|
|
377
377
|
{
|
|
378
378
|
"path": "clients/bongos-client/package.json",
|
|
@@ -2114,20 +2114,25 @@
|
|
|
2114
2114
|
"mode": "0000644",
|
|
2115
2115
|
"sha256": "c1993ab3ca464c5aa7256301343e7b963914d085043638faa46c440e0511de08"
|
|
2116
2116
|
},
|
|
2117
|
+
{
|
|
2118
|
+
"path": "docs/adr/0325-the-managed-fleet-control-plane-is-the-hub-we-already-run.md",
|
|
2119
|
+
"mode": "0000644",
|
|
2120
|
+
"sha256": "cf6e701b8791462057fa5bc53dc716ef8f80b8ee2efa12a07581305f788a5e48"
|
|
2121
|
+
},
|
|
2117
2122
|
{
|
|
2118
2123
|
"path": "docs/adr/README.md",
|
|
2119
2124
|
"mode": "0000644",
|
|
2120
|
-
"sha256": "
|
|
2125
|
+
"sha256": "8d2b9a793fed7214d50fa667848b6c3037f129bff3e33113071bdbcd5acc34cf"
|
|
2121
2126
|
},
|
|
2122
2127
|
{
|
|
2123
2128
|
"path": "docs/api-reference.md",
|
|
2124
2129
|
"mode": "0000644",
|
|
2125
|
-
"sha256": "
|
|
2130
|
+
"sha256": "9b60667dcf534053345a0de7ced96b840718de0ac89c7a4099a5a2c1ec24f17f"
|
|
2126
2131
|
},
|
|
2127
2132
|
{
|
|
2128
2133
|
"path": "docs/api/openapi.json",
|
|
2129
2134
|
"mode": "0000644",
|
|
2130
|
-
"sha256": "
|
|
2135
|
+
"sha256": "dd53ef353bb82815a0d5de1959065ebcfd021584577257837990ec85ec8294b0"
|
|
2131
2136
|
},
|
|
2132
2137
|
{
|
|
2133
2138
|
"path": "docs/architecture.md",
|
|
@@ -2152,12 +2157,12 @@
|
|
|
2152
2157
|
{
|
|
2153
2158
|
"path": "docs/copy-inventory.md",
|
|
2154
2159
|
"mode": "0000644",
|
|
2155
|
-
"sha256": "
|
|
2160
|
+
"sha256": "ea08a06744310c312420c4af1e0f67154e324fe2be552b6d5c2fc683e18a63e8"
|
|
2156
2161
|
},
|
|
2157
2162
|
{
|
|
2158
2163
|
"path": "docs/copy-registry.json",
|
|
2159
2164
|
"mode": "0000644",
|
|
2160
|
-
"sha256": "
|
|
2165
|
+
"sha256": "45091c4acfe82fa8968be566f8b14f414ee3df68772b13617cba55735de93d1e"
|
|
2161
2166
|
},
|
|
2162
2167
|
{
|
|
2163
2168
|
"path": "docs/design/apex-pass-2-direction.md",
|
|
@@ -2687,7 +2692,7 @@
|
|
|
2687
2692
|
{
|
|
2688
2693
|
"path": "docs/module-api-changelog.md",
|
|
2689
2694
|
"mode": "0000644",
|
|
2690
|
-
"sha256": "
|
|
2695
|
+
"sha256": "e153e8b45c3cda248dfa42e233d2650353711b41650e7066287d94a43a347fc8"
|
|
2691
2696
|
},
|
|
2692
2697
|
{
|
|
2693
2698
|
"path": "docs/modules-contract.md",
|
|
@@ -6549,6 +6554,11 @@
|
|
|
6549
6554
|
"mode": "0000644",
|
|
6550
6555
|
"sha256": "29dea0e1c5b5f320bb4c03caab3d8834bc8797f86e63692536c58f36502c032b"
|
|
6551
6556
|
},
|
|
6557
|
+
{
|
|
6558
|
+
"path": "modules/provisioning/env-manifest.js",
|
|
6559
|
+
"mode": "0000644",
|
|
6560
|
+
"sha256": "a99596f5d3780a426ce9a5fcb287123dc860a04dfa08ce32a9ab81b2e06938ef"
|
|
6561
|
+
},
|
|
6552
6562
|
{
|
|
6553
6563
|
"path": "modules/provisioning/migrations/provisioning_001_tables.sql",
|
|
6554
6564
|
"mode": "0000644",
|
|
@@ -6659,10 +6669,15 @@
|
|
|
6659
6669
|
"mode": "0000644",
|
|
6660
6670
|
"sha256": "c00e4b64ebb875dc58a42d6b28ce3915ac03d0f22478610e031b87b5b9e435aa"
|
|
6661
6671
|
},
|
|
6672
|
+
{
|
|
6673
|
+
"path": "modules/provisioning/migrations/provisioning_023_env_manifest.sql",
|
|
6674
|
+
"mode": "0000644",
|
|
6675
|
+
"sha256": "1fb60b17c411d9529f4c0ecd0b3d1efc9b2061567a1b31bf74fa706e2e83b3d7"
|
|
6676
|
+
},
|
|
6662
6677
|
{
|
|
6663
6678
|
"path": "modules/provisioning/module.json",
|
|
6664
6679
|
"mode": "0000644",
|
|
6665
|
-
"sha256": "
|
|
6680
|
+
"sha256": "7b8f3cd100c15c8aaf16bf8d0d173993f4215cb58590a6e2f517a57bc099d240"
|
|
6666
6681
|
},
|
|
6667
6682
|
{
|
|
6668
6683
|
"path": "modules/provisioning/onboard-plan.js",
|
|
@@ -6724,6 +6739,11 @@
|
|
|
6724
6739
|
"mode": "0000644",
|
|
6725
6740
|
"sha256": "3969c5ace3225593a06baddaef7d7b92cb51fd18e34f2d290d4645fa517eecb8"
|
|
6726
6741
|
},
|
|
6742
|
+
{
|
|
6743
|
+
"path": "modules/provisioning/routes/env-manifest.js",
|
|
6744
|
+
"mode": "0000644",
|
|
6745
|
+
"sha256": "e7e85fb30fc6ac32b926902e5e064736ee65eb47ad90838f906f5fb21fbbec76"
|
|
6746
|
+
},
|
|
6727
6747
|
{
|
|
6728
6748
|
"path": "modules/provisioning/routes/provisioning.js",
|
|
6729
6749
|
"mode": "0000644",
|
|
@@ -7252,7 +7272,7 @@
|
|
|
7252
7272
|
{
|
|
7253
7273
|
"path": "modules/public-landing/public/projects.html",
|
|
7254
7274
|
"mode": "0000644",
|
|
7255
|
-
"sha256": "
|
|
7275
|
+
"sha256": "3465b462b09b49a93b4e6dcd4a2ec23753bf270b5a1a9d8d92f2013c7d2e3bf8"
|
|
7256
7276
|
},
|
|
7257
7277
|
{
|
|
7258
7278
|
"path": "modules/public-landing/public/projects.probes.json",
|
|
@@ -8052,12 +8072,12 @@
|
|
|
8052
8072
|
{
|
|
8053
8073
|
"path": "package-lock.json",
|
|
8054
8074
|
"mode": "0000644",
|
|
8055
|
-
"sha256": "
|
|
8075
|
+
"sha256": "4e5b09dee33c9b46fdc4e4e0a1025df50e79bdcad9ed6be9aee8d81a52d14953"
|
|
8056
8076
|
},
|
|
8057
8077
|
{
|
|
8058
8078
|
"path": "package.json",
|
|
8059
8079
|
"mode": "0000644",
|
|
8060
|
-
"sha256": "
|
|
8080
|
+
"sha256": "e8b75ca994f900a282dedd9e5c0589c151f3e8b0eab513e4c51b4ed254ab34f2"
|
|
8061
8081
|
},
|
|
8062
8082
|
{
|
|
8063
8083
|
"path": "public-docs/index.html",
|
|
@@ -10062,7 +10082,7 @@
|
|
|
10062
10082
|
{
|
|
10063
10083
|
"path": "src/module-api.js",
|
|
10064
10084
|
"mode": "0000644",
|
|
10065
|
-
"sha256": "
|
|
10085
|
+
"sha256": "a3909e0963c8f56e17bca596d30f27ee3e10100cefb055bf8a2f3506139a18ed"
|
|
10066
10086
|
},
|
|
10067
10087
|
{
|
|
10068
10088
|
"path": "src/module-loader/catalog.js",
|
|
@@ -13194,6 +13214,11 @@
|
|
|
13194
13214
|
"mode": "0000644",
|
|
13195
13215
|
"sha256": "d1c9456ad442caf2485c5c38e5bad98ef6c834589e405eade59cbeffec534ea0"
|
|
13196
13216
|
},
|
|
13217
|
+
{
|
|
13218
|
+
"path": "tests/provisioning_env_manifest.mjs",
|
|
13219
|
+
"mode": "0000644",
|
|
13220
|
+
"sha256": "1c6ea2d8ec4a8b3836a1976511ac3727fd158ac88db373efe89950bbadf4296f"
|
|
13221
|
+
},
|
|
13197
13222
|
{
|
|
13198
13223
|
"path": "tests/provisioning_github_app_routes.mjs",
|
|
13199
13224
|
"mode": "0000644",
|
|
@@ -5,7 +5,7 @@ A **generated**, zero-dependency typed client for the Bongos API — produced fr
|
|
|
5
5
|
by hand; it regenerates when the spec changes, so it can never drift from the routes.
|
|
6
6
|
|
|
7
7
|
- API version: **v1** (served at `/api/bongos/v1`)
|
|
8
|
-
-
|
|
8
|
+
- 409 operations across 61 resource groups
|
|
9
9
|
|
|
10
10
|
## Use it from your project
|
|
11
11
|
|
|
@@ -743,6 +743,14 @@ function createClient(opts = {}) {
|
|
|
743
743
|
postProvisioningInstancesIdDisconnect: (args) => request("POST", "/provisioning/instances/{id}/disconnect", { hasBody: true }, args),
|
|
744
744
|
// POST /provisioning/instances/{id}/domain — rank: any-builder — POST /provisioning/instances/:id/domain
|
|
745
745
|
postProvisioningInstancesIdDomain: (args) => request("POST", "/provisioning/instances/{id}/domain", { hasBody: true }, args),
|
|
746
|
+
// GET /provisioning/instances/{id}/env-manifest — rank: any-builder — GET /provisioning/instances/:id/env-manifest
|
|
747
|
+
getProvisioningInstancesIdEnvManifest: (args) => request("GET", "/provisioning/instances/{id}/env-manifest", { hasBody: false }, args),
|
|
748
|
+
// POST /provisioning/instances/{id}/env-manifest — rank: any-builder — POST /provisioning/instances/:id/env-manifest
|
|
749
|
+
postProvisioningInstancesIdEnvManifest: (args) => request("POST", "/provisioning/instances/{id}/env-manifest", { hasBody: true }, args),
|
|
750
|
+
// DELETE /provisioning/instances/{id}/env-manifest/{varId} — rank: any-builder — DELETE /provisioning/instances/:id/env-manifest/:varId
|
|
751
|
+
deleteProvisioningInstancesIdEnvManifestVarId: (args) => request("DELETE", "/provisioning/instances/{id}/env-manifest/{varId}", { hasBody: false }, args),
|
|
752
|
+
// PATCH /provisioning/instances/{id}/env-manifest/{varId} — rank: any-builder — PATCH /provisioning/instances/:id/env-manifest/:varId
|
|
753
|
+
patchProvisioningInstancesIdEnvManifestVarId: (args) => request("PATCH", "/provisioning/instances/{id}/env-manifest/{varId}", { hasBody: true }, args),
|
|
746
754
|
// POST /provisioning/instances/{id}/force-teardown — rank: metic+archon — POST /provisioning/instances/:id/force-teardown
|
|
747
755
|
postProvisioningInstancesIdForceTeardown: (args) => request("POST", "/provisioning/instances/{id}/force-teardown", { hasBody: true }, args),
|
|
748
756
|
// POST /provisioning/instances/{id}/github-app — rank: any-builder — POST /provisioning/instances/:id/github-app
|
|
@@ -742,6 +742,14 @@ function createClient(opts = {}) {
|
|
|
742
742
|
postProvisioningInstancesIdDisconnect: (args) => request("POST", "/provisioning/instances/{id}/disconnect", { hasBody: true }, args),
|
|
743
743
|
// POST /provisioning/instances/{id}/domain — rank: any-builder — POST /provisioning/instances/:id/domain
|
|
744
744
|
postProvisioningInstancesIdDomain: (args) => request("POST", "/provisioning/instances/{id}/domain", { hasBody: true }, args),
|
|
745
|
+
// GET /provisioning/instances/{id}/env-manifest — rank: any-builder — GET /provisioning/instances/:id/env-manifest
|
|
746
|
+
getProvisioningInstancesIdEnvManifest: (args) => request("GET", "/provisioning/instances/{id}/env-manifest", { hasBody: false }, args),
|
|
747
|
+
// POST /provisioning/instances/{id}/env-manifest — rank: any-builder — POST /provisioning/instances/:id/env-manifest
|
|
748
|
+
postProvisioningInstancesIdEnvManifest: (args) => request("POST", "/provisioning/instances/{id}/env-manifest", { hasBody: true }, args),
|
|
749
|
+
// DELETE /provisioning/instances/{id}/env-manifest/{varId} — rank: any-builder — DELETE /provisioning/instances/:id/env-manifest/:varId
|
|
750
|
+
deleteProvisioningInstancesIdEnvManifestVarId: (args) => request("DELETE", "/provisioning/instances/{id}/env-manifest/{varId}", { hasBody: false }, args),
|
|
751
|
+
// PATCH /provisioning/instances/{id}/env-manifest/{varId} — rank: any-builder — PATCH /provisioning/instances/:id/env-manifest/:varId
|
|
752
|
+
patchProvisioningInstancesIdEnvManifestVarId: (args) => request("PATCH", "/provisioning/instances/{id}/env-manifest/{varId}", { hasBody: true }, args),
|
|
745
753
|
// POST /provisioning/instances/{id}/force-teardown — rank: metic+archon — POST /provisioning/instances/:id/force-teardown
|
|
746
754
|
postProvisioningInstancesIdForceTeardown: (args) => request("POST", "/provisioning/instances/{id}/force-teardown", { hasBody: true }, args),
|
|
747
755
|
// POST /provisioning/instances/{id}/github-app — rank: any-builder — POST /provisioning/instances/:id/github-app
|
|
@@ -13,6 +13,7 @@ export interface DeleteGovernmentAssignmentsBuilderIdRankKeyResponse { ok: boole
|
|
|
13
13
|
export interface DeleteGovernmentRanksRankKeyPermissionsPermissionKeyResponse { ok: boolean; rank_key: unknown; permission_key: unknown; removed: unknown }
|
|
14
14
|
export interface DeleteInboxIdSparkResponse { idea: unknown; sparked: boolean; unchanged: boolean }
|
|
15
15
|
export interface DeleteMemoryFileResponse { deleted: boolean; builder_id: unknown; path: unknown }
|
|
16
|
+
export interface DeleteProvisioningInstancesIdEnvManifestVarIdResponse { ok: boolean; removed: boolean; message: unknown }
|
|
16
17
|
export interface DeleteSpecialitiesIdDocumentsDocIdResponse { ok: boolean }
|
|
17
18
|
export interface DeleteTasksIdCriteriaCriterionIdResponse { ok: boolean; removed: unknown }
|
|
18
19
|
export interface DeleteTasksIdDependenciesDepIdResponse { ok: boolean; removed: unknown }
|
|
@@ -107,6 +108,7 @@ export interface GetProjectsResponse { projects: unknown }
|
|
|
107
108
|
export interface GetProvisioningFleetResponse { instances: unknown; capacity: unknown }
|
|
108
109
|
export interface GetProvisioningGithubAppStatusResponse { ok: boolean; status: unknown; ready: unknown; terminal: unknown; domain: unknown; instance_status: unknown; app_slug: unknown }
|
|
109
110
|
export interface GetProvisioningHealthResponse { module: unknown; ok: boolean; coreVersion: unknown }
|
|
111
|
+
export interface GetProvisioningInstancesIdEnvManifestResponse { instance: unknown; env_vars: unknown; environments: unknown; values_held: boolean; note: unknown }
|
|
110
112
|
export interface GetProvisioningInstancesIdInviteSuggestionsResponse { suggested: unknown; seeking: unknown; reason: unknown; unavailable?: unknown }
|
|
111
113
|
export interface GetProvisioningInstancesIdResponse { instance: unknown; open_intent: unknown; github_app: unknown }
|
|
112
114
|
export interface GetProvisioningInstancesIdSettingsResponse { ok: boolean; settings: unknown; planet: unknown; options: unknown }
|
|
@@ -197,6 +199,8 @@ export interface PatchProjectsIdStatusRequest { status: string }
|
|
|
197
199
|
export interface PatchProjectsIdStatusResponse { project: unknown }
|
|
198
200
|
export interface PatchProvisioningInstancesIdDetailRequest { description?: string; detail?: Record<string, unknown>; modules?: string[]; type?: string }
|
|
199
201
|
export interface PatchProvisioningInstancesIdDetailResponse { ok: boolean; description: unknown; detail: unknown; options: unknown; publish: unknown; modules: unknown; type: unknown }
|
|
202
|
+
export interface PatchProvisioningInstancesIdEnvManifestVarIdRequest { name?: string; description?: string; environments?: unknown[]; required?: boolean }
|
|
203
|
+
export interface PatchProvisioningInstancesIdEnvManifestVarIdResponse { ok: boolean; env_var: unknown; values_held: boolean }
|
|
200
204
|
export interface PatchProvisioningInstancesIdSettingsResponse { ok: boolean; saved: unknown; planet: unknown; push: unknown }
|
|
201
205
|
export interface PatchSpecialitiesIdRequest { name?: string; summary?: string; contract_md?: string; visibility?: string; sellable?: boolean }
|
|
202
206
|
export interface PatchSpecialitiesIdResponse { speciality: unknown }
|
|
@@ -368,6 +372,7 @@ export interface PostProvisioningInstancesIdDisconnectRequest { confirm_slug: st
|
|
|
368
372
|
export interface PostProvisioningInstancesIdDisconnectResponse { ok: boolean; queued: unknown; action: unknown; status: unknown; message: unknown }
|
|
369
373
|
export interface PostProvisioningInstancesIdDomainRequest { domain: string }
|
|
370
374
|
export interface PostProvisioningInstancesIdDomainResponse { ok: boolean; changed: boolean; queued: unknown; domain: unknown; message: unknown; replaced?: unknown }
|
|
375
|
+
export interface PostProvisioningInstancesIdEnvManifestRequest { name: string; description?: string; environments?: unknown[]; required?: boolean }
|
|
371
376
|
export interface PostProvisioningInstancesIdForceTeardownResponse { ok: boolean; queued: unknown; action: unknown; status: unknown; superseded: unknown; message: unknown }
|
|
372
377
|
export interface PostProvisioningInstancesIdGithubAppRequest { account?: string }
|
|
373
378
|
export interface PostProvisioningInstancesIdGithubAppResponse { state: unknown; account: unknown; post_url: unknown; personal_url: unknown; org_url: unknown; org_login: unknown; manifest: unknown; redirect_url: unknown; expires_at: unknown }
|
|
@@ -1119,6 +1124,14 @@ export interface BongosClient {
|
|
|
1119
1124
|
postProvisioningInstancesIdDisconnect(args: RequestArgs & { body: PostProvisioningInstancesIdDisconnectRequest }): Promise<PostProvisioningInstancesIdDisconnectResponse>;
|
|
1120
1125
|
/** POST /provisioning/instances/{id}/domain — rank: any-builder */
|
|
1121
1126
|
postProvisioningInstancesIdDomain(args: RequestArgs & { body: PostProvisioningInstancesIdDomainRequest }): Promise<PostProvisioningInstancesIdDomainResponse>;
|
|
1127
|
+
/** GET /provisioning/instances/{id}/env-manifest — rank: any-builder */
|
|
1128
|
+
getProvisioningInstancesIdEnvManifest(args?: RequestArgs): Promise<GetProvisioningInstancesIdEnvManifestResponse>;
|
|
1129
|
+
/** POST /provisioning/instances/{id}/env-manifest — rank: any-builder */
|
|
1130
|
+
postProvisioningInstancesIdEnvManifest(args: RequestArgs & { body: PostProvisioningInstancesIdEnvManifestRequest }): Promise<ApiResponse>;
|
|
1131
|
+
/** DELETE /provisioning/instances/{id}/env-manifest/{varId} — rank: any-builder */
|
|
1132
|
+
deleteProvisioningInstancesIdEnvManifestVarId(args?: RequestArgs): Promise<DeleteProvisioningInstancesIdEnvManifestVarIdResponse>;
|
|
1133
|
+
/** PATCH /provisioning/instances/{id}/env-manifest/{varId} — rank: any-builder */
|
|
1134
|
+
patchProvisioningInstancesIdEnvManifestVarId(args?: RequestArgs & { body?: PatchProvisioningInstancesIdEnvManifestVarIdRequest }): Promise<PatchProvisioningInstancesIdEnvManifestVarIdResponse>;
|
|
1122
1135
|
/** POST /provisioning/instances/{id}/force-teardown — rank: metic+archon */
|
|
1123
1136
|
postProvisioningInstancesIdForceTeardown(args?: RequestArgs): Promise<PostProvisioningInstancesIdForceTeardownResponse>;
|
|
1124
1137
|
/** POST /provisioning/instances/{id}/github-app — rank: any-builder */
|
|
@@ -739,6 +739,14 @@ export function createClient(opts = {}) {
|
|
|
739
739
|
postProvisioningInstancesIdDisconnect: (args) => request("POST", "/provisioning/instances/{id}/disconnect", { hasBody: true }, args),
|
|
740
740
|
// POST /provisioning/instances/{id}/domain — rank: any-builder — POST /provisioning/instances/:id/domain
|
|
741
741
|
postProvisioningInstancesIdDomain: (args) => request("POST", "/provisioning/instances/{id}/domain", { hasBody: true }, args),
|
|
742
|
+
// GET /provisioning/instances/{id}/env-manifest — rank: any-builder — GET /provisioning/instances/:id/env-manifest
|
|
743
|
+
getProvisioningInstancesIdEnvManifest: (args) => request("GET", "/provisioning/instances/{id}/env-manifest", { hasBody: false }, args),
|
|
744
|
+
// POST /provisioning/instances/{id}/env-manifest — rank: any-builder — POST /provisioning/instances/:id/env-manifest
|
|
745
|
+
postProvisioningInstancesIdEnvManifest: (args) => request("POST", "/provisioning/instances/{id}/env-manifest", { hasBody: true }, args),
|
|
746
|
+
// DELETE /provisioning/instances/{id}/env-manifest/{varId} — rank: any-builder — DELETE /provisioning/instances/:id/env-manifest/:varId
|
|
747
|
+
deleteProvisioningInstancesIdEnvManifestVarId: (args) => request("DELETE", "/provisioning/instances/{id}/env-manifest/{varId}", { hasBody: false }, args),
|
|
748
|
+
// PATCH /provisioning/instances/{id}/env-manifest/{varId} — rank: any-builder — PATCH /provisioning/instances/:id/env-manifest/:varId
|
|
749
|
+
patchProvisioningInstancesIdEnvManifestVarId: (args) => request("PATCH", "/provisioning/instances/{id}/env-manifest/{varId}", { hasBody: true }, args),
|
|
742
750
|
// POST /provisioning/instances/{id}/force-teardown — rank: metic+archon — POST /provisioning/instances/:id/force-teardown
|
|
743
751
|
postProvisioningInstancesIdForceTeardown: (args) => request("POST", "/provisioning/instances/{id}/force-teardown", { hasBody: true }, args),
|
|
744
752
|
// POST /provisioning/instances/{id}/github-app — rank: any-builder — POST /provisioning/instances/:id/github-app
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# ADR 0325 — The managed fleet control plane is the hub we already run
|
|
2
|
+
|
|
3
|
+
- **Status:** accepted
|
|
4
|
+
- **Date:** 2026-09-22
|
|
5
|
+
- **Task:** [task 1001948](https://cloudbongos.com/builders#/task/1001948) (goal 1000106 — working area 1, Project creation)
|
|
6
|
+
- **Deciders:** the owner's decisions are [ADR 0323](0323-hosting-is-three-shapes-and-we-are-not-the-landlord.md)'s. Claude ran the inventory, and decisions 1–4 and 6 follow from that inventory plus 0323 without a new owner call — each one retires a plan rather than choosing between options. **Decision 5 is a recommendation, not a ruling**, and says so; it is filed for the owner rather than acted on.
|
|
7
|
+
- **Supersedes in part:** [ADR 0111](0111-instance-hosting-provisioning-module.md) §5/§7 (the "managed layer is greenfield, deferred, spec-first" sequencing)
|
|
8
|
+
- **Depends on:** [ADR 0323](0323-hosting-is-three-shapes-and-we-are-not-the-landlord.md) (the shapes and the platform this operates)
|
|
9
|
+
- **Gates:** [task 1004142](https://cloudbongos.com/builders#/task/1004142) (the platform leg) · [task 1001949](https://cloudbongos.com/builders#/task/1001949) (metering) · [task 1001621](https://cloudbongos.com/builders#/task/1001621) (the spend rollup)
|
|
10
|
+
- **Filed by this ADR:** [task 1004149](https://cloudbongos.com/builders#/task/1004149) (the $0 cost ledger — decision 6) · [task 1004150](https://cloudbongos.com/builders#/task/1004150) (the subscription roster — decision 5)
|
|
11
|
+
|
|
12
|
+
## Context
|
|
13
|
+
|
|
14
|
+
Task 1001948 was written against [ADR 0111](0111-instance-hosting-provisioning-module.md) §5, which described the managed offering as *self-host plus (a) the provisioning module pointed at Cloud Bongos's own infra and (b) a central operator driving the existing `bongos upgrade` channel*, and §7, which deferred it as **"genuinely greenfield (no fleet registry exists)"** behind two gates: R85 (Mercury, the first real second-repo consumer) and R86 (a real `bongos upgrade`).
|
|
15
|
+
|
|
16
|
+
Both gates are shipped. The premise is not.
|
|
17
|
+
|
|
18
|
+
**Measuring the call sites first changed the answer, as it did for [task 1004141](https://cloudbongos.com/builders#/task/1004141).** A fleet registry exists, an operator exists, and a fleet update channel exists. Naming them is most of this ADR's content, because a spec that designs what is already running is worse than no spec: it invites a second implementation of each leg.
|
|
19
|
+
|
|
20
|
+
### What already exists
|
|
21
|
+
|
|
22
|
+
| ADR 0111 §5 called it | It is | Where |
|
|
23
|
+
|---|---|---|
|
|
24
|
+
| "a central fleet control plane — greenfield; sits *above* per-instance GDS DBs" | **the hub's own database.** `provisioning_instances` is the fleet registry: one row per instance with its owner, slug, shape, domain, database, port, tier, lifecycle status, cost estimate, onboard mode, settings, module selection, the liveness overlay and the served/available core-version snapshot. `provisioning_intents` is the work queue, `provisioning_events` the audit trail. The control plane has a row of its own, enrolled by `scripts/gds/enroll-control-plane.js` ([ADR 0293](0293-the-owners-deploy-door-is-an-intent-the-control-plane-drains.md) D2), so the operator is *inside* the fleet it operates rather than above it | `modules/provisioning/migrations/` |
|
|
25
|
+
| "the operator" | **`scripts/gds/provision.js`** — the single token-holder that drains the intent queue, with an explicit per-action dispatch (`provision`, `teardown`, `domain-attach`, `settings-apply`, `restart`, `disconnect`, `core-upgrade`, `core-upgrade-preflight`) and a refusal for any action it does not recognise | ADR 0111 §2 |
|
|
26
|
+
| "fleet oversight" | **`GET /provisioning/fleet`** (the roster plus the box's slot capacity) and **`GET /provisioning/fleet/cost-ledger`** (per-instance and total monthly estimate), both gated on `provisioning.fleet.manage`, the same atom as force-teardown | `modules/provisioning/routes/provisioning.js` |
|
|
27
|
+
| "operator drives `bongos upgrade` across the fleet (same channel)" | **two channels, both shipped.** The owner's per-instance door ([ADR 0293](0293-the-owners-deploy-door-is-an-intent-the-control-plane-drains.md)) previews a target version and drives the proven `bongos upgrade` path with its health gate and auto-rollback; the unattended patch-only sweep (`core-update-subscription`, [ADR 0136](0136-update-channel-subscription-policy.md)) takes the newest published patch for each opted-in instance every 15 minutes | `scripts/gds/provision-core-upgrade.js`, `.claude/scheduled-tasks/core-update-subscription/subscribe.js` |
|
|
28
|
+
| — (not in §5) | **observation**: a ~5-minute liveness sweep that probes each active instance and writes only its overlay columns, with edge-triggered Discord notices, plus a fleet-wide core-version snapshot after every drain | `modules/provisioning/pollers/` |
|
|
29
|
+
|
|
30
|
+
The gates §7 named have both been paid: Mercury proved the two-repo path, and `bongos upgrade` is not only real but is what the deploy door drives.
|
|
31
|
+
|
|
32
|
+
## Decision
|
|
33
|
+
|
|
34
|
+
### 1. There is no greenfield fleet control plane to design, and none will be built
|
|
35
|
+
|
|
36
|
+
The hub *is* the fleet control plane. The registry is its own table, the operator is its own runner, and the trust boundary between them is [ADR 0111](0111-instance-hosting-provisioning-module.md) §2, unchanged: the internet-facing web tier only ever enqueues an intent and holds no cloud token.
|
|
37
|
+
|
|
38
|
+
This retires ADR 0111 §5's "sits *above* per-instance GDS DBs" framing. It never needed to sit above them: [ADR 0100](0100-two-repo-instance-model.md) §3 locks each instance's database to its own repo, and the operator has never needed to read one. It needs the roster, which is a hub table, and a shell or an API on the platform, which is the runner's.
|
|
39
|
+
|
|
40
|
+
### 2. The one thing genuinely missing is a platform leg, and it is task 1004142's
|
|
41
|
+
|
|
42
|
+
After [ADR 0323](0323-hosting-is-three-shapes-and-we-are-not-the-landlord.md), a `cloud-host` is stood up on Render, and `grep -rl 'RENDER_API\|render\.com' scripts modules src` returns nothing: every standup leg the runner has writes a systemd unit, a unix account, a Caddy snippet or a `createdb`. That is the whole of the managed layer's residue, it is already scoped, and **this ADR deliberately specifies none of it** — a second spec for one implementation is how two drift.
|
|
43
|
+
|
|
44
|
+
### 3. The fleet the operator DRIVES and the fleet it OBSERVES are different populations
|
|
45
|
+
|
|
46
|
+
`byo-host` is in the registry and is not operated: nothing is pushed to it, and its liveness is the [ADR 0252](0252-the-self-hosted-visibility-report-is-a-pull-and-auto-appear-buys-a-retraction-lever.md) pull against an owner-supplied origin. It is already excluded from settings-apply and restart for the same reason `dedicated` was, and [task 1004141](https://cloudbongos.com/builders#/task/1004141) made the provision leg a no-op for it.
|
|
47
|
+
|
|
48
|
+
**Every operator surface states which population it is counting.** A roster that silently mixes them reports a fleet-wide upgrade as complete while an unknown number of instances were never asked, which is indistinguishable from success.
|
|
49
|
+
|
|
50
|
+
### 4. The operator holds exactly one credential, and it is its own
|
|
51
|
+
|
|
52
|
+
[ADR 0323](0323-hosting-is-three-shapes-and-we-are-not-the-landlord.md) D6 settled that Bongos stores environment-variable NAMES and never values, so that it is not the custodian of other companies' credentials. That rule is about the *customer's* secrets and is unchanged here.
|
|
53
|
+
|
|
54
|
+
The managed layer needs one credential of its own — the platform account token that creates a `cloud-host` — and it belongs where every other operator token already lives: root-only on the control plane (`/etc/cloudbongos/box.env`), read by the runner, never by the web tier, never in the database, and never per-customer. A stolen builder token still cannot spin anything up; it can only ask.
|
|
55
|
+
|
|
56
|
+
### 5. The subscription roster should move into the registry — recorded, not built here
|
|
57
|
+
|
|
58
|
+
Every fleet fact lives in `provisioning_instances` except one: which instances take unattended patch upgrades, which lives in `config/update-subscriptions.json`, a file that ships empty and is edited on the box. It is the operator's own arming switch and works, but it is the only fleet fact the roster route cannot report and the only one a restored backup can lose independently of the rows it describes.
|
|
59
|
+
|
|
60
|
+
This ADR records the direction and files it as [task 1004150](https://cloudbongos.com/builders#/task/1004150) rather than building it, because it is a migration plus a route change on a surface [task 1004142](https://cloudbongos.com/builders#/task/1004142) is about to rewrite.
|
|
61
|
+
|
|
62
|
+
### 6. Metering is now real work, and the cost ledger is currently wrong
|
|
63
|
+
|
|
64
|
+
`costEstimateUsd` returns **0** for `standalone` — correct when a project was a process on a box we already paid for, and wrong the moment it becomes a ~$14/month Render service. Until it is fixed the fleet cost ledger reports \$0 for every project the platform is about to be billed for, and it reports it with no error.
|
|
65
|
+
|
|
66
|
+
That is a defect, not a deferral, and it is filed as [task 1004149](https://cloudbongos.com/builders#/task/1004149). The metering and invoicing track ([task 1001949](https://cloudbongos.com/builders#/task/1001949)) and the spend rollup ([task 1001621](https://cloudbongos.com/builders#/task/1001621)) stay separate work, as [ADR 0323](0323-hosting-is-three-shapes-and-we-are-not-the-landlord.md) already says.
|
|
67
|
+
|
|
68
|
+
## Consequences
|
|
69
|
+
|
|
70
|
+
- **1001948 closes as a spec that deletes most of its own scope.** The honest output of a spike whose premise was stale is the inventory that retires it, plus the named residue — not a design for the four legs that were already running.
|
|
71
|
+
- **No fleet-wide "upgrade everything" button.** Both existing channels are per-instance by construction: the owner's door moves one pin with a health gate and auto-rollback, and the patch sweep walks the opted-in roster one at a time and refuses to overlap runs. A single action that moves the whole fleet is the shape most likely to take every customer down at once, and nothing about the managed offering needs it.
|
|
72
|
+
- **The hub is the single point of failure, visibly.** With projects on Render, what is lost with the droplet is the control plane's own database — which is what makes offsite backups ([task 1002214](https://cloudbongos.com/builders#/task/1002214)) the highest-consequence open item in this goal, exactly as ADR 0323 said.
|
|
73
|
+
- **AGPL posture unchanged.** [ADR 0111](0111-instance-hosting-provisioning-module.md) §5's one-sentence rule survives its own table: self-host and managed run the same artifact and the same module, and the only differences are whose credentials the operator holds and who operates it. Nothing here is an open-core layer.
|
|
74
|
+
|
|
75
|
+
## Alternatives considered
|
|
76
|
+
|
|
77
|
+
- **Build the separate fleet control plane ADR 0111 §5 described.** Rejected: it would be a second registry beside `provisioning_instances` and a second operator beside `provision.js`, with the same rows in both and no mechanism keeping them equal. The measurement is the argument — every leg §5 listed already has an owner named in the table above.
|
|
78
|
+
- **Spec the Render integration here.** Rejected as duplicate scope. [Task 1004142](https://cloudbongos.com/builders#/task/1004142) carries it with those files open; a spec written a task earlier would be read as authority over code it never saw.
|
|
79
|
+
- **Read per-instance databases from the hub for fleet reporting.** Rejected. [ADR 0100](0100-two-repo-instance-model.md) §3's lock is what makes an instance's data its own, and the operator has never needed it: the roster, the intents and the liveness overlay answer every question the fleet view asks, from the hub's own tables.
|
|
80
|
+
- **Keep deferring the whole managed layer** (ADR 0111 §7's posture). Rejected: its two gates are shipped, and the deferral is now the reason the cost ledger reads \$0 for a fleet about to cost real money.
|
package/docs/adr/README.md
CHANGED
|
@@ -416,3 +416,4 @@ This keeps the decision history honest and traceable.
|
|
|
416
416
|
| 0322 | [**The five states are derived once, and a remnant needs evidence** ([task 1004103](https://cloudbongos.com/builders#/task/1004103), goal 1000095 — BONGOS-V2). The ideator surface draws a thought as one of five objects — star, protostar, satellite, remnant, cold body — and three surfaces are about to draw them (the room, the descent, the sky). Every fact already existed: `idea_inbox.status` (`core_195`), [ADR 0321](0321-brightness-is-a-weighted-share-and-the-targets-are-a-row.md)’s `hasWork` / `share` / `attributable`, and `tasks.goal_id` + `tasks.status`. What was missing was ONE place that reads them: a state inferred in three renderers is three states, and the sky can say a thought went out while the room beside it still calls it alive, with nothing erroring. **Decision:** one pure resolver (`modules/ideas/idea-state.js`), brightness exposed on the `reward` port (ideas may not reach `credit_log` any other way — [ADR 0083](0083-modular-architecture-strangler-build-plan.md) rule 1 — and already declares `reward` in `consumes`), and the ORDER of the tests IS the decision. **MERGED wins over everything:** a folded thought keeps its own money (`trail-size.js` is emphatic) but is no longer its own point in the sky, so letting work win draws one idea twice, beside the thought that absorbed it. **WORK beats a DISCARD:** a cold body “never ignited, so it has no size at all” is a stated PROPERTY, and testing work first makes it true by construction — triage really can discard a duplicate whose twin already routed and paid, and `updateIdea` never touches the ledger. **A REMNANT needs EVIDENCE, never the absence of an answer:** `share: 0` is a confirmed removal, `share: null` is an unreadable one, and ADR 0321 pays for that distinction upstream — so an idea with work and no readable facts is a STAR carrying `attributable: false`, because reading a falsy share as death turns one unmounted module into a sky of hollow rings. **A PROTOSTAR is the absence of WORK, not of a status:** ignition is being PAID for something, so a promoted-but-unshipped idea is still unignited, which is exactly what the nod queue exists for. **Position is a second axis** (`well` / `open-space` / `awaitingNod`) because the design says the object alone cannot carry it, and the nod is asked of EVERY task, not only the one that named the well. **`suggested_goal_id` is deliberately NOT a well** — `projection.js` records it as strictly advisory, the routing hint on a filing that did not convert, so reading it would put a thought in a nod queue with no task to promote; the binding `tasks.goal_id` now rides on both of the trail’s task joins at no extra round trip. The page reads the ledger ONCE: the state attach hands economy back the `byTask` breakdown size just computed, all of them or none (a partial map reads a missing entry as “no work” and draws a paid star as a protostar). An absent `reward` port does not empty the sky — states still resolve, flagged unattributable. Nothing writes; ADR 0313 stands. Rejected: resolving in each renderer; deriving from `share` alone (it merges a protostar with a remnant, the exact BV2.R19 distinction); treating an unreadable share as zero; status-first ordering; reading the hint as a home; a sixth object for “unknown brightness” (the vocabulary is the owner’s and is five — the uncertainty is a flag on the answer, not a new thing in the sky).](0322-a-remnant-needs-evidence-and-merged-wins.md) | ideas / economy |
|
|
417
417
|
| 0323 | [**Hosting is three shapes, and we are not the landlord** ([task 1004140](https://cloudbongos.com/builders#/task/1004140), goal 1000106 — BONGOS-V2). Five projects ran as one Node process each on the control-plane droplet, about $0.80/month apiece — the cheapest arrangement available, bought with ~4,600 lines of `provision*.js` that write systemd units, create unix accounts, render Caddy snippets, `createdb`, install backup timers and count memory against a ceiling. That is the cost nobody priced: **we are a landlord.** The owner reframed the question — build on what others maintain, and let an owner keep the deployment platform they already use. **Seven decisions.** The shapes become `byo-host` (the owner runs it), `cloud-host` (we stand it up for them) and `dedicated-cloud-host` (the same path, larger plan); `co-tenant` is **retired, not renamed**, because it means *a process on our box from our checkout* — the opposite of bring-your-own-host, so reusing the name would make every existing comment lie. **DigitalOcean hosts hubs only**, which closes [ADR 0145](0145-free-hosted-project-tier-isolation-and-domain-separation.md) Finding 1 by removing its precondition rather than hardening the cookie: with no customer code on a `cloudbongos.com` subdomain there is no tenant to receive the hub session. **Render is the default** — its Postgres carries PITR and automated backups on every paid tier; Fly's managed Postgres starts ~$38/mo and Railway's is explicitly unmanaged, which is the exact responsibility being shed. **Cloudflare is the front door, not the compute**: Workers is a V8-isolate runtime that would require rewriting Express and `pg`, and Containers is built for bursty workloads that sleep, which a hall with background sweeps and a live event stream does not survive. **No deployment-platform adapter is built at all** — their application already deploys itself from the repository, so the platform we do not talk to is the platform we do not have to add. **Secrets: the platform is the system of record and Bongos stores names, never values** — a manifest of name, purpose and environment, no application secret, no platform API token, no "is it set?" probe, because each of those makes us custodian of other companies' credentials for convenience the manifest already delivers; `byo-host` marks the manifest unverifiable rather than opening an exception. **The free tier becomes a capped trial**, since ~$14/project cannot be subsidised indefinitely. Per-project cost rises ~17×, which is the price of not being a landlord and the reason metering stops being deferred work. Mostly a DELETION: [ADR 0306](0306-instance-supervision-is-a-seam-with-systemd-as-the-default.md)'s supervision seam is not extended (Render supervises its own processes), [ADR 0285](0285-a-shared-box-holds-about-twelve-projects-per-gb-and-memory-is-the-wall.md) stays true and stops being load-bearing with its queued resize held rather than spent, and the hub's offsite backup becomes the highest-consequence open item. Rejected: AWS/GCP (2–3× the cost for a bare instance, plus an IAM and billing surface to learn — maintenance added where the goal is to remove it); keeping the shared box and fixing only the cookie (cheaper, closes the finding, leaves the landlord runner and answers nothing else); a Vercel/Netlify/Render adapter layer (speculative pluggability); Bongos as the secrets vault pushing values through platform APIs.](0323-hosting-is-three-shapes-and-we-are-not-the-landlord.md) | hosting / provisioning / cost |
|
|
418
418
|
| 0324 | [**The public npm CLI channel is retired** ([task 1004144](https://cloudbongos.com/builders#/task/1004144), goal 1000091 — BONGOS-V2). The owner deleted `@cloudbongos/cli` from the registry on 2026-09-22 (`npm view` → E404; the `@cloudbongos` scope is empty). This supersedes [ADR 0258](0258-the-public-cli-is-a-generated-client-package-not-the-published-core.md) and [ADR 0292](0292-both-install-paths-stay-and-vendoring-is-the-production-one.md) §2 — the latter titled "`@cloudbongos/cli` STAYS, on a new justification" and written *because* task 1003872 proposed removing it once the public core flip dissolved its original reason. 0292's measured argument (~0.5 MB front door vs a 46 MB core pull to print a task list) is still true and is NOT what changed; the owner's decision about carrying a public channel at all is. `scripts/gds/build-cli-package.js` + `tests/cli_package.mjs` are deleted, no doc offers `npx @cloudbongos/cli`, and the way in is the hall at `<origin>/builders` or a checkout. The COMPILED binary channel (`build-bongos.js` → `/downloads/bongos/*` via `config/bongos-cli.json`) is a different artifact and is untouched — the same near-twin task 1003889 warned about. Three tests used the package's FILES manifest as a stand-in for "what a terminal user can run" and now read `bin/bongos.js`'s verb table, which is what that set always meant.](0324-the-public-npm-cli-channel-is-retired.md) | distribution / CLI |
|
|
419
|
+
| 0325 | [**The managed fleet control plane is the hub we already run** ([task 1001948](https://cloudbongos.com/builders#/task/1001948), goal 1000106 — BONGOS-V2). The spike was written against [ADR 0111](0111-instance-hosting-provisioning-module.md) §5/§7, which deferred the managed offering as **"genuinely greenfield (no fleet registry exists)"** behind two gates — Mercury and a real `bongos upgrade`. Both gates are shipped and **the premise is not**: measuring the call sites first changed the answer, as it did for [task 1004141](https://cloudbongos.com/builders#/task/1004141). The registry is `provisioning_instances` (owner, shape, domain, database, port, lifecycle, cost estimate, module selection, the liveness overlay and the served/available core-version snapshot), with `provisioning_intents` as the queue and `provisioning_events` as the audit trail; the operator is `scripts/gds/provision.js`, the single token-holder draining that queue behind ADR 0111 §2's trust boundary; oversight is `GET /provisioning/fleet` and `/fleet/cost-ledger`; and the update channel is not one mechanism but two shipped ones — the owner's per-instance deploy door ([ADR 0293](0293-the-owners-deploy-door-is-an-intent-the-control-plane-drains.md), health-gated with auto-rollback) and the opt-in patch-only sweep ([ADR 0136](0136-update-channel-subscription-policy.md)). **Decision: there is no greenfield fleet control plane to design, and none will be built — the hub IS it.** It never needed to sit *above* the per-repo databases [ADR 0100](0100-two-repo-instance-model.md) §3 locks: the roster is a hub table and the operator has never read one. The genuinely missing leg is a **platform leg** — `grep -rl RENDER_API scripts modules src` returns nothing, and after [ADR 0323](0323-hosting-is-three-shapes-and-we-are-not-the-landlord.md) a `cloud-host` is a Render service — and this ADR **deliberately specifies none of it**, because that is [task 1004142](https://cloudbongos.com/builders#/task/1004142)'s with those files open and a second spec for one implementation is how two drift. The fleet **driven** and the fleet **observed** are different populations: `byo-host` is in the registry and operated by nobody, its liveness the [ADR 0252](0252-the-self-hosted-visibility-report-is-a-pull-and-auto-appear-buys-a-retraction-lever.md) pull, so every operator surface must state which it counts — a roster that mixes them reports a fleet-wide upgrade as complete while an unknown number were never asked. The operator holds **exactly one** credential and it is **its own** (the platform account token, root-only on the control plane, never the web tier, never per-customer); ADR 0323 D6's "names, never values" rule is about the CUSTOMER's secrets and is unchanged. Two gaps are filed rather than fixed here: the subscription roster living in `config/update-subscriptions.json` is the one fleet fact the roster route cannot report and a restored backup can lose independently of the rows it describes ([task 1004150](https://cloudbongos.com/builders#/task/1004150), a recommendation and not a ruling), and **`costEstimateUsd` returns 0 for every shape but `dedicated`** — measured live, all 24 fleet rows report `$0`, confidently and with no error, for projects about to cost ~\$14/month each ([task 1004149](https://cloudbongos.com/builders#/task/1004149)). **No fleet-wide "upgrade everything" button**: both channels are per-instance by construction, and one action that moves the whole fleet is the shape most likely to take every customer down at once. AGPL posture unchanged — ADR 0111 §5's one-sentence rule survives its own table. Rejected: building the separate control plane §5 described (a second registry and a second operator holding the same rows with nothing keeping them equal); specifying Render here; reading per-instance databases for fleet reporting; and continuing to defer the whole layer, which is now the reason the ledger reads \$0 for a fleet about to cost real money.](0325-the-managed-fleet-control-plane-is-the-hub-we-already-run.md) | hosting / fleet / provisioning |
|