@bongos/core 1.19.838 → 1.19.840

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.
Files changed (38) hide show
  1. package/.bongos-core.json +69 -39
  2. package/clients/bongos-client/index.d.ts +1 -1
  3. package/config/branding.neutral.json +2 -1
  4. package/docs/adr/0303-provisioning-verifies-the-base-caddyfile-and-never-edits-it.md +49 -0
  5. package/docs/adr/README.md +1 -0
  6. package/docs/api/openapi.json +2 -0
  7. package/docs/architecture.md +2 -2
  8. package/docs/branding-contract.md +1 -1
  9. package/docs/branding-fill-prompt.md +1 -1
  10. package/docs/copy-inventory.md +3 -3
  11. package/docs/copy-registry.json +5 -5
  12. package/docs/module-api-changelog.md +4 -0
  13. package/modules/lifecycle/github-push.js +2 -2
  14. package/modules/lifecycle/pr-assign.js +77 -0
  15. package/modules/lifecycle/routes/tasks.js +16 -1
  16. package/modules/status-ui/public/index.html +2 -1
  17. package/package-lock.json +2 -2
  18. package/package.json +1 -1
  19. package/scripts/gds/fitness-checks-identity.js +9 -1
  20. package/scripts/gds/fitness.js +34 -1
  21. package/scripts/gds/paste-token.js +65 -14
  22. package/scripts/gds/provision-config.js +3 -0
  23. package/scripts/gds/provision-net.js +32 -0
  24. package/scripts/gds/provision.js +21 -4
  25. package/scripts/gds/ship-land.js +12 -0
  26. package/scripts/status-mirror-sync.js +6 -2
  27. package/src/bongos/routes/auth.js +24 -3
  28. package/src/bongos/serve-internal.js +9 -2
  29. package/src/branding.js +22 -2
  30. package/src/module-api.js +1 -1
  31. package/tests/bongos_cli.mjs +13 -4
  32. package/tests/cli_token_reissue.mjs +54 -7
  33. package/tests/fitness.mjs +47 -0
  34. package/tests/paste_token_api_base.mjs +87 -0
  35. package/tests/pr_assign.mjs +189 -0
  36. package/tests/provision.mjs +12 -0
  37. package/tests/provision_caddy_wiring.mjs +148 -0
  38. package/tests/status_company_attribution.mjs +60 -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.838",
6
- "core_contract": "1.19.838",
7
- "source_commit": "f5c78584166eedac7972dcb10f61e55d7c1685e7",
5
+ "core_version": "1.19.840",
6
+ "core_contract": "1.19.840",
7
+ "source_commit": "47fbba53d86033101f57cf1e1a85aef7367fe320",
8
8
  "source_ref": "HEAD",
9
- "built_at": "2026-09-17T17:02:39.487Z",
9
+ "built_at": "2026-09-17T17:45:41.665Z",
10
10
  "redaction": {
11
11
  "model": "docs-redacted+functional-verbatim",
12
- "docs_redacted": 494,
12
+ "docs_redacted": 495,
13
13
  "agent_docs_stubbed": 26,
14
- "functional_verbatim": 2257,
14
+ "functional_verbatim": 2262,
15
15
  "rules": 3,
16
16
  "gate_literals": 3,
17
17
  "gate": "passed"
18
18
  },
