@bongos/core 1.19.737 → 1.19.739

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,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.737",
6
- "core_contract": "1.19.737",
7
- "source_commit": "1ceb8f6a2ad0321079098910ad8a721dffa67454",
5
+ "core_version": "1.19.739",
6
+ "core_contract": "1.19.739",
7
+ "source_commit": "007654ffe674e9720a1e9227b87dc010b35178d9",
8
8
  "source_ref": "HEAD",
9
- "built_at": "2026-09-14T01:10:42.991Z",
9
+ "built_at": "2026-09-14T11:30:11.483Z",
10
10
  "redaction": {
11
11
  "model": "docs-redacted+functional-verbatim",
12
12
  "docs_redacted": 479,
13
13
  "agent_docs_stubbed": 24,
14
- "functional_verbatim": 2109,
14
+ "functional_verbatim": 2107,
15
15
  "rules": 3,
16
16
  "gate_literals": 3,
17
17
  "gate": "passed"
18
18
  },
19
- "file_count": 2612,
20
- "tree_sha256": "c34cdc2f87b36f12f71f276e47ce740238e94841fab60b4245613189e0c527d2",
19
+ "file_count": 2610,
20
+ "tree_sha256": "387827a54efbdc59d0986629636d11366a72c83e8518a3f621bfffe13e020b61",
21
21
  "files": [
22
22
  {
23
23
  "path": ".claude/skills/ask-for-help/SKILL.md",
@@ -2492,7 +2492,7 @@
2492
2492
  {
2493
2493
  "path": "docs/module-api-changelog.md",
2494
2494
  "mode": "0000644",
2495
- "sha256": "d1b64255f150e8c8a1b88cffa7326633b7e5bc22e22744f7b1d9ee47410f9e13"
2495
+ "sha256": "e1e65fc287020112074f69a451cba3e80b6c6e04ab746404c7a0db44f95f1c46"
2496
2496
  },
2497
2497
  {
2498
2498
  "path": "docs/modules-contract.md",
@@ -2702,7 +2702,7 @@
2702
2702
  {
2703
2703
  "path": "docs/recipes/private-npm-distribution.md",
2704
2704
  "mode": "0000644",
2705
- "sha256": "33e4f7f3083b8ac5d10f117d4d93e78b9d8b7b4d082087051b69698cb67b03b1"
2705
+ "sha256": "6d488eff1dd8201debb991ba854c59b5404235b1a433403dbedecaf0638128e4"
2706
2706
  },
2707
2707
  {
2708
2708
  "path": "docs/recipes/redteam-patrol.md",
@@ -2752,7 +2752,7 @@
2752
2752
  {
2753
2753
  "path": "docs/recipes/upgrading-the-core.md",
2754
2754
  "mode": "0000644",
2755
- "sha256": "36e3c53a8bbe0b987e19333f12cac4dc0951822686759623e3273843e313188e"
2755
+ "sha256": "3691b017562bde760fbe3ff29a5efb80d886dd770f1187dc4673459871cec513"
2756
2756
  },
2757
2757
  {
2758
2758
  "path": "docs/recipes/windows-builders.md",
@@ -7537,12 +7537,12 @@
7537
7537
  {
7538
7538
  "path": "package-lock.json",
7539
7539
  "mode": "0000644",
7540
- "sha256": "fc4587a7fbaaa14aad1e0296015adb626e89da62f4a90a95647f1bbb55a61f1e"
7540
+ "sha256": "f49b1b8e922b2717a695ddb091d8cb8e4f574634f2d6b57cb888a7a4dc7d38b0"
7541
7541
  },
7542
7542
  {
7543
7543
  "path": "package.json",
7544
7544
  "mode": "0000644",
7545
- "sha256": "d7595c39c515821f76d0b5045c3a9cbb400f2c36a663046d19dfc7cab098cf69"
7545
+ "sha256": "7f2a45d17d4d9dadcf80e5da376e648e607de28b3b8150fea3120e1dd4a3fee4"
7546
7546
  },
7547
7547
  {
7548
7548
  "path": "public-docs/index.html",
@@ -8122,7 +8122,7 @@
8122
8122
  {
8123
8123
  "path": "scripts/gds/go-live.js",
8124
8124
  "mode": "0000644",
8125
- "sha256": "95c5109be4ab748c5ee59f22489ba5e3de45bafccf1c543d8cf174fc89d75dab"
8125
+ "sha256": "f246947d973eec84ea174736bc87bf52b4716c939fc86bff4037b330f6790f9a"
8126
8126
  },
8127
8127
  {
8128
8128
  "path": "scripts/gds/goal-names.js",
@@ -8157,7 +8157,7 @@
8157
8157
  {
8158
8158
  "path": "scripts/gds/init.js",
8159
8159
  "mode": "0000644",
8160
- "sha256": "e81ab71bcd7d2f06b5aaf462dd099776c966f9840a109846a9753f12482b5281"
8160
+ "sha256": "3879084ce7246d6fdcf3a053006e86e7d533016b6dbb9b5aa160d65a3e9336f5"
8161
8161
  },
8162
8162
  {
8163
8163
  "path": "scripts/gds/install-git-hooks.js",
@@ -8299,11 +8299,6 @@
8299
8299
  "mode": "0000644",
8300
8300
  "sha256": "d34cbccec1fa3053f960c1ac0950ce7e6b4b05f9d32a23304c4c3abc41c22ef7"
8301
8301
  },
8302
- {
8303
- "path": "scripts/gds/npmrc.js",
8304
- "mode": "0000644",
8305
- "sha256": "1beffeed111e8528cfa0a629d5ef81c766023de5c6820a81659a390070868228"
8306
- },
8307
8302
  {
8308
8303
  "path": "scripts/gds/oauth-secret.js",
8309
8304
  "mode": "0000644",
@@ -8332,7 +8327,7 @@
8332
8327
  {
8333
8328
  "path": "scripts/gds/package-core.js",
8334
8329
  "mode": "0000644",
8335
- "sha256": "89ec2a080f397e1af5c0dede40a7eb22a2e3343d219da4b4a4ba903337bcc420"
8330
+ "sha256": "d9a6748c0dadbff9954809df1c83345979e1356559312723e107f19949edfd7b"
8336
8331
  },
8337
8332
  {
8338
8333
  "path": "scripts/gds/paste-token.js",
@@ -8362,7 +8357,7 @@
8362
8357
  {
8363
8358
  "path": "scripts/gds/provision-config.js",
8364
8359
  "mode": "0000644",
8365
- "sha256": "359674dd4ba6c7c4dd446882ec88f7d9feffd474300e17736d3129c2526082c1"
8360
+ "sha256": "48d13ecda786629148713e0bd89f0d020ee47de107fa5a2327d44989783907ac"
8366
8361
  },
8367
8362
  {
8368
8363
  "path": "scripts/gds/provision-net.js",
@@ -8377,7 +8372,7 @@
8377
8372
  {
8378
8373
  "path": "scripts/gds/provision-repo.js",
8379
8374
  "mode": "0000644",
8380
- "sha256": "77ca585d99f4058427f1abbe629d66ce5ec9f87b6c974aebc114d1c0e734d8e2"
8375
+ "sha256": "c5822f3b29f90998c831639df2b0925033f17758cc329609de507f5e6e1ad7c8"
8381
8376
  },
8382
8377
  {
8383
8378
  "path": "scripts/gds/provision-units.js",
@@ -8387,7 +8382,7 @@
8387
8382
  {
8388
8383
  "path": "scripts/gds/provision.js",
8389
8384
  "mode": "0000644",
8390
- "sha256": "007c5cfa3b42567df0016038fc7ec5c7dbdab60ec6ec8ecd9d06386933e5641d"
8385
+ "sha256": "614aa63d02f3ae97f39e492cb0948a92b40364e25f4780d87546b64c01e5c69c"
8391
8386
  },
8392
8387
  {
8393
8388
  "path": "scripts/gds/publish-credential-check.js",
@@ -8857,7 +8852,7 @@
8857
8852
  {
8858
8853
  "path": "scripts/gds/start.js",
8859
8854
  "mode": "0000644",
8860
- "sha256": "1bfa9d27bc91836f636f57466876d40c56e13bdb1038451e8298c8a32cb674d6"
8855
+ "sha256": "7e4b7cd97e9a414cf55e1118c902b4ab17239d3810fe26e5b90aa18d6a948432"
8861
8856
  },
8862
8857
  {
8863
8858
  "path": "scripts/gds/status.js",
@@ -8902,12 +8897,12 @@
8902
8897
  {
8903
8898
  "path": "scripts/gds/update-channel.js",
8904
8899
  "mode": "0000644",
8905
- "sha256": "db54fba6065c8b662f574aa9443e686df4e5a152865342999b1f20d50ece01ed"
8900
+ "sha256": "1aeeb6171244e1d42ee7189a3aa2ae5d83fd175bfe1ce89300d6ca7511819f0b"
8906
8901
  },
8907
8902
  {
8908
8903
  "path": "scripts/gds/upgrade.js",
8909
8904
  "mode": "0000644",
8910
- "sha256": "64b2571770a5be9dc046f6c64adf45a09642c6961a6036c9405e1c933be6c3b7"
8905
+ "sha256": "7a82fb0cf919e14700ceba718e28da1324df2eab669a847348271c85b2f7bd66"
8911
8906
  },
8912
8907
  {
8913
8908
  "path": "scripts/gds/validate-design.js",
@@ -9347,7 +9342,7 @@
9347
9342
  {
9348
9343
  "path": "src/module-api.js",
9349
9344
  "mode": "0000644",
9350
- "sha256": "9ad95d6241cc8f3dfda5d02445246c1f7fd62b75b5014a15a9254c15a620aef7"
9345
+ "sha256": "778ccc0ac1fe12d16e101aad85011d19d8fab03f00fa42fce71a26ec9b600fe2"
9351
9346
  },
9352
9347
  {
9353
9348
  "path": "src/module-loader/catalog.js",
@@ -10567,7 +10562,7 @@
10567
10562
  {
10568
10563
  "path": "tests/go_live.mjs",
10569
10564
  "mode": "0000644",
10570
- "sha256": "2fc62d14a1c7e4fbd63fe4a18a4d1be85dfc481bc12a913cdd0d3812984bd79f"
10565
+ "sha256": "fb937d66f8efa9fdcfb58715785a0c9d94e52034d04175aec78310c70bd53a7f"
10571
10566
  },
10572
10567
  {
10573
10568
  "path": "tests/goal_abuse_matrix.mjs",
@@ -11287,7 +11282,7 @@
11287
11282
  {
11288
11283
  "path": "tests/init.mjs",
11289
11284
  "mode": "0000644",
11290
- "sha256": "3026220335d63e157a1732d03ee6e66f0509bf965cb8676a651cbb3e1780be34"
11285
+ "sha256": "945aaac204e9e60772d3e7b361c97c099d236e6be6b4b794cce36ecb391d8a1a"
11291
11286
  },
11292
11287
  {
11293
11288
  "path": "tests/instance_config.mjs",
@@ -11679,11 +11674,6 @@
11679
11674
  "mode": "0000644",
11680
11675
  "sha256": "c57c0211709ab041ee895bbb4ade9f4b146b43147570bf282e6d6a808560db76"
11681
11676
  },
11682
- {
11683
- "path": "tests/npmrc.mjs",
11684
- "mode": "0000644",
11685
- "sha256": "1aaa68ee1a4c4a58563cef86aac44cfa0e046ad7376d3a2108bf6dd455580942"
11686
- },
11687
11677
  {
11688
11678
  "path": "tests/oauth_secret.mjs",
11689
11679
  "mode": "0000644",
@@ -11982,7 +11972,7 @@
11982
11972
  {
11983
11973
  "path": "tests/provision.mjs",
11984
11974
  "mode": "0000644",
11985
- "sha256": "0e177a4817094985e4aeec0c74883502762304d2174c1e13e77a87404b739088"
11975
+ "sha256": "25fbd2adccf79e921c642f8f57b082f5f19fa01cd981cd5b813dc1070511aadb"
11986
11976
  },
11987
11977
  {
11988
11978
  "path": "tests/provision_restart.mjs",
@@ -12702,7 +12692,7 @@
12702
12692
  {
12703
12693
  "path": "tests/start_env_guard.mjs",
12704
12694
  "mode": "0000644",
12705
- "sha256": "9b0a7d7aeb761cb6fb0c5e1ae650e0848af12e92887ebd630814b35b434c1f41"
12695
+ "sha256": "dd7171dbc59e024de99a063763219237814cf4c943fb3d873cc324f3bfde7f25"
12706
12696
  },
12707
12697
  {
12708
12698
  "path": "tests/start_rebase_warning.mjs",
@@ -12922,7 +12912,7 @@
12922
12912
  {
12923
12913
  "path": "tests/upgrade.mjs",
12924
12914
  "mode": "0000644",
12925
- "sha256": "010c9ac8b05e25c10b9d43c7efd02454facf0d5663df11df555ea913697e23e8"
12915
+ "sha256": "def73ab0f532c04df167d77917981eaf7e0dc7e1d2a7e7c85f3f962d7d65ce2a"
12926
12916
  },
12927
12917
  {
12928
12918
  "path": "tests/upgrade_persist_pin.mjs",
@@ -1948,5 +1948,9 @@ is load-bearing: the script throws rather than guess if it is missing, and
1948
1948
  landed since 1.19.735 with no explicit bump. run 34771773838. (task 1002620)
1949
1949
  1.19.737 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
1950
1950
  landed since 1.19.736 with no explicit bump. run 34795026282. (task 1002620)
1951
+ 1.19.738 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
1952
+ landed since 1.19.737 with no explicit bump. run 34797297122. (task 1002620)
1953
+ 1.19.739 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
1954
+ landed since 1.19.738 with no explicit bump. run 34838475604. (task 1002620)
1951
1955
  ---------------------------------------------------------------------------
1952
1956
  ```
@@ -14,7 +14,7 @@
14
14
  |---|---|---|
15
15
  | `@bongos/core` | the platform an instance installs + the `bongos` CLI (`bin`) | `bongos package-core` (`scripts/gds/package-core.js`) — synthesizes the package.json + a pruned lockfile, redacts, and packs a `.tgz` |
16
16
 
17
- `@bongos/core` carries `publishConfig: { access: "restricted" }`, so **publishing it is private by default** — no public leak if a flag is forgotten.
17
+ `@bongos/core` carries `publishConfig: { access: "public" }`, and `publishArgs` passes `--access public` as well — both, so neither drifting alone can silently take the package back to restricted ([ADR 0283](../adr/0283-the-core-publishes-publicly-because-access-was-never-the-thing-protecting-it.md)). It read `restricted` until 2026-09-13; what protects the artifact is the no-leak gate on the build, not who may fetch it.
18
18
 
19
19
  The generated typed client (`node scripts/gds/gen-api-client.js` → `clients/bongos-client/`) is deliberately **not** in this table: it is not published, carries no `publishConfig`, and is consumed by vendoring. See its own README.
20
20
 
@@ -33,15 +33,15 @@ These involve payment and permanent, account-level actions. They are **not** don
33
33
  Always preview with a dry-run first (no upload, no login needed):
34
34
 
35
35
  ```sh
36
- # Core — build the tarball and DRY-RUN the private publish:
36
+ # Core — build the tarball and DRY-RUN the publish:
37
37
  node scripts/gds/package-core.js --publish
38
38
  ```
39
39
 
40
- Look for `restricted access` and the `@bongos/core@<version>` line. When it looks right, publish for real (this is **permanent** — npm can't delete a name after 72h):
40
+ Look for `public access` and the `@bongos/core@<version>` line. When it looks right, publish for real (this is **permanent and public** — npm can't delete a name after 72h, and the version is never reusable):
41
41
 
42
42
  ```sh
43
43
  npm login # once per machine (+ 2FA)
44
- node scripts/gds/package-core.js --publish --live # actually publishes, still private
44
+ node scripts/gds/package-core.js --publish --live # actually publishes, PUBLIC
45
45
  ```
46
46
 
47
47
  For the client (note the `./` — without it, npm reads the path as a git package spec, not a folder):
@@ -101,13 +101,13 @@ A tokenless registry `npm ci` 404s cleanly (`E404 … @bongos/core … could not
101
101
 
102
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.
103
103
 
104
- ## Going public at launch
104
+ ## Going public — DONE 2026-09-13
105
105
 
106
- Pick either — both keep the daily `bongos` command working:
106
+ `@bongos/core` is public ([ADR 0283](../adr/0283-the-core-publishes-publicly-because-access-was-never-the-thing-protecting-it.md), [task 1003872](https://cloudbongos.com/builders#/task/1003872)). `publishConfig.access` is `"public"` and the registry-side status was flipped with the one-time owner step:
107
107
 
108
- - **Flip the same scoped packages public** (simplest):
109
- ```sh
110
- npm access public @bongos/core
111
- ```
112
- (Or change `publishConfig.access` to `"public"` and republish.) Public installs need no token; the paid seats can be dropped.
108
+ ```sh
109
+ npm access set status=public @bongos/core
110
+ ```
111
+
112
+ That registry flip is separate from `--access public` on a publish, which governs only what *that* publish declares and is historically a no-op when re-publishing an already-private package. Verify the flip by a tokenless install from a clean `HOME`, not by the flag's presence. Public installs need no token; the paid private-package seats can be dropped.
113
113
  - **Also publish the unscoped vanity name** for `npm install -g cloudbongos` — that is [task 2025](https://example.com/builders#/task/2025) (rename in `package-core.js`/`upgrade.js` + docs), done at launch.
@@ -108,7 +108,7 @@ node .claude/scheduled-tasks/core-update-subscription/subscribe.js #
108
108
  node .claude/scheduled-tasks/core-update-subscription/subscribe.js --apply # upgrade
109
109
  ```
110
110
 
111
- Each subscribed instance: read its installed core → ask the registry (`npm view`, needs `NPM_TOKEN`) → resolve the newest version its channel permits → if newer, `bongos upgrade --to <v> --registry` with **auto-rollback ON**. It prints one line per instance plus a tally; a non-zero exit means an upgrade failed (already auto-reverted).
111
+ Each subscribed instance: read its installed core → ask the registry (`npm view`, no credential needed — the package is public) → resolve the newest version its channel permits → if newer, `bongos upgrade --to <v> --registry` with **auto-rollback ON**. It prints one line per instance plus a tally; a non-zero exit means an upgrade failed (already auto-reverted).
112
112
 
113
113
  **Triple-gated, so nothing moves by accident:** the `core-update-subscription` routine is `requiresAutonomy: true` + **default OFF** in [`config/scheduled-routines.json`](../../config/scheduled-routines.json), the roster ships empty, and every upgrade is health-gated. Arm it by enabling the routine's timer on the control plane once autonomy is on.
114
114
 
@@ -116,7 +116,8 @@ Each subscribed instance: read its installed core → ask the registry (`npm vie
116
116
 
117
117
  ## Gotchas
118
118
 
119
- - **`--registry` no longer needs a hand-exported `NPM_TOKEN`** (task 1002754). The instance `.npmrc` reads `_authToken=<redacted>, and npm gives that per-project key precedence — so with the env var unset it expanded EMPTY and **masked a perfectly valid literal token in `~/.npmrc`** (npm went anonymous; the private core 404'd; the error blamed a "missing" credential the box actually held). `bongos upgrade --registry` now resolves the credential itself: env `NPM_TOKEN` wins, else the **user-level `~/.npmrc`'s literal `//registry.npmjs.org/:_authToken=…`** is injected into the install's environment (forward install and rollback reinstall both). A pin bump on a box whose `~/.npmrc` authenticates therefore succeeds with **no exported env var**. Resolver: `resolveNpmToken()` in [`scripts/gds/npmrc.js`](../../scripts/gds/npmrc.js).
119
+ - **`--registry` needs no credential at all** (task 1003878). `@bongos/core` is **public** on npm (task 1003872), so `bongos upgrade --registry` resolves it from the default registry like any other dependency — no token, no login, and no instance `.npmrc`. An install failure on this path is therefore a *real* failure (network, registry outage, or a version that was never published), never a missing credential.
120
+ *History, so the symptom is recognisable on an old instance:* the core used to publish private, and the instance `.npmrc` read `_authToken=<redacted> npm gives that per-project key precedence, so with the env var unset it expanded EMPTY and **masked a perfectly valid literal token in `~/.npmrc`** — npm went anonymous, the private core 404'd, and the error blamed a credential the box actually held. Task 1002754 worked around it by resolving the token in-process; task 1003878 deleted the whole mechanism along with the `.npmrc` that caused it. An instance still carrying a committed `.npmrc` from a pre-1003878 scaffold can safely delete it.
120
121
  - **A green `publish` run is not evidence of a publish.** The publish-on-merge lane succeeds on its no-op paths too (cascade terminator, armed-gate). Read the run's **job summary**, which now states the verdict explicitly: `PUBLISHED @bongos/core@<v>` vs `NO-OP — nothing was published this run`.
121
122
  - **`PGDATABASE` is load-bearing on a co-tenant droplet.** `migrate.sh` defaults `PGDATABASE` to `example` — **production**. Always set `PGDATABASE=<instance-db>` explicitly, or you migrate the wrong database.
122
123
  - **The health check polls the restart** (fixed in task [#1984](https://example.com/builders#/task/1984)). The service binds its port ~1s after `systemctl restart`, so `--health-url` now polls up to ~30s for a 2xx before reporting a warning — an immediate `fetch failed` no longer means a failed upgrade. (Pre-1984 cores fetched once and spuriously reported `fetch failed`; there, confirm with `is-active` + a manual `curl`.)
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.19.737",
3
+ "version": "1.19.739",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.19.737",
9
+ "version": "1.19.739",
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.737",
3
+ "version": "1.19.739",
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",
@@ -84,7 +84,7 @@ function isSafeToken(v) {
84
84
  // unit name that the script would then try to `systemctl stop`. Absent, the stop and
85
85
  // the restart are simply skipped: there is no `reset --hard` to race.
86
86
  const REQUIRED_FIELDS = ['instanceDir', 'service', 'healthUrl', 'versionUrl'];
87
- const OPTIONAL_FIELDS = ['ssh', 'local', 'deployTimer', 'deployService', 'npmEnvFile', 'runAs', 'backupDb', 'backupDir', 'env', 'registryPackage', 'pinMode'];
87
+ const OPTIONAL_FIELDS = ['ssh', 'local', 'deployTimer', 'deployService', 'runAs', 'backupDb', 'backupDir', 'env', 'registryPackage', 'pinMode'];
88
88
 
89
89
  // How the bump is made durable — the other half of co-tenant mode (task 1003521).
90
90
  //
@@ -153,7 +153,6 @@ const CONFIG_TEMPLATE = `{
153
153
  "versionUrl": "http://127.0.0.1:3002/version", // box-side truth read back after
154
154
  "deployTimer": "my-deploy.timer", // optional: pull-deploy timer (its reset --hard is the hazard)
155
155
  "deployService": "my-deploy.service", // optional: forced tick proves the pin stuck
156
- "npmEnvFile": "/etc/my/npm.env", // optional: root-only NPM_TOKEN for a PRIVATE package
157
156
  "runAs": "me", // optional: unprivileged owner of the instance
158
157
  "backupDb": "mydb", // optional: pg_dump before the bump
159
158
  "backupDir": "~/backups",
@@ -247,40 +246,19 @@ function remote(target, script, { run = spawnSync, timeoutMs = 900000 } = {}) {
247
246
  // `sudo -u <user> -H env K=V … <cmd>` — the shape every runbook entry uses. Returns
248
247
  // a string safe to embed because every component was allowlist-validated.
249
248
  //
250
- // A privileged child needs npm auth, and there is NO WAY to hand it a secret through
251
- // sudo without logging it (task 1003218, corrected).
252
- //
253
- // `env NPM_TOKEN="$NPM_TOKEN" …` → sudo logs it in COMMAND=
254
- // `sudo --preserve-env=NPM_TOKEN …` → sudo logs it in ENV=
255
- //
256
- // The first was the original bug; the second was my "fix", which only MOVED the value
257
- // to a different field of the same log line. Both put a live credential in a
258
- // persistent journal. What matters is that sudo logs its whole invocation, so the
259
- // secret must never be part of it.
260
- //
261
- // So the child DERIVES the token itself, from a file it can already read. Only the
262
- // `sed` appears in argv; the value never does. This is the shape docs/recipes/
263
- // core-release-pipeline.md already documents as the safe one. The guard matters as
264
- // much as the derivation: an EMPTY token silently sends npm to the public registry,
265
- // which 404s the private package, which auto-rolls-back "cleanly" having taught you
266
- // nothing — so an empty read aborts loudly instead of half-running.
267
- //
268
- // (The nightly lane never takes this path: it runs under systemd as the owner with an
269
- // EnvironmentFile, no sudo, and leaks nothing. This is the manual/agent path only.)
270
- const NPM_AUTH_PREAMBLE = [
271
- 'export NPM_TOKEN="${NPM_TOKEN:-$(sed -n \'s/.*_authToken=//p\' "$HOME/.npmrc" 2>/dev/null | head -1)}"',
272
- '[ -n "$NPM_TOKEN" ] || { echo "go-live: EMPTY NPM_TOKEN — npm would 404 the private core and roll back; aborting" >&2; exit 12; }',
273
- ].join('; ');
274
-
275
249
  // `sudo -u <user> -H env K=V … <cmd>` — the shape every runbook entry uses. Returns
276
250
  // a string safe to embed because every component was allowlist-validated.
277
251
  //
278
- // `needsNpm` wraps the command so it derives its own npm auth (see above) instead of
279
- // having a secret handed across the privilege boundary. Only NON-secret configuration
280
- // (PGDATABASE, HOME) travels in the plain `env K=V` list — seeing those in a log is
281
- // useful rather than harmful.
282
- function asOwner(target, cmd, { needsNpm = false } = {}) {
283
- const inner = needsNpm ? `sh -c ${shq(`${NPM_AUTH_PREAMBLE}; ${cmd}`)}` : cmd;
252
+ // Only NON-secret configuration (PGDATABASE, HOME) travels in the plain `env K=V` list —
253
+ // seeing those in a log is useful rather than harmful.
254
+ //
255
+ // This used to take a `needsNpm` option wrapping the command in a preamble that DERIVED an
256
+ // npm credential on the far side, because sudo logs its whole invocation and there was no way
257
+ // to hand a privileged child a secret without journalling it (task 1003218). @bongos/core is
258
+ // public (task 1003872), so the deploy path needs no npm credential at all and the whole
259
+ // hazard is gone rather than mitigated (task 1003878).
260
+ function asOwner(target, cmd) {
261
+ const inner = cmd;
284
262
  const envPairs = Object.entries(target.env || {}).map(([k, v]) => `${k}=${v}`);
285
263
  if (!target.runAs) {
286
264
  const joined = envPairs.join(' ');
@@ -622,23 +600,21 @@ function renderReport(r) {
622
600
  // Phases
623
601
  // ---------------------------------------------------------------------------
624
602
 
625
- // Registry proof + optional content proof, in ONE round-trip. The registry is
626
- // consulted ON THE BOX because a PRIVATE package 404s without the root-only
627
- // NPM_TOKEN, and because the box's answer is the one that matters.
603
+ // Registry proof + optional content proof, in ONE round-trip. The registry is still
604
+ // consulted ON THE BOX — not because auth lives there (the package is public now), but
605
+ // because the box's answer is the one that matters: its network, its resolver, its cache.
628
606
  function probeRemote(target, targetVersion, marker, opts = {}) {
629
607
  const pkg = target.registryPackage || '@bongos/core';
630
- const src = target.npmEnvFile ? `set -a; . ${target.npmEnvFile}; set +a` : 'true';
631
608
  const script = `
632
609
  set -uo pipefail
633
- ${src}
634
610
  echo "##REGISTRY##"
635
- ${asOwner(target, `npm view ${pkg}@${targetVersion} version`, { needsNpm: true })} 2>&1 | tail -1
611
+ ${asOwner(target, `npm view ${pkg}@${targetVersion} version`)} 2>&1 | tail -1
636
612
  echo "##TREE##"
637
613
  ( cd ${target.instanceDir} && ${target.runAs ? `sudo -u ${target.runAs} -H ` : ''}git status --porcelain | head -20 )
638
614
  echo "##INSTALLED##"
639
615
  ${asOwner(target, `node -e "console.log(require('${target.instanceDir}/node_modules/${pkg}/package.json').version)"`)} 2>&1 | tail -1
640
616
  ${marker ? `echo "##MARKER##"
641
- ${asOwner(target, `sh -c ${shq(markerProbe(pkg, targetVersion, marker))}`, { needsNpm: true })}` : ''}
617
+ ${asOwner(target, `sh -c ${shq(markerProbe(pkg, targetVersion, marker))}`)}` : ''}
642
618
  echo "##END##"
643
619
  `;
644
620
  const res = remote(target, script, opts);
@@ -709,7 +685,6 @@ function applyGoLive(target, targetName, targetVersion, from, opts = {}) {
709
685
  const pkg = target.registryPackage || '@bongos/core';
710
686
  const pinMode = pinModeOf(target);
711
687
  const steps = [];
712
- const src = target.npmEnvFile ? `set -a; . ${target.npmEnvFile}; set +a` : 'true';
713
688
 
714
689
  // Phase A — stop the pull-deploy timer, then back up. The timer's `reset --hard`
715
690
  // is the hazard: it would revert an uncommitted pin mid-flight. A co-tenant has no
@@ -738,8 +713,7 @@ echo "##END##"
738
713
  const upgrade = upgradeCommand(target, targetVersion);
739
714
  const phaseB = remote(target, `
740
715
  set -uo pipefail
741
- ${src}
742
- ${asOwner(target, `bash -c "cd ${target.instanceDir} && ${upgrade}"`, { needsNpm: true })}
716
+ ${asOwner(target, `bash -c "cd ${target.instanceDir} && ${upgrade}"`)}
743
717
  `, opts);
744
718
  const rolledBack = /rolled back/i.test(phaseB.stdout + phaseB.stderr);
745
719
  steps.push({
@@ -995,7 +969,7 @@ if (require.main === module) {
995
969
  module.exports = {
996
970
  loadConfig, selectTarget, validateTarget, planGoLive, artistGateEntries, readArtistGate, ARTIST_GATE_PATH, ARTIST_GATE_LEVELS,
997
971
  parseMarker, parseMarkerHits,
998
- renderPlan, renderReport, failureExcerpt, asOwner, isSafeToken, NPM_AUTH_PREAMBLE, refreshMain, isLocalTarget, gitRefExists, SUDO_PREAMBLE,
972
+ renderPlan, renderReport, failureExcerpt, asOwner, isSafeToken, refreshMain, isLocalTarget, gitRefExists, SUDO_PREAMBLE,
999
973
  applyGoLive, upgradeCommand, pinStep, pinModeOf,
1000
974
  CONFIG_TEMPLATE, HALTS, SEMVER_RE, PIN_MODES, DEFAULT_PIN_MODE,
1001
975
  };
@@ -59,13 +59,13 @@
59
59
  // bongos init --vendor-core <tgz> greenfield only: vendor the @bongos/core .tgz + pin
60
60
  // package.json to file:vendor/… + generate package-lock.json,
61
61
  // so the scaffold is `npm ci`-installable (ADR 0108 gap 1/4).
62
- // Without it the scaffold pins the caret range (which now
63
- // resolves from the PRIVATE registry — see --registry-core).
64
- // bongos init --registry-core greenfield only: keep the caret pin + write an env-fed
65
- // .npmrc (`_authToken=${NPM_TOKEN}`) so `npm ci` pulls the
66
- // PRIVATE @bongos/core from the npm registry (ADR 0134).
67
- // With NPM_TOKEN set it also generates package-lock.json;
68
- // without a token it 404s (the expected tokenless outcome).
62
+ // Without it the scaffold pins the caret range, which
63
+ // resolves from the public npm registry (--registry-core).
64
+ // bongos init --registry-core greenfield only: keep the caret pin and generate
65
+ // package-lock.json from the registry. @bongos/core is
66
+ // PUBLIC (task 1003872), so this needs no token, no login
67
+ // and no .npmrc — it resolves from the default registry
68
+ // like any other dependency.
69
69
  // Mutually exclusive with --vendor-core (registry vs tarball).
70
70
 
71
71
  const fs = require('node:fs');
@@ -78,7 +78,6 @@ const { materializeClaude } = require('./claude-materialize'); // ADR 0108 §3 (
78
78
  const { detectStack, formatStackSummary } = require('./adopt-detect'); // ADR 0121 §Decision 4 (task 2043): inspect an existing repo before layering
79
79
  const { resolveBacklogTasks, DEFAULT_MAX: BACKLOG_MAX } = require('./adopt-import'); // ADR 0121 §Decision 3 (task 2044): import an existing backlog
80
80
  const { preflightAdopt, formatPreflight } = require('./adopt-preflight'); // ADR 0121 §Decision 5 (task 2045): the accept-gated conflict pre-flight
81
- const { writeNpmrc, hasNpmToken } = require('./npmrc'); // ADR 0134 (task 2090): the registry-install path's env-fed .npmrc
82
81
 
83
82
  const KNOWN_MODULES = ['game', 'art-pipeline', 'discord', 'dev-box'];
84
83
 
@@ -946,7 +945,7 @@ async function main(argv) {
946
945
  const adopt = hasFlag('--adopt', args); // brownfield: LAYER onto an existing repo (ADR 0121)
947
946
  const accept = hasFlag('--accept', args) || hasFlag('--yes', args) || hasFlag('-y', args); // adopt pre-flight accept-gate (§Decision 5)
948
947
  const vendorCore = arg('--vendor-core', args); // greenfield: vendor + file:-pin a core .tgz, then generate the lockfile (ADR 0108 gap 1/4, task 2053)
949
- const registryCore = hasFlag('--registry-core', args); // greenfield: caret pin + env-fed .npmrc → `npm ci` pulls the PRIVATE core from the registry (ADR 0134, task 2090)
948
+ const registryCore = hasFlag('--registry-core', args); // greenfield: keep the caret pin and generate the lockfile from the public registry (task 1003878)
950
949
 
951
950
  if (adopt && force) {
952
951
  console.error('init: --force is a greenfield-only overwrite; --adopt never overwrites, so --force is ignored.');
@@ -1030,30 +1029,23 @@ async function main(argv) {
1030
1029
  }
1031
1030
  }
1032
1031
 
1033
- // Registry-install path (ADR 0134, task 2090): keep the caret pin writeConfigs wrote
1034
- // and add an env-fed .npmrc so `npm ci` pulls the PRIVATE @bongos/core from the
1035
- // registry. With NPM_TOKEN present we also generate package-lock.json (a reproducible
1036
- // `npm ci` later); without a token we still write the caret pin + .npmrc so the
1037
- // scaffold is registry-shaped, and say plainly that the private package 404s until a
1038
- // token is provided — the expected tokenless outcome the ADR calls to verify.
1032
+ // Registry-install path: keep the caret pin writeConfigs wrote and generate
1033
+ // package-lock.json from the registry, so a later `npm ci` is reproducible.
1034
+ //
1035
+ // This used to write an env-fed npm auth file and skip the lockfile when no credential was
1036
+ // present, because @bongos/core published PRIVATE. It is public now (task 1003872), so it
1037
+ // resolves from the default registry like any other dependency: no auth file, no credential,
1038
+ // and no tokenless-404 branch to explain (task 1003878).
1039
1039
  if (registryCore) {
1040
1040
  if (dryRun) {
1041
- console.log(`\n[dry-run] would write .npmrc (env-fed @bongos auth) and ${hasNpmToken()
1042
- ? 'run `npm install` to generate package-lock.json from the registry'
1043
- : 'SKIP lockfile generation (no NPM_TOKEN — the private core 404s until one is provided)'}`);
1041
+ console.log('\n[dry-run] would run `npm install` to generate package-lock.json from the registry');
1044
1042
  } else {
1045
- console.log('\nWiring the registry-install path (ADR 0134):');
1046
- writeNpmrc(dir, { log: console.log });
1047
- if (hasNpmToken()) {
1048
- const inst = upgrade.npmInstall(dir);
1049
- if (!inst.ok) {
1050
- console.error(` ! npm install failed (${inst.reason || 'exit ' + inst.code}) — confirm NPM_TOKEN reaches the private @bongos org; package-lock.json not generated.`);
1051
- } else {
1052
- console.log(' ✓ npm install → package-lock.json generated (pulled @bongos/core from the registry)');
1053
- }
1043
+ console.log('\nWiring the registry-install path:');
1044
+ const inst = upgrade.npmInstall(dir);
1045
+ if (!inst.ok) {
1046
+ console.error(` ! npm install failed (${inst.reason || 'exit ' + inst.code}) — package-lock.json not generated. @bongos/core is public, so this is a real failure (network, registry outage, or an unpublished version), not a missing credential.`);
1054
1047
  } else {
1055
- console.log(' • no NPM_TOKEN in the environment — wrote the caret pin + .npmrc but skipped lockfile generation.');
1056
- console.log(' Provide a read-only automation token (paid `bongos` org) as NPM_TOKEN before `npm ci`; without it the private package 404s cleanly.');
1048
+ console.log(' ✓ npm install → package-lock.json generated (pulled @bongos/core from the registry)');
1057
1049
  }
1058
1050
  }
1059
1051
  }
@@ -580,7 +580,8 @@ function publishArgs(tgzPath, live) {
580
580
 
581
581
  // Publish (or dry-run) the built .tgz to npm. Auth is npm's own concern — this
582
582
  // never holds a token; it shells out to `npm publish` and the owner's `npm login`
583
- // (+ the cloudbongos org on a paid plan, for a PRIVATE scoped publish) provides it.
583
+ // provides it. The package is PUBLIC (task 1003872, ADR 0283), so no paid org plan
584
+ // is involved — a public scoped publish needs none.
584
585
  // The child's stdout is routed to OUR stderr so main()'s machine-readable pin on
585
586
  // stdout stays clean for CI capture.
586
587
  function publishTarball({ tgzPath, live }) {
@@ -589,16 +590,16 @@ function publishTarball({ tgzPath, live }) {
589
590
  console.error(`[package-core] ${live ? 'PUBLISH (permanent)' : 'publish DRY-RUN'} → ${NPM_BIN} ${args.join(' ')}`);
590
591
  if (!live) {
591
592
  console.error(' Dry-run: nothing is uploaded. To publish for real: re-run with --publish --live.');
592
- console.error(' That is PERMANENT and requires `npm login` + the cloudbongos npm org on a paid plan');
593
- console.error(' (private scoped publish needs one). Going public later: `npm access public @bongos/core`.');
593
+ console.error(' That is PERMANENT and PUBLIC (ADR 0283) — anyone can fetch it, and a version is never');
594
+ console.error(' reusable once taken. It requires `npm login`; no paid org plan (the package is public).');
594
595
  }
595
596
  const r = spawnSync(NPM_BIN, args, { cwd: REPO_ROOT, stdio: ['inherit', process.stderr, 'inherit'] });
596
597
  if (r.error || r.status !== 0) {
597
598
  console.error(`[package-core] npm publish ${live ? '' : '--dry-run '}FAILED (${r.error ? r.error.message : `exit ${r.status}`}).`);
598
- if (live) console.error(' Common causes: not logged in (`npm login`), the org isn’t on a paid plan (private scoped publish requires one), or 2FA.');
599
+ if (live) console.error(' Common causes: not logged in (`npm login`), no publish rights on the @bongos scope, that version already taken, or 2FA.');
599
600
  process.exit(r.status || 1);
600
601
  }
601
- console.error(`[package-core] npm publish ${live ? 'succeeded — @bongos/core is live (private).' : 'dry-run OK — the tarball is publishable.'}`);
602
+ console.error(`[package-core] npm publish ${live ? 'succeeded — @bongos/core is live and PUBLIC on the registry.' : 'dry-run OK — the tarball is publishable.'}`);
602
603
  }
603
604
 
604
605
  // The neutral FLOOR — customer-zero identity tokens a portable core must never carry (task 1002220
@@ -1048,7 +1049,7 @@ module.exports = {
1048
1049
  isAgentDoc, CORE_AGENT_DOC_STUB,
1049
1050
  // task 1882 (ADR 0108 §2) — the real installable npm package
1050
1051
  CORE_DEP_NAMES, buildPackageJson, resolveLockDep, lockfileClosure, prunePackageLock,
1051
- // task 2089 (ADR 0134) — private-by-default npm publish
1052
+ // task 2089 (ADR 0134) — the npm publish argv; PUBLIC since task 1003872 / ADR 0283
1052
1053
  publishArgs,
1053
1054
  // task 1002218 (audit H3) — build-time self-verify (reproduce pin + optional bare-install boot)
1054
1055
  selfVerify, reproducePinFromTree,
@@ -19,12 +19,6 @@ const REPO_ROOT = path.resolve(__dirname, '..', '..');
19
19
  // plain require (its require.main guard means importing it runs no CLI).
20
20
  const oauthSecret = require('./oauth-secret');
21
21
 
22
- // The registry-install gates + env-fed .npmrc (ADR 0134, task 2090). Lets the
23
- // standalone scaffold OPT IN to pulling the PRIVATE @bongos/core from the npm
24
- // registry (PROVISION_CORE_FROM_REGISTRY=1) instead of vendoring a tarball, with a
25
- // fail-soft fallback to the vendored path when no NPM_TOKEN is present.
26
- const { registryCoreRequested, hasNpmToken } = require('./npmrc');
27
-
28
22
  // The HTTP User-Agent the manifest conversion stamps its GitHub call with (GitHub
29
23
  // requires a UA). Overridable so a fork can identify itself; not secret.
30
24
  const MANIFEST_UA = process.env.PROVISION_GITHUB_UA || 'cloudbongos-provisioner';
@@ -195,10 +189,8 @@ module.exports = {
195
189
  REPO_ROOT,
196
190
  coreVersionSafe,
197
191
  hasFlag,
198
- hasNpmToken,
199
192
  loadDeps,
200
193
  numEnv,
201
194
  oauthSecret,
202
195
  provisionerBotEmail,
203
- registryCoreRequested,
204
196
  };