@bongos/core 1.19.959 → 1.19.960

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.959",
6
- "core_contract": "1.19.959",
7
- "source_commit": "762c63db2672a1dac87eb25a78bd3a93717b6d1a",
5
+ "core_version": "1.19.960",
6
+ "core_contract": "1.19.960",
7
+ "source_commit": "07224a002beb8673193550edb973a83f63733ac9",
8
8
  "source_ref": "HEAD",
9
- "built_at": "2026-09-22T14:26:09.898Z",
9
+ "built_at": "2026-09-22T16:38:38.841Z",
10
10
  "redaction": {
11
11
  "model": "docs-redacted+functional-verbatim",
12
12
  "docs_redacted": 514,
@@ -17,7 +17,7 @@
17
17
  "gate": "passed"
18
18
  },
19
19
  "file_count": 2898,
20
- "tree_sha256": "f587a606503aa60197f7ffbed71f7a3df76cfafda2da9b932090732dfc2cc512",
20
+ "tree_sha256": "de8dc0908595fb0f613026497e94dc03c3a50bb631a2adc952bdaefedcb96199",
21
21
  "files": [
22
22
  {
23
23
  "path": ".claude/skills/ask-for-help/SKILL.md",
@@ -2692,7 +2692,7 @@
2692
2692
  {
2693
2693
  "path": "docs/module-api-changelog.md",
2694
2694
  "mode": "0000644",
2695
- "sha256": "164da9dadc56356e91897f78786de7c42b32e37534971b6743985abeba81d718"
2695
+ "sha256": "4cc28a5f552998a430d1d1ecb6ec1882607ccd555f5d2e01c7a2197b6414b67a"
2696
2696
  },
2697
2697
  {
2698
2698
  "path": "docs/modules-contract.md",
@@ -8082,12 +8082,12 @@
8082
8082
  {
8083
8083
  "path": "package-lock.json",
8084
8084
  "mode": "0000644",
8085
- "sha256": "e0d9870134171629f6021c929775456f72729ad7131249f17c5e99cd64d2405f"
8085
+ "sha256": "f1832d9a38929a6d0cd0a93720088edcba0cc7952bbe2df7d57ec0986c1dea49"
8086
8086
  },
8087
8087
  {
8088
8088
  "path": "package.json",
8089
8089
  "mode": "0000644",
8090
- "sha256": "830c6340ff774389b6a391c8045116ccabc871a23ba09688e03380892f340a2a"
8090
+ "sha256": "98d101c939c100960c78ea1887fd429720d643c3931840ac847df15544312e72"
8091
8091
  },
8092
8092
  {
8093
8093
  "path": "public-docs/index.html",
@@ -8432,7 +8432,7 @@
8432
8432
  {
8433
8433
  "path": "scripts/gds/claude-materialize.js",
8434
8434
  "mode": "0000644",
8435
- "sha256": "2209cc2ca1476efc0af464e6e58ae87c5a5dc8d5a70b2ad067867adbfe0aab06"
8435
+ "sha256": "717de4da1c2e966aff2cb8dff997a4cedcf5d92eea85a9ceeb75118bef1f6859"
8436
8436
  },
8437
8437
  {
8438
8438
  "path": "scripts/gds/claude-md-template.js",
@@ -10092,7 +10092,7 @@
10092
10092
  {
10093
10093
  "path": "src/module-api.js",
10094
10094
  "mode": "0000644",
10095
- "sha256": "73781b940ba631e4642209a973a119375f7ff12f23027033030b1c95f0cc45f7"
10095
+ "sha256": "23025d957268d4f44b0a2a8a552f1371a9c50af1220239de8ca3fd9b0bd605f3"
10096
10096
  },
10097
10097
  {
10098
10098
  "path": "src/module-loader/catalog.js",
@@ -10872,7 +10872,7 @@
10872
10872
  {
10873
10873
  "path": "tests/claude_materialize.mjs",
10874
10874
  "mode": "0000644",
10875
- "sha256": "60eb54f19f26f3557afd6b12f0f2867208638a2b6547f58abd192b6f07e805c3"
10875
+ "sha256": "0f0d53e5c63f9b776aa5ea9c2e0408e350e54230ee0c214af26faf34eaa7bb5f"
10876
10876
  },
10877
10877
  {
10878
10878
  "path": "tests/clawback_confirmed_backlog.mjs",
@@ -2392,5 +2392,7 @@ is load-bearing: the script throws rather than guess if it is missing, and
2392
2392
  landed since 1.19.957 with no explicit bump. run 35735231183. (task 1002620)
2393
2393
  1.19.959 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
2394
2394
  landed since 1.19.958 with no explicit bump. run 35740227000. (task 1002620)
2395
+ 1.19.960 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
2396
+ landed since 1.19.959 with no explicit bump. run 35755397900. (task 1002620)
2395
2397
  ---------------------------------------------------------------------------
2396
2398
  ```
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.19.959",
3
+ "version": "1.19.960",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.19.959",
9
+ "version": "1.19.960",
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.959",
3
+ "version": "1.19.960",
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",
@@ -385,12 +385,35 @@ const STARTER_COMMANDS = [
385
385
  const STARTER_SHELL_TOOLS = ['Bash', 'PowerShell'];
386
386
  const STARTER_ALLOW = STARTER_SHELL_TOOLS.flatMap((tool) => STARTER_COMMANDS.map((command) => `${tool}(${command})`));
387
387
 
388
+ // The deny half (task 1003966): what the local agent may not READ. Keys are the
389
+ // same exposure on every instance, so those rules ship verbatim.
390
+ const STARTER_DENY_SECRETS = [
391
+ 'Read(.env)', 'Read(**/.env)', 'Read(.env.local)', 'Read(**/.env.local)',
392
+ 'Read(.env.*.local)', 'Read(**/.env.*.local)',
393
+ 'Read(**/*.pem)', 'Read(**/id_rsa*)', 'Read(**/id_ed25519*)',
394
+ ];
395
+
396
+ // Credential files are derived from where the CLI actually reads them, not rebranded from the
397
+ // core's literals: the rebrand would also move the host-keyed store, which sits at a fixed anchor
398
+ // whatever the brand (cli-lib sessionStoreDir, task 1003741). The shelf rule is the only layer an
399
+ // instance gets against the repo-global stash stack, since the git-guard hook does not ship.
400
+ function starterDeny(branding = {}) {
401
+ const dirs = [...new Set([effConfigDir(branding), ...ic.LEGACY_DIRS])];
402
+ const credentialFiles = ['env', ...ic.SESSION_FILENAMES];
403
+ return [
404
+ ...STARTER_DENY_SECRETS,
405
+ ...dirs.flatMap((dir) => credentialFiles.map((file) => `Read(~/.config/${dir}/${file})`)),
406
+ `Read(~/.config/${ic.FALLBACK_DIR}/instances/**)`,
407
+ ...STARTER_SHELL_TOOLS.map((tool) => `${tool}(git stash:*)`),
408
+ ];
409
+ }
410
+
388
411
  // The starter an instance gets when the core root carries no settings.json — a
389
412
  // package-only install. Permissions ONLY: no hooks (they are topology), no env.
390
- function renderStarterSettings() {
413
+ function renderStarterSettings(branding = {}) {
391
414
  return `${JSON.stringify({
392
- _doc: 'Starter written by scripts/gds/claude-materialize.js (task 1003962). Permissions only — the blessed Bongos commands, mirroring the core repo\'s own allow-list (ADR 0104). An allow-rule lets the local agent RUN a command; it grants no authority, which the server decides per request from your live rank (ADR 0016). Yours to edit: a later materialize never clobbers this file.',
393
- permissions: { allow: STARTER_ALLOW },
415
+ _doc: 'Starter written by scripts/gds/claude-materialize.js (tasks 1003962, 1003966). Permissions only — the blessed Bongos commands, mirroring the core repo\'s own allow-list (ADR 0104), and the deny rules that keep the local agent from reading keys and session tokens. An allow-rule lets the local agent RUN a command; it grants no authority, which the server decides per request from your live rank (ADR 0016). Yours to edit: a later materialize never clobbers this file.',
416
+ permissions: { allow: STARTER_ALLOW, deny: starterDeny(branding) },
394
417
  }, null, 2)}\n`;
395
418
  }
396
419
 
@@ -685,7 +708,7 @@ function materializeClaude({ coreRoot, instanceDir, dryRun = false, overwrite =
685
708
  } else {
686
709
  if (!dryRun) {
687
710
  fs.mkdirSync(destClaude, { recursive: true });
688
- fs.writeFileSync(settingsDest, renderStarterSettings());
711
+ fs.writeFileSync(settingsDest, renderStarterSettings(toBranding));
689
712
  }
690
713
  addWritten('.claude/settings.json');
691
714
  summary.settings = true;
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.959'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
74
+ const CORE_VERSION = '1.19.960'; // 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');
@@ -18,14 +18,16 @@
18
18
  import { strict as assert } from 'node:assert';
19
19
  import { createRequire } from 'node:module';
20
20
  import { mkdtempSync, mkdirSync, writeFileSync, readFileSync, existsSync, readdirSync, rmSync } from 'node:fs';
21
- import { tmpdir } from 'node:os';
22
- import { join, dirname } from 'node:path';
21
+ import { tmpdir, homedir } from 'node:os';
22
+ import { join, dirname, relative, sep } from 'node:path';
23
23
  import { fileURLToPath } from 'node:url';
24
24
 
25
25
  const require = createRequire(import.meta.url);
26
26
  const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..');
27
27
  const m = require('../scripts/gds/claude-materialize.js');
28
28
  const claim = require('../scripts/gds/claim.js');
29
+ const cliLib = require('../scripts/gds/cli-lib.js');
30
+ const ic = require('../src/instance-config.js');
29
31
  // The live verb table, so a starter allow-rule naming a verb that does not exist
30
32
  // fails here rather than shipping as an inert permission (task 1003962).
31
33
  const { VERBS: cliVerbs } = require('../bin/bongos.js');
@@ -804,6 +806,55 @@ t('a package-only install (no settings.json at the core root) GETS the starter',
804
806
  rmSync(inst, { recursive: true, force: true });
805
807
  });
806
808
 
809
+ // task 1003966 — the starter shipped the allow half alone: the convenience with none of the
810
+ // hardening. The deny half is what stops the agent reading a key or a session token.
811
+ const starterDenyFor = (branding) => JSON.parse(m.renderStarterSettings(branding)).permissions.deny;
812
+ const homeRel = (abs) => `~/${relative(homedir(), abs).split(sep).join('/')}`;
813
+
814
+ t("a package-only install gets the deny rules, pointed at THAT instance's config dir", () => {
815
+ const core = mkdtempSync(join(tmpdir(), 'w3-deny-core-'));
816
+ const inst = mkdtempSync(join(tmpdir(), 'w3-deny-inst-'));
817
+ mkdirSync(join(core, '.claude', 'skills'), { recursive: true });
818
+ mkdirSync(join(inst, 'config'), { recursive: true });
819
+ writeFileSync(join(inst, 'config', 'branding.json'), JSON.stringify({ configDir: 'acme', envPrefix: 'ACME' }));
820
+ m.materializeClaude({ coreRoot: core, instanceDir: inst, dryRun: false });
821
+ const deny = JSON.parse(readFileSync(join(inst, '.claude', 'settings.json'), 'utf8')).permissions.deny;
822
+ assert.ok(Array.isArray(deny), 'the starter must carry permissions.deny');
823
+ for (const secret of ['Read(**/.env)', 'Read(**/.env.*.local)', 'Read(**/*.pem)', 'Read(**/id_rsa*)']) {
824
+ assert.ok(deny.includes(secret), `missing ${secret}`);
825
+ }
826
+ assert.ok(deny.includes('Read(~/.config/acme/bongos-session.json)'), "the instance's OWN session file");
827
+ assert.deepEqual(deny.filter((r) => /\.config\/(?!acme\/)/.test(r) && !r.startsWith(`Read(${homeRel(cliLib.sessionStoreDir())}`)
828
+ && !ic.LEGACY_DIRS.some((d) => r.includes(`.config/${d}/`))), [],
829
+ "a session rule naming another brand's dir guards the wrong path and leaves this one open");
830
+ for (const tool of ['Bash', 'PowerShell']) assert.ok(deny.includes(`${tool}(git stash:*)`), `${tool}(git stash:*)`);
831
+ rmSync(core, { recursive: true, force: true });
832
+ rmSync(inst, { recursive: true, force: true });
833
+ });
834
+
835
+ t('every file the CLI reads a session from is denied, the host-keyed store included', () => {
836
+ // Pinned to cli-lib's own read list, so a new session filename or dir fails HERE.
837
+ let brand = {};
838
+ try { brand = require('../src/branding').branding(); } catch { /* unbranded checkout */ }
839
+ const deny = starterDenyFor(brand);
840
+ for (const p of cliLib.SESSION_READ_PATHS) assert.ok(deny.includes(`Read(${homeRel(p)})`), `Read(${homeRel(p)})`);
841
+ assert.ok(deny.includes(`Read(${homeRel(cliLib.sessionStoreDir())}/**)`),
842
+ 'the store sits at a fixed anchor whatever the brand (task 1003741), so it is never rebranded');
843
+ });
844
+
845
+ t("every deny rule in the core's own settings.json reaches the starter", () => {
846
+ // The two lists drift the way the allow halves could: a rule added to the core
847
+ // protects the core and silently no instance. A config-dir file is compared by
848
+ // name, in the instance's dir; everything else must match verbatim.
849
+ const core = JSON.parse(readFileSync(join(ROOT, '.claude', 'settings.json'), 'utf8')).permissions.deny;
850
+ const deny = starterDenyFor({ configDir: 'acme' });
851
+ for (const rule of core) {
852
+ const cfg = rule.match(/^Read\(~\/\.config\/[^/]+\/(.+)\)$/);
853
+ const want = cfg ? `Read(~/.config/acme/${cfg[1]})` : rule;
854
+ assert.ok(deny.includes(want), `core deny ${rule} → starter must carry ${want}`);
855
+ }
856
+ });
857
+
807
858
  // task 1004012 — the other source: a CHECKOUT core root does carry a settings.json, and the
808
859
  // instance used to receive its allow-list verbatim, naming scripts its skills no longer run.
809
860
  t('a checkout-materialized instance gets allow-rules in the bongos form, end to end', () => {