@bongos/core 1.19.789 → 1.19.791
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 +143 -53
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +6 -0
- package/clients/bongos-client/index.cjs +6 -0
- package/clients/bongos-client/index.d.ts +6 -0
- package/clients/bongos-client/index.mjs +6 -0
- package/docs/adr/0293-the-owners-deploy-door-is-an-intent-the-control-plane-drains.md +124 -0
- package/docs/adr/README.md +1 -0
- package/docs/api/openapi.json +129 -1
- package/docs/api-reference.md +5 -2
- package/docs/copy-inventory.md +89 -72
- package/docs/copy-registry.json +272 -98
- package/docs/module-api-changelog.md +4 -0
- package/docs/recipes/core-release-pipeline.md +12 -0
- package/migrations/core_241_core_upgrades_actor.sql +65 -0
- package/migrations/core_242_core_version_snapshot.sql +54 -0
- package/modules/dev-box/app/src/vendor/bongos-client.cjs +6 -0
- package/modules/government/catalog.js +8 -0
- package/modules/government/migrations/government_014_core_pin_move.sql +46 -0
- package/modules/hall-ui/public/deploy.html +64 -0
- package/modules/hall-ui/public/deploy.js +188 -0
- package/modules/hall-ui/public/hall-kit.js +7 -65
- package/modules/hall-ui/public/hall-render.js +1 -1
- package/modules/hall-ui/public/hall-visual.js +85 -0
- package/modules/hall-ui/public/index.html +1 -0
- package/modules/hall-ui/public/profile.html +1 -0
- package/modules/hall-ui/public/profile.js +2 -2
- package/modules/hall-ui/public/shell.js +4 -0
- package/modules/hall-ui/public/studio.css +1 -1
- package/modules/hall-ui/public/studio.html +1 -0
- package/modules/hall-ui/public/studio.js +4 -3
- package/modules/hall-ui/public/style.css +0 -16
- package/modules/hall-ui/public/task.html +1 -0
- package/modules/hall-ui/public/task.js +10 -13
- package/modules/provisioning/control-plane-guard.js +102 -0
- package/modules/provisioning/core-upgrade.js +289 -0
- package/modules/provisioning/migrations/provisioning_018_core_upgrade_intent.sql +98 -0
- package/modules/provisioning/module.json +1 -1
- package/modules/provisioning/provisioning.js +8 -8
- package/modules/provisioning/routes/core-upgrade.js +95 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/scripts/gds/accessibility-replay.js +195 -0
- package/scripts/gds/enroll-control-plane.js +101 -0
- package/scripts/gds/provision-core-upgrade.js +311 -0
- package/scripts/gds/provision.js +4 -2
- package/scripts/gds/upgrade.js +53 -14
- package/src/bongos/auth.js +19 -0
- package/src/bongos/serve-internal.js +21 -0
- package/src/module-api.js +1 -1
- package/tests/accessibility_replay.mjs +201 -0
- package/tests/auth_resolve_failure.mjs +4 -2
- package/tests/core_upgrade_door.mjs +408 -0
- package/tests/core_upgrade_runner.mjs +377 -0
- package/tests/fixtures/accessibility-replay.json +93 -0
- package/tests/hall_nav.mjs +4 -1
- package/tests/hall_page_gate_map.mjs +7 -0
- package/tests/hall_studio_world.mjs +19 -11
- package/tests/provisioning_disconnect.mjs +30 -5
- package/tests/provisioning_paid_shape_gate.mjs +14 -4
- package/tests/provisioning_restart.mjs +25 -9
- package/tests/provisioning_settings_apply.mjs +11 -2
- package/tests/provisioning_teardown_intent.mjs +7 -0
- package/tests/task_visuals.mjs +91 -27
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# 0293 — The owner's deploy door is an INTENT the control plane drains, not a web tier with a shell
|
|
2
|
+
|
|
3
|
+
- **Status:** Accepted
|
|
4
|
+
- **Date:** 2026-09-16
|
|
5
|
+
- **Tasks:** [#1003159](https://cloudbongos.com/builders#/task/1003159) (goal 1000090) — *"The owner has no path to deploy"*
|
|
6
|
+
- **Related:** 0016 (authority is resolved server-side, never from a local file), 0043 (the control plane is a separate privilege domain), 0128 (how the runner escalates), 0130 (the runner drives remote boxes over ssh), 0136 (channel policy), 0137 / 0161 (the three publish gates), 0111 (the provisioning intent queue this reuses)
|
|
7
|
+
- **Supersedes in practice:** the manual leg of [#1003156](https://cloudbongos.com/builders#/task/1003156) — that bump stays valid, it just stops being the *only* way
|
|
8
|
+
|
|
9
|
+
## Context
|
|
10
|
+
|
|
11
|
+
The owner, 2026-08-22, verbatim: **"I don't think I can make anything live."** The same sentence arrived again on 2026-09-16, phrased *"this is above my paygrade."*
|
|
12
|
+
|
|
13
|
+
Moving an instance's core pin is gate 3 of the release pipeline and the **only** act that makes shipped work visible to users. Today it is reachable from exactly one place: a shell on the control-plane box, holding an ssh key, running
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
bongos upgrade --registry --to <ver> --service <unit> --health-url <url> --commit-pin
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
There is no route and no hall control. So a release reaching users depends on one person being at one machine. And an agent cannot close the loop either — the permission classifier blocks an agent from a mutating `sudo -u lars` over ssh, by design.
|
|
20
|
+
|
|
21
|
+
That is a structural mismatch with the platform's own premise. Cloud Bongos is AI-first: agents write the code, the human owner holds the hard gates. **Deploy is the owner's gate, and it is the one gate with no instrument.**
|
|
22
|
+
|
|
23
|
+
The concrete cost, measured the day this was written: `cloudbongos.com` served core 1.19.754 while 1.19.757 — carrying a shipped fix to the claimable-task feed — sat published and unreachable. Four releases of shipped work that nobody could see, and no one present could act.
|
|
24
|
+
|
|
25
|
+
## Decision
|
|
26
|
+
|
|
27
|
+
### D1 — The door is an INTENT. The web tier never gets a shell.
|
|
28
|
+
|
|
29
|
+
The hall writes a row to `provisioning_intents`; the control-plane timer
|
|
30
|
+
(`provision-intent-runner.timer` → `provision.js run-intents --apply`) drains it and does
|
|
31
|
+
the privileged work. The web process gains no sudo, no ssh key, and no new capability
|
|
32
|
+
whatsoever — it gains the ability to *ask*.
|
|
33
|
+
|
|
34
|
+
This is not a new mechanism. It is the seam ADR 0111 already built and that `provision`,
|
|
35
|
+
`teardown`, `domain-attach`, `settings-apply` and `restart` already ride. `enqueueIntent`
|
|
36
|
+
already carries `requested_by`, so the actor requirement is satisfied by the existing
|
|
37
|
+
shape rather than bolted on.
|
|
38
|
+
|
|
39
|
+
**Rejected: giving the web tier sudo.** ADR 0043 makes the control plane a separate
|
|
40
|
+
privilege domain on purpose. An HTTP-reachable process that can restart services is the
|
|
41
|
+
single highest-value target in the system; the whole point of the split is that
|
|
42
|
+
compromising the web tier must not hand over the box. The intent queue crosses that
|
|
43
|
+
boundary through one narrow, auditable, append-only row.
|
|
44
|
+
|
|
45
|
+
**Rejected: a new bespoke queue for upgrades.** A second queue is a second set of
|
|
46
|
+
claim/settle/retry semantics to get right, and the failure modes of this one are already
|
|
47
|
+
understood in production.
|
|
48
|
+
|
|
49
|
+
### D2 — The control plane is enrolled as a self-row, and that row is NOT teardownable
|
|
50
|
+
|
|
51
|
+
`provisioning_intents.instance_id` is `NOT NULL REFERENCES provisioning_instances(id)`.
|
|
52
|
+
To ride the queue at all, the control plane needs a row. It does not have one today — the
|
|
53
|
+
table holds only tenant projects.
|
|
54
|
+
|
|
55
|
+
So the control plane is enrolled, with `hosting_shape='control-plane'`: a shape that
|
|
56
|
+
exists to be **excluded**, not to be operated on. The non-negotiable that comes with it:
|
|
57
|
+
|
|
58
|
+
> **Every destructive intent action MUST refuse a `control-plane` row.** `teardown` on the
|
|
59
|
+
> self-row would destroy the platform from inside the platform's own UI.
|
|
60
|
+
|
|
61
|
+
This is the load-bearing risk of D2 and the reason the shape is a distinct value rather
|
|
62
|
+
than reusing `standalone`. A test asserts the refusal for every action in `INTENT_ACTIONS`
|
|
63
|
+
except the ones explicitly allowed on the self-row (`core-upgrade`, `restart`).
|
|
64
|
+
|
|
65
|
+
**Rejected: a nullable `instance_id` meaning "the control plane".** A null that means
|
|
66
|
+
something is the kind of implicit encoding that reads as a bug at every later call site,
|
|
67
|
+
and it would silently widen every existing query that assumes the FK holds.
|
|
68
|
+
|
|
69
|
+
### D3 — The target version comes from the registry, never from the request
|
|
70
|
+
|
|
71
|
+
The request names a version; the runner refuses any version that is not **published** and
|
|
72
|
+
not permitted by the channel policy (`update-channel.js`). Free text never reaches
|
|
73
|
+
`bongos upgrade`.
|
|
74
|
+
|
|
75
|
+
This is not theoretical tidiness. The release lane mints a git tag **before** the npm
|
|
76
|
+
publish completes — measured at 8–13 minutes on 2026-09-16, against ~100 seconds recorded
|
|
77
|
+
earlier (learnings 1000263, 1000266). A door that accepted "whatever main says" would
|
|
78
|
+
routinely queue a version that does not exist yet and fail with `npm error notarget`. The
|
|
79
|
+
registry is the only honest source of "can this actually be taken".
|
|
80
|
+
|
|
81
|
+
### D4 — Two-step: preview, then confirm. The owner's choice, 2026-09-16.
|
|
82
|
+
|
|
83
|
+
The control shows what a bump would move — live version → target, and what sits between
|
|
84
|
+
them — and fires only on an explicit second act.
|
|
85
|
+
|
|
86
|
+
Auto-rollback (task 2149) already guards the *bad release* case. This guards the different
|
|
87
|
+
one: the **wrong button**. A single click that restarts the live service is recoverable but
|
|
88
|
+
not free, and the owner asked for the confirmation. Rejected: typing the version to arm it
|
|
89
|
+
(strongest, but friction on an action that should be routine once trusted).
|
|
90
|
+
|
|
91
|
+
### D5 — Archon-gated server-side, ledgered with an actor
|
|
92
|
+
|
|
93
|
+
`requireRank`-equivalent on the route, resolved server-side per request with no caching
|
|
94
|
+
(ADR 0016). Nothing about this is reachable by editing a local file.
|
|
95
|
+
|
|
96
|
+
`core_upgrades` gains an actor column. The ledger records **who asked**, not merely what
|
|
97
|
+
moved — a privileged action with an unattributed row is not acceptable, and today the table
|
|
98
|
+
carries only a free-text `note`.
|
|
99
|
+
|
|
100
|
+
### D6 — The truth read back is the BOX's, not the tool's
|
|
101
|
+
|
|
102
|
+
Success is `curl http://127.0.0.1:<port>/version` **on the box**, matching the requested
|
|
103
|
+
version — the DONE-WHEN task 1003156 already established. The apex sits behind pre-launch
|
|
104
|
+
basic_auth and its HTML rides a CDN, so an apex read can report a stale or gated answer
|
|
105
|
+
with total confidence. The health gate and auto-rollback inside `bongos upgrade` are
|
|
106
|
+
reused unchanged; this decision is only about what the *control* believes and displays.
|
|
107
|
+
|
|
108
|
+
## Consequences
|
|
109
|
+
|
|
110
|
+
- The owner can move the pin from the hall, and read back the version the box actually
|
|
111
|
+
serves, without a shell or an ssh key. That is the whole point.
|
|
112
|
+
- **Tenant instances are NOT in this change.** The queue supports them and the follow-up is
|
|
113
|
+
small, but the control plane is where the pain is and mixing the two doubles the surface
|
|
114
|
+
of a first cut on the most privileged action in the system. Filed separately.
|
|
115
|
+
- The runner upgrades the box it runs on. This is safe for a reason worth stating rather
|
|
116
|
+
than assuming: the runner and the web service are **different systemd units**, so
|
|
117
|
+
restarting the web service does not kill the run in progress. The running process also
|
|
118
|
+
keeps the code it already loaded, so an upgrade that rewrites the runner's own files
|
|
119
|
+
takes effect on the *next* tick, never mid-run.
|
|
120
|
+
- Re-running a settled-but-unconfirmed intent is safe: `bongos upgrade`'s same-version
|
|
121
|
+
guard makes the leg a no-op when the box already serves the target.
|
|
122
|
+
- **What this does not do:** it does not make deploys automatic. ADR 0137's three gates
|
|
123
|
+
stand — land, release, deploy — and this only gives the third gate a handle. An owner who
|
|
124
|
+
never presses it ships nothing, exactly as today.
|
package/docs/adr/README.md
CHANGED
|
@@ -384,3 +384,4 @@ This keeps the decision history honest and traceable.
|
|
|
384
384
|
| 0290 | [**"Disconnect from Bongos entirely" is the owner-initiated exception to retention** ([task 1003249](https://cloudbongos.com/builders#/task/1003249), owner decision 2026-08-24 + this session). ADR 0181 keeps a torn-down project's database, dumps, port and address so "Take it offline" is reversible; this is the separate true delete. Own-scoped + archon override, with the typed name checked SERVER-side (`confirm_slug`); refused while any other run is open (supersedes nothing). The runner tears down, drops the database and its role, erases the dumps by their EXACT nightly filename, removes env/state/checkout/account, forgets the catalog + membership + client rows (new `project.catalog` verb `forget`, both claim rules), and deletes the provisioning row LAST — releasing name, port and address, and keeping every earlier failure retryable. The GitHub repository and any sign-in App are never touched.](0290-disconnect-is-the-owner-initiated-exception-to-address-retention.md) |
|
|
385
385
|
| 0291 | [**The accessibility dimension scores the transcript, so it lives in the session evaluator, not the grader panel** ([task 1002516](https://cloudbongos.com/builders#/task/1002516), clause 4 of criterion `accessibility-fluency-profile`). The task and its sibling task 1003937 both point at `modules/grading/`, but a v4 worker is handed `{ task, valueSummary, diff, changedFiles, … }` and **no transcript** — it judges the change a session produced, never how the builder was spoken to while producing it. The transcript lives in `session_records`, so the dimension does too: `modules/sessions/accessibility.js`, a pure scorer over the digest's per-turn detail, scored SERVER-SIDE at upload (a score the uploader could supply is a score nobody can read; and the SessionEnd hook's 6-second watchdog rules out a second round trip for the profile). Three signals, exactly the three the criterion names. **Profile-relative, and a sanctioned signal is not scored at all** — `engineer` waives jargon + framing, `comfortable` waives terminal-first, `mixed` widens rather than waives, and an all-waived session scores `null`, not a number, so a profile-sanctioned reply cannot score low even in principle; `verbosity` is not an input and every rate is per-1000-characters, so brevity is arithmetically incapable of lowering it. Advisory, gating nothing (ADR 0241's shape). The profile crosses the module boundary as DATA — the route resolves the `builder-settings` kernel port, never a sibling require (ADR 0083), with a test as the tripwire against a value rename. Detectors deliberately undercount (word-bounded jargon, after a substring match fired `ORM` on "format"; a click-path table of UIs that actually exist), because a metric that punishes correct work is what the task itself calls actively harmful. Does NOT answer task 1003937, whose copy/voice lens judges a DIFF and still needs the owner on cost, gate-or-advisory, and what standard to judge against.](0291-the-accessibility-dimension-scores-the-transcript-not-the-diff.md) | grading / sessions / accessibility |
|
|
386
386
|
| 0292 | [**Both install paths stay, and vendoring turns out to be the production one** ([task 1003879](https://cloudbongos.com/builders#/task/1003879), goal 1000090 — public-npm **gate 6**, the decision row held back from gate 5's mechanical cleanup because both candidates were large deletions that could as easily be mistakes). Gate 6 framed the **vendored-tarball path** (~124 refs across `upgrade.js`/`init.js`/`provision-repo.js`/`package-core.js`) as insurance for hosts that cannot reach npm, to be weighed against the AGPL self-host promise. Read that way it loses. **That is not what the code does with it:** `provision-repo.js` resolves the core for every wizard-created project as "a tarball built from THIS CHECKOUT, file:-pinned", with `--registry-core` as the opt-out — vendoring is the production provisioning path, not the fallback, and deleting it breaks project creation. `resolveVendorableCoreTarball`'s three-rung ladder (reuse the control plane's own pinned tarball → pack the core it has INSTALLED → rebuild from source) exists to answer one question a registry install cannot express: *what is the control plane actually running?* Rungs 1 and 2 each name the bug they fixed — empty new repos when the runner is itself a consumer (task 1002275), and "the frozen-old-tarball births" (task 1002706). The owner's input — a checkout routinely runs ahead of the registry — holds, though not via `main`: since publish-on-merge, 136 of the last 300 `main` commits are release bumps, releases land every 9–51 min, and `main`'s `CORE_VERSION` equalled the registry `latest` (1.19.785) when this was written. Provisioning does not provision from `main`; it provisions from the control plane's own tree, and an unmerged branch or a deliberately-held pin exists on the registry at NO version, ever. **Supersedes ADR 0134's "vendored is the default until the registry path is proven end-to-end" clause:** the condition is MET twice over — with a token 2026-07-08 (task 2118 / blocker 71) and tokenless since [ADR 0283](0283-the-core-publishes-publicly-because-access-was-never-the-thing-protecting-it.md) (`npm pack @bongos/core` from a clean `HOME` fetches 25.5 MB) — and the default stands anyway, for load-bearing reasons rather than for want of proof. Costs accepted with open eyes: ~124 refs, a second install mode every scaffold/upgrade/provision path must keep straight, a Windows-only test failure ([task 1003839](https://cloudbongos.com/builders#/task/1003839)), `refreshStandaloneCorePin`. **Candidate 2 — `@cloudbongos/cli` — also STAYS, on a NEW justification:** task 1003872 was right that the original reason (reaching the board without a private-registry credential) is gone, so it needs one of its own, and the measurement supplies it — 32 files / 0.5 MB / `undici`, versus the core's 2,611 files / 46.4 MB / `express`+`pg`+`pino`. Collapsing it would make `npx` a 46 MB download dragging in a web server and a Postgres driver to print a task list. **Unpublishing was never on the table** (public at 0.1.3, strangers may have installed it); [ADR 0258](0258-the-public-cli-is-a-generated-client-package-not-the-published-core.md) stands with its *rationale* amended — a deliberately small entry point, no longer a private-core workaround. The one real rot the flip left is rung 2's stale "still-private registry package" clause, a comment. **Gate 6 closes keep/keep, no removal task.** Reopens if provisioning ever stops meaning "match the control plane".](0292-both-install-paths-stay-and-vendoring-is-the-production-one.md) | distribution / provisioning |
|
|
387
|
+
| 0293 | [**The owner's deploy door is an INTENT the control plane drains, not a web tier with a shell** ([task 1003159](https://cloudbongos.com/builders#/task/1003159), goal 1000090). The owner, twice in a month: *"I don't think I can make anything live"*, then *"this is above my paygrade"*. Moving a core pin is gate 3 of the release pipeline and the ONLY act that makes shipped work visible to users — and it was reachable from exactly one place: a shell on the control-plane box holding an ssh key. No route, no hall control. An agent could not close the loop either, because the permission classifier blocks a mutating `sudo -u lars` over ssh BY DESIGN — so the gate had no owner-reachable control AND no agent-reachable one, which is a structural mismatch with a platform whose premise is that agents build and the human holds the gates. Measured cost the day it was written: cloudbongos.com served 1.19.754 while 1.19.757, carrying a shipped fix to the claimable-task feed, sat published and unreachable. **Decision: the door is an INTENT the control-plane runner drains** — the seam [ADR 0111](0111-instance-hosting-provisioning-module.md) already built and that provision/teardown/domain-attach/settings-apply/restart already ride; `enqueueIntent` already carries `requested_by`, so the actor requirement is met by the existing shape rather than bolted on. The web tier gains no sudo, no ssh key, no new capability — it gains the ability to ASK. Rejected: giving the web tier sudo (ADR 0043 makes the control plane a separate privilege domain precisely so compromising the HTTP-reachable process does not hand over the box), and a second bespoke queue (another set of claim/settle/retry semantics to get right, when this one's failure modes are already understood in production). **The control plane is enrolled as a SELF ROW** because `provisioning_intents.instance_id` is NOT NULL and the box has never had one — which is exactly why the door had nothing to point at. That same row puts a teardown button one query away from the platform, so `hosting_shape='control-plane'` is a distinct value every destructive leg can refuse BY NAME, and the rule is an ALLOW-list (`core-upgrade`, `restart`) rather than a deny-list of today's destructive actions: a deny-list would admit tomorrow's by omission, and "permitted by omission" here means the platform deleting itself. A partial unique index makes a second self row impossible. Rejected: a nullable `instance_id` meaning "the control plane" (an implicit encoding that reads as a bug at every later call site and silently widens every query assuming the FK holds). **The target version comes from the published registry, never the request** — not tidiness: the release lane mints a git tag BEFORE the npm publish completes (measured 8–13 minutes on 2026-09-16 against ~100s recorded earlier, learnings 1000263/1000266), so a door trusting "whatever main says" would routinely queue a version that does not exist and die on `npm error notarget`. **Two-step preview-then-confirm**, the owner's own call: auto-rollback already guards the bad-release case, this guards the different one — the wrong button. **Archon-gated server-side, no caching** (ADR 0016), with `core_241` giving the ledger an actor and a source, because a privileged action with an unattributed row is not acceptable and the table carried only a free-text note. **Truth is the BOX's `/version`, not the apex** (the apex sits behind pre-launch basic_auth and its HTML rides a CDN, so an apex read reports a stale answer with total confidence). Self-restart is safe for a stated reason rather than an assumed one: the runner and the web service are DIFFERENT systemd units, and a running process keeps the code it already loaded, so an upgrade rewriting the runner's own files takes effect next tick, never mid-run. Tenant instances ride the same queue and are a filed follow-up. What it does NOT do: make deploys automatic — ADR 0137's three gates stand, and this only gives the third a handle.](0293-the-owners-deploy-door-is-an-intent-the-control-plane-drains.md) | deploy / authority / provisioning |
|
package/docs/api/openapi.json
CHANGED
|
@@ -12032,6 +12032,40 @@
|
|
|
12032
12032
|
]
|
|
12033
12033
|
}
|
|
12034
12034
|
},
|
|
12035
|
+
"/provisioning/control-plane": {
|
|
12036
|
+
"get": {
|
|
12037
|
+
"operationId": "get_provisioning_control_plane",
|
|
12038
|
+
"tags": [
|
|
12039
|
+
"provisioning"
|
|
12040
|
+
],
|
|
12041
|
+
"summary": "GET /provisioning/control-plane",
|
|
12042
|
+
"description": "GET /provisioning/control-plane — \"which row is this platform, and where does it stand?\" The deploy page's first call: the self row is created once by hand at install time, so its id is per-install and nobody's to memorise. Same atom again, so the entrance is no wider than the door it leads to.\n\n**Rank:** `archon` — Archon only (rank and identity management + the escalation keys — the trust boundary).\n\n**Permissions:** `core.pin.move` (all required).",
|
|
12043
|
+
"x-rank": "archon",
|
|
12044
|
+
"x-source": "modules/provisioning/routes/core-upgrade.js",
|
|
12045
|
+
"x-permissions": [
|
|
12046
|
+
"core.pin.move"
|
|
12047
|
+
],
|
|
12048
|
+
"responses": {
|
|
12049
|
+
"200": {
|
|
12050
|
+
"description": "Success."
|
|
12051
|
+
},
|
|
12052
|
+
"400": {
|
|
12053
|
+
"$ref": "#/components/responses/BadRequest"
|
|
12054
|
+
},
|
|
12055
|
+
"401": {
|
|
12056
|
+
"$ref": "#/components/responses/Unauthorized"
|
|
12057
|
+
},
|
|
12058
|
+
"403": {
|
|
12059
|
+
"$ref": "#/components/responses/Forbidden"
|
|
12060
|
+
}
|
|
12061
|
+
},
|
|
12062
|
+
"security": [
|
|
12063
|
+
{
|
|
12064
|
+
"builderSession": []
|
|
12065
|
+
}
|
|
12066
|
+
]
|
|
12067
|
+
}
|
|
12068
|
+
},
|
|
12035
12069
|
"/provisioning/fleet": {
|
|
12036
12070
|
"get": {
|
|
12037
12071
|
"operationId": "get_provisioning_fleet",
|
|
@@ -12314,6 +12348,100 @@
|
|
|
12314
12348
|
]
|
|
12315
12349
|
}
|
|
12316
12350
|
},
|
|
12351
|
+
"/provisioning/instances/{id}/core-upgrade": {
|
|
12352
|
+
"get": {
|
|
12353
|
+
"operationId": "get_provisioning_instances_id_core_upgrade",
|
|
12354
|
+
"tags": [
|
|
12355
|
+
"provisioning"
|
|
12356
|
+
],
|
|
12357
|
+
"summary": "GET /provisioning/instances/:id/core-upgrade",
|
|
12358
|
+
"description": "GET the same path — step ONE of the two-step (ADR 0293 D4): what a move would do. GATED THE SAME AS THE POST, deliberately. Reading it discloses the instance's exact core version and how far behind it is, which is a vulnerability-window disclosure — precisely the sort of thing ADR 0043 keeps off unauthenticated surfaces. A preview anyone could read would also let an attacker time a request against a known-stale window. The read being no cheaper than the write costs the owner nothing: they hold the atom by construction, because they are the only one who can press the button.\n\n**Rank:** `archon` — Archon only (rank and identity management + the escalation keys — the trust boundary).\n\n**Permissions:** `core.pin.move` (all required).",
|
|
12359
|
+
"x-rank": "archon",
|
|
12360
|
+
"x-source": "modules/provisioning/routes/core-upgrade.js",
|
|
12361
|
+
"x-permissions": [
|
|
12362
|
+
"core.pin.move"
|
|
12363
|
+
],
|
|
12364
|
+
"parameters": [
|
|
12365
|
+
{
|
|
12366
|
+
"name": "id",
|
|
12367
|
+
"in": "path",
|
|
12368
|
+
"required": true,
|
|
12369
|
+
"schema": {
|
|
12370
|
+
"type": "string"
|
|
12371
|
+
},
|
|
12372
|
+
"description": "Path parameter `id`."
|
|
12373
|
+
}
|
|
12374
|
+
],
|
|
12375
|
+
"responses": {
|
|
12376
|
+
"200": {
|
|
12377
|
+
"description": "Success."
|
|
12378
|
+
},
|
|
12379
|
+
"400": {
|
|
12380
|
+
"$ref": "#/components/responses/BadRequest"
|
|
12381
|
+
},
|
|
12382
|
+
"401": {
|
|
12383
|
+
"$ref": "#/components/responses/Unauthorized"
|
|
12384
|
+
},
|
|
12385
|
+
"403": {
|
|
12386
|
+
"$ref": "#/components/responses/Forbidden"
|
|
12387
|
+
},
|
|
12388
|
+
"404": {
|
|
12389
|
+
"$ref": "#/components/responses/NotFound"
|
|
12390
|
+
}
|
|
12391
|
+
},
|
|
12392
|
+
"security": [
|
|
12393
|
+
{
|
|
12394
|
+
"builderSession": []
|
|
12395
|
+
}
|
|
12396
|
+
]
|
|
12397
|
+
},
|
|
12398
|
+
"post": {
|
|
12399
|
+
"operationId": "post_provisioning_instances_id_core_upgrade",
|
|
12400
|
+
"tags": [
|
|
12401
|
+
"provisioning"
|
|
12402
|
+
],
|
|
12403
|
+
"summary": "POST /provisioning/instances/:id/core-upgrade",
|
|
12404
|
+
"description": "POST /provisioning/instances/:id/core-upgrade — move the core pin. rank: `core.pin.move` — archon floor AND system:true, so the catalog's own invariant forbids granting it to metic and no custom rank can be handed it without a deliberate, visible edit. BV1.R105 / ADR 0151 is why this is an atom and not `requireRank`: tests/government_require_permission.mjs fails the build on a new requireRank in a modules/*/routes gate. Body: { to: \"1.19.757\" } — and requiring it IS the second step of the two-step the owner asked for (ADR 0293 D4). A request that cannot fire without naming its target cannot fire from a stray click, and \"move to whatever is newest\", resolved at drain time, would be a different act from the one previewed: main moves between the preview and the confirm. What this does NOT check: whether the version is PUBLISHED, and whether the channel policy admits it. Both need the registry, and the only tool that reads it (update-channel.js `listAvailableVersions`) shells out synchronously — it would block the event loop for every other request on this process. It also lives in scripts/gds/, which a module may not require (ADR 0083). The runner enforces both, where it is a CLI and both are free. See ../core-upgrade.js.\n\n**Rank:** `archon` — Archon only (rank and identity management + the escalation keys — the trust boundary).\n\n**Permissions:** `core.pin.move` (all required).",
|
|
12405
|
+
"x-rank": "archon",
|
|
12406
|
+
"x-source": "modules/provisioning/routes/core-upgrade.js",
|
|
12407
|
+
"x-permissions": [
|
|
12408
|
+
"core.pin.move"
|
|
12409
|
+
],
|
|
12410
|
+
"parameters": [
|
|
12411
|
+
{
|
|
12412
|
+
"name": "id",
|
|
12413
|
+
"in": "path",
|
|
12414
|
+
"required": true,
|
|
12415
|
+
"schema": {
|
|
12416
|
+
"type": "string"
|
|
12417
|
+
},
|
|
12418
|
+
"description": "Path parameter `id`."
|
|
12419
|
+
}
|
|
12420
|
+
],
|
|
12421
|
+
"responses": {
|
|
12422
|
+
"200": {
|
|
12423
|
+
"description": "Success."
|
|
12424
|
+
},
|
|
12425
|
+
"400": {
|
|
12426
|
+
"$ref": "#/components/responses/BadRequest"
|
|
12427
|
+
},
|
|
12428
|
+
"401": {
|
|
12429
|
+
"$ref": "#/components/responses/Unauthorized"
|
|
12430
|
+
},
|
|
12431
|
+
"403": {
|
|
12432
|
+
"$ref": "#/components/responses/Forbidden"
|
|
12433
|
+
},
|
|
12434
|
+
"404": {
|
|
12435
|
+
"$ref": "#/components/responses/NotFound"
|
|
12436
|
+
}
|
|
12437
|
+
},
|
|
12438
|
+
"security": [
|
|
12439
|
+
{
|
|
12440
|
+
"builderSession": []
|
|
12441
|
+
}
|
|
12442
|
+
]
|
|
12443
|
+
}
|
|
12444
|
+
},
|
|
12317
12445
|
"/provisioning/instances/{id}/detail": {
|
|
12318
12446
|
"patch": {
|
|
12319
12447
|
"operationId": "patch_provisioning_instances_id_detail",
|
|
@@ -23826,7 +23954,7 @@
|
|
|
23826
23954
|
"description": "A required dependency/feature is not configured or is temporarily down."
|
|
23827
23955
|
}
|
|
23828
23956
|
},
|
|
23829
|
-
"x-endpoint-count":
|
|
23957
|
+
"x-endpoint-count": 378,
|
|
23830
23958
|
"x-schema-count": 415,
|
|
23831
23959
|
"x-undocumented-bodies": 9,
|
|
23832
23960
|
"x-response-schemas": 282,
|
package/docs/api-reference.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# Bongos API reference
|
|
4
4
|
|
|
5
|
-
> **Generated from the live route files** — the route file is authoritative.
|
|
5
|
+
> **Generated from the live route files** — the route file is authoritative. 378 endpoints across 62 route files.
|
|
6
6
|
> Machine-readable spec: [`docs/api/openapi.json`](api/openapi.json) (OpenAPI 3.1). Rendered docs site: **`/docs`** (e.g. `cloudbongos.com/docs`).
|
|
7
7
|
|
|
8
8
|
Base path: `/api/bongos`. Ranks (enforced server-side, [ADR 0016](adr/0016-trust-boundary-server-enforced-permissions.md)): `public` < `any-builder` < `metic+archon` < `archon`.
|
|
@@ -489,10 +489,11 @@ Base path: `/api/bongos`. Ranks (enforced server-side, [ADR 0016](adr/0016-trust
|
|
|
489
489
|
| GET | `/api/bongos/projects/featured` | `public` | — | the public map of projects; no auth by design. |
|
|
490
490
|
| POST | `/api/bongos/projects/invite` | `metic+archon` | `client_id`, `github_login` | own-or-metic in effect: invite a builder into a project (ADR 0141 §5, task #1002115), called by a `project.curate` holder OR the project'… |
|
|
491
491
|
|
|
492
|
-
## `provisioning` (
|
|
492
|
+
## `provisioning` (26)
|
|
493
493
|
|
|
494
494
|
| Method | Path | Rank | Body | Description |
|
|
495
495
|
|---|---|---|---|---|
|
|
496
|
+
| GET | `/api/bongos/provisioning/control-plane` | `archon` | — | GET /provisioning/control-plane — "which row is this platform, and where does it stand?" The deploy page's first call: the self row is cr… |
|
|
496
497
|
| GET | `/api/bongos/provisioning/fleet` | `metic+archon` | — | GET /provisioning/fleet — the full instance roster across all owners. |
|
|
497
498
|
| GET | `/api/bongos/provisioning/fleet/cost-ledger` | `metic+archon` | — | GET /provisioning/fleet/cost-ledger — per-instance monthly estimate + total. |
|
|
498
499
|
| GET | `/api/bongos/provisioning/github-app/callback` | `public` | — | GET /provisioning/github-app/callback?code=&state= — GitHub redirects the owner's BROWSER here after they approve the App Manifest (task … |
|
|
@@ -501,6 +502,8 @@ Base path: `/api/bongos`. Ranks (enforced server-side, [ADR 0016](adr/0016-trust
|
|
|
501
502
|
| GET | `/api/bongos/provisioning/instances` | `any-builder` | — | GET /provisioning/instances — the caller's own instances + each open intent. |
|
|
502
503
|
| POST | `/api/bongos/provisioning/instances` | `any-builder` | `slug`, `target_ref`, `hosting_shape`, `domain`, `no_address`, `tier`, `onboard_mode`, `type`, `description`, `detail`, `modules` | POST /provisioning/instances — request a new instance. |
|
|
503
504
|
| GET | `/api/bongos/provisioning/instances/:id` | `any-builder` | — | GET /provisioning/instances/:id — read one instance (the manage page's read). |
|
|
505
|
+
| GET | `/api/bongos/provisioning/instances/:id/core-upgrade` | `archon` | — | GET the same path — step ONE of the two-step (ADR 0293 D4): what a move would do. |
|
|
506
|
+
| POST | `/api/bongos/provisioning/instances/:id/core-upgrade` | `archon` | — | POST /provisioning/instances/:id/core-upgrade — move the core pin. |
|
|
504
507
|
| PATCH | `/api/bongos/provisioning/instances/:id/detail` | `any-builder` | `description`, `detail`, `modules`, `type` | ── The optional project detail (task 1002334, goal 1000046) ────────────── PATCH /provisioning/instances/:id/detail — answer (or re-answe… |
|
|
505
508
|
| POST | `/api/bongos/provisioning/instances/:id/disconnect` | `any-builder` | `confirm_slug` | POST /provisioning/instances/:id/disconnect — rank: any authenticated builder (own resource); an archon may disconnect any project. |
|
|
506
509
|
| POST | `/api/bongos/provisioning/instances/:id/domain` | `any-builder` | `domain` | POST /provisioning/instances/:id/domain — give the caller's OWN instance its address (task 1002692, gap 5: no path existed to add a domai… |
|