@bongos/core 1.19.847 → 1.19.849
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 +35 -20
- package/Dockerfile +6 -1
- package/docs/module-api-changelog.md +4 -0
- package/docs/recipes/self-host.md +1 -1
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/scripts/gds/package-core.js +96 -16
- package/scripts/gds/paste-token.js +19 -2
- package/scripts/gds/provision-repo.js +40 -23
- package/scripts/gds/provision.js +3 -1
- package/scripts/gds/ship-escalation.js +148 -0
- package/scripts/gds/ship-flow.js +34 -4
- package/src/module-api.js +1 -1
- package/tests/no_artifact_escalation.mjs +248 -0
- package/tests/package_core.mjs +65 -0
- package/tests/paste_token_session_store.mjs +188 -0
- package/tests/provision.mjs +72 -0
package/.bongos-core.json
CHANGED
|
@@ -2,22 +2,22 @@
|
|
|
2
2
|
"artifact": "bongos-core",
|
|
3
3
|
"manifest_schema": 1,
|
|
4
4
|
"generator": "scripts/gds/package-core.js",
|
|
5
|
-
"core_version": "1.19.
|
|
6
|
-
"core_contract": "1.19.
|
|
7
|
-
"source_commit": "
|
|
5
|
+
"core_version": "1.19.849",
|
|
6
|
+
"core_contract": "1.19.849",
|
|
7
|
+
"source_commit": "a027efb6aa355bb0aa4454dba3a3e2de3627f108",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-09-
|
|
9
|
+
"built_at": "2026-09-17T20:09:50.093Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
12
|
"docs_redacted": 496,
|
|
13
13
|
"agent_docs_stubbed": 26,
|
|
14
|
-
"functional_verbatim":
|
|
14
|
+
"functional_verbatim": 2276,
|
|
15
15
|
"rules": 3,
|
|
16
16
|
"gate_literals": 3,
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
|
-
"file_count":
|
|
20
|
-
"tree_sha256": "
|
|
19
|
+
"file_count": 2798,
|
|
20
|
+
"tree_sha256": "894ee6e6b895465db909d512fe4cff3a9ac80665702d069dcdb6c5cafdc61050",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/ask-for-help/SKILL.md",
|
|
@@ -322,7 +322,7 @@
|
|
|
322
322
|
{
|
|
323
323
|
"path": "Dockerfile",
|
|
324
324
|
"mode": "0000644",
|
|
325
|
-
"sha256": "
|
|
325
|
+
"sha256": "e0876b6353244e13917d10510d9f60f61dd85983dee9b19a5da762c574cbae4b"
|
|
326
326
|
},
|
|
327
327
|
{
|
|
328
328
|
"path": "GOVERNANCE.md",
|
|
@@ -2582,7 +2582,7 @@
|
|
|
2582
2582
|
{
|
|
2583
2583
|
"path": "docs/module-api-changelog.md",
|
|
2584
2584
|
"mode": "0000644",
|
|
2585
|
-
"sha256": "
|
|
2585
|
+
"sha256": "5795b2567e4a68c109fb1c36a46346dd3b40ded0ea206d4368ec58cd23916849"
|
|
2586
2586
|
},
|
|
2587
2587
|
{
|
|
2588
2588
|
"path": "docs/modules-contract.md",
|
|
@@ -2812,7 +2812,7 @@
|
|
|
2812
2812
|
{
|
|
2813
2813
|
"path": "docs/recipes/self-host.md",
|
|
2814
2814
|
"mode": "0000644",
|
|
2815
|
-
"sha256": "
|
|
2815
|
+
"sha256": "135f3942a01e50af87c4645f8920b25e0b779aac45cb5101b39e62cacfa59168"
|
|
2816
2816
|
},
|
|
2817
2817
|
{
|
|
2818
2818
|
"path": "docs/recipes/server-bring-up.md",
|
|
@@ -7907,12 +7907,12 @@
|
|
|
7907
7907
|
{
|
|
7908
7908
|
"path": "package-lock.json",
|
|
7909
7909
|
"mode": "0000644",
|
|
7910
|
-
"sha256": "
|
|
7910
|
+
"sha256": "3a5f72a3b77cc19f6404799e646685ee91e3996ebb614b66b756722a6c23edc5"
|
|
7911
7911
|
},
|
|
7912
7912
|
{
|
|
7913
7913
|
"path": "package.json",
|
|
7914
7914
|
"mode": "0000644",
|
|
7915
|
-
"sha256": "
|
|
7915
|
+
"sha256": "61c0a90cce564a70963e5ab3b301e6349e89c135e56767a94107df0817a42c31"
|
|
7916
7916
|
},
|
|
7917
7917
|
{
|
|
7918
7918
|
"path": "public-docs/index.html",
|
|
@@ -8767,12 +8767,12 @@
|
|
|
8767
8767
|
{
|
|
8768
8768
|
"path": "scripts/gds/package-core.js",
|
|
8769
8769
|
"mode": "0000644",
|
|
8770
|
-
"sha256": "
|
|
8770
|
+
"sha256": "4d439440fe0fe498382c7ee6e1684637a5bcad46ae67878c0d2c98d5de6cbbdd"
|
|
8771
8771
|
},
|
|
8772
8772
|
{
|
|
8773
8773
|
"path": "scripts/gds/paste-token.js",
|
|
8774
8774
|
"mode": "0000644",
|
|
8775
|
-
"sha256": "
|
|
8775
|
+
"sha256": "9c3fd12318b04062382d8f1309d2da64de7436c93ff7bb25d2b1f5ac5cec7795"
|
|
8776
8776
|
},
|
|
8777
8777
|
{
|
|
8778
8778
|
"path": "scripts/gds/plain-cards.js",
|
|
@@ -8837,7 +8837,7 @@
|
|
|
8837
8837
|
{
|
|
8838
8838
|
"path": "scripts/gds/provision-repo.js",
|
|
8839
8839
|
"mode": "0000644",
|
|
8840
|
-
"sha256": "
|
|
8840
|
+
"sha256": "b04300749c4f119912ecfb2e6df4a6703f004130570e47a4e5bfaffacd7e2118"
|
|
8841
8841
|
},
|
|
8842
8842
|
{
|
|
8843
8843
|
"path": "scripts/gds/provision-teardown.js",
|
|
@@ -8852,7 +8852,7 @@
|
|
|
8852
8852
|
{
|
|
8853
8853
|
"path": "scripts/gds/provision.js",
|
|
8854
8854
|
"mode": "0000644",
|
|
8855
|
-
"sha256": "
|
|
8855
|
+
"sha256": "cbf44d415e3c5bb803e68af7b70ebfb08e7d0ab2388cc7f17a114512f29662e8"
|
|
8856
8856
|
},
|
|
8857
8857
|
{
|
|
8858
8858
|
"path": "scripts/gds/publish-credential-check.js",
|
|
@@ -9189,6 +9189,11 @@
|
|
|
9189
9189
|
"mode": "0000644",
|
|
9190
9190
|
"sha256": "96ad972d929f3733f871d45df1a9ff7013f9f74154f9e37d5e2df55e7959c1a5"
|
|
9191
9191
|
},
|
|
9192
|
+
{
|
|
9193
|
+
"path": "scripts/gds/ship-escalation.js",
|
|
9194
|
+
"mode": "0000644",
|
|
9195
|
+
"sha256": "500c73254cb071f9e9ef08e4a3fc934f6a49a25193be9975c39704fb19e67429"
|
|
9196
|
+
},
|
|
9192
9197
|
{
|
|
9193
9198
|
"path": "scripts/gds/ship-finish.js",
|
|
9194
9199
|
"mode": "0000644",
|
|
@@ -9197,7 +9202,7 @@
|
|
|
9197
9202
|
{
|
|
9198
9203
|
"path": "scripts/gds/ship-flow.js",
|
|
9199
9204
|
"mode": "0000644",
|
|
9200
|
-
"sha256": "
|
|
9205
|
+
"sha256": "4b8179e5c1aae3fc4845457a4089c57f6cf105c33d03d21d3ade99776371e50f"
|
|
9201
9206
|
},
|
|
9202
9207
|
{
|
|
9203
9208
|
"path": "scripts/gds/ship-git.js",
|
|
@@ -9847,7 +9852,7 @@
|
|
|
9847
9852
|
{
|
|
9848
9853
|
"path": "src/module-api.js",
|
|
9849
9854
|
"mode": "0000644",
|
|
9850
|
-
"sha256": "
|
|
9855
|
+
"sha256": "2b2515e91e33976af14b7538bff4d39a0ea053a33b84fb86a848254a4c3e9af7"
|
|
9851
9856
|
},
|
|
9852
9857
|
{
|
|
9853
9858
|
"path": "src/module-loader/catalog.js",
|
|
@@ -12354,6 +12359,11 @@
|
|
|
12354
12359
|
"mode": "0000644",
|
|
12355
12360
|
"sha256": "c57c0211709ab041ee895bbb4ade9f4b146b43147570bf282e6d6a808560db76"
|
|
12356
12361
|
},
|
|
12362
|
+
{
|
|
12363
|
+
"path": "tests/no_artifact_escalation.mjs",
|
|
12364
|
+
"mode": "0000644",
|
|
12365
|
+
"sha256": "3d2b03a39cbefc02565a190dee0612e14576edbf32ea0fccf798d6ed28fee8f1"
|
|
12366
|
+
},
|
|
12357
12367
|
{
|
|
12358
12368
|
"path": "tests/oauth_secret.mjs",
|
|
12359
12369
|
"mode": "0000644",
|
|
@@ -12427,7 +12437,7 @@
|
|
|
12427
12437
|
{
|
|
12428
12438
|
"path": "tests/package_core.mjs",
|
|
12429
12439
|
"mode": "0000644",
|
|
12430
|
-
"sha256": "
|
|
12440
|
+
"sha256": "7d64e3c217274af48f24557d49deeb2cb54d7929bd9e939f15b3d3ceb757336c"
|
|
12431
12441
|
},
|
|
12432
12442
|
{
|
|
12433
12443
|
"path": "tests/parallel-load.mjs",
|
|
@@ -12444,6 +12454,11 @@
|
|
|
12444
12454
|
"mode": "0000644",
|
|
12445
12455
|
"sha256": "e48806b48a9f7a4f9f9c010f45750e6b440d2de68feacdabacd9bd25d98f2046"
|
|
12446
12456
|
},
|
|
12457
|
+
{
|
|
12458
|
+
"path": "tests/paste_token_session_store.mjs",
|
|
12459
|
+
"mode": "0000644",
|
|
12460
|
+
"sha256": "9901e8e0b857c3061421909180bb1e34f1752a1ccfaca8150a52e7a79bf1227b"
|
|
12461
|
+
},
|
|
12447
12462
|
{
|
|
12448
12463
|
"path": "tests/patch_gate_ordering.mjs",
|
|
12449
12464
|
"mode": "0000644",
|
|
@@ -12702,7 +12717,7 @@
|
|
|
12702
12717
|
{
|
|
12703
12718
|
"path": "tests/provision.mjs",
|
|
12704
12719
|
"mode": "0000644",
|
|
12705
|
-
"sha256": "
|
|
12720
|
+
"sha256": "d763e2b5a10fbdd9a0e95fae493b524c7a52b437663125e3dbaf753435402e9c"
|
|
12706
12721
|
},
|
|
12707
12722
|
{
|
|
12708
12723
|
"path": "tests/provision_caddy_wiring.mjs",
|
package/Dockerfile
CHANGED
|
@@ -15,8 +15,13 @@ FROM node:22-bookworm-slim
|
|
|
15
15
|
|
|
16
16
|
# postgresql-client gives us `psql` for scripts/migrate.sh; bash runs it; tini
|
|
17
17
|
# is a proper PID 1 so signals/zombies are handled. curl powers HEALTHCHECK.
|
|
18
|
+
# git is the provisioning scaffold's only unmet tool (task 1003995): scaffoldStandaloneRepo
|
|
19
|
+
# materializes a child instance's repo with `git init`/`clone` → commit → push, so without it
|
|
20
|
+
# `provision.js run-intents --apply` cannot run here at all. The image still carries NO .git
|
|
21
|
+
# of its own (.dockerignore excludes it by design) — that half is package-core's
|
|
22
|
+
# --from-worktree rung, not an apt line.
|
|
18
23
|
RUN apt-get update \
|
|
19
|
-
&& apt-get install -y --no-install-recommends postgresql-client bash tini curl ca-certificates \
|
|
24
|
+
&& apt-get install -y --no-install-recommends postgresql-client bash tini curl ca-certificates git \
|
|
20
25
|
&& rm -rf /var/lib/apt/lists/*
|
|
21
26
|
|
|
22
27
|
WORKDIR /app
|
|
@@ -2168,5 +2168,9 @@ is load-bearing: the script throws rather than guess if it is missing, and
|
|
|
2168
2168
|
landed since 1.19.845 with no explicit bump. run 35264882014. (task 1002620)
|
|
2169
2169
|
1.19.847 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
2170
2170
|
landed since 1.19.846 with no explicit bump. run 35266409194. (task 1002620)
|
|
2171
|
+
1.19.848 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
2172
|
+
landed since 1.19.847 with no explicit bump. run 35267380720. (task 1002620)
|
|
2173
|
+
1.19.849 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
2174
|
+
landed since 1.19.848 with no explicit bump. run 35269098215. (task 1002620)
|
|
2171
2175
|
---------------------------------------------------------------------------
|
|
2172
2176
|
```
|
|
@@ -39,7 +39,7 @@ Author `config/branding.json` from the neutral starter — see [the branding con
|
|
|
39
39
|
|
|
40
40
|
**Or scaffold it with `bongos init`** ([task #1204](https://example.com/builders#/task/1204)): a short interview (or `bongos init --from spec.json` for a hands-free / CI run) that writes both config files, **seeds your first version + its done-when goal**, and **files the human-only kickoff steps** (register a GitHub OAuth app, first owner sign-in, repo binding, admission) as real tracked tasks — so a fresh instance has a claimable queue on day one instead of an empty hall. It never invents your branding (it points you at the fill prompt for that). Preview the whole plan with `bongos init --dry-run`.
|
|
41
41
|
|
|
42
|
-
**Production hosting, optional (the `provisioning` module — [ADR 0111](../adr/0111-instance-hosting-provisioning-module.md) §3).** If you enable the `provisioning` module and answer the interview's hosting questions (shape — co-tenant `$0` or a dedicated droplet — bring-your-own domain, credential ref, tier), init turns those answers into a real **provisioning request** (a queued `provision` intent the control-plane runner drains) instead of only filing a manual "production topology" task. **A queued request does nothing until a runner drains it:** on the control plane that is a systemd timer running `node scripts/gds/provision.js run-intents --apply`. This Docker image
|
|
42
|
+
**Production hosting, optional (the `provisioning` module — [ADR 0111](../adr/0111-instance-hosting-provisioning-module.md) §3).** If you enable the `provisioning` module and answer the interview's hosting questions (shape — co-tenant `$0` or a dedicated droplet — bring-your-own domain, credential ref, tier), init turns those answers into a real **provisioning request** (a queued `provision` intent the control-plane runner drains) instead of only filing a manual "production topology" task. **A queued request does nothing until a runner drains it:** on the control plane that is a systemd timer running `node scripts/gds/provision.js run-intents --apply`. This Docker image now carries the tools the scaffold needs — git, and a core it can package out of its own `.git`-less checkout (task 1003995) — but the standup leg still assumes root + systemd, which a container has not got (task 1003997), so on a compose self-host a new project waits indefinitely. The two irreducibly-human legs — the GitHub OAuth app and the first owner sign-in — stay guided/manual. If the module isn't enabled on the target instance, init falls back to the manual production-topology task, so nothing is silently skipped.
|
|
43
43
|
|
|
44
44
|
## Configuration surface
|
|
45
45
|
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.849",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.19.
|
|
9
|
+
"version": "1.19.849",
|
|
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.
|
|
3
|
+
"version": "1.19.849",
|
|
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",
|
|
@@ -51,9 +51,22 @@
|
|
|
51
51
|
// The manifest ALSO records tarball.sha256 to verify the exact downloaded bytes.
|
|
52
52
|
//
|
|
53
53
|
// USAGE:
|
|
54
|
-
// node scripts/gds/package-core.js [--ref <git-ref>] [--out-dir dist]
|
|
54
|
+
// node scripts/gds/package-core.js [--ref <git-ref> | --from-worktree] [--out-dir dist]
|
|
55
55
|
// [--version <v>] [--droplet-ip <ip>] [--self-verify [--self-verify-boot]] [--publish [--live]]
|
|
56
56
|
// Defaults: --ref HEAD, --out-dir dist, --version = platform CORE_VERSION.
|
|
57
|
+
// --from-worktree build from the files ON DISK instead of a commit, for a core checkout
|
|
58
|
+
// that carries no git history — which is exactly what the shipped
|
|
59
|
+
// self-host image is (.dockerignore excludes .git by design), and the
|
|
60
|
+
// only way the provisioning scaffold inside it can vendor a core into a
|
|
61
|
+
// child instance (task 1003995). Selection is unchanged (isPublishable);
|
|
62
|
+
// the artifact simply has no commit to anchor to, so source_commit is null
|
|
63
|
+
// and the tree_sha256 pin is the whole lineage. It packages what is ON DISK,
|
|
64
|
+
// which in the image is exactly what `COPY . .` put there — so on a DEV
|
|
65
|
+
// checkout prefer the ref build: measured on this repo the two select an
|
|
66
|
+
// IDENTICAL 2793-file set, but a Windows working tree hands back CRLF where
|
|
67
|
+
// the blob is LF, which changes every text file's hash and would ship a
|
|
68
|
+
// CRLF shebang. That is why rung 4 in provision-repo.js is reached only
|
|
69
|
+
// when there is no history to build from.
|
|
57
70
|
// --self-verify after building, prove the artifact reproduces its pin (unpack the .tgz,
|
|
58
71
|
// recompute tree_sha256 == manifest + verify tarball.sha256). Fatal on mismatch.
|
|
59
72
|
// (task 1002218 / audit H3) — deterministic + network-free.
|
|
@@ -85,7 +98,7 @@ const REPO_ROOT = path.resolve(__dirname, '..', '..');
|
|
|
85
98
|
|
|
86
99
|
// Reuse — never re-type. The core definition, the redaction, and the tree parser
|
|
87
100
|
// all already exist and are unit-tested at their homes.
|
|
88
|
-
const { isPublishable } = require(path.join(REPO_ROOT, 'scripts/gds/publish-manifest.js'));
|
|
101
|
+
const { isPublishable, PUBLISH_ALLOWLIST } = require(path.join(REPO_ROOT, 'scripts/gds/publish-manifest.js'));
|
|
89
102
|
const {
|
|
90
103
|
buildRedactionRules, collectSensitiveLiterals, findLeaks,
|
|
91
104
|
} = require(path.join(REPO_ROOT, 'scripts/gds/mirror-redact.js'));
|
|
@@ -544,6 +557,47 @@ function buildTar(entries) {
|
|
|
544
557
|
return zlib.gzipSync(tar, { level: 9 });
|
|
545
558
|
}
|
|
546
559
|
|
|
560
|
+
// ---- the git-LESS source: enumerate the working tree (task 1003995) --------
|
|
561
|
+
//
|
|
562
|
+
// Everything below the enumeration step is already git-free, so the ONE thing that
|
|
563
|
+
// makes package-core unrunnable without history is `git ls-tree`. That matters because
|
|
564
|
+
// the shipped self-host image IS a core checkout with no `.git` (.dockerignore excludes
|
|
565
|
+
// it by design), and the provisioning scaffold inside it has no other way to build the
|
|
566
|
+
// tarball it vendors into a child instance — the fourth rung of the ladder in
|
|
567
|
+
// scripts/gds/provision-repo.js.
|
|
568
|
+
//
|
|
569
|
+
// The walk starts at PUBLISH_ALLOWLIST's own entries rather than the repo root. The
|
|
570
|
+
// allowlist is already the single source of truth for what the core is, so starting
|
|
571
|
+
// there means node_modules/ and dist/ are never descended into at all. Selection still
|
|
572
|
+
// runs through isPublishable() in main(), so the denylist applies exactly as it does on
|
|
573
|
+
// the git path — this function only decides what is OFFERED, never what is kept.
|
|
574
|
+
//
|
|
575
|
+
// Rows match parseLsTree's shape ({ path, type, mode }) minus `sha`: there is no object
|
|
576
|
+
// to name, so main() reads these blobs off disk instead. Mode is derived the way git
|
|
577
|
+
// derives it — the executable bit and nothing else. fs injectable for tests.
|
|
578
|
+
function walkWorktreeEntries(root, { allow = PUBLISH_ALLOWLIST, fsImpl = fs } = {}) {
|
|
579
|
+
const out = [];
|
|
580
|
+
const addFile = (rel) => {
|
|
581
|
+
let st;
|
|
582
|
+
try { st = fsImpl.statSync(path.join(root, rel)); } catch { return; } // absent here → simply not in the core set
|
|
583
|
+
if (st.isFile()) out.push({ path: rel, type: 'blob', mode: (st.mode & 0o111) ? '100755' : '100644' });
|
|
584
|
+
};
|
|
585
|
+
const walk = (rel) => {
|
|
586
|
+
let children;
|
|
587
|
+
try { children = fsImpl.readdirSync(path.join(root, rel), { withFileTypes: true }); } catch { return; }
|
|
588
|
+
for (const child of children) {
|
|
589
|
+
const childRel = `${rel}/${child.name}`;
|
|
590
|
+
if (child.isDirectory()) walk(childRel);
|
|
591
|
+
else if (child.isFile()) addFile(childRel);
|
|
592
|
+
}
|
|
593
|
+
};
|
|
594
|
+
for (const entry of allow) {
|
|
595
|
+
if (entry.endsWith('/')) walk(entry.replace(/\/$/, ''));
|
|
596
|
+
else addFile(entry);
|
|
597
|
+
}
|
|
598
|
+
return out.sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));
|
|
599
|
+
}
|
|
600
|
+
|
|
547
601
|
// ---- git plumbing (impure) ------------------------------------------------
|
|
548
602
|
function git(args) {
|
|
549
603
|
return execFileSync('git', ['-C', REPO_ROOT, ...args], { encoding: 'utf8', maxBuffer: 64 * 1024 * 1024 });
|
|
@@ -773,16 +827,22 @@ function selfVerify({ tgzPath, sidecar, outDir, boot }) {
|
|
|
773
827
|
}
|
|
774
828
|
|
|
775
829
|
function main() {
|
|
776
|
-
|
|
830
|
+
// --from-worktree: build from the files on disk instead of a commit (task 1003995).
|
|
831
|
+
// The lineage anchor of such a build is the tree hash alone — there is no commit to
|
|
832
|
+
// name — so source_commit is null and source_ref says where it came from.
|
|
833
|
+
const fromWorktree = process.argv.includes('--from-worktree');
|
|
834
|
+
const ref = fromWorktree ? '(worktree)' : arg('--ref', 'HEAD');
|
|
777
835
|
const outDir = path.resolve(REPO_ROOT, arg('--out-dir', 'dist'));
|
|
778
836
|
const version = arg('--version', coreVersion());
|
|
779
837
|
const contract = coreVersion();
|
|
780
838
|
const dropletIp = arg('--droplet-ip', process.env.DROPLET_IP || null);
|
|
781
839
|
|
|
782
840
|
// 1. Resolve the source ref → commit SHA (the lineage anchor of this artifact).
|
|
783
|
-
let sha;
|
|
784
|
-
|
|
785
|
-
|
|
841
|
+
let sha = null;
|
|
842
|
+
if (!fromWorktree) {
|
|
843
|
+
try { sha = git(['rev-parse', ref]).trim(); }
|
|
844
|
+
catch (e) { console.error(`refusing: could not resolve --ref "${ref}": ${e.message}`); process.exit(2); }
|
|
845
|
+
}
|
|
786
846
|
|
|
787
847
|
// 2. Enumerate + partition the tree through the manifest (single source of truth).
|
|
788
848
|
// package.json/package-lock.json are SYNTHESIZED (task 1882, ADR 0108 §2) —
|
|
@@ -790,7 +850,12 @@ function main() {
|
|
|
790
850
|
// ships them verbatim), but the core package needs its OWN identity + a pruned
|
|
791
851
|
// dependency set, not the game's. Pulled out of the generic per-file loop below.
|
|
792
852
|
const SYNTHESIZED_PATHS = new Set(['package.json', 'package-lock.json']);
|
|
793
|
-
const entries =
|
|
853
|
+
const entries = fromWorktree
|
|
854
|
+
? walkWorktreeEntries(REPO_ROOT)
|
|
855
|
+
: parseLsTree(git(['ls-tree', '-r', sha])).filter((e) => e.type === 'blob');
|
|
856
|
+
// A blob is named by its git object on the ref path and by its own path on the
|
|
857
|
+
// worktree path; everything downstream just wants the bytes.
|
|
858
|
+
const readBlob = (e) => (fromWorktree ? fs.readFileSync(path.join(REPO_ROOT, e.path)) : gitBuf(['cat-file', 'blob', e.sha]));
|
|
794
859
|
const selected = entries.filter((e) => isPublishable(e.path) && !SYNTHESIZED_PATHS.has(e.path));
|
|
795
860
|
// Drop npm-never-installed files (.gitignore, .npmignore, .npmrc) so the manifest +
|
|
796
861
|
// tree_sha256 pin describe exactly what `npm install` materializes — otherwise the
|
|
@@ -798,7 +863,10 @@ function main() {
|
|
|
798
863
|
// auto-rolls-back (idea 1000543). Done here, so they never reach the tarball or the pin.
|
|
799
864
|
const npmStripped = selected.filter((e) => isNpmStripped(e.path));
|
|
800
865
|
const publishable = selected.filter((e) => !isNpmStripped(e.path));
|
|
801
|
-
|
|
866
|
+
// The worktree walk is already allowlist-scoped and never offers the two synthesized
|
|
867
|
+
// paths, so only the ref path discounts them from what it enumerated.
|
|
868
|
+
const excludedCount = entries.length - selected.length - (fromWorktree ? 0 : SYNTHESIZED_PATHS.size);
|
|
869
|
+
console.error(`[package-core] ${ref}${sha ? ` = ${sha.slice(0, 12)}` : ''} — ${entries.length} files, ${publishable.length} in the core (+2 synthesized), ${excludedCount} excluded.`);
|
|
802
870
|
if (npmStripped.length) {
|
|
803
871
|
console.error(`[package-core] dropped ${npmStripped.length} npm-never-installed file(s) from the pin (npm strips these on install; idea 1000543): ${npmStripped.map((e) => e.path).join(', ')}`);
|
|
804
872
|
}
|
|
@@ -811,14 +879,24 @@ function main() {
|
|
|
811
879
|
// SOURCE files at this ref (never the live working tree, so --ref stays
|
|
812
880
|
// reproducible) — express+pg only, name @bongos/core, version = the
|
|
813
881
|
// platform version being packaged.
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
882
|
+
// On the worktree path there is no ref to stay reproducible against, so the two
|
|
883
|
+
// files come off disk beside everything else in the build.
|
|
884
|
+
const sourceRootFile = (rel) => {
|
|
885
|
+
if (fromWorktree) {
|
|
886
|
+
const abs = path.join(REPO_ROOT, rel);
|
|
887
|
+
return fs.existsSync(abs) ? fs.readFileSync(abs) : null;
|
|
888
|
+
}
|
|
889
|
+
const blob = entries.find((e) => e.path === rel);
|
|
890
|
+
return blob ? gitBuf(['cat-file', 'blob', blob.sha]) : null;
|
|
891
|
+
};
|
|
892
|
+
const pkgJsonRaw = sourceRootFile('package.json');
|
|
893
|
+
const pkgLockRaw = sourceRootFile('package-lock.json');
|
|
894
|
+
if (!pkgJsonRaw || !pkgLockRaw) {
|
|
895
|
+
console.error(`refusing: source package.json/package-lock.json not found at ${fromWorktree ? 'the repo root' : 'this ref'} — cannot synthesize the core package.`);
|
|
818
896
|
process.exit(1);
|
|
819
897
|
}
|
|
820
|
-
const sourcePkgJson = JSON.parse(
|
|
821
|
-
const sourcePkgLock = JSON.parse(
|
|
898
|
+
const sourcePkgJson = JSON.parse(pkgJsonRaw.toString('utf8'));
|
|
899
|
+
const sourcePkgLock = JSON.parse(pkgLockRaw.toString('utf8'));
|
|
822
900
|
const corePkgJson = buildPackageJson({ version, sourceDeps: sourcePkgJson.dependencies, sourceOverrides: sourcePkgJson.overrides });
|
|
823
901
|
const corePkgLock = prunePackageLock(sourcePkgLock, {
|
|
824
902
|
name: corePkgJson.name, version, rootRanges: { express: corePkgJson.dependencies.express, pg: corePkgJson.dependencies.pg },
|
|
@@ -865,7 +943,7 @@ function main() {
|
|
|
865
943
|
let stubCount = 0; // agent docs (CLAUDE.md/AGENTS.md) replaced with the brand-free stub (task 2121)
|
|
866
944
|
for (const e of publishable) {
|
|
867
945
|
let raw;
|
|
868
|
-
try { raw =
|
|
946
|
+
try { raw = readBlob(e); }
|
|
869
947
|
catch (err) { leaks.push({ path: e.path, label: `read-failed:${err.message}` }); continue; }
|
|
870
948
|
const { buf, leaks: fileLeaks, advisories: fileAdvisories, kind } =
|
|
871
949
|
processCoreFile({ pathRel: e.path, raw, rules, sensitiveLiterals, envSecrets, knownDocNames });
|
|
@@ -955,7 +1033,7 @@ function main() {
|
|
|
955
1033
|
|
|
956
1034
|
console.error('');
|
|
957
1035
|
console.error(`[package-core] Cloud Bongos core v${version} packaged (contract ^${contract}).`);
|
|
958
|
-
console.error(` source_commit ${sha}`);
|
|
1036
|
+
console.error(` source_commit ${sha || '(none — built from the working tree)'}`);
|
|
959
1037
|
console.error(` file_count ${filesMeta.length}`);
|
|
960
1038
|
console.error(` tree_sha256 ${treeHash}`);
|
|
961
1039
|
console.error(` tarball ${path.relative(REPO_ROOT, tgzPath)} (${(tgz.length / 1024).toFixed(1)} KiB, sha256 ${sidecar.tarball.sha256.slice(0, 12)}…)`);
|
|
@@ -1059,6 +1137,8 @@ module.exports = {
|
|
|
1059
1137
|
leakScanVerdict,
|
|
1060
1138
|
// task 1002217 (audit H4) — the fixed neutral floor injected into the doc-redaction + fail-closed gate
|
|
1061
1139
|
PRIOR_INSTANCE_FLOOR,
|
|
1140
|
+
// task 1003995 — --from-worktree's enumerator, for a core checkout with no git history
|
|
1141
|
+
walkWorktreeEntries,
|
|
1062
1142
|
};
|
|
1063
1143
|
|
|
1064
1144
|
if (require.main === module) main();
|
|
@@ -28,6 +28,13 @@ const ic = require('../../src/instance-config');
|
|
|
28
28
|
// Destructured for the same reason as cli-lib: a namespace-only `ic.X` read is
|
|
29
29
|
// invisible to knip, which then calls the export dead code.
|
|
30
30
|
const { SESSION_FILENAMES, readSessionConfigSync } = require('../../src/instance-config');
|
|
31
|
+
// SESSIONS ARE PER INSTANCE (ADR 0269), and saveSession is what makes that true — see
|
|
32
|
+
// step 4. Requiring cli-lib here was weighed rather than assumed: it costs ~13ms and
|
|
33
|
+
// prints nothing at load (undici and the surface machinery are all lazy), and login.js
|
|
34
|
+
// — the sibling pre-session flow, shipped in the same standalone on-ramp bundle as this
|
|
35
|
+
// script — already requires it. So this adds no module to the package and no visible
|
|
36
|
+
// cost to a paste. (task 1004003)
|
|
37
|
+
const { saveSession } = require('./cli-lib');
|
|
31
38
|
|
|
32
39
|
// WRITE the session to the configured instance dir (OTB → ~/.config/otb,
|
|
33
40
|
// unchanged; a fresh instance → its neutral dir). R61 / task 1200.
|
|
@@ -160,10 +167,20 @@ async function main() {
|
|
|
160
167
|
api_base: API_BASE,
|
|
161
168
|
};
|
|
162
169
|
|
|
163
|
-
// 4. Ensure dir exists with safe perms, then
|
|
170
|
+
// 4. Ensure dir exists with safe perms, then save 0600 THROUGH saveSession.
|
|
171
|
+
//
|
|
172
|
+
// WHY NOT A BARE writeFileSync HERE (task 1004003). This used to write SESSION_PATH
|
|
173
|
+
// directly, which is the ACTIVE POINTER and nothing else. Under ADR 0269 a machine has
|
|
174
|
+
// several instances, and saveSession is the only writer that knows it: it archives the
|
|
175
|
+
// OUTGOING session into the host-keyed store before overwriting the pointer, and files
|
|
176
|
+
// the incoming one there too. Writing the pointer directly skipped both, so pasting a
|
|
177
|
+
// token for instance B while signed in to instance A destroyed A's session with no
|
|
178
|
+
// archive — and `bongos login <A>`, which exists to switch back without a device flow,
|
|
179
|
+
// reads that store and found nothing, so the switch back cost a full re-auth. The state
|
|
180
|
+
// was unrecoverable, because the pointer is the file that was overwritten.
|
|
164
181
|
try {
|
|
165
182
|
fs.mkdirSync(path.dirname(SESSION_PATH), { recursive: true, mode: 0o700 });
|
|
166
|
-
|
|
183
|
+
await saveSession(next);
|
|
167
184
|
// chmod again in case the file already existed with looser perms (writeFile's
|
|
168
185
|
// mode arg is ignored when the file already exists on some platforms).
|
|
169
186
|
fs.chmodSync(SESSION_PATH, 0o600);
|
|
@@ -399,7 +399,17 @@ function installedCorePackDir({ instanceRoot, fsImpl = fs } = {}) {
|
|
|
399
399
|
return null;
|
|
400
400
|
}
|
|
401
401
|
|
|
402
|
-
//
|
|
402
|
+
// Can package-core.js enumerate this checkout from GIT? It builds the core set from
|
|
403
|
+
// `git ls-tree` at a ref, so a checkout with no `.git` aborts at `git rev-parse HEAD` —
|
|
404
|
+
// and that is not a broken install, it is the SHIPPED SELF-HOST IMAGE: .dockerignore
|
|
405
|
+
// excludes .git by design, so /app is a complete core source tree with no history
|
|
406
|
+
// (task 1003995). The presence of `.git` — a directory in a clone, a file in a linked
|
|
407
|
+
// worktree — is the whole question. fs injectable for tests; read-only → dry-run safe.
|
|
408
|
+
function coreCheckoutHasGit({ repoRoot = REPO_ROOT, fsImpl = fs } = {}) {
|
|
409
|
+
return fsImpl.existsSync(path.join(repoRoot, '.git'));
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// Resolve a vendorable @bongos/core tarball — the four-rung ladder, freshest-correct
|
|
403
413
|
// first, shared by the fresh scaffold and the re-provision pin refresh (task 1003050):
|
|
404
414
|
// 1. a `file:`-pinned control plane reuses its own pinned tarball (task 1002275 —
|
|
405
415
|
// rebuilding via package-core.js aborts with an EMPTY core set when this runner
|
|
@@ -408,31 +418,37 @@ function installedCorePackDir({ instanceRoot, fsImpl = fs } = {}) {
|
|
|
408
418
|
// it has INSTALLED — the child pins the SAME, CURRENT core the hub runs, ending
|
|
409
419
|
// the frozen-old-tarball births (task 1002706, gap 13) without requiring the
|
|
410
420
|
// child's owner to hold a token for the still-private registry package;
|
|
411
|
-
// 3. a genuine core source checkout rebuilds fresh via package-core.js
|
|
421
|
+
// 3. a genuine core source checkout rebuilds fresh via package-core.js;
|
|
422
|
+
// 4. a core checkout with NO git history — the self-host image — packs its working
|
|
423
|
+
// tree via package-core.js --from-worktree (task 1003995). LAST because it is the
|
|
424
|
+
// only rung with no commit to anchor the artifact to: whenever history is there,
|
|
425
|
+
// rung 3's reproducible build is the better answer for the identical file set.
|
|
412
426
|
// Returns { tgz, packDest } — packDest (set only on rung 2) is a temp dir the CALLER
|
|
413
427
|
// must `rm -rf` once the tarball has been vendored.
|
|
414
|
-
function resolveVendorableCoreTarball({ instanceRoot, exec, log, coreV, tag = '[core]' }) {
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
428
|
+
function resolveVendorableCoreTarball({ instanceRoot, repoRoot = REPO_ROOT, exec, log, coreV, tag = '[core]' }) {
|
|
429
|
+
const pinned = installedCoreTarball({ instanceRoot });
|
|
430
|
+
if (pinned) {
|
|
431
|
+
log(` ${tag} reusing the pinned core tarball this control plane runs (${path.basename(pinned)}) — no source rebuild (task 1002275)`);
|
|
432
|
+
return { tgz: pinned, packDest: null };
|
|
433
|
+
}
|
|
434
|
+
const packSrc = installedCorePackDir({ instanceRoot });
|
|
435
|
+
if (packSrc) {
|
|
436
|
+
// Keyed by VERSION, not slug: no request-derived value enters this command, and
|
|
437
|
+
// two concurrent scaffolds of the same core pack identical bytes — collision-safe.
|
|
438
|
+
const packDest = path.join(os.tmpdir(), `bongos-core-pack-${packSrc.version}`);
|
|
439
|
+
log(` ${tag} packing the registry-installed core this control plane runs (v${packSrc.version}) — no source rebuild (task 1002706)`);
|
|
440
|
+
exec(`mkdir -p ${packDest}`);
|
|
441
|
+
exec(`npm pack ${packSrc.dir} --pack-destination ${packDest}`);
|
|
442
|
+
return { tgz: path.join(packDest, packSrc.tarballName), packDest };
|
|
443
|
+
}
|
|
444
|
+
if (coreCheckoutHasGit({ repoRoot })) {
|
|
445
|
+
log(` ${tag} building a fresh vendored tarball from source (package-core.js)`);
|
|
446
|
+
exec(`node scripts/gds/package-core.js`); // cwd = REPO_ROOT → dist/bongos-core-<v>.tgz
|
|
419
447
|
} else {
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
// Keyed by VERSION, not slug: no request-derived value enters this command, and
|
|
423
|
-
// two concurrent scaffolds of the same core pack identical bytes — collision-safe.
|
|
424
|
-
packDest = path.join(os.tmpdir(), `bongos-core-pack-${packSrc.version}`);
|
|
425
|
-
log(` ${tag} packing the registry-installed core this control plane runs (v${packSrc.version}) — no source rebuild (task 1002706)`);
|
|
426
|
-
exec(`mkdir -p ${packDest}`);
|
|
427
|
-
exec(`npm pack ${packSrc.dir} --pack-destination ${packDest}`);
|
|
428
|
-
tgz = path.join(packDest, packSrc.tarballName);
|
|
429
|
-
} else {
|
|
430
|
-
log(` ${tag} building a fresh vendored tarball from source (package-core.js)`);
|
|
431
|
-
exec(`node scripts/gds/package-core.js`); // cwd = REPO_ROOT → dist/bongos-core-<v>.tgz
|
|
432
|
-
tgz = path.join(REPO_ROOT, 'dist', `bongos-core-${coreV}.tgz`);
|
|
433
|
-
}
|
|
448
|
+
log(` ${tag} this checkout IS the core and carries no git history — packing the working tree (package-core.js --from-worktree, task 1003995)`);
|
|
449
|
+
exec(`node scripts/gds/package-core.js --from-worktree`);
|
|
434
450
|
}
|
|
435
|
-
return { tgz, packDest };
|
|
451
|
+
return { tgz: path.join(repoRoot, 'dist', `bongos-core-${coreV}.tgz`), packDest: null };
|
|
436
452
|
}
|
|
437
453
|
|
|
438
454
|
// The onboard mode a provisioning row carries (migration provisioning_004, task 1002562).
|
|
@@ -633,7 +649,7 @@ async function scaffoldStandaloneRepo(inst, deps) {
|
|
|
633
649
|
const spec = instanceInitSpec(inst, { ownerLogin });
|
|
634
650
|
const specPath = path.join(os.tmpdir(), `bongos-init-${inst.slug}.json`);
|
|
635
651
|
writeFile(specPath, JSON.stringify(spec, null, 2));
|
|
636
|
-
// Vendored tarball — resolveVendorableCoreTarball's
|
|
652
|
+
// Vendored tarball — resolveVendorableCoreTarball's four-rung ladder (shared
|
|
637
653
|
// with the re-provision pin refresh, task 1003050).
|
|
638
654
|
const { tgz, packDest } = resolveVendorableCoreTarball({
|
|
639
655
|
instanceRoot: deps.instanceRoot, exec, log, coreV, tag: '[scaffold] core install:',
|
|
@@ -1007,6 +1023,7 @@ module.exports = {
|
|
|
1007
1023
|
ownerLoginOf,
|
|
1008
1024
|
asPostgres,
|
|
1009
1025
|
buildCorePinRefreshCommit,
|
|
1026
|
+
coreCheckoutHasGit,
|
|
1010
1027
|
dbCreateCmd,
|
|
1011
1028
|
dbName,
|
|
1012
1029
|
dbRoleCmd,
|
package/scripts/gds/provision.js
CHANGED
|
@@ -50,7 +50,7 @@ const cp = require('node:child_process');
|
|
|
50
50
|
const crypto = require('node:crypto');
|
|
51
51
|
|
|
52
52
|
const { CONFIG, MANIFEST_UA, MANIFEST_VERIFY_INTERVAL_MS, MANIFEST_VERIFY_TRIES, MAX_INTENT_ATTEMPTS, REPO_ROOT, coreVersionSafe, hasFlag, loadDeps, oauthSecret, provisionerBotEmail } = require('./provision-config.js');
|
|
53
|
-
const { APP_USER, alreadyScaffolded, buildCorePinRefreshCommit, dbCreateCmd, dbName, dbRoleCmd, dbRoleCmdShown, dbRoleGrantsCmd, ensureInstanceUserCmd, generateDbPassword, grantInstanceRepoReadCmd, instanceDbRole, instanceStateDir, instanceUser, deployKeyPath, deployKeyTitle, ensurePrivateRepoAccess, installedCorePackDir, installedCoreTarball, instanceInitSpec, instanceRepoRemote, migrateCmd, onboardMode, ownerLoginOf, parseTargetRef, planCorePinRefresh, refreshStandaloneCorePin, resolveOwnerGithubToken, resolveVendorableCoreTarball, safeVersionLabel, scaffoldStandaloneRepo, seedFirstVersionCmd, standaloneInstallCmd, standaloneMigrateCmd, standalonePullCmd, standaloneRegenDocsCmd, standaloneRoot } = require('./provision-repo.js');
|
|
53
|
+
const { APP_USER, alreadyScaffolded, buildCorePinRefreshCommit, coreCheckoutHasGit, dbCreateCmd, dbName, dbRoleCmd, dbRoleCmdShown, dbRoleGrantsCmd, ensureInstanceUserCmd, generateDbPassword, grantInstanceRepoReadCmd, instanceDbRole, instanceStateDir, instanceUser, deployKeyPath, deployKeyTitle, ensurePrivateRepoAccess, installedCorePackDir, installedCoreTarball, instanceInitSpec, instanceRepoRemote, migrateCmd, onboardMode, ownerLoginOf, parseTargetRef, planCorePinRefresh, refreshStandaloneCorePin, resolveOwnerGithubToken, resolveVendorableCoreTarball, safeVersionLabel, scaffoldStandaloneRepo, seedFirstVersionCmd, standaloneInstallCmd, standaloneMigrateCmd, standalonePullCmd, standaloneRegenDocsCmd, standaloneRoot } = require('./provision-repo.js');
|
|
54
54
|
const { backupScriptPath, backupService, backupServicePath, backupTimer, backupTimerPath, backupUnitName, instanceManifestCmd, originEnvVarsFor, serviceUnit, serviceUnitPath, settingsConsumed, settingsEnvVarsFor, upsertEnvVars, webEnvBody, webEnvPath } = require('./provision-units.js');
|
|
55
55
|
const { caddyBlock, caddySnippetPath, checkCaddyWiring, checkDnsTokenScope, classifyZoneScope, dnsReleaseEnv, dnsUpsertEnv, federateInstance, federationHubOrigin, healthzCmd, identityCmd, identityVerdict, shouldFederate, zoneForDomain } = require('./provision-net.js');
|
|
56
56
|
const { capacityPreflight, statusCapacityLine } = require('./provision-capacity.js');
|
|
@@ -1457,6 +1457,8 @@ module.exports = {
|
|
|
1457
1457
|
parseTargetRef, instanceRepoRemote, instanceInitSpec, installedCoreTarball, installedCorePackDir, scaffoldStandaloneRepo, coreVersionSafe,
|
|
1458
1458
|
// re-provision core-pin refresh (gap-list W3, task 1003050)
|
|
1459
1459
|
resolveVendorableCoreTarball, planCorePinRefresh, safeVersionLabel, buildCorePinRefreshCommit, refreshStandaloneCorePin,
|
|
1460
|
+
// the git-less core checkout — the self-host image's rung (task 1003995)
|
|
1461
|
+
coreCheckoutHasGit,
|
|
1460
1462
|
// adopt vs greenfield onboarding (ADR 0121, task 1002562)
|
|
1461
1463
|
onboardMode, alreadyScaffolded,
|
|
1462
1464
|
// private-repo deploy key (ADR 0176, task 1002735)
|