@bongos/core 1.19.872 → 1.19.873

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 CHANGED
@@ -2,11 +2,11 @@
2
2
  "artifact": "bongos-core",
3
3
  "manifest_schema": 1,
4
4
  "generator": "scripts/gds/package-core.js",
5
- "core_version": "1.19.872",
6
- "core_contract": "1.19.872",
7
- "source_commit": "847fc8087c9a995a4d3cd60606d594d66e209773",
5
+ "core_version": "1.19.873",
6
+ "core_contract": "1.19.873",
7
+ "source_commit": "1d3dfa0829d700ba97eb830d482b68a977610a2f",
8
8
  "source_ref": "HEAD",
9
- "built_at": "2026-09-19T03:43:12.261Z",
9
+ "built_at": "2026-09-19T03:49:42.858Z",
10
10
  "redaction": {
11
11
  "model": "docs-redacted+functional-verbatim",
12
12
  "docs_redacted": 500,
@@ -17,7 +17,7 @@
17
17
  "gate": "passed"
18
18
  },
19
19
  "file_count": 2826,
20
- "tree_sha256": "5535807e0c119bfd1e626826227726d034838568c556eb76fe7d367566a79155",
20
+ "tree_sha256": "ece1aac30701d080a0e7e7482e31b711842592491f60223e72a66df2552de91f",
21
21
  "files": [
22
22
  {
23
23
  "path": ".claude/skills/ask-for-help/SKILL.md",
@@ -1382,7 +1382,7 @@
1382
1382
  {
1383
1383
  "path": "docs/adr/0180-dependency-intake-and-drift-policy.md",
1384
1384
  "mode": "0000644",
1385
- "sha256": "9d50f2f71de5c4eba8d5af40aca201c6e054b1eec937a9e1342d1bb3b4ce45c1"
1385
+ "sha256": "d302683d028895648062a8128815c9a54db95ef174eee14f06af676587a380e5"
1386
1386
  },
1387
1387
  {
1388
1388
  "path": "docs/adr/0181-teardown-state-gate-and-address-retention.md",
@@ -2602,7 +2602,7 @@
2602
2602
  {
2603
2603
  "path": "docs/module-api-changelog.md",
2604
2604
  "mode": "0000644",
2605
- "sha256": "a581f0b40b8f6ba0fe33d5ae858c3c4048ba0bdec90f212d4e28de1191533565"
2605
+ "sha256": "0b7de1e357e06de9f5ae3a3dcf310aa0c6989ecff27a1f9edc8ce22cb847515d"
2606
2606
  },
2607
2607
  {
2608
2608
  "path": "docs/modules-contract.md",
@@ -2812,7 +2812,7 @@
2812
2812
  {
2813
2813
  "path": "docs/recipes/private-npm-distribution.md",
2814
2814
  "mode": "0000644",
2815
- "sha256": "6d488eff1dd8201debb991ba854c59b5404235b1a433403dbedecaf0638128e4"
2815
+ "sha256": "5e4213d19d7f13ff0a378f996c7e909e00cf262516e72f96e44d4ddf0d4cc861"
2816
2816
  },
2817
2817
  {
2818
2818
  "path": "docs/recipes/redteam-patrol.md",
@@ -7932,12 +7932,12 @@
7932
7932
  {
7933
7933
  "path": "package-lock.json",
7934
7934
  "mode": "0000644",
7935
- "sha256": "05e6bfd775abf8fa8ae50d6f40600767660564b8a4873d52beea120773cf5d96"
7935
+ "sha256": "ca13b6dc7bbe3e5c3f3c849abca3b6407cb3f60151c55b086000674b9ad34324"
7936
7936
  },
7937
7937
  {
7938
7938
  "path": "package.json",
7939
7939
  "mode": "0000644",
7940
- "sha256": "bc23e285afda64a58c1480f713447d5d5b18d6aa6e0fc5ccd77d0f275e297103"
7940
+ "sha256": "38bd8eef71b250778ea555c4c7ff575c8e5b31c9ab5fc0b5766f2f17158d78e4"
7941
7941
  },
7942
7942
  {
7943
7943
  "path": "public-docs/index.html",
@@ -8892,7 +8892,7 @@
8892
8892
  {
8893
8893
  "path": "scripts/gds/provision-repo.js",
8894
8894
  "mode": "0000644",
8895
- "sha256": "b04300749c4f119912ecfb2e6df4a6703f004130570e47a4e5bfaffacd7e2118"
8895
+ "sha256": "2ce976a62d123a284172c054bbc378c33977d2efda44e4b94ef2751a2626944d"
8896
8896
  },
8897
8897
  {
8898
8898
  "path": "scripts/gds/provision-teardown.js",
@@ -9922,7 +9922,7 @@
9922
9922
  {
9923
9923
  "path": "src/module-api.js",
9924
9924
  "mode": "0000644",
9925
- "sha256": "cb2d09a82fc7149dafd2ec0934ae6304481ce2c14e56fa75f3516fab2d37d974"
9925
+ "sha256": "08810a27fe37057c9a737a28db9f84fa1c2d58ce3c19e7884a680d329627158b"
9926
9926
  },
9927
9927
  {
9928
9928
  "path": "src/module-loader/catalog.js",
@@ -12572,7 +12572,7 @@
12572
12572
  {
12573
12573
  "path": "tests/package_core.mjs",
12574
12574
  "mode": "0000644",
12575
- "sha256": "7d64e3c217274af48f24557d49deeb2cb54d7929bd9e939f15b3d3ceb757336c"
12575
+ "sha256": "dafc62752d62949ed465d102b1bc5598ca3d4210a6d5d01d0be8bcf93f8413fc"
12576
12576
  },
12577
12577
  {
12578
12578
  "path": "tests/parallel-load.mjs",
@@ -37,6 +37,12 @@ The publish-on-merge lane (ADR 0161) authenticates with a long-lived `NPM_TOKEN`
37
37
  > - **Provenance had to be turned OFF.** Trusted publishing enables it by default and npm *refuses* to generate provenance from a **private source repository** — so the "provenance for free" clause above does not apply to this repo, and leaving the default would have failed every publish.
38
38
  > - **The registry could no longer answer "is this version taken?".** `@bongos/core` is private, so an unauthenticated `npm view` is a flat `E404` rather than a "no", and OIDC authenticates `npm publish` and nothing else. Keeping that check would have skipped the auto-bump on every run and collided on the published version — **breaking after exactly one release**. The lane now reads its own `release: core X.Y.Z` commits (`laneReleasedVersions`), which need no auth and are the source the cascade terminator already trusts.
39
39
 
40
+ > **Closed 2026-09-18 — task 1003262. Migrating the lane is not the same as deleting the credential, and for three weeks only the first had happened.** `publish.yml` stopped using `NPM_TOKEN` on 2026-08-25 and the repository secret was removed, after which this ADR, [ADR 0161](0161-publish-on-merge.md) and the red-team follow-up all recorded that "no long-lived npm credential exists to steal". That was true of the *repo* and false of the *npm account*: the granular write token `ci-publish-on-merge` (scope `@bongos`, package:write, 2FA-bypass, expiring 2026-11-06) was never revoked — it simply stopped being used, which produces no signal at all. A credential nothing reads is not a credential nothing can use.
41
+ >
42
+ > The same shape a second time, for reads: `droplet-registry-read` outlived [ADR 0283](0283-the-core-publishes-publicly-because-access-was-never-the-thing-protecting-it.md)'s public flip and `fcea51ca`'s deletion of every code path that consumed it — and that one had already leaked, in cleartext, into the droplet's persistent journal (blocker 1000118).
43
+ >
44
+ > **The rule this adds: a credential is retired when it is revoked at the issuer, not when the last caller stops passing it.** Removing the use is the safe half and the reversible half; it is not the finish. Both tokens are enumerated in this task's ship notes, and `npm token list` is the check — the repo cannot answer this question about itself, which is why nothing caught it.
45
+
40
46
  ## What this deliberately does not cover
41
47
 
42
48
  New-dependency *intake vetting* (registry existence/age/download checks — the slopsquatting defense) lives in the weekly patrol (task 1003128), and one-off vulnerability *fixes* (sharp ≥ 0.35 for the libvips CVEs, the colyseus chain for nanoid) are ordinary tasks that shrink the baseline — filed in the idea inbox at adoption time.
@@ -2218,5 +2218,7 @@ is load-bearing: the script throws rather than guess if it is missing, and
2218
2218
  landed since 1.19.870 with no explicit bump. run 35393164740. (task 1002620)
2219
2219
  1.19.872 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
2220
2220
  landed since 1.19.871 with no explicit bump. run 35419318693. (task 1002620)
2221
+ 1.19.873 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
2222
+ landed since 1.19.872 with no explicit bump. run 35419621747. (task 1002620)
2221
2223
  ---------------------------------------------------------------------------
2222
2224
  ```
@@ -23,10 +23,11 @@ The generated typed client (`node scripts/gds/gen-api-client.js` → `clients/bo
23
23
  These involve payment and permanent, account-level actions. They are **not** done inside a build session.
24
24
 
25
25
  1. **Put the `bongos` npm org on a paid plan.** Private scoped publishing requires it. On npmjs.com → your org → Billing → upgrade to a plan that allows private packages (npm Teams, ~$7/member/month). Add as members only the people who need to publish or install during pre-launch — each is a billed seat.
26
- 2. **Create an access token.** npm retired classic (Automation/Publish) tokens — use a **granular access token**: npmjs.com → Access Tokens → *Generate New Token* → **Granular Access Token**, scoped to the `@bongos` packages with **Read and write** permission, and — for a token that publishes — enable **"Bypass two-factor authentication"** (npm otherwise requires interactive 2FA at publish). A **Read-only** granular token is enough for machines that only install. Granular tokens **expire** (pick the longest expiry the policy allows and calendar the rotation — a silently expired token is a 404 on the private package, the same symptom as a missing one). Store it as an environment variable, never in a committed file:
27
- ```sh
28
- export NPM_TOKEN=<redacted>
29
- ```
26
+ 2. **Register a trusted publisher — do NOT create a token.** The automatic lane publishes with **OIDC trusted publishing** ([ADR 0180](../adr/0180-dependency-intake-and-drift-policy.md) decision 4, [task 1003202](https://cloudbongos.com/builders#/task/1003202)): npmjs.com → the `@bongos/core` package → Settings → Trusted publisher → GitHub Actions, naming this repo and the workflow filename `publish.yml`. The runner mints a short-lived identity token per run; nothing long-lived exists to steal or rotate. **If the workflow is ever renamed, update the trusted-publisher entry in the same change** or every publish starts failing 403.
27
+
28
+ A **manual** `package-core.js --publish --live` still needs a human `npm login` (+ 2FA) on that machine — an interactive session, not a stored credential.
29
+
30
+ > **No install token, and none can be issued to any purpose.** `@bongos/core` is public ([ADR 0283](../adr/0283-the-core-publishes-publicly-because-access-was-never-the-thing-protecting-it.md)), so nothing needs a credential to *read* it, and [`fcea51ca`](https://github.com/example-owner/cloud-bongos/commit/fcea51ca) deleted the auth plumbing that used to consume one. A read-only `NPM_TOKEN` in an environment here buys nothing and is one more thing to leak — which is exactly how it leaked: the value sat in the droplet's journal in cleartext for a week (blocker 1000118).
30
31
 
31
32
  ## Publish (the real, permanent step — owner)
32
33
 
@@ -54,7 +55,7 @@ npm publish ./clients/bongos-client # real (publishConfig makes it
54
55
 
55
56
  ## Cutting a core release (the policy — [ADR 0161](../adr/0161-publish-on-merge.md), superseding ADR 0137)
56
57
 
57
- **Publishing is automatic on merge** as of 2026-08-08: the `publish` workflow rides behind the required `unit` check, auto-bumps a patch when nobody bumped, builds through the fail-closed no-leak gate, publishes, and tags — see the workflow header (`.github/workflows/publish.yml`) and ADR 0161. It ships inert until the owner arms it (repo variable `PUBLISH_ON_MERGE=1` + secrets `NPM_TOKEN`, `RELEASE_PUSH_TOKEN`). A **minor/major bump stays a human in-task edit**. The update subscription ([ADR 0136](../adr/0136-update-channel-subscription-policy.md)) consumes whatever is published, on its next sweep.
58
+ **Publishing is automatic on merge** as of 2026-08-08: the `publish` workflow rides behind the required `unit` check, auto-bumps a patch when nobody bumped, builds through the fail-closed no-leak gate, publishes, and tags — see the workflow header (`.github/workflows/publish.yml`) and ADR 0161. It ships inert until the owner arms it: repo variable `PUBLISH_ON_MERGE=1`, a **trusted publisher** registered on npmjs.com for `@bongos/core` naming this repo + `publish.yml`, and the secret `RELEASE_PUSH_TOKEN` (contents:write, for the bump commit). There is deliberately **no** `NPM_TOKEN` — the lane authenticates by OIDC, and the workflow sets neither `NPM_TOKEN` nor `NODE_AUTH_TOKEN` because either one would send npm down the legacy token path and skip the OIDC exchange entirely. A **minor/major bump stays a human in-task edit**. The update subscription ([ADR 0136](../adr/0136-update-channel-subscription-policy.md)) consumes whatever is published, on its next sweep.
58
59
 
59
60
  The steps below remain as the **manual fallback** (lane disarmed, npm outage, or a deliberate out-of-band release):
60
61
 
@@ -71,35 +72,29 @@ The steps below remain as the **manual fallback** (lane disarmed, npm outage, or
71
72
 
72
73
  > **Automating this** (a tag-triggered CI publish) is deferred to the managed-fleet epic ([#1948](https://example.com/builders#/task/1948)); revisit when the manual step becomes a real bottleneck. See [ADR 0137](../adr/0137-upstream-publish-policy-manual-owner-gated.md) "When to revisit".
73
74
 
74
- ## Install a private package (any consumer machine or instance)
75
-
76
- Add a one-line `.npmrc` (repo-root or `~/.npmrc`) that reads the token from the environment:
75
+ ## Install the core (any consumer machine or instance)
77
76
 
78
- ```
79
- //registry.npmjs.org/:_authToken=<redacted>
77
+ ```sh
78
+ npm install -g @bongos/core # global → the `bongos` command, then `bongos init`
80
79
  ```
81
80
 
82
- Then, with `NPM_TOKEN` set in the environment:
81
+ That is the whole step. No `.npmrc`, no token, no org membership — `@bongos/core` is public ([ADR 0283](../adr/0283-the-core-publishes-publicly-because-access-was-never-the-thing-protecting-it.md)). Verify from a clean environment rather than trusting a logged-in shell, which would succeed either way:
83
82
 
84
83
  ```sh
85
- npm install -g @bongos/core # global → the `bongos` command, then `bongos init`
84
+ npm pack @bongos/core --userconfig /dev/null # no credential is read at all
86
85
  ```
87
86
 
88
87
  (A project that calls the API vendors the generated client instead — it is not installable.)
89
88
 
90
- Without a valid token + org membership the install 404s — that is the private gate working.
91
-
92
- ## Provisioning: registry pull vs vendored tarball (task 2090)
89
+ > **This section used to say the opposite**, and the difference was invisible: a tokenless read of a *restricted* package returns a flat `E404`, indistinguishable from "no such package". That one ambiguity is what killed the nightly auto-upgrade sweep silently for three weeks, reporting `failed=0` the whole time — the sweep's unit passed no `NPM_TOKEN`, the env-fed `.npmrc` expanded to empty, and a skip is not a failure in that script's accounting. Do not reintroduce an install credential here.
93
90
 
94
- Provisioning defaults to installing the core from a **vendored tarball** committed into each instance repo (task 2053) — offline, needs no token, and stays the default/fallback until the registry path is proven on a real greenfield standup. The registry pull is **opt-in and fail-soft**:
91
+ ## Provisioning: how a new instance gets its core
95
92
 
96
- - **Turn it on** by setting `PROVISION_CORE_FROM_REGISTRY=1` in the control-plane runner's environment (e.g. `/etc/cloudbongos/box.env`) **and** providing a read-only `NPM_TOKEN` (a **granular access token** — Read-only on the `@bongos` packages — from the paid `bongos` org; mind its expiry). With both present, the standalone scaffold pins `@bongos/core@<version>` from the registry and writes an env-fed `.npmrc` (`_authToken=<redacted> instead of vendoring a tarball; `npm ci` then pulls the private package (the token is inherited from the runner env — never committed).
97
- - **Fail-soft**: if the flag is set but `NPM_TOKEN` is absent, provisioning logs a notice and **falls back to the vendored tarball**, so a tokenless standup still works.
98
- - **Directly**: `bongos init --registry-core` scaffolds the registry shape (caret pin + `.npmrc`; generates the lockfile when `NPM_TOKEN` is set). `bongos upgrade --to <v> --registry` bumps a consumer to a registry version. Both are mutually exclusive with the vendored `--vendor-core` / `--from` inputs.
93
+ Provisioning vendors a **tarball** into each new instance repo, and the instance pins it as `file:vendor/bongos-core-<v>.tgz`. `resolveVendorTarball` in [`provision-repo.js`](../../scripts/gds/provision-repo.js) walks a ladder for the freshest correct one: reuse the control plane's own `file:`-pinned tarball → `npm pack` the control plane's registry-installed core ([ADR 0168](../adr/0168-scaffold-packs-installed-core.md)) → rebuild from source. A child is therefore born on the same core its parent is actually running.
99
94
 
100
- A tokenless registry `npm ci` 404s cleanly (`E404 … @bongos/core … could not be found or you do not have permission to access it`) — the expected pre-launch outcome.
95
+ **There is no token in any of this, and no opt-in fork either.** An earlier design gated a registry pull behind `PROVISION_CORE_FROM_REGISTRY=1` plus a read-only `NPM_TOKEN`, fail-soft to vendoring when the credential was absent. [`fcea51ca`](https://github.com/example-owner/cloud-bongos/commit/fcea51ca) deleted that whole path along with `scripts/gds/npmrc.js`: two install modes diverging on an environment variable is a defect shape, and the credential they diverged on stopped being necessary the moment the package went public. `bongos init --registry-core` and the env-fed `.npmrc` it used to write are gone with it.
101
96
 
102
- > **Proven end-to-end (2026-07-08, task 2118 / blocker 71).** A real `bongos init --registry-core` scaffold, with a valid read-only token in `NPM_TOKEN`, resolved `@bongos/core@1.17.0` straight from `registry.npmjs.org` and generated a reproducible `package-lock.json`. Both legs are confirmed: the registry pull works with a token and fails closed (404) without one. Provisioning still defaults to the vendored tarball until an owner explicitly sets `PROVISION_CORE_FROM_REGISTRY=1` on a live runner.
97
+ > **Vendoring is not a stale-by-design choice.** It is what keeps twenty co-tenant instances pinned to a local path with no registry channel to follow — going public was necessary to fix that, and is not sufficient ([ADR 0283](../adr/0283-the-core-publishes-publicly-because-access-was-never-the-thing-protecting-it.md)). Moving a co-tenant forward is still the `package-core` → scp → `bongos upgrade` path.
103
98
 
104
99
  ## Going public — DONE 2026-09-13
105
100
 
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.19.872",
3
+ "version": "1.19.873",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.19.872",
9
+ "version": "1.19.873",
10
10
  "license": "AGPL-3.0-or-later",
11
11
  "dependencies": {
12
12
  "express": "^4.21.2",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.19.872",
3
+ "version": "1.19.873",
4
4
  "description": "Cloud Bongos — the AI-first build platform core (GDS + platform surfaces + module system), installed as a versioned dependency (ADR 0108).",
5
5
  "license": "AGPL-3.0-or-later",
6
6
  "main": "src/platform-server.js",
@@ -374,8 +374,10 @@ function installedCoreTarball({ instanceRoot, fsImpl = fs } = {}) {
374
374
  // abort of task 1002275, resurrected by the cutover). The installed package itself is
375
375
  // still the exact core this hub runs: return its node_modules dir + version + the
376
376
  // `npm pack` tarball name so the scaffold can pack it into a vendorable .tgz. The child
377
- // stays file:-pinned — its owner needs no npm token (the registry package is PRIVATE
378
- // until the ADR 0134 go-public step). Returns { dir, version, tarballName } or null
377
+ // stays file:-pinned — its owner needs no npm token. (That was originally because the
378
+ // registry package was PRIVATE; since ADR 0283 it is public and a token would buy
379
+ // nothing either way. The file: pin is kept for the reason in ADR 0168 — a child is born
380
+ // on the core its parent actually runs — not for access control.) Returns { dir, version, tarballName } or null
379
381
  // (a file: pin, an uninstalled dep, or a source checkout). fs reads only → dry-run safe.
380
382
  function installedCorePackDir({ instanceRoot, fsImpl = fs } = {}) {
381
383
  const root = instanceRoot || process.env.BONGOS_INSTANCE_ROOT || process.cwd();
package/src/module-api.js CHANGED
@@ -71,7 +71,7 @@ const { responsibilityFor, ROLE_RESPONSIBILITIES } = require('./role-responsibil
71
71
  // there. scripts/gds/bump-version.js still rewrites the literal below; it appends
72
72
  // the entry to that file. Look for a version's history there, not here.
73
73
  // ---------------------------------------------------------------------------
74
- const CORE_VERSION = '1.19.872'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
74
+ const CORE_VERSION = '1.19.873'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
75
75
 
76
76
  // A namespaced logger so a module's log lines are attributable + consistent.
77
77
  // Usage: const log = api.logger('dev-box'); log.info('mounted');
@@ -419,6 +419,37 @@ t('the publish workflow publishes PUBLIC, matching publishArgs', () => {
419
419
  );
420
420
  });
421
421
 
422
+ // THE CREDENTIAL, NOT THE CALLER (task 1003262). Migrating the lane to OIDC is only half
423
+ // of "delete NPM_TOKEN" — the half a future edit can quietly undo. If either token env is
424
+ // ever SET here, npm takes the legacy path and never attempts the OIDC exchange, so the
425
+ // migration would read as done while depending on a long-lived secret again. Asserted on
426
+ // ASSIGNMENTS, not on the string: the workflow names NPM_TOKEN three times on purpose, in
427
+ // comments that say there deliberately is none, and a naive grep would forbid saying so.
428
+ t('the publish lane sets no long-lived npm credential (OIDC only)', () => {
429
+ const yml = readFileSync(new URL('../.github/workflows/publish.yml', import.meta.url), 'utf8');
430
+ const uncommented = yml
431
+ .split('\n')
432
+ .filter((line) => !/^\s*#/.test(line))
433
+ .join('\n');
434
+
435
+ for (const name of ['NPM_TOKEN', 'NODE_AUTH_TOKEN']) {
436
+ assert.ok(
437
+ !new RegExp(`^\\s*${name}\\s*:`, 'm').test(uncommented),
438
+ `${name} must never be assigned in publish.yml — setting it sends npm down the legacy `
439
+ + 'token path and silently skips the OIDC exchange (ADR 0180 decision 4)',
440
+ );
441
+ }
442
+ assert.ok(
443
+ !/_authToken/.test(uncommented),
444
+ 'no .npmrc auth line may reappear in the lane',
445
+ );
446
+ assert.ok(
447
+ /id-token:\s*write/.test(uncommented),
448
+ 'publish.yml must request id-token: write — without it the runner mints no OIDC token '
449
+ + 'and npm publishes unauthenticated, failing with a misleading 404',
450
+ );
451
+ });
452
+
422
453
  t('buildPackageJson: omits `overrides` when the source has none', () => {
423
454
  const pkg = buildPackageJson({ version: '1.0.0', sourceDeps: { express: '^4.0.0', pg: '^8.0.0', pino: '^9.0.0' } });
424
455
  assert.equal(pkg.overrides, undefined);