19
- "file_count": 2777,
20
- "tree_sha256": "61bef3a77f3f90a904e83ae13d78fa314c21c944451e91a369fb047fc78d9b7a",
19
+ "file_count": 2783,
20
+ "tree_sha256": "5ddd99652c37c0ad56f34d618732a671733aed9b055c1c4ee161135581d0c80a",
21
21
  "files": [
22
22
  {
23
23
  "path": ".claude/skills/ask-for-help/SKILL.md",
@@ -367,7 +367,7 @@
367
367
  {
368
368
  "path": "clients/bongos-client/index.d.ts",
369
369
  "mode": "0000644",
370
- "sha256": "7f48743a6878f01e484b8f2884b4b12588b956db307fd6bc83a67a19df61a52b"
370
+ "sha256": "031abfbccd58bf9d6cdf9368f0d3bc862ddcd2f373b585a6911196b28596fae2"
371
371
  },
372
372
  {
373
373
  "path": "clients/bongos-client/index.mjs",
@@ -382,7 +382,7 @@
382
382
  {
383
383
  "path": "config/branding.neutral.json",
384
384
  "mode": "0000644",
385
- "sha256": "b56024a95ccf9e236bf8248b7d1f96c4510e3feac55c2885497ecd8f841bdb7b"
385
+ "sha256": "3c46f007c474b0fe300ad1c6e2c80237948a81a8ad726f15f74ec5ba7cf2b610"
386
386
  },
387
387
  {
388
388
  "path": "config/design-tokens.neutral.json",
@@ -1999,10 +1999,15 @@
1999
1999
  "mode": "0000644",
2000
2000
  "sha256": "08ea781a488d7454ca49b17e9540bb4c7bb9f1925aedf8ad4634057a89dd0d22"
2001
2001
  },
2002
+ {
2003
+ "path": "docs/adr/0303-provisioning-verifies-the-base-caddyfile-and-never-edits-it.md",
2004
+ "mode": "0000644",
2005
+ "sha256": "62ffb9c1aeeaaafd5c51767f1ffff1c67adb3563e8d55ceb81ef77511184faa4"
2006
+ },
2002
2007
  {
2003
2008
  "path": "docs/adr/README.md",
2004
2009
  "mode": "0000644",
2005
- "sha256": "5c20d4bf4c0202a391a617d99daaa97d8a566d9d5c8534b5bd060faa8f8afe02"
2010
+ "sha256": "48323e44fced698a0da997eeb16564ab3b04430c8233a8f7997c0fec2b123401"
2006
2011
  },
2007
2012
  {
2008
2013
  "path": "docs/api-reference.md",
@@ -2012,22 +2017,22 @@
2012
2017
  {
2013
2018
  "path": "docs/api/openapi.json",
2014
2019
  "mode": "0000644",
2015
- "sha256": "11ffe0dfc04855064064f335396b1b57887a1d4e61b21559c376d279ea7513fd"
2020
+ "sha256": "a0107dc012a5355baacf3668c92b17675b35ce0cffc96052b1d92d99533c5744"
2016
2021
  },
2017
2022
  {
2018
2023
  "path": "docs/architecture.md",
2019
2024
  "mode": "0000644",
2020
- "sha256": "02a8f7f07465f7059bc888a367793812c78d689f2335d333529712833c452116"
2025
+ "sha256": "029e8c5a9562171251df353e08791f1520c2e17fa7d286c7b4d278dea195c6d7"
2021
2026
  },
2022
2027
  {
2023
2028
  "path": "docs/branding-contract.md",
2024
2029
  "mode": "0000644",
2025
- "sha256": "807e79e99b60a7a310c532af8ce5d3c9a23da4f5ac568cf6dd501feeca93ba46"
2030
+ "sha256": "78cb92eedefb10a2cd23b0dff8e2a6d5499295487372b7ef7da5619f99d4cdde"
2026
2031
  },
2027
2032
  {
2028
2033
  "path": "docs/branding-fill-prompt.md",
2029
2034
  "mode": "0000644",
2030
- "sha256": "38961b4a3b3e0789b64365dc3cb70eb3407f45e9a8ecdd1667ef80ec8470e272"
2035
+ "sha256": "91e0fab10096223b9e99ba764330b61f5c64f754d0b2845a050057ddbe5a34e0"
2031
2036
  },
2032
2037
  {
2033
2038
  "path": "docs/canonical-permissions.md",
@@ -2037,12 +2042,12 @@
2037
2042
  {
2038
2043
  "path": "docs/copy-inventory.md",
2039
2044
  "mode": "0000644",
2040
- "sha256": "47070b89dd6a26d425e630db6b592b9b99ce73197e39c21b416caa5a12b330df"
2045
+ "sha256": "e457040f6938ad12012fb8ceb1b59de65d43f2fca74849181f1e448979a4c811"
2041
2046
  },
2042
2047
  {
2043
2048
  "path": "docs/copy-registry.json",
2044
2049
  "mode": "0000644",
2045
- "sha256": "cceabb45bbe7b9aabe8484777a8567fc9d73f274486475a80b1c04a43152f7c8"
2050
+ "sha256": "77a0c2bb223c37af27b3dac3ca8864bad9b1bcb0bc6c326c96f40cec6f9ba95d"
2046
2051
  },
2047
2052
  {
2048
2053
  "path": "docs/design/apex-pass-2-direction.md",
@@ -2567,7 +2572,7 @@
2567
2572
  {
2568
2573
  "path": "docs/module-api-changelog.md",
2569
2574
  "mode": "0000644",
2570
- "sha256": "aed7cd21803eab964c3d296c5f938e8c94b9e05fb865ce3bb5780574a5fdcebb"
2575
+ "sha256": "f8fa8c024b4d6741ea672a7be33957d6cc58c6b85867ef2b5499bcc31fdf4b50"
2571
2576
  },
2572
2577
  {
2573
2578
  "path": "docs/modules-contract.md",
@@ -5682,7 +5687,7 @@
5682
5687
  {
5683
5688
  "path": "modules/lifecycle/github-push.js",
5684
5689
  "mode": "0000644",
5685
- "sha256": "f9c22388c70ba8476a6dba8cd072d92abc44da79c296a4685abab81e24edf6ed"
5690
+ "sha256": "84114ecaf06c6ac52b7d7e4b5eaff97086908d6f7df0a3e07e4be3aa923935bc"
5686
5691
  },
5687
5692
  {
5688
5693
  "path": "modules/lifecycle/goal-advisory.js",
@@ -5804,6 +5809,11 @@
5804
5809
  "mode": "0000644",
5805
5810
  "sha256": "81ed257d2156988e911d737e10cba2b8375f47fc6e27bf77c06d9dc18258380b"
5806
5811
  },
5812
+ {
5813
+ "path": "modules/lifecycle/pr-assign.js",
5814
+ "mode": "0000644",
5815
+ "sha256": "08ace9d2690ef7f563455bdd52f89e8f60587eb399a246726cdb6ae06e98699f"
5816
+ },
5807
5817
  {
5808
5818
  "path": "modules/lifecycle/publish-reconciler.js",
5809
5819
  "mode": "0000644",
@@ -5912,7 +5922,7 @@
5912
5922
  {
5913
5923
  "path": "modules/lifecycle/routes/tasks.js",
5914
5924
  "mode": "0000644",
5915
- "sha256": "22a55af5d5c8453a5c27351a74b03bd635638ea177f599763513752f88f7a224"
5925
+ "sha256": "760c76c6ce7ba52991991e7c5fd00a48466d4ad8ae8e61620d962cd4496ea06c"
5916
5926
  },
5917
5927
  {
5918
5928
  "path": "modules/lifecycle/routes/version-route-authz.js",
@@ -7432,7 +7442,7 @@
7432
7442
  {
7433
7443
  "path": "modules/status-ui/public/index.html",
7434
7444
  "mode": "0000644",
7435
- "sha256": "c9a37a51e098cd7ab7dfbb1a800b767ac25f5ade6ef40c22ea47ca326a4b8dc3"
7445
+ "sha256": "25e98c2822bb8b3613edbe369ac374128e07bc6c8abf126f90a781e5e0238e8e"
7436
7446
  },
7437
7447
  {
7438
7448
  "path": "modules/status-ui/public/snapshots/.gitkeep",
@@ -7867,12 +7877,12 @@
7867
7877
  {
7868
7878
  "path": "package-lock.json",
7869
7879
  "mode": "0000644",
7870
- "sha256": "9baa1aa306e1c7925d66fc09484dc2c214ad13fa47e4d71c39fc6d7ca2d6e8a6"
7880
+ "sha256": "d54803c0123c3ee0b857092be850581f787ed85dc36cae70e737977f14f0356b"
7871
7881
  },
7872
7882
  {
7873
7883
  "path": "package.json",
7874
7884
  "mode": "0000644",
7875
- "sha256": "9b0fe6c23bdb7eb915bcb6d049e341258d1daa654a791355ac8ca92bfd58a02b"
7885
+ "sha256": "0a21605b0152921f84f2e67716df7e251b09a23b24c3b9569d3332d8d98e754c"
7876
7886
  },
7877
7887
  {
7878
7888
  "path": "public-docs/index.html",
@@ -8372,7 +8382,7 @@
8372
8382
  {
8373
8383
  "path": "scripts/gds/fitness-checks-identity.js",
8374
8384
  "mode": "0000644",
8375
- "sha256": "583b21bf6527b657058ebfab50aff8c51cec96a0b1baa4d01daea55e90d0ccf7"
8385
+ "sha256": "ae5589886edaa98d66d7bb4b918b47220628c82337535ffeaa2b5cb0c5e2adc0"
8376
8386
  },
8377
8387
  {
8378
8388
  "path": "scripts/gds/fitness-checks-packaging.js",
@@ -8412,7 +8422,7 @@
8412
8422
  {
8413
8423
  "path": "scripts/gds/fitness.js",
8414
8424
  "mode": "0000644",
8415
- "sha256": "42c49bcab43be3367f05b99f4dfded590edf89ae10c1a3df2d9312b6e0d33fc9"
8425
+ "sha256": "5f96e59cb4c27e20c6acaabf8f97b94dc69875b5fb5770fb37e4fb838128b189"
8416
8426
  },
8417
8427
  {
8418
8428
  "path": "scripts/gds/gate-review.js",
@@ -8727,7 +8737,7 @@
8727
8737
  {
8728
8738
  "path": "scripts/gds/paste-token.js",
8729
8739
  "mode": "0000644",
8730
- "sha256": "b61317006e5df92afb5744bd9fb9b3180bf8a674b7f691773114280660a42a2a"
8740
+ "sha256": "cfc9e6a9640f8392a38077a7fa50bd1caae671a60c4735d2758c0cc5a6d10610"
8731
8741
  },
8732
8742
  {
8733
8743
  "path": "scripts/gds/plain-cards.js",
@@ -8767,7 +8777,7 @@
8767
8777
  {
8768
8778
  "path": "scripts/gds/provision-config.js",
8769
8779
  "mode": "0000644",
8770
- "sha256": "1f60ee3d585ad3ab8c84c6b0abac99573ed218183b8e8403f2ddf3e951ff6b3d"
8780
+ "sha256": "b5ec038d150da93b4c54eeb864586a70e46610655ccb5bf10bd1adaa0432a305"
8771
8781
  },
8772
8782
  {
8773
8783
  "path": "scripts/gds/provision-core-upgrade.js",
@@ -8782,7 +8792,7 @@
8782
8792
  {
8783
8793
  "path": "scripts/gds/provision-net.js",
8784
8794
  "mode": "0000644",
8785
- "sha256": "cab7a609869465fcac7234c69e4202f259293e69842163f8ff7b6edfd5d30b4a"
8795
+ "sha256": "f1ca176ff8cdb8de65940af1e30316db600cb5df529abbc997697abf1cbc2c2d"
8786
8796
  },
8787
8797
  {
8788
8798
  "path": "scripts/gds/provision-repo-auth.js",
@@ -8807,7 +8817,7 @@
8807
8817
  {
8808
8818
  "path": "scripts/gds/provision.js",
8809
8819
  "mode": "0000644",
8810
- "sha256": "94a77427cb16c8a959eb279866f3777830d7a33617f02aed5eedb383ca26e84a"
8820
+ "sha256": "43dd54ec69ff29ad83e631d53a65c1a2d22b89ef07181e4df14581e5199bfa61"
8811
8821
  },
8812
8822
  {
8813
8823
  "path": "scripts/gds/publish-credential-check.js",
@@ -9182,7 +9192,7 @@
9182
9192
  {
9183
9193
  "path": "scripts/gds/ship-land.js",
9184
9194
  "mode": "0000644",
9185
- "sha256": "fafa751359543787133e26a7aa18307fec163b0955eea38677a1b319d4dc6162"
9195
+ "sha256": "805017578a7d69ca2c79d105e21add7862b41e352256e5fbdfad3e3740e69ced"
9186
9196
  },
9187
9197
  {
9188
9198
  "path": "scripts/gds/ship-memory-sync.js",
@@ -9457,7 +9467,7 @@
9457
9467
  {
9458
9468
  "path": "scripts/status-mirror-sync.js",
9459
9469
  "mode": "0000644",
9460
- "sha256": "845c8dad87038fe273563e8eda7126e70ac1afe8f9eee8c4364a4404490c4f5f"
9470
+ "sha256": "013860b6269302ca2b9db511cfdd3b915e9963bfcbd3d528dcd086cbe40f94a8"
9461
9471
  },
9462
9472
  {
9463
9473
  "path": "scripts/status-probe.mjs",
@@ -9692,7 +9702,7 @@
9692
9702
  {
9693
9703
  "path": "src/bongos/routes/auth.js",
9694
9704
  "mode": "0000644",
9695
- "sha256": "66abf424a565cdcc4de85350774d91e93be826a3b01a23ad75fee1feb7977a3c"
9705
+ "sha256": "c29f47f08ea45126a8ddd6fd432bdf5c17a98cac0ad6f6ae527bf23e62ffa001"
9696
9706
  },
9697
9707
  {
9698
9708
  "path": "src/bongos/routes/backup.js",
@@ -9772,7 +9782,7 @@
9772
9782
  {
9773
9783
  "path": "src/bongos/serve-internal.js",
9774
9784
  "mode": "0000644",
9775
- "sha256": "697273b43a6825da4828c7b9e6e721843dcf9fd74bd30b94cc22e129995110f4"
9785
+ "sha256": "fb6c9149b602356e3973b62c5042c373c92018e04242decd4a29c2c0897ddec3"
9776
9786
  },
9777
9787
  {
9778
9788
  "path": "src/bongos/uptime-poller.js",
@@ -9782,7 +9792,7 @@
9782
9792
  {
9783
9793
  "path": "src/branding.js",
9784
9794
  "mode": "0000644",
9785
- "sha256": "1d9d566c08fcba34060177bf1bd9c47df08f7c61b6805c305a34fd2d0d280310"
9795
+ "sha256": "086e80cd5effd41bcbada467b5538afa2d35f549b8efd90408d99e39ab2e29ec"
9786
9796
  },
9787
9797
  {
9788
9798
  "path": "src/build-info.js",
@@ -9802,7 +9812,7 @@
9802
9812
  {
9803
9813
  "path": "src/module-api.js",
9804
9814
  "mode": "0000644",
9805
- "sha256": "3363fefd57cbb3a31327b1c613ebdd4a325e26071a575237a7e0ec467a3f74c7"
9815
+ "sha256": "8bfa67a85b56ce33dbb502537edced8bf39de30551c7949bf3aa9460a1deffa3"
9806
9816
  },
9807
9817
  {
9808
9818
  "path": "src/module-loader/catalog.js",
@@ -10277,7 +10287,7 @@
10277
10287
  {
10278
10288
  "path": "tests/bongos_cli.mjs",
10279
10289
  "mode": "0000644",
10280
- "sha256": "ef01af16d62c55325a3b12cc7968a9d797994d2f46364149668bf4e2839cb0e0"
10290
+ "sha256": "4e845333aa949256b8b5c6dc73bf75fcc77047543b0f5e15e89172cad4161772"
10281
10291
  },
10282
10292
  {
10283
10293
  "path": "tests/bongos_dispatch.mjs",
@@ -10562,7 +10572,7 @@
10562
10572
  {
10563
10573
  "path": "tests/cli_token_reissue.mjs",
10564
10574
  "mode": "0000644",
10565
- "sha256": "a82544c109464d884e44731cbdd73e9e141d31d8e18a9241222d6a26223e19c2"
10575
+ "sha256": "da6b45619e6848c37ebc8d53ffc9d8f9d5379df26563c9030b339a8c389e645d"
10566
10576
  },
10567
10577
  {
10568
10578
  "path": "tests/client_baseurl_guard.mjs",
@@ -11032,7 +11042,7 @@
11032
11042
  {
11033
11043
  "path": "tests/fitness.mjs",
11034
11044
  "mode": "0000644",
11035
- "sha256": "b222f8b2436ccf9f392c836104e9f889a8bbe537390fcd53872f359114388f0a"
11045
+ "sha256": "7252f8134ae29b16df7c136865acb110263cc862781d7ae26ee416398caa1f09"
11036
11046
  },
11037
11047
  {
11038
11048
  "path": "tests/fitness_gds_ratchet.mjs",
@@ -12374,6 +12384,11 @@
12374
12384
  "mode": "0000644",
12375
12385
  "sha256": "fd69c44ebf3d246ecedf527275448389cca809e231d1c6cb34e4c12bfdc38111"
12376
12386
  },
12387
+ {
12388
+ "path": "tests/paste_token_api_base.mjs",
12389
+ "mode": "0000644",
12390
+ "sha256": "e48806b48a9f7a4f9f9c010f45750e6b440d2de68feacdabacd9bd25d98f2046"
12391
+ },
12377
12392
  {
12378
12393
  "path": "tests/patch_gate_ordering.mjs",
12379
12394
  "mode": "0000644",
@@ -12434,6 +12449,11 @@
12434
12449
  "mode": "0000644",
12435
12450
  "sha256": "08a7496090f4617cef7e270afddcef8ee3a0b58bff6920b56bc2f2bbb49ed4ca"
12436
12451
  },
12452
+ {
12453
+ "path": "tests/pr_assign.mjs",
12454
+ "mode": "0000644",
12455
+ "sha256": "1f422614784bff05cfddff5196fabfb9458e8b1247e6e6ecb636f1e4f236fc78"
12456
+ },
12437
12457
  {
12438
12458
  "path": "tests/pre_compact.mjs",
12439
12459
  "mode": "0000644",
@@ -12627,7 +12647,12 @@
12627
12647
  {
12628
12648
  "path": "tests/provision.mjs",
12629
12649
  "mode": "0000644",
12630
- "sha256": "09a759a1b1e0cac3ddafdd64adca4daed0f6a8ab4c48a07f42954f38ed510433"
12650
+ "sha256": "5a1abd44776798ca89266f86663037086c1270aca569e54647607d4488cbfea9"
12651
+ },
12652
+ {
12653
+ "path": "tests/provision_caddy_wiring.mjs",
12654
+ "mode": "0000644",
12655
+ "sha256": "a4d448622c05e963602cbc0488215ba168c909bd12cd0db20eaa6fcb7de7a91e"
12631
12656
  },
12632
12657
  {
12633
12658
  "path": "tests/provision_disconnect.mjs",
@@ -13509,6 +13534,11 @@
13509
13534
  "mode": "0000644",
13510
13535
  "sha256": "369981934c65608fb4198c7840bd918c25caa3e5a1aa44b22fe38663e728e7f2"
13511
13536
  },
13537
+ {
13538
+ "path": "tests/status_company_attribution.mjs",
13539
+ "mode": "0000644",
13540
+ "sha256": "4b92c96ae496736b0f44182e64d42a24f108cb51fbeb5cf7cd5814ad336b14f6"
13541
+ },
13512
13542
  {
13513
13543
  "path": "tests/status_mirror_sync.mjs",
13514
13544
  "mode": "0000644",
@@ -216,7 +216,7 @@ export interface PostAuthAppPairStartRequest { device_name?: string; platform?:
216
216
  export interface PostAuthAppPairStartResponse { pair_code: unknown; poll_token: unknown; verify_path: unknown; verify_url: unknown; expires_in: unknown; interval: unknown }
217
217
  export interface PostAuthBackchannelLogoutRequest { logout_token: string }
218
218
  export interface PostAuthBackchannelLogoutResponse { ok: boolean }
219
- export interface PostAuthCliTokenIssueResponse { ok: boolean; token: unknown; expires_at: unknown; builder_login: unknown; paste_command: unknown }
219
+ export interface PostAuthCliTokenIssueResponse { ok: boolean; token: unknown; expires_at: unknown; builder_login: unknown; api_base: unknown; paste_command: unknown }
220
220
  export interface PostAuthDeviceExchangeRequest { assertion: string }
221
221
  export interface PostAuthDeviceExchangeResponse { token: unknown; builder: unknown }
222
222
  export interface PostAuthDevicePollRequest { device_code: string }
@@ -5,7 +5,8 @@
5
5
  "worldName": "Cloud Bongos",
6
6
  "company": {
7
7
  "name": "",
8
- "site": ""
8
+ "site": "",
9
+ "tagline": ""
9
10
  }
10
11
  },
11
12
  "llmProjectName": "this project",
@@ -0,0 +1,49 @@
1
+ # ADR 0303 — Provisioning verifies the base Caddyfile, and never edits it
2
+
3
+ **Status:** accepted · **Date:** 2026-09-17 · **Task:** [task 1002860](https://cloudbongos.com/builders#/task/1002860) (goal 1000106 — project hosting & fleet; from idea 1000524)
4
+
5
+ **Related:** [ADR 0111](0111-instance-hosting-provisioning-module.md) §4 (on-demand ACME behind the tls-check ask) · [ADR 0128](0128-provisioning-runner-privilege-and-db-auth-model.md) (the runner writes snippets under `sudo install`; it found the live Caddyfile missing the ask)
6
+
7
+ ## Context
8
+
9
+ The provisioning runner writes one `<slug>.caddy` site snippet per instance and reloads Caddy. It
10
+ depends on two lines in the box's base `/etc/caddy/Caddyfile` that it never checked:
11
+ `on_demand_tls { ask …/provisioning/tls-check }` and `import /etc/caddy/sites/*.caddy`. Both were
12
+ hand-applied on the live boxes. The reload ran with `allowFail: true`. So on a box missing
13
+ either line, provisioning reported success and the instance was simply unreachable, with no
14
+ signal to anyone. The base Caddyfile is not in this repository. It is host configuration, and
15
+ it also serves the platform's own apex.
16
+
17
+ ## Decision
18
+
19
+ 1. **Detect and refuse, never edit.** The runner reads the base Caddyfile and does not write it.
20
+ A missing directive stops the run with the exact line to add and the file to add it to.
21
+ An automatic edit was rejected for two reasons. `on_demand_tls` has to sit inside the
22
+ global options block, which an append cannot place and a rewrite could corrupt. And a bad
23
+ write to that file takes down every site on the box, the control plane's own apex included.
24
+ The directives are a one-time setup per box, so the refusal only ever fires on a box that
25
+ was never configured.
26
+ 2. **Read Caddy's resolved config, not the file's text.** The check runs
27
+ `caddy adapt --config <Caddyfile> --adapter caddyfile`. That command reads config files only,
28
+ so it needs no privilege and never touches certificate keys, and the runner's scoped sudo is
29
+ not widened. It inspects the JSON. The ask must appear under `apps.tls.automation.on_demand`,
30
+ which covers both Caddy 2.7's `ask` string and 2.8+'s `permission` module. The domain must
31
+ appear under `apps.http`. Because this is the resolved config, it doesn't matter how the operator
32
+ wrote the directives: an absolute or relative import, a split across imported files, a
33
+ `/api/gds` alias. The base path is `PROVISION_CADDYFILE`, default `/etc/caddy/Caddyfile`.
34
+ 3. **Two checkpoints.** A preflight checks the ask before the box is touched, in both the
35
+ provision and domain-attach legs, beside the DNS-scope preflight. The import can only be
36
+ proven once a snippet exists, so a second check runs after the write and requires the domain
37
+ to be served. It fails the run before any reload. A snippet in a directory nothing imports
38
+ is inert, so leaving it on disk is safe.
39
+ 4. **The reload is loud.** `systemctl reload caddy` no longer passes `allowFail`. Caddy's
40
+ graceful reload validates and swaps atomically, and it keeps the old config when it rejects
41
+ the new one. A failed reload fails the run and never leaves Caddy half-configured. There is
42
+ no separate `caddy validate` step: it would load certificate keys, which needs root, and
43
+ `caddy adapt` plus the reload already cover what it would catch.
44
+
45
+ ## Consequences
46
+
47
+ - A box that was never set up gets a named instruction instead of a silently unreachable project.
48
+ - Domainless instances, dry runs and dedicated droplets (Caddy comes from cloud-init) skip the check.
49
+ - `caddy` must be on the runner's `PATH` on the box that serves the snippet. Every supported shape already has it there, because it reloads that Caddy.
@@ -394,3 +394,4 @@ This keeps the decision history honest and traceable.
394
394
  | 0300 | [**A missing protected-surface registry is a broken install, not a module opt-out** ([task 1003662](https://cloudbongos.com/builders#/task/1003662), goal 1000091). Filed as a bug with a remedy attached: `require('./module-scope-map')` throws when `modules/government/protected-surfaces.json` is absent, so make it degrade to an empty protected set + a warning, matching `resolveOptional`. The throw reproduces, and the rest is wrong four times over. **The throw is not in the named file** — `module-scope-map.js` has no throw and no file read; it requires `permission-path-check.js`, whose `loadRegistry()` throws at module scope. **It is a written contract, not an oversight** — that file's header says FAIL-CLOSED in capitals and already pre-answers the layering worry (the read is `fs`, never `require`, so ADR 0083's one-way rule holds; static data, not code; `route-rank-check.js` reads `modules/*/routes/*.js` the same way). **Disabling the module cannot cause it** — enablement is a runtime flag, never a file deletion, and the read never consults it. **"Not yet materialized" cannot either** — no `files[]` allowlist, so `npm pack --dry-run` puts the 13,274-byte registry in the tarball; per ADR 0108 §1 you cannot have `@bongos/core` without it. The `resolveOptional` precedent does not transfer: that is the module **seam/port** resolver for a missing runtime *capability*, whereas this is the static data the boundary is made of, read where there is no server and no DB (a git hook, a CI job) — borrowing it converts "the boundary is unreadable" into "there is no boundary". **Decision: fail-closed stays, no behavior change, the remedy is rejected — and the one genuine defect is fixed: the message now names the cause and the fix** (reinstall the core; do not empty the set), because stating a refusal without a remedy is exactly how a reader concludes the module is optional and files this task. The asymmetry is the point: a false "nothing is protected" is silent, permanent and grants authority; a false "this install is broken" is loud and grants nothing. Revisit only if a core-only checkout without `modules/` becomes legitimate — and then the question is *where the registry lives*, not whether it is optional.](0300-a-missing-protected-surface-registry-is-a-broken-install.md) | governance / permissions |
395
395
  | 0301 | [**A red PR names its failing tests in a commit status, and says so when it cannot see** ([task 1003988](https://cloudbongos.com/builders#/task/1003988), goal 1000095). One pull request took nine red CI runs in four hours because the builder box could not learn which test failed: without Checks:read GitHub nulls every check-run node, the server dropped the holes silently, and the red PR was reported as a review-or-pending wait. Now the merge state carries checks_unreadable and a red, unreadable rollup reads failing_check_unreadable; the unit workflow posts the failing test names as a unit-report commit STATUS (readable without the permission) from a separate job that holds the only write token and checks out no PR code; the server returns it as failing_tests, reduced to test-list vocabulary because the PR controls the workflow that wrote it.](0301-a-red-pr-names-its-failing-tests-in-a-commit-status.md) | GDS / shipping pipeline |
396
396
  | 0302 | [**The third identical failure stops the session and asks** ([task 1003990](https://cloudbongos.com/builders#/task/1003990), goal 1000095). Sessions looped without noticing: one PR re-shipped into the same red check nine times, and 45 of 169 claim attempts were the same refusal retried minutes apart. A PostToolUse hook now counts walls per session — the signature is the `[otb-plain]` card's "What happened" line + the task id + the RED line, so a red check naming different failing tests each run reads as progress — and on the third identical one injects a single instruction: stop retrying, diagnose (path named), or /ask-for-help. Waiting cards ("Nothing to do…") are never counted; git-guard and worktree-guard count their own blocks through the same helper; it never blocks a tool call and echoes nothing a tool printed. Rejected: counting any repeated failing command (red→green is that), blocking the fourth attempt (a new gate, ADR 0162), server-side detection (cannot see guard blocks or speak into the session).](0302-the-third-identical-failure-stops-the-session-and-asks.md) | GDS / sessions / hooks |
397
+ | 0303 | [**Provisioning verifies the base Caddyfile, and never edits it** ([task 1002860](https://cloudbongos.com/builders#/task/1002860), goal 1000106, from idea 1000524). The runner assumed the box's `/etc/caddy/Caddyfile` carried the `on_demand_tls` tls-check ask and `import /etc/caddy/sites/*.caddy`, and reloaded with `allowFail` — a box missing either provisioned "successfully" and the instance was unreachable. It now reads Caddy's RESOLVED config (`caddy adapt`: unprivileged, no key access, indifferent to how the directives are written) before touching the box (the ask) and after writing the snippet (the domain is served ⇒ the import reaches it), refusing with the exact line to add, and the reload is loud. Rejected: editing the base file (`on_demand_tls` must sit inside the global block an append cannot place, and a bad write takes down the control plane's own apex); `caddy validate` (loads certificate keys, needs root).](0303-provisioning-verifies-the-base-caddyfile-and-never-edits-it.md) | provisioning / infra |
@@ -20991,6 +20991,7 @@
20991
20991
  "token": {},
20992
20992
  "expires_at": {},
20993
20993
  "builder_login": {},
20994
+ "api_base": {},
20994
20995
  "paste_command": {}
20995
20996
  },
20996
20997
  "required": [
@@ -20998,6 +20999,7 @@
20998
20999
  "token",
20999
21000
  "expires_at",
21000
21001
  "builder_login",
21002
+ "api_base",
21001
21003
  "paste_command"
21002
21004
  ]
21003
21005
  },
@@ -14,7 +14,7 @@
14
14
  - **Cloud Bongos control-plane droplet (NEW 2026-07-05, [ADR 0126](adr/0126-dedicated-cloudbongos-control-plane-droplet.md), task [#2063](https://example.com/builders#/task/2063)):** DigitalOcean, hostname `cloudbongos`, IPv4 `REDACTED_IP`, NYC3, `s-1vcpu-2gb` (2 GB / 1 vCPU), Ubuntu 24.04, ~$12/mo, tag `cloudbongos-control`. Runs **cloudbongos.com + `builders.` + `status.`** (`src/platform-server.js`, Cloud Bongos brand, port 3002, own `cloudbongos` DB, checkout `/home/lars/cloudbongos-instance`, the root subscribed to the nightly core-patch sweep) and is the **sole token-holding control plane** — a SECOND root, `/home/lars/cloudbongos`, survives only to vendor `infra/` (the box cloud-init the core package does not ship, ADR 0150); it is not subscribed, so nothing may resolve core code from it (task 1002738, [ADR 0144](adr/0144-devbox-rehome-onto-cloudbongos-plane.md) follow-up 5) — `/etc/cloudbongos/box.env` holds the account-wide Cloudflare token (IP-locked to this box) + the DigitalOcean token; this is where `provision.js` provisions instance subdomains. **Structurally separate SSH** (own `cloudbongos_ed25519` key). IPv6 egress disabled so the IPv4-locked CF token can't be spuriously rejected. cloudbongos.com was previously a co-tenant on the co-hosting box; that unit was decommissioned at cutover. **Follow-ups:** deploy automation (task [#2064](https://example.com/builders#/task/2064)) + fully stripping cloud tokens off the co-hosting box (task [#2065](https://example.com/builders#/task/2065)).
15
15
  - **Server stack:** Node.js 22 LTS · PostgreSQL 16 · Caddy 2.11.2. `example.service` (systemd) runs `node server.js` as user `lars` on `127.0.0.1:3000`; restart-on-failure.
16
16
  - **Hardening:** `lars` user with passwordless sudo; SSH key-only (root login disabled, password auth disabled); ufw allows 22/80/443; fail2ban active; unattended-upgrades enabled; timezone `America/New_York`.
17
- - **TLS:** Origin cert at `/etc/caddy/origin.crt` + `/etc/caddy/origin.key` for the example apexes on the co-hosting box. **cloudbongos.com's apexes moved to the dedicated control-plane droplet** ([ADR 0126](adr/0126-dedicated-cloudbongos-control-plane-droplet.md)) with their own copy of the Cloudflare Origin Cert (SAN `*.cloudbongos.com, cloudbongos.com`) at `/etc/caddy/cloudbongos-origin.{crt,key}` on `REDACTED_IP`. Cloudflare SSL/TLS mode = Full (strict). Renewal is a non-issue for ~15 years. **Provisioned instance hosts use a different, coexisting posture** ([ADR 0111](adr/0111-instance-hosting-provisioning-module.md) §4 / task P4): Caddy **on-demand ACME** (Let's Encrypt) via the global `on_demand_tls { ask … }` block, gated to hostnames the `provisioning` module actually stood up (`/api/bongos/provisioning/tls-check`). Their DNS records are **proxied=false (grey cloud)** so the ACME challenge reaches Caddy — the opposite of the origin-cert apexes; do not enable the proxy on them or renewal breaks silently ([ADR 0003](adr/0003-cloudflare-origin-cert-over-letsencrypt.md)). Per-instance site snippets live in `/etc/caddy/sites/*.caddy` (`import`ed by `infra/Caddyfile`). **Tradeoff (accepted, SEC review of task 1945):** `proxied=false` publishes the box's real origin IP in public DNS for every co-tenant provisioned hostname — defeating, for those names, the Cloudflare-proxy IP-hiding the apexes enjoy (a co-tenant host resolves to the same `REDACTED_IP` that serves the main game). This is inherent to on-demand ACME (the challenge must hit the origin) and acceptable under the budget/trust model; prefer a **dedicated droplet** (its own IP) for any instance expected to draw hostile attention.
17
+ - **TLS:** Origin cert at `/etc/caddy/origin.crt` + `/etc/caddy/origin.key` for the example apexes on the co-hosting box. **cloudbongos.com's apexes moved to the dedicated control-plane droplet** ([ADR 0126](adr/0126-dedicated-cloudbongos-control-plane-droplet.md)) with their own copy of the Cloudflare Origin Cert (SAN `*.cloudbongos.com, cloudbongos.com`) at `/etc/caddy/cloudbongos-origin.{crt,key}` on `REDACTED_IP`. Cloudflare SSL/TLS mode = Full (strict). Renewal is a non-issue for ~15 years. **Provisioned instance hosts use a different, coexisting posture** ([ADR 0111](adr/0111-instance-hosting-provisioning-module.md) §4 / task P4): Caddy **on-demand ACME** (Let's Encrypt) via the global `on_demand_tls { ask … }` block, gated to hostnames the `provisioning` module actually stood up (`/api/bongos/provisioning/tls-check`). Their DNS records are **proxied=false (grey cloud)** so the ACME challenge reaches Caddy — the opposite of the origin-cert apexes; do not enable the proxy on them or renewal breaks silently ([ADR 0003](adr/0003-cloudflare-origin-cert-over-letsencrypt.md)). Per-instance site snippets live in `/etc/caddy/sites/*.caddy` (`import`ed by `infra/Caddyfile`); the provisioning runner verifies the box's resolved Caddy config carries that import and the `on_demand_tls` ask before it reports a domain wired, and refuses with the line to add rather than editing the base file ([ADR 0303](adr/0303-provisioning-verifies-the-base-caddyfile-and-never-edits-it.md)). **Tradeoff (accepted, SEC review of task 1945):** `proxied=false` publishes the box's real origin IP in public DNS for every co-tenant provisioned hostname — defeating, for those names, the Cloudflare-proxy IP-hiding the apexes enjoy (a co-tenant host resolves to the same `REDACTED_IP` that serves the main game). This is inherent to on-demand ACME (the challenge must hit the origin) and acceptable under the budget/trust model; prefer a **dedicated droplet** (its own IP) for any instance expected to draw hostile attention.
18
18
  - **Platform observability — REMOVED 2026-09-15 ([ADR 0284](adr/0284-the-control-plane-carries-no-metrics-stack.md), task 1003876):** the control plane runs **no Grafana and no Prometheus**, and `metrics.cloudbongos.com` has no Caddy site. They cost ~300 MB RSS + 2.5 GB of data on the box every provisioned project shares, and scraped none of those projects. Every instance still exports `/metrics` ([`src/bongos/metrics.js`](../src/bongos/metrics.js), loopback or `METRICS_TOKEN`), so a scraper can return from a box that is *not* the control plane. The rest of this bullet is the **historical** ADR 0127 layout, kept so its leftovers (co-hosting-box forwarders, instance `infra/` copies) can be found: this watched the **Bongos API** (`gds_http`/`tasks`/`claims`/`builders`/`auth`), not any one game, so it lived **on the control-plane box** (REDACTED_IP) at **`https://metrics.cloudbongos.com`** and observed the **whole fleet**. **Prometheus** (`127.0.0.1:9090`, loopback-only) scrapes every instance's `/metrics` ([`src/bongos/metrics.js`](../src/bongos/metrics.js)), each tagged with a `project` label ([`infra/prometheus/prometheus.yml`](../infra/prometheus/prometheus.yml)): **cloudbongos** locally (`:3002`); **example + emersonian-circles** on the co-hosting box, reached over the **private VPC** via `socat` forwarders on `10.108.0.2:9101/9102` (→ their loopback `:3000`/`:3004`), ufw-locked to the control-plane box's private IP — raw TCP relays, so the exporter still sees a loopback request and needs no token; nothing is public. **Grafana** (`127.0.0.1:3003`, loopback-only) renders the 4 provisioned-as-code dashboards (folder **Cloud Bongos**: system-overview, error-watch, builder-activity, auth-security), each with a **`project`** template variable for per-instance / fleet views. Reverse-proxied on the `*.cloudbongos.com` origin cert — Archon-only, gated by Caddy `basic_auth` (user `archon`) **and** Grafana's own admin login (both secrets owner-held, never in the repo). Grafana config is a systemd `GF_*` env drop-in (`grafana-server.service.d/override.conf`, `chmod 600`). ~256 MB RAM. A future dedicated-droplet instance is added as a `METRICS_TOKEN` Bearer target instead of a VPC forwarder.
19
19
 
20
20
  **Application stack:**
@@ -226,7 +226,7 @@ Bongos views: `claimable_tasks` (rebuilt in 006 to use SELECT t.*; rebuilt again
226
226
  - Own-scoped session self-service ([ADR 0206](adr/0206-own-scoped-session-revocation-no-absolute-ceiling.md), task 1003341), in [`src/bongos/routes/my-sessions.js`](../src/bongos/routes/my-sessions.js): `GET /me/sessions` (every live session on the caller's own account — source, user agent, created/last-used/expires, a `current` flag; the token value is never selected), `POST /me/sessions/revoke` (`{session_id}` — one of your own; a miss answers `revoked: 0` so a stranger's id cannot be probed) and `POST /me/sessions/revoke-all` (sign out everywhere, the requesting session included). All three gate on `requireBuilder` alone and key every statement on `req.builder.id` — an account is never named in the query, body or path. They exist because a bearer refreshes to `now()+24h` on every call, so a leaked one never expires, and the cross-builder pair (`POST /auth/revoke`, `/auth/revoke-all`, both still gated on the admin `auth.session.revoke` atom) needed an Archon the victim might not have. Surfaced in the hall at Settings → Active sessions.
227
227
  - Authenticated CLI/web: `GET /me` (returns `rank`), `GET /versions[/progress]`, `GET /tasks[?version=&status=&kind=&discipline=]`, `GET /tasks/claimable[?version=&discipline=]`, `GET /tasks/:id`, `POST /tasks` (accepts `discipline` + `criterion_ids`; **metic+** — [ADR 0090](adr/0090-metic-task-authoring.md)), `PATCH /tasks/:id` (supports `parent_task_id`, `kind`, `discipline`, `goal_id` — (re)assign the task's goal; must be a goal in the task's own version, task 1763; **metic+**), `POST /tasks/:id/promote` (**metic+**), `POST /claims`, `POST /claims/:id/resolve`, `POST /cost`.
228
228
  - Criterion rollup + task↔criterion links ([ADR 0025](adr/0025-structured-criterion-task-link.md), [#435](https://example.com/builders#/task/435)/[#438](https://example.com/builders#/task/438)): `GET /versions/:id/progress` rolls up each done-when criterion → its gating tasks (via `task_criteria`) → live status counts + the not-yet-shipped `remaining[]` + `unattributed_tasks` (the read behind the `/status` skill, in `src/bongos/done-when.js criterionProgress`). The link CRUD mirrors the dependency endpoints: `GET /tasks/:id/criteria`, `POST /tasks/:id/criteria` (**metic+** — [ADR 0090](adr/0090-metic-task-authoring.md)) and `DELETE /tasks/:id/criteria/:criterionId` (**metic+**). A criterion ref is a numeric `done_when_criteria.id`, a positional `"Cn"` token, or a `criterion_id` slug, resolved against the task's version (`POST /tasks` links at create time so `/status` counts the task with no backfill).
229
- - Server-mediated branch publish (ADR 0055 / [#1025](https://example.com/builders#/task/1025) — lets a dev box with no GitHub push credential ship). Owner-gated like ship: `POST /tasks/:id/publish-branch` (`requireBuilder` + `gateTaskOwnership`, **NOT** `allowBoxScope` → a box-scoped session is `403 box_scope`, so the builder must `/builder-reauth` first; own 32 MB json parser for the base64 thin-bundle body, 20 MB decoded cap; the server pushes the branch + opens the PR + auto-merges with a server-side push credential — a **GitHub App** installation token (short-lived, repo-scoped; preferred, [ADR 0055](adr/0055-server-mediated-branch-publish.md) update / task 1028) or the `GITHUB_PUSH_TOKEN` PAT fallback; `503 push_unconfigured` when neither is set; `400 tip_mismatch` if the bundle tip ≠ the claimed `head_sha`) and `GET /tasks/:id/publish-status?branch=…` (polls PR + deploy-prod state derived live from GitHub). When the PR is still open it also carries `merge_driver`, whose `reason` says why it has not merged; on a red PR `failing_tests` names the failing unit tests (read from the `unit-report` commit status the `unit` workflow posts) and `checks_unreadable: true` says the check list is blind because the App lacks `Checks: read` — so an empty `checks[]` is never read as "nothing failed" (task 1003988, ADR 0301). `ship.js` uses these in `ci` mode only when `pushVia()` resolves to server (a box, or `OTB_PUSH_VIA_SERVER=1`); a laptop with `gh` keeps the local push path unchanged. Implementation in `src/bongos/github-push.js`.
229
+ - Server-mediated branch publish (ADR 0055 / [#1025](https://example.com/builders#/task/1025) — lets a dev box with no GitHub push credential ship). Owner-gated like ship: `POST /tasks/:id/publish-branch` (`requireBuilder` + `gateTaskOwnership`, **NOT** `allowBoxScope` → a box-scoped session is `403 box_scope`, so the builder must `/builder-reauth` first; own 32 MB json parser for the base64 thin-bundle body, 20 MB decoded cap; the server pushes the branch + opens the PR + auto-merges with a server-side push credential — a **GitHub App** installation token (short-lived, repo-scoped; preferred, [ADR 0055](adr/0055-server-mediated-branch-publish.md) update / task 1028) or the `GITHUB_PUSH_TOKEN` PAT fallback; `503 push_unconfigured` when neither is set; `400 tip_mismatch` if the bundle tip ≠ the claimed `head_sha`) and `GET /tasks/:id/publish-status?branch=…` (polls PR + deploy-prod state derived live from GitHub). When the PR is still open it also carries `merge_driver`, whose `reason` says why it has not merged; on a red PR `failing_tests` names the failing unit tests (read from the `unit-report` commit status the `unit` workflow posts) and `checks_unreadable: true` says the check list is blind because the App lacks `Checks: read` — so an empty `checks[]` is never read as "nothing failed" (task 1003988, ADR 0301). Because the server's credential is the PR's AUTHOR, publish also **assigns the PR to the task's claim holder** and names them in the body (`modules/lifecycle/pr-assign.js`, task 1003991) — best-effort: the 201 carries `pr_assignee` (the login, or `null` when GitHub would not assign it) and a failed assignment never fails the publish. `ship.js` uses these in `ci` mode only when `pushVia()` resolves to server (a box, or `OTB_PUSH_VIA_SERVER=1`); a laptop with `gh` keeps the local push path unchanged. Implementation in `src/bongos/github-push.js`.
230
230
  - Archon monitoring reads backing the `/watch` page (ADR 0036): `GET /builders/roster`, `GET /grades/by-builder[?days=N]` (**archon-only**, [#726](https://example.com/builders#/task/726) — per-builder grade breakdown for the MARKS section; the project-wide aggregate stays public at `/public/grades`), `GET /audit-log`, `GET /override-requests`, `GET /access-requests`, `GET /security/reports`. Personal-prefs writes backing `/settings`: `GET/PATCH /me/skill-prefs`, `PATCH /me/disciplines`.
231
231
  - Per-builder "needs" + own Gemini key ([ADR 0073](adr/0073-builder-needs-signal-and-byok-gemini-key.md), [#1013](https://example.com/builders#/task/1013)): `GET /me` now also carries `needs` ({items, action_needed_count} — the consistent "the system needs an input from you" signal; `modules/builder-settings/builder-needs.js` is the SSOT — carved out in BV1.R80, resolved by `GET /me` via the `builder-settings` kernel port — rendered by the hall Standing card + the CLI `printNeedsNudge`). The own-key (pragmatic BYOK) endpoints, all own-scoped: `GET /me/art-key/own` (masked meta — last4 only), `PUT /me/art-key/own` (validate-on-save via a Google list-models call → encrypt with `src/bongos/secret-box.js` → store; `503 storage_not_configured` until `BUILDER_SECRET_KEY` is set, `422 invalid_key` on a bad key), `DELETE /me/art-key/own`. The existing `GET /me/art-key` (shared-key box-sync delivery) is extended to also deliver the decrypted own key, which `scripts/gds/fetch-art-key.js` syncs into the local `gemini_api_key` slot (own key wins in the `gen_api.py` cascade).
232
232
  - Builder memory (ADR 0024 cloneable memory + ADR 0026 BFG). Self-scoped (owner is always `req.builder.id`, never input): `POST /memory/sync` (push own memory; own 64 MB parser, vs the global 64 KB), `GET /memory/files`, `GET /memory/file`, `DELETE /memory/file` (the BFG delete affordance, 6C.2). Cross-builder (input-owner) endpoints — all privileged + audit-logged fail-closed: `GET /memory/builders/:id/files`+`/file` (**archon forensic** read), `GET /memory/bfg/builders/:id/files`+`/file` (**BFG-principal only**, 6B.1 read), and the one cross-builder WRITE `POST /memory/builders/:id/bfg-write` (**BFG-principal only**, kill-switched `BFG_WRITE_ENABLED` default-OFF, namespaced `bfg/`, attributed `author: BFG`, write+audit in one tx — dreams + transparent corrections, 6C.1/6C.2).
@@ -21,7 +21,7 @@ env override → instance config (config/branding.json) → neutral star
21
21
  |---|---|
22
22
  | `identity.productName` | The product name (e.g. "Example"). **Required.** |
23
23
  | `identity.worldName` | The in-world / civilization name rendered at runtime (e.g. "Example"). **Required.** |
24
- | `identity.company` | `{ name, site }` of the operating company (may be empty). |
24
+ | `identity.company` | `{ name, site, tagline }` of the operating company — every field may be empty. `tagline` is the free-text clause that follows the name in the status page footer ("{{worldName}} is the work of **name**, *tagline*."), so put a location there if you want one; there is no separate location field. The footer stamps them as `{{companyName}}` / `{{companySite}}` / `{{companyTagline}}`, and each clause sits in a `<!--brand-if:KEY-->…<!--/brand-if:KEY-->` region that drops whole when its field is empty: no name hides the whole sentence, no site renders the name unlinked, no tagline ends the sentence at the name. (task [#1002853](https://cloudbongos.com/builders#/task/1002853)) |
25
25
  | `llmProjectName` | The project name injected into grader / architect / onboarding **LLM prompts**, so a non-OTB instance is graded against its own identity. **Required.** |
26
26
  | `domains.publicOrigin` | Public game/app origin. **Required.** |
27
27
  | `domains.buildersOrigin` / `statusOrigin` | Hall + status origins. **These three (`publicOrigin`/`buildersOrigin`/`statusOrigin`) are the docs-discovery contract** ([ADR 0114](adr/0114-instance-docs-discovery-contract.md)): the hall is the canonical doc-reading surface, `/docs` on `publicOrigin` renders the API reference, the status page is the outage fallback. `GET <publicOrigin>/api/bongos` re-advertises them (`hall`/`status`/`public`) so an agent resolves the surfaces with no hard-coded host. |
@@ -8,7 +8,7 @@ Paste the block below into your own LLM (Claude, etc.). It interviews you and em
8
8
  >
9
9
  > Interview me for:
10
10
  > 1. **Product name** and the **in-world / display name** (can be the same).
11
- > 2. **Operating company** name + website (optional).
11
+ > 2. **Operating company** name + website + a one-line descriptor such as "a rocket works in Dayton, Ohio" (all optional; `identity.company.tagline`).
12
12
  > 3. **Domains:** public origin, hall origin, status origin, the OAuth sign-in origin, and the session cookie domain (use `""` for a host-only cookie unless you run subdomains — never widen it casually).
13
13
  > 4. **GitHub repo** (`owner`, `name`) this instance ships to.
14
14
  > 5. **Reward currency label** (the unit builders earn; the math is fixed — this is display only).
@@ -1472,7 +1472,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
1472
1472
  | id | conf | text | where |
1473
1473
  |---|---|---|---|
1474
1474
  | `33aaf5eb47b2` | certain | ${…} of ${…} spent | `modules/status-ui/public/status.js:222` |
1475
- | `c5fbe5ea4335` | certain | , a third-party logistics house in Cleveland, Ohio. | `modules/status-ui/public/index.html:355` |
1475
+ | `d31b6094f7ac` | certain | , {{companyTagline}} | `modules/status-ui/public/index.html:356` |
1476
1476
  | `2045078fb115` | certain | 90 days ago | `modules/status-ui/public/status.js:477` |
1477
1477
  | `e4848e2d9386` | certain | A new portrait is set down each time the city's stones are reshaped. Scroll to see how {{worldName}} has grown | `modules/status-ui/public/index.html:344` |
1478
1478
  | `390faeaffb47` | certain | A project | `modules/status-ui/public/explore.js:71` |
@@ -1560,10 +1560,10 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
1560
1560
  | `b97f429c33fe` | certain | works shipped (90d) | `modules/status-ui/public/index.html:218` |
1561
1561
  | `71a303e1ba09` | certain | works wrought, last 7 days | `modules/status-ui/public/index.html:167` |
1562
1562
  | `24ea04f49b3d` | certain | year's purse | `modules/status-ui/public/index.html:259` |
1563
- | `4c45bbf03088` | certain | {{Currency}} are wrought by the lawmen's review; redemption awaits their seal. | `modules/status-ui/public/index.html:356` |
1563
+ | `4c45bbf03088` | certain | {{Currency}} are wrought by the lawmen's review; redemption awaits their seal. | `modules/status-ui/public/index.html:357` |
1564
1564
  | `89aa84ef4595` | certain | {{Currency}} spent, against the value wrought. | `modules/status-ui/public/index.html:245` |
1565
1565
  | `35872b9ecc49` | certain | {{currency}} spent | `modules/status-ui/public/index.html:251` |
1566
- | `e87f28d25342` | certain | {{worldName}} is the work of | `modules/status-ui/public/index.html:355` |
1566
+ | `e87f28d25342` | certain | {{worldName}} is the work of | `modules/status-ui/public/index.html:356` |
1567
1567
  | `7a15e19adbd2` | certain | {{worldName}} — the public ledger | `modules/status-ui/public/index.html:6` |
1568
1568
  | `7a15e19adbd2` | certain | {{worldName}} — the public ledger | `modules/status-ui/public/index.html:6` |
1569
1569
  | `12e0efd49a32` | certain | {…} goal{…} · {…} achieved | `modules/status-ui/public/status.js:582` |
@@ -17802,11 +17802,11 @@
17802
17802
  "confidence": "certain"
17803
17803
  },
17804
17804
  {
17805
- "id": "c5fbe5ea4335",
17805
+ "id": "d31b6094f7ac",
17806
17806
  "surface": "status-dashboard",
17807
- "text": ", a third-party logistics house in Cleveland, Ohio.",
17807
+ "text": ", {{companyTagline}}",
17808
17808
  "file": "modules/status-ui/public/index.html",
17809
- "line": 355,
17809
+ "line": 356,
17810
17810
  "origin": "html-text",
17811
17811
  "confidence": "certain"
17812
17812
  },
@@ -18598,7 +18598,7 @@
18598
18598
  "surface": "status-dashboard",
18599
18599
  "text": "{{Currency}} are wrought by the lawmen's review; redemption awaits their seal.",
18600
18600
  "file": "modules/status-ui/public/index.html",
18601
- "line": 356,
18601
+ "line": 357,
18602
18602
  "origin": "html-text",
18603
18603
  "confidence": "certain"
18604
18604
  },
@@ -18625,7 +18625,7 @@
18625
18625
  "surface": "status-dashboard",
18626
18626
  "text": "{{worldName}} is the work of",
18627
18627
  "file": "modules/status-ui/public/index.html",
18628
- "line": 355,
18628
+ "line": 356,
18629
18629
  "origin": "html-text",
18630
18630
  "confidence": "certain"
18631
18631
  },
@@ -2150,5 +2150,9 @@ is load-bearing: the script throws rather than guess if it is missing, and
2150
2150
  landed since 1.19.836 with no explicit bump. run 35248928789. (task 1002620)
2151
2151
  1.19.838 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
2152
2152
  landed since 1.19.837 with no explicit bump. run 35250225228. (task 1002620)
2153
+ 1.19.839 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
2154
+ landed since 1.19.838 with no explicit bump. run 35252591179. (task 1002620)
2155
+ 1.19.840 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
2156
+ landed since 1.19.839 with no explicit bump. run 35254616836. (task 1002620)
2153
2157
  ---------------------------------------------------------------------------
2154
2158
  ```
@@ -1479,8 +1479,8 @@ module.exports = {
1479
1479
  landProvenForTask,
1480
1480
  prReferencesTask,
1481
1481
  findTaskPullRequest,
1482
- scrubCredential,
1483
- truncateLoudly, // task 1003222 — exported so its behaviour is testable, not just its effect
1482
+ scrubCredential, ghHeaders,
1483
+ truncateLoudly, // task 1003222 — exported so its behaviour is testable, not just its effect
1484
1484
  STDERR_MAX,
1485
1485
  isAncestorOfDefaultBranch,
1486
1486
  resolveToken,