@bongos/core 1.19.1072 → 1.19.1074

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 (297) hide show
  1. package/.bongos-core.json +320 -465
  2. package/.claude/skills/feedback/SKILL.md +2 -2
  3. package/clients/bongos-client/README.md +1 -1
  4. package/clients/bongos-client/bongos-client.global.js +4 -60
  5. package/clients/bongos-client/index.cjs +4 -60
  6. package/clients/bongos-client/index.d.ts +5 -90
  7. package/clients/bongos-client/index.mjs +4 -60
  8. package/config/branding.neutral.json +1 -5
  9. package/config/modules.neutral.json +2 -3
  10. package/config/scheduled-routines.json +0 -2
  11. package/docs/adr/0145-free-hosted-project-tier-isolation-and-domain-separation.md +1 -0
  12. package/docs/adr/0285-a-shared-box-holds-about-twelve-projects-per-gb-and-memory-is-the-wall.md +1 -0
  13. package/docs/adr/0323-hosting-is-three-shapes-and-we-are-not-the-landlord.md +1 -1
  14. package/docs/adr/0327-a-cloud-host-runs-on-the-owners-account-and-the-key-is-borrowed.md +1 -1
  15. package/docs/adr/0341-the-page-is-the-unit-of-tweak-mode.md +6 -2
  16. package/docs/adr/0345-we-host-every-projects-hall-and-its-app-deploys-where-the-owner-chooses.md +94 -0
  17. package/docs/adr/README.md +2 -1
  18. package/docs/api/openapi.json +171 -1661
  19. package/docs/api-reference.md +9 -41
  20. package/docs/architecture.md +16 -1
  21. package/docs/branding-contract.md +0 -3
  22. package/docs/copy-inventory.md +454 -479
  23. package/docs/copy-registry.json +746 -978
  24. package/docs/file-map.md +5 -34
  25. package/docs/module-api-changelog.md +4 -0
  26. package/docs/modules-contract.md +13 -1
  27. package/docs/onboarding/diagrams/04-architecture.mmd +11 -22
  28. package/docs/onboarding/diagrams/README.md +3 -3
  29. package/docs/onboarding/diagrams/assertions.json +2 -22
  30. package/docs/onboarding/primer.md +9 -28
  31. package/docs/page-inventory.json +4 -1
  32. package/docs/page-readings.json +1032 -1077
  33. package/docs/recipes/render-pilot.md +29 -10
  34. package/migrations/core_257_module_store_registry.sql +87 -0
  35. package/migrations/core_258_drop_dev_box_tables.sql +55 -0
  36. package/modules/agents/module.json +1 -0
  37. package/modules/autonomy/module.json +1 -0
  38. package/modules/builder-settings/module.json +1 -0
  39. package/modules/builder-settings/render-prefs.js +4 -5
  40. package/modules/copy-desk/docx.js +78 -5
  41. package/modules/copy-desk/module.json +1 -0
  42. package/modules/copy-desk/routes/copy-desk.js +9 -9
  43. package/modules/copy-desk/tests/copy_docx.mjs +107 -1
  44. package/modules/copy-desk/tests/fixtures/word-docx.mjs +30 -0
  45. package/modules/discord/module.json +1 -0
  46. package/modules/economy/module.json +1 -0
  47. package/modules/economy/routes/credits.js +1 -2
  48. package/modules/government/catalog.js +13 -17
  49. package/modules/government/migrations/government_019_retire_box_permissions.sql +33 -0
  50. package/modules/government/module.json +1 -0
  51. package/modules/government/protected-surfaces.json +3 -11
  52. package/modules/government/resolver.js +3 -46
  53. package/modules/government/routes/government.js +0 -2
  54. package/modules/government/session-scopes.js +43 -83
  55. package/modules/government/session-scopes.json +8 -18
  56. package/modules/grading/module.json +1 -0
  57. package/modules/hall-ui/module.json +1 -0
  58. package/modules/hall-ui/public/board-lib.js +2 -2
  59. package/modules/hall-ui/public/builders.js +29 -40
  60. package/modules/hall-ui/public/collab.html +1 -1
  61. package/modules/hall-ui/public/collab.js +1 -1
  62. package/modules/hall-ui/public/diagrams.html +3 -3
  63. package/modules/hall-ui/public/dom-utils.js +1 -1
  64. package/modules/hall-ui/public/gate.js +2 -2
  65. package/modules/hall-ui/public/gate.states.json +1 -1
  66. package/modules/hall-ui/public/hall-render.js +43 -79
  67. package/modules/hall-ui/public/index.html +5 -1
  68. package/modules/hall-ui/public/modules.js +1 -0
  69. package/modules/hall-ui/public/oversight.css +5 -5
  70. package/modules/hall-ui/public/palette.js +0 -11
  71. package/modules/hall-ui/public/primer.js +1 -1
  72. package/modules/hall-ui/public/sessions.html +1 -1
  73. package/modules/hall-ui/public/settings-sessions.js +1 -1
  74. package/modules/hall-ui/public/settings.css +0 -1
  75. package/modules/hall-ui/public/settings.html +4 -33
  76. package/modules/hall-ui/public/settings.js +3 -33
  77. package/modules/hall-ui/public/settings.states.json +2 -2
  78. package/modules/hall-ui/public/studio.html +1 -1
  79. package/modules/hall-ui/public/style.css +10 -13
  80. package/modules/hall-ui/public/task.html +4 -1
  81. package/modules/hall-ui/public/task.js +36 -1
  82. package/modules/hall-ui/public/tweak-editor-lib.js +18 -1
  83. package/modules/hall-ui/public/tweak-editor.css +16 -0
  84. package/modules/hall-ui/public/tweak-editor.html +26 -3
  85. package/modules/hall-ui/public/tweak-editor.js +32 -1
  86. package/modules/hall-ui/public/work.js +1 -1
  87. package/modules/hall-ui/records/tweak-editor.md +1 -0
  88. package/modules/ideas/module.json +1 -0
  89. package/modules/lifecycle/db-claim-reads.js +1 -58
  90. package/modules/lifecycle/db-ship.js +0 -5
  91. package/modules/lifecycle/db.js +0 -4
  92. package/modules/lifecycle/kickoff-checklist.js +0 -4
  93. package/modules/lifecycle/lifecycle.js +0 -8
  94. package/modules/lifecycle/module.json +1 -0
  95. package/modules/lifecycle/routes/tasks.js +5 -7
  96. package/modules/memory/module.json +1 -0
  97. package/modules/npm-release/module.json +1 -0
  98. package/modules/onboarding/module.json +1 -0
  99. package/modules/onboarding/onboarding-state.js +17 -36
  100. package/modules/onboarding/routes/access-requests.js +3 -3
  101. package/modules/platform-identity/module.json +1 -0
  102. package/modules/platform-identity/platform-identity.js +13 -5
  103. package/modules/platform-identity/routes/sso.js +2 -2
  104. package/modules/platform-identity/tests/platform-identity.mjs +17 -3
  105. package/modules/provisioning/module.json +1 -0
  106. package/modules/provisioning/provisioning.js +2 -2
  107. package/modules/provisioning/starter-bundles.js +5 -25
  108. package/modules/public-landing/module.json +1 -0
  109. package/modules/public-landing/public/assets/cosmos.css +1 -1
  110. package/modules/public-landing/public/contact.html +1 -1
  111. package/modules/public-landing/public/privacy.html +1 -1
  112. package/modules/public-landing/public/projects.html +101 -32
  113. package/modules/public-landing/public/projects.probes.json +2 -2
  114. package/modules/public-landing/public/projects.states.json +4 -3
  115. package/modules/public-landing/public/terms.html +1 -1
  116. package/modules/security/module.json +1 -0
  117. package/modules/sessions/module.json +1 -0
  118. package/modules/specialities/module.json +1 -0
  119. package/modules/status-ui/module.json +1 -0
  120. package/modules/ui-design/module.json +1 -0
  121. package/package-lock.json +2 -2
  122. package/package.json +1 -1
  123. package/release-notes.json +84 -0
  124. package/scripts/gds/artifact-format.js +203 -0
  125. package/scripts/gds/audit-authorship.sh +1 -13
  126. package/scripts/gds/claim.js +0 -15
  127. package/scripts/gds/cli-lib.js +8 -8
  128. package/scripts/gds/diagram-facts.js +12 -23
  129. package/scripts/gds/do-api.js +13 -80
  130. package/scripts/gds/doc-cli-guard.js +2 -0
  131. package/scripts/gds/doctor.js +1 -1
  132. package/scripts/gds/feedback-latest.js +139 -9
  133. package/scripts/gds/fitness-checks-identity.js +2 -7
  134. package/scripts/gds/fitness-checks-write-validation.js +4 -0
  135. package/scripts/gds/fitness-ratchets.js +4 -4
  136. package/scripts/gds/fitness.js +10 -10
  137. package/scripts/gds/gen-diagrams.js +49 -71
  138. package/scripts/gds/http-api-client.js +12 -19
  139. package/scripts/gds/init.js +1 -1
  140. package/scripts/gds/lint-infra-exec.js +1 -1
  141. package/scripts/gds/local-preview-lib.js +8 -39
  142. package/scripts/gds/local-preview.js +2 -15
  143. package/scripts/gds/migration-namespace.js +3 -3
  144. package/scripts/gds/module-artifact.js +212 -0
  145. package/scripts/gds/module.js +95 -6
  146. package/scripts/gds/package-core.js +5 -116
  147. package/scripts/gds/provision-config.js +2 -2
  148. package/scripts/gds/provision-net.js +2 -3
  149. package/scripts/gds/provision.js +9 -9
  150. package/scripts/gds/publish-manifest.js +1 -2
  151. package/scripts/gds/regen-instance-docs.js +1 -1
  152. package/scripts/gds/run-unit-tests.js +2 -16
  153. package/scripts/gds/sandbox-stage.js +39 -341
  154. package/scripts/gds/ship-flow.js +6 -6
  155. package/scripts/gds/ship-land.js +3 -4
  156. package/scripts/gds/ship-regen.js +10 -41
  157. package/scripts/gds/ship.js +4 -24
  158. package/scripts/gds/start.js +0 -15
  159. package/scripts/hall-preview/README.md +5 -5
  160. package/scripts/hall-preview/server.js +6 -6
  161. package/scripts/render-diagrams.sh +1 -1
  162. package/src/bongos/api-errors.js +1 -1
  163. package/src/bongos/auth-github.js +2 -3
  164. package/src/bongos/auth.js +51 -126
  165. package/src/bongos/db-kernel.js +2 -3
  166. package/src/bongos/db.js +5 -28
  167. package/src/bongos/module-scope-map.js +14 -29
  168. package/src/bongos/module-store.js +141 -0
  169. package/src/bongos/route-rank-check.js +3 -16
  170. package/src/bongos/routes/auth.js +0 -116
  171. package/src/bongos/routes/builders.js +5 -15
  172. package/src/bongos/routes/instance.js +8 -14
  173. package/src/bongos/routes/me.js +6 -22
  174. package/src/bongos/routes/modules.js +83 -0
  175. package/src/bongos/routes.js +5 -0
  176. package/src/module-api.js +13 -6
  177. package/src/module-loader/catalog.js +3 -0
  178. package/src/module-loader/loader.js +4 -4
  179. package/src/module-loader/manifest-schema.js +15 -7
  180. package/src/module-seams.js +2 -3
  181. package/src/modules.js +42 -14
  182. package/tests/agents_spend_guard.mjs +1 -1
  183. package/tests/api_client.mjs +0 -15
  184. package/tests/auth_page_gate.mjs +10 -10
  185. package/tests/canonical_profile_url.mjs +1 -3
  186. package/tests/claim_action.mjs +4 -1
  187. package/tests/claim_from_task_and_home.mjs +127 -0
  188. package/tests/consumer_layout_boot.mjs +2 -0
  189. package/tests/copy_desk_page_docx.mjs +33 -2
  190. package/tests/core_upgrade_runner.mjs +6 -6
  191. package/tests/deploy_divergence_line.mjs +11 -11
  192. package/tests/diagram_facts_offset.mjs +1 -1
  193. package/tests/do_api.mjs +106 -0
  194. package/tests/feedback_latest.mjs +122 -0
  195. package/tests/fitness.mjs +44 -42
  196. package/tests/gate_approvals.mjs +0 -1
  197. package/tests/go_live.mjs +3 -3
  198. package/tests/government_abuse_matrix.mjs +20 -19
  199. package/tests/government_ownership_scope.mjs +33 -35
  200. package/tests/government_parity.mjs +2 -2
  201. package/tests/government_protected_surfaces.mjs +56 -37
  202. package/tests/government_require_permission.mjs +3 -3
  203. package/tests/government_seed.mjs +37 -4
  204. package/tests/government_session_scope.mjs +92 -366
  205. package/tests/hall_error_envelope.mjs +4 -0
  206. package/tests/hall_landing_boot.mjs +4 -1
  207. package/tests/hall_palette.mjs +1 -16
  208. package/tests/hall_record_world.mjs +6 -0
  209. package/tests/hall_tweak_editor.mjs +52 -1
  210. package/tests/host_topology_skips.mjs +1 -2
  211. package/tests/html_comment_nesting.mjs +137 -0
  212. package/tests/infra_exec_paths.mjs +9 -10
  213. package/tests/init.mjs +4 -5
  214. package/tests/instance_manifest.mjs +18 -25
  215. package/tests/kickoff_checklist.mjs +1 -1
  216. package/tests/lib_sh_resolution.mjs +0 -221
  217. package/tests/local_preview.mjs +0 -12
  218. package/tests/module-scope-map.mjs +9 -9
  219. package/tests/module_catalog.mjs +1 -1
  220. package/tests/module_cli.mjs +15 -15
  221. package/tests/module_contributions.mjs +6 -6
  222. package/tests/module_loader.mjs +6 -6
  223. package/tests/module_manifest.mjs +13 -4
  224. package/tests/module_store_publish.mjs +307 -0
  225. package/tests/module_store_publish_route.mjs +121 -0
  226. package/tests/module_store_registry_migration.mjs +70 -0
  227. package/tests/modules.mjs +56 -26
  228. package/tests/onboarding_route_signals.mjs +21 -31
  229. package/tests/onboarding_state.mjs +35 -49
  230. package/tests/permission_path.mjs +0 -3
  231. package/tests/platform_boot.mjs +1 -2
  232. package/tests/profile_route.mjs +1 -6
  233. package/tests/projects_hub.mjs +5 -5
  234. package/tests/projects_hub_app_step.mjs +138 -0
  235. package/tests/projects_hub_module_picker.mjs +2 -2
  236. package/tests/projects_hub_pre_uat.mjs +4 -5
  237. package/tests/provision.mjs +7 -7
  238. package/tests/provisioning_starter_bundles.mjs +0 -22
  239. package/tests/publish_branch_route.mjs +10 -18
  240. package/tests/publish_manifest.mjs +1 -2
  241. package/tests/rank_tier_single_source.mjs +1 -13
  242. package/tests/repo_map.mjs +3 -4
  243. package/tests/runner_drift.mjs +3 -21
  244. package/tests/sandbox_stage.mjs +57 -390
  245. package/tests/session_rename_fallback.mjs +0 -21
  246. package/tests/ship_error_shape.mjs +1 -1
  247. package/tests/task_visual_slots.mjs +31 -0
  248. package/tests/terms_acceptance.mjs +1 -1
  249. package/tests/upgrade.mjs +4 -4
  250. package/tests/watch_sealed_floor.mjs +0 -1
  251. package/tests/wizard_draft_resume.mjs +2 -2
  252. package/tests/wizard_intent_resume.mjs +21 -10
  253. package/tests/wizard_preselect_why.mjs +18 -18
  254. package/docs/onboarding/browser-terminal-guide.md +0 -73
  255. package/docs/recipes/managed-settings-remote-control.md +0 -416
  256. package/modules/dev-box/CLAUDE.md +0 -15
  257. package/modules/dev-box/box-access.js +0 -623
  258. package/modules/dev-box/box-credential.js +0 -94
  259. package/modules/dev-box/box-onboard.js +0 -486
  260. package/modules/dev-box/boxes.js +0 -1038
  261. package/modules/dev-box/db.js +0 -39
  262. package/modules/dev-box/module.json +0 -19
  263. package/modules/dev-box/routes/box.js +0 -1223
  264. package/scripts/gds/box-auth-check.js +0 -160
  265. package/scripts/gds/box-infra.js +0 -321
  266. package/scripts/gds/box-sync.js +0 -216
  267. package/scripts/gds/box.js +0 -1332
  268. package/scripts/gds/cf-tunnel.js +0 -558
  269. package/scripts/gds/smoke-box.sh +0 -150
  270. package/scripts/gds/tree-preflight.js +0 -241
  271. package/src/bongos/app-pair.js +0 -172
  272. package/tests/app_pair.mjs +0 -168
  273. package/tests/box_access.mjs +0 -578
  274. package/tests/box_auth_check.mjs +0 -114
  275. package/tests/box_code_staleness.mjs +0 -129
  276. package/tests/box_connect_e2e.mjs +0 -196
  277. package/tests/box_cost.mjs +0 -70
  278. package/tests/box_credential.mjs +0 -125
  279. package/tests/box_dns_repoint.mjs +0 -153
  280. package/tests/box_env.mjs +0 -64
  281. package/tests/box_host_keys.mjs +0 -126
  282. package/tests/box_infra_resolve.mjs +0 -246
  283. package/tests/box_onboard.mjs +0 -317
  284. package/tests/box_scope_session.mjs +0 -119
  285. package/tests/box_sweep_docs.mjs +0 -119
  286. package/tests/box_sync.mjs +0 -55
  287. package/tests/box_sync_scope_report.mjs +0 -126
  288. package/tests/box_task_scope.mjs +0 -169
  289. package/tests/box_task_scope_pause.mjs +0 -66
  290. package/tests/box_terminal_ssrf.mjs +0 -96
  291. package/tests/boxes.mjs +0 -1585
  292. package/tests/cf_ruleset_preflight.mjs +0 -142
  293. package/tests/cf_tunnel.mjs +0 -496
  294. package/tests/docs_scrubber_damage.mjs +0 -111
  295. package/tests/module_scope_active_claims.mjs +0 -94
  296. package/tests/ship_api_client_pathspec.mjs +0 -100
  297. package/tests/tree_preflight.mjs +0 -243
@@ -1,416 +0,0 @@
1
- # Recipe - managed-settings SSH + browser-terminal on-ramps
2
-
3
- > **Task [#599](https://example.com/builders#/task/599) · [ADR 0031](../adr/0031-cloud-dev-environments-for-builders.md) §1.** This is the operator guide for distributing a locked Claude Desktop SSH connection to builders and for serving the browser web-terminal on the box. It assumes a box is already provisioned and running; the lifecycle runbook that explained how to get one (`builder-box-lifecycle.md`) was retired with the rest of the published dev-box recipes (task 1004013, [ADR 0305](../adr/0305-a-recipe-lives-with-what-it-documents.md)). This recipe documents the same module and is queued to follow it.
4
-
5
- > **Chromebook/browser path (ADR 0038 → [ADR 0040](../adr/0040-remote-control-default-browser-onramp.md), [#760](https://example.com/builders#/task/760) / [#824](https://example.com/builders#/task/824)):** the box serves its own web terminal - `ttyd` bound to localhost, exposed via an outbound-only Cloudflare Tunnel - and the builder runs a NORMAL interactive `claude` inside it from any browser. Because there is a real human at a real browser-backed terminal, `/login`, workspace-trust, and Trusted-Devices enrollment all succeed (the things that made headless `claude rc` unworkable). **ADR 0040 narrows that terminal to a one-time launchpad:** the builder logs in there once, runs the in-session **`/remote-control`** slash command, and then drives the box from the Claude web/mobile app — the terminal is no longer their daily surface. **The SSH path (Mac/Windows) below is unaffected and works.**
6
-
7
- ---
8
-
9
- ## The two on-ramps
10
-
11
- | Builder's device | Path | What this recipe wires |
12
- |---|---|---|
13
- | **Mac / Windows** | Claude Desktop → SSH → box | Pre-distributed `managed-settings.json` (locked `sshConfigs`) |
14
- | **Chromebook / any browser** | Browser -> `ttyd` web terminal over an outbound Cloudflare Tunnel -> box (reached at `GET /box/terminal`) -> log in once, then `/remote-control` -> drive the box from the Claude web/mobile app (ADR 0040) | `box-terminal.service` (ttyd, localhost-only) + `cloudflared.service` (outbound tunnel) + `box-report-terminal` cron |
15
-
16
- Both paths land the builder on the same box and the same `/workspace`. The choice is which GUI they prefer, not which machine they reach.
17
-
18
- ---
19
-
20
- ## Part 0 — the one-command flow ([#685](https://example.com/builders#/task/685), the default)
21
-
22
- > **This is the path to use now.** Parts 1, 3 and 4 below document the underlying
23
- > pieces (and the manual fallbacks); [#685](https://example.com/builders#/task/685) collapses the builder's side to a single
24
- > command and the operator's side to a single command. Nobody has to `scp` a JSON
25
- > file, paste a public key into `authorized_keys`, or `sudo cp` anything by hand.
26
-
27
- **The split:**
28
-
29
- | Who | Runs | What it does |
30
- |---|---|---|
31
- | **Builder** (own Mac/Windows) | `/builder-connect` — or, on a bare laptop, the hosted bootstrap (below) | GitHub sign-in → create+register their SSH key → install `managed-settings.json` |
32
- | **Operator** (control plane) | `node scripts/gds/box.js onboard <login> --apply` | provision the box (bakes the builder's registered key) + print the welcome packet |
33
-
34
- **Builder side** — guided in Claude Desktop (`/builder-connect`), or paste the hosted bootstrap on a bare laptop:
35
-
36
- ```bash
37
- # macOS / Linux
38
- curl -fsSL https://example.com/api/bongos/box/connect.sh | bash
39
- # Windows (PowerShell)
40
- irm https://example.com/api/bongos/box/connect.ps1 | iex
41
- # Plain-text guide (no auth): https://example.com/api/bongos/box/connect
42
- ```
43
-
44
- The bootstrap (`infra/box-connect.sh` / `.ps1`, served by `GET /box/connect.sh|.ps1`)
45
- is self-contained — only `curl` + `ssh-keygen` (or PowerShell). It device-flow signs
46
- the builder in, creates `~/.ssh/otb_builder` if absent, registers the **public** key
47
- (`POST /box/ssh-key`), and downloads + installs `managed-settings.json`
48
- (`GET /box/managed-settings`) at the OS path. The private key never leaves the machine;
49
- the token is held only in memory for the two authenticated calls (passed to `curl` via a
50
- 0600 config file, never on the command line).
51
-
52
- > **Operator note (staging):** the served script's `GDS_API_BASE` is pinned to
53
- > `GDS_PUBLIC_ORIGIN` (default `https://example.com`) — the SAME env var the OAuth
54
- > `redirect_uri` uses — and is **never** derived from a request header, so a spoofed
55
- > `Host` can't redirect a victim's `curl … | bash`. On staging, set
56
- > `GDS_PUBLIC_ORIGIN=https://staging.example.com` in the server env (you already do,
57
- > for OAuth) and the connect scripts target staging automatically.
58
-
59
- **Operator side:**
60
-
61
- ```bash
62
- node scripts/gds/box.js onboard <login> # dry-run: shows the plan + key count
63
- node scripts/gds/box.js onboard <login> --apply # provision + print the welcome packet
64
- ```
65
-
66
- `onboard` refuses if the builder has **no registered SSH key** (the box would have no
67
- way in) — the canonical order is **builder connects first, then operator onboards**.
68
- Override with `--allow-no-keys` only if you will re-provision once they register a key.
69
-
70
- **How the key reaches the box without an operator SSH round-trip:** `box.js` reads the
71
- builder's registered keys from the DB and **bakes** them into the cloud-init managed
72
- block at provision (so the first login works before the box has any Bongos token). From
73
- then on, `infra/<redacted>.sh` (a 10-min cron on the box, wired by the
74
- cloud-init) keeps `authorized_keys` in sync via `GET /box/authorized-keys` — which is
75
- **gated on the builder's LIVE rank+status** exactly like source access ([#600](https://example.com/builders#/task/600)). A
76
- demoted/deactivated builder gets a 403 and the cron empties the managed block — instant
77
- SSH-login revocation — **without touching the operator's own key** (only the lines
78
- between the `otb-managed` markers are rewritten). The droplet teardown follows via
79
- `box.js reconcile`.
80
-
81
- The rest of this recipe is the manual decomposition + fallbacks.
82
-
83
- ---
84
-
85
- ## Part 1 — Claude Desktop SSH (Mac / Windows)
86
-
87
- ### How it works
88
-
89
- The builder installs Claude Desktop (free; no paid plan needed for the app shell). You pre-distribute a `managed-settings.json` that contains the SSH connection for their box. When they open Claude Desktop → Code → Environment, they see exactly one entry: **"Example Dev Box"** — no configuration required on their end.
90
-
91
- Claude Desktop SSHes to the box, auto-installs Claude Code on the host (on first connect), and starts a Code session directly in `/workspace`. The builder is ready to work.
92
-
93
- ### Step 1 — Provision the box
94
-
95
- ```bash
96
- node scripts/gds/box.js provision <login> --apply
97
- ```
98
-
99
- This creates the DigitalOcean droplet, runs the cloud-init, and registers the box. The builder's hostname is `box-<login>.dev.example.com`.
100
-
101
- ### Step 2 — Generate the managed-settings.json
102
-
103
- ```bash
104
- bash infra/gen-managed-settings.sh <login> > /tmp/<login>-managed-settings.json
105
- cat /tmp/<login>-managed-settings.json
106
- ```
107
-
108
- The output is a small JSON file, e.g.:
109
-
110
- ```json
111
- {
112
- "sshConfigs": [
113
- {
114
- "id": "otb-alice",
115
- "name": "Example Dev Box",
116
- "sshHost": "root@box-alice.dev.example.com",
117
- "sshIdentityFile": "~/.ssh/otb_builder",
118
- "startDirectory": "/workspace"
119
- }
120
- ],
121
- "sshHostAllowlist": ["*.dev.example.com"]
122
- }
123
- ```
124
-
125
- ### Step 3 — Give it to the builder
126
-
127
- Send them the file and these instructions (copy-paste friendly):
128
-
129
- > **Mac:** Open Terminal and run:
130
- > ```bash
131
- > sudo mkdir -p "/Library/Application Support/ClaudeCode"
132
- > sudo cp ~/Downloads/<login>-managed-settings.json "/Library/Application Support/ClaudeCode/managed-settings.json"
133
- > ```
134
- > Then restart Claude Desktop. You'll see "Example Dev Box" in Code → Environment.
135
- >
136
- > **Windows:** Save the file to `C:\Program Files\ClaudeCode\managed-settings.json` (create the folder if it doesn't exist; needs Administrator). Restart Claude Desktop.
137
-
138
- > ⚠️ **The directory is `ClaudeCode`, not `Claude`.** Claude Code reads managed settings from `/Library/Application Support/ClaudeCode/` (macOS) and `%ProgramFiles%\ClaudeCode\` (Windows). A file under `…/Claude/` or `%ProgramData%\Claude\` is silently ignored and the box never appears in the Environment picker (the [#808](https://example.com/builders#/task/808) onboarding bug). The legacy `%ProgramData%\ClaudeCode\` Windows path was dropped in v2.1.75.
139
-
140
- ### Step 4 — Builder's SSH key
141
-
142
- The `managed-settings.json` points to `~/.ssh/otb_builder` as the identity file. The builder needs this key pair generated on their machine, and you need to add the **public key** to the box.
143
-
144
- **Builder runs (once):**
145
- ```bash
146
- ssh-keygen -t ed25519 -f ~/.ssh/otb_builder -C "otb-builder"
147
- cat ~/.ssh/otb_builder.pub
148
- ```
149
-
150
- **Operator adds the public key to the box:**
151
- ```bash
152
- ssh root@box-<login>.dev.example.com \
153
- "echo '<public key here>' >> ~/.ssh/authorized_keys"
154
- ```
155
-
156
- Or, if the builder's key is already registered as a DO SSH key, pass it via `BOX_SSH_KEY_IDS` at provision time so it's injected automatically.
157
-
158
- ### What `sshHostAllowlist` does
159
-
160
- The `sshHostAllowlist: ["*.dev.example.com"]` entry prevents the builder from using Claude Desktop SSH to connect to arbitrary hosts. Their SSH picker only shows the pre-configured OTB box — they can't misconfigure or wander off it. This is a `managed-settings`-only field; user-level settings cannot override it.
161
-
162
- ---
163
-
164
- ## Part 2 - Browser on-ramp: terminal launchpad -> Remote Control (Chromebook / any browser) - ADR 0038 -> [ADR 0040](../adr/0040-remote-control-default-browser-onramp.md), [#760](https://example.com/builders#/task/760) / [#824](https://example.com/builders#/task/824)
165
-
166
- ### How it works
167
-
168
- The box serves its own web terminal. Two systemd units do the work:
169
-
170
- - **`box-terminal.service`** runs `ttyd` (a web-terminal daemon) bound to `127.0.0.1:7681`, basic-auth protected with a per-box generated password, dropping whoever connects into a shell in `/workspace`. It listens on localhost only - the box opens **no inbound port**.
171
- - **`cloudflared.service`** (via `infra/cloudflared-run.sh`) runs a Cloudflare Tunnel that makes that localhost terminal reachable from a browser. The tunnel is **outbound-only**: the box dials out to Cloudflare, so nothing has to be opened in the firewall.
172
-
173
- The builder opens the tunnel URL in any browser, authenticates at the basic-auth prompt, and then runs a **normal interactive `claude`** in the terminal. The first `claude` run prompts a normal browser `/login`; because there is a real human at a real browser-backed terminal, `/login`, workspace-trust, and Trusted-Devices enrollment all succeed - the exact steps that headless `claude rc` could never perform.
174
-
175
- **The terminal is a one-time launchpad, not the daily surface (ADR 0040).** Once the builder is signed in to that interactive `claude` session, they run the in-session **`/remote-control`** slash command. It prints a pairing link; opening that link drives the box from the Claude web/mobile app. From there the builder lives in the Claude app — the ttyd terminal is just where they logged in and kicked RC off.
176
-
177
- > ⚠️ **Use the in-session `/remote-control` slash command — NOT the standalone `claude rc` shell command.** The standalone command mis-checks workspace trust ([anthropics/claude-code#35817](https://github.com/anthropics/claude-code/issues/35817)): it reports `Workspace not trusted` even when the directory is trusted, and a plain shell can't accept the trust dialog. Starting RC from *inside* an already-running, already-trusted `claude` session is the working path (verified live 2026-06-07). Closing the terminal window ends the remote session and closes the box — a property of the session lifecycle, stated up front so builders aren't surprised.
178
-
179
- > **Workspace trust is pre-seeded ([#824](https://example.com/builders#/task/824)).** The cloud-init writes `/root/.claude.json` with `<redacted>` for `/root` + `/workspace`, so the first `claude` launch usually skips the trust dialog entirely. This is **non-load-bearing** (ADR 0040): the seed is best-effort and only written when the file is absent (upstream pre-seeding is flaky, [#9113](https://github.com/anthropics/claude-code/issues/9113)). If it doesn't take, the builder just accepts trust once — the flow works either way.
180
-
181
- The cloud-init wires both units automatically (and `infra/box-source-fetch.sh` re-installs them on its post-clone bring-up, so a box provisioned before this shipped picks them up on its next refresh). It copies `infra/box-terminal.service` and `infra/cloudflared.service` to `/etc/systemd/system/`, enables them with `systemctl enable --now`, and wires `infra/box-report-terminal.sh` as a 1-minute cron that publishes the terminal URL + credential **up** to Bongos (`POST /box/terminal`) so the builder's self-serve flow can read them back.
182
-
183
- **Tunnel modes:**
184
-
185
- - **Quick tunnel (default, zero operator secrets).** `cloudflared` opens a tokenless quick tunnel and gets a random `https://<id>.trycloudflare.com` hostname. Nothing to configure - works out of the box.
186
- - **Named tunnel (production upgrade, stable hostname).** When `/etc/otb/box.env` carries `BOX_TUNNEL_TOKEN` + `BOX_TERMINAL_HOSTNAME`, `cloudflared` runs a named tunnel instead and the terminal is reachable at a stable `term-<login>.example.com` host. As of [#771](https://example.com/builders#/task/771) the control plane **creates and bakes those automatically** on `box.js provision` (and tears them down on `deprovision`) — see the [named-tunnel automation](#<redacted>) section below.
187
-
188
- ### Getting onto the terminal (builder self-serve)
189
-
190
- No SSH and no pairing script - two API calls:
191
-
192
- ```bash
193
- # 1. Get a box (provisions or wakes it; see Part 0 / #701):
194
- POST /api/bongos/box/ensure
195
-
196
- # 2. Poll until the box has reported its terminal up:
197
- GET /api/bongos/box/terminal
198
- # -> {"url": "https://<id>.trycloudflare.com", "credential": "<redacted>"}
199
- ```
200
-
201
- Then:
202
-
203
- 1. Open the returned `url` in a browser.
204
- 2. At the browser's basic-auth prompt, enter username **`otb`** and the password (the `credential` value).
205
- 3. In the terminal, type `claude` and press enter. On first run it walks the normal browser `/login` (workspace trust is pre-seeded, [#824](https://example.com/builders#/task/824), so the trust dialog usually doesn't appear; accept once if it does). You're now in an interactive Claude Code session in `/workspace`.
206
- 4. Inside that session, run the **`/remote-control`** slash command. It prints a pairing link.
207
- 5. Open that link to drive the box from the Claude web/mobile app. The terminal was the launchpad — work from the Claude app from here. (Closing the terminal window ends the remote session and closes the box.)
208
-
209
- ### Service lifecycle
210
-
211
- | State | What happens |
212
- |---|---|
213
- | Box first boots | `box-terminal.service` (ttyd) + `cloudflared.service` start; `box-report-terminal` cron publishes the URL + credential to Bongos within ~1 min |
214
- | Builder connects (first time) | Opens the tunnel URL, basic-auths as `otb`, runs `claude` (one-time browser `/login`), then `/remote-control` to get a pairing link and drive the box from the Claude app (ADR 0040) |
215
- | Builder works (each session) | Lives in the Claude web/mobile app via the RC link; the ttyd terminal is only the launchpad they logged in through |
216
- | Session ends | Builder closes the browser tab; the terminal + tunnel stay up for the next visit |
217
- | Box is parked (idle-suspend) | Both units stop naturally when the droplet is destroyed; they restart clean on wake and the cron re-publishes a fresh URL |
218
-
219
-
220
- ### Checking the services
221
-
222
- ```bash
223
- # On the box:
224
- sudo systemctl status box-terminal # the ttyd web terminal
225
- sudo systemctl status cloudflared # the outbound tunnel
226
- journalctl -u box-terminal -f # live tail
227
- journalctl -u cloudflared -f # tunnel logs - quick-tunnel URL appears here
228
- journalctl -u cloudflared -n 100 # last 100 lines
229
- sudo systemctl restart box-terminal cloudflared
230
- ```
231
-
232
- The quick-tunnel hostname is printed in the `cloudflared` journal; the same value is published to Bongos by the `box-report-terminal` cron (verify with `crontab -l | grep box-report-terminal`).
233
-
234
- ### Named-tunnel automation (control plane, [#771](https://example.com/builders#/task/771))
235
-
236
- The named-tunnel upgrade is automated on the control plane (`scripts/gds/box.js` + `src/bongos/cf-tunnel.js`). It is **OPT-IN, default OFF** — leave it off and every box uses the zero-secret quick tunnel.
237
-
238
- **Turning it on (operator, in the control-plane `box.env` — the same file that holds `DO_API_TOKEN`, NEVER the web tier):**
239
-
240
- ```bash
241
- BOX_NAMED_TUNNEL=1
242
- CLOUDFLARE_API_TOKEN=<token with Account:Argo Tunnel:Edit + Zone:DNS:Edit>
243
- CLOUDFLARE_ACCOUNT_ID=<the Cloudflare account id>
244
- # optional (defaults shown):
245
- CLOUDFLARE_ZONE=example.com # the DNS zone the record lives in
246
- BOX_TERMINAL_ZONE=example.com # hostname suffix → term-<login>.example.com
247
- ```
248
-
249
- This reuses the **same `CLOUDFLARE_API_TOKEN`** as `box-dns-cloudflare.sh` (the [#725](https://example.com/builders#/task/725) `BOX_DNS_HOOK`) — just add the Tunnel scope to it. In the Cloudflare token UI the permission is **Account → Argo Tunnel → Edit** (the older label for the Cloudflare Tunnel / `cfd_tunnel` API; "Cloudflare Tunnel" on newer accounts). Keep the existing **Zone → DNS → Edit**.
250
-
251
- > ⚠️ **TLS coverage — why the host is at the apex, not `dev.*` ([#771](https://example.com/builders#/task/771)).** The terminal rides a **proxied** tunnel (Cloudflare terminates HTTPS at its edge), so the hostname needs an edge certificate. Free **Universal SSL covers `example.com` and `*.example.com` (one label) but NOT a two-label host** like `term-x.dev.example.com` — a `dev.*` host fails the TLS handshake at the edge (verified live). So `BOX_TERMINAL_ZONE` defaults to the **apex** (`term-<login>.example.com`, one label, covered for free). Only override it to `dev.example.com` if you've bought an **Advanced Certificate** for `*.dev.example.com`. (The SSH A-record path stays on `dev.*` — it's `proxied=false`/direct-IP and needs no edge cert.)
252
-
253
- **What the lifecycle does when it's on:**
254
-
255
- | Op | Tunnel action |
256
- |---|---|
257
- | `provision` | create-or-reuse a per-builder named tunnel (`otb-term-<login>`), point its ingress at `127.0.0.1:7681`, route a proxied CNAME `term-<login>.example.com → <tunnel-id>.cfargotunnel.com`, fetch the connector token, and **bake** `BOX_TUNNEL_TOKEN` + `BOX_TERMINAL_HOSTNAME` into the box's `/etc/otb/box.env` via cloud-init. The tunnel is created **before** the droplet so its token is present at first boot. |
258
- | `park` → `wake` | nothing — the tunnel + DNS are IP-independent and durable, and `box.env` rides the snapshot, so the stable hostname survives park/wake with no control-plane call. |
259
- | `deprovision` (incl. `sweep-dormant` / `reconcile` clawback) | best-effort teardown: delete the CNAME and the tunnel (the droplet is already gone, so connections have dropped). Never blocks the deprovision. |
260
-
261
- `box.js provision` (dry-run, the default) prints the tunnel plan without touching Cloudflare; pass `--apply` to create it for real. If `BOX_NAMED_TUNNEL=1` but the token/account id are missing, provision **fails loudly** rather than silently falling back.
262
-
263
- **Per-box manual override (no automation):** you can still set the two values by hand on a single box and restart — `cloudflared-run.sh` detects the token and switches modes:
264
-
265
- ```bash
266
- # In /etc/otb/box.env on the box:
267
- BOX_TUNNEL_TOKEN=<redacted> named-tunnel token>
268
- BOX_TERMINAL_HOSTNAME=term-<login>.example.com
269
- sudo systemctl restart cloudflared
270
- ```
271
-
272
- ### Verifying the browser on-ramp end-to-end ([#771](https://example.com/builders#/task/771))
273
-
274
- The build is unit- and smoke-covered, but the loop crosses real hardware + a human at a browser. Run this once per change to `ttyd`/`cloudflared`/the terminal routes. **Operator deps:** a box provision (small DO cost) and, for the named-tunnel leg, the CF token with Tunnel scope.
275
-
276
- **Leg A — quick tunnel (zero secrets).**
277
-
278
- 1. Provision a box for a test builder: `node scripts/gds/box.js onboard <login> --apply` (builder must have a registered SSH key, or `--allow-no-keys`). Confirm cloud-init finished: SSH in and `cloud-init status --wait` → `done`; `systemctl is-active box-terminal cloudflared` → `active`.
279
- 2. Confirm the box published its terminal: `tail /tmp/box-report-terminal.log` should show `terminal access reported`. Then as the builder: `GET /api/bongos/box/terminal` (or `/builder-box`) → returns `{ url: https://<id>.trycloudflare.com, credential }`.
280
- 3. Open the `url` in a browser. Basic-auth as **`otb`** + the `credential`. You should land in a `/workspace` shell.
281
- 4. In that terminal run `claude`. Complete the browser `/login` (trust is pre-seeded, [#824](https://example.com/builders#/task/824) — accept once if still prompted) and Trusted-Devices enrollment. Confirm an interactive session starts. **This is the leg `claude rc` could never do** ([#745](https://example.com/builders#/task/745)).
282
- 5. Inside that session, run **`/remote-control`** (the slash command, NOT `claude rc`). Confirm it prints a pairing link; open the link and confirm the Claude web/mobile app drives the box (ADR 0040, verified live 2026-06-07).
283
- 6. Full Chromebook loop: from a browser-only device, `POST /box/ensure` → poll `GET /box/terminal` → open → `claude` → `/remote-control` → drive from the Claude app. No SSH, no Desktop.
284
-
285
- **Leg B — named tunnel (stable hostname).**
286
-
287
- 1. With the CF token (Tunnel + DNS scope) in the control-plane `box.env`, set `BOX_NAMED_TUNNEL=1` + `CLOUDFLARE_ACCOUNT_ID`. Dry-run first: `node scripts/gds/box.js provision <login>` — confirm it prints the `term-<login>.example.com` plan and the CF calls without touching anything.
288
- 2. `node scripts/gds/box.js provision <login> --apply`. Confirm in the Cloudflare dashboard: a tunnel `otb-term-<login>` exists with one healthy connector, and a proxied CNAME `term-<login>.example.com → <id>.cfargotunnel.com`.
289
- 3. `GET /box/terminal` returns the stable `https://term-<login>.example.com`. Open it, basic-auth, `claude` — same as Leg A but on the stable host.
290
- 4. Teardown: `node scripts/gds/box.js deprovision <login> --apply`. Confirm the tunnel **and** the CNAME are gone from the Cloudflare dashboard.
291
-
292
- ### Gotchas surfaced by hardware verification ([#771](https://example.com/builders#/task/771))
293
-
294
- - **Packaged ttyd collision (fixed).** The Ubuntu `ttyd` apt package ships its OWN auto-started `ttyd.service` that binds `127.0.0.1:7681` with **no `--credential`** and runs `login`. It both blocks our `box-terminal.service` (the unit crash-loops on `EADDRINUSE`) and — because the tunnel fronts whatever sits on 7681 — would expose an **unauthenticated** terminal. Cloud-init and the `box-source-fetch` post-clone bring-up now `systemctl mask ttyd` so only our basic-auth ttyd binds the port. On a box provisioned before this fix: `sudo systemctl disable --now ttyd && sudo systemctl mask ttyd && sudo systemctl restart box-terminal`.
295
- - **IPv4-only Cloudflare calls (fixed).** The CF API token is IP-locked to the droplet's IPv4 (least privilege). The droplet also has IPv6 egress; `cf-tunnel.js` pins IPv4 (undici `Agent{connect:{family:4}}`) or every call 401s — mirroring `box-dns-cloudflare.sh`'s `curl -4`.
296
- - **⚠ Browser-only bootstrap gap (OPEN — follow-up).** The `ttyd`/`cloudflared` systemd units live in the repo (`/workspace/infra/*.service`), so they only start **after** `box-source-fetch` clones — which needs the **builder's Bongos token on the box**. A pure Chromebook builder has no SSH and no terminal yet, so there is **no first-boot path to place that token** → the terminal never comes up for them. Today it only works if the token is seeded another way (SSH, or an operator). The fix (a follow-up) is to bring ttyd up at **first boot independent of the clone** — e.g. cloud-init writes the unit inline rather than copying it from the repo. Until then, the browser on-ramp is verified to work *given a token on the box*, not from a truly bare browser-only device.
297
-
298
- ---
299
-
300
- ## Part 3 — The box-heartbeat cron
301
-
302
- The cloud-init also wires `infra/box-heartbeat.sh` as a 5-minute cron job. This pings `/api/bongos/box/heartbeat` while the box is in active use (interactive SSH session or CPU load > 0.2). Silence = the idle-suspend sweep ([#598](https://example.com/builders#/task/598)) is free to park the box.
303
-
304
- Without this cron the idle sweep measures **uptime** instead of **activity** and may park a box someone is actively using. Always verify it's wired:
305
-
306
- ```bash
307
- crontab -l | grep box-heartbeat
308
- ```
309
-
310
- If missing (pre-[#599](https://example.com/builders#/task/599) box):
311
- ```bash
312
- (crontab -l 2>/dev/null; echo "*/5 * * * * /workspace/infra/box-heartbeat.sh >> /tmp/box-heartbeat.log 2>&1") | crontab -
313
- ```
314
-
315
- ---
316
-
317
- ## Part 4 — Rank-gated source access ([#600](https://example.com/builders#/task/600), ADR 0031 §6 / §9.2)
318
-
319
- The box holds **no baked credential**. Instead it fetches a rank-scoped clone spec
320
- from Bongos at boot and on a 10-minute cron, via `infra/box-source-fetch.sh`
321
- (carried onto the box as base64 inside the cloud-init `write_files`). The flow:
322
-
323
- 1. The box reads the builder's Bongos session token (it appears after they authenticate
324
- Claude Code / run `/builder-reauth` — so the **first clone lands after the builder
325
- connects**, not at boot).
326
- 2. It calls `GET /api/bongos/box/source-access`. Bongos checks the builder's **live**
327
- rank + status (ADR 0016 — unforgeable from the box):
328
- - **403 `SOURCE_ACCESS_REVOKED`** — builder demoted below the floor or deactivated.
329
- The script cuts the credentialed remote (box-side clawback); `box.js reconcile`
330
- deprovisions the droplet.
331
- - **200 `configured:false`** — the server's source credential isn't set yet (see
332
- env below); the box logs guidance and no-ops.
333
- - **200 `configured:true`** — returns the scope-appropriate spec + a short-lived
334
- credential. The box clones/refreshes `/workspace`, **never persisting the token
335
- in `.git/config`** (clones via a one-shot authenticated URL, then resets the
336
- remote to the token-less URL).
337
- 3. **Scope by rank** (`src/bongos/box-access.js`): a **Xenos → `starter`** sparse-checkout
338
- of the safe surface (`.devcontainer`, `art`, `docs`, `modules/status-ui`,
339
- `generative-ideas.md` — the §9.2 "first tasks on a scoped surface"); a **Metic+ →
340
- `full`** clone. Graduation (`box.js reconcile`) rescopes a starter box to full; the
341
- box re-clones on its next refresh.
342
-
343
- **Operator: configure the source credential** (in the **prod env**, never the repo —
344
- ADR 0022). The default provider (`src/bongos/box-credential.js`) is env-backed:
345
-
346
- ```bash
347
- # In the prod environment (e.g. the droplet's systemd unit / .env):
348
- BOX_SOURCE_REPO_URL=https://github.com/<org>/example.git
349
- BOX_SOURCE_FULL_TOKEN=<redacted> PAT or installation token, contents:read>
350
- # Optional — make 'starter' a HARD boundary (a physically separate repo that cannot
351
- # read the crown jewels) instead of the default sparse-checkout defense-in-depth:
352
- BOX_SOURCE_STARTER_REPO_URL=https://github.com/<org>/example-starter.git
353
- BOX_SOURCE_STARTER_TOKEN=<token scoped to the starter repo>
354
- # Optional — advisory token TTL (how often the box re-fetches + re-checks live rank):
355
- BOX_SOURCE_TOKEN_TTL_SECONDS=3600
356
- ```
357
-
358
- > **Honest limit (ADR 0031 §6).** Git cannot sub-path-scope a single private repo and
359
- > sparse-checkout is client-side, so the default `starter` surface is
360
- > **defense-in-depth + audit + revocation, not insider-proof DRM**. Set
361
- > `BOX_SOURCE_STARTER_REPO_URL` for a hard boundary. Every grant/denial is audited to
362
- > `box_events` (`source_grant` / `source_deny` / `source_revoke` / `source_rescope`).
363
-
364
- **The clawback (clean offboarding).** Revoking rank or deactivating a builder cuts
365
- source on two timelines: the **credential layer is instant** (their next
366
- `/box/source-access` fetch 403s), and the **droplet teardown** follows when the
367
- control plane runs reconcile:
368
-
369
- ```bash
370
- node scripts/gds/box.js reconcile # dry-run: show what would change
371
- node scripts/gds/box.js reconcile --apply # deprovision below-floor/inactive boxes,
372
- # rescope graduated ones (run on a schedule)
373
- ```
374
-
375
- Verify the source-fetch cron + check progress on the box:
376
-
377
- ```bash
378
- crontab -l | grep box-source-fetch
379
- tail /tmp/box-source-fetch.log
380
- WORKSPACE=/workspace /usr/local/bin/box-source-fetch.sh # manual run
381
- ```
382
-
383
- ---
384
-
385
- ## Quick reference: full operator flow
386
-
387
- ```bash
388
- # 0. One-time: set BOX_SOURCE_REPO_URL + BOX_SOURCE_FULL_TOKEN in the prod env (Part 4)
389
-
390
- # 1. Provision the box
391
- node scripts/gds/box.js provision alice --apply
392
-
393
- # 2. Add the builder's SSH public key to the box
394
- ssh root@box-alice.dev.example.com "echo '...' >> ~/.ssh/authorized_keys"
395
-
396
- # 3. Generate and send managed-settings.json (Desktop / SSH path)
397
- bash infra/gen-managed-settings.sh alice > /tmp/alice-managed-settings.json
398
- # → email/DM to builder with placement instructions
399
-
400
- # 4. For thin-device (Chromebook) path: the web terminal is ready automatically
401
- # (box-terminal.service + cloudflared.service start at boot; box-report-terminal
402
- # publishes the URL + credential to Bongos within ~1 min)
403
- # Builder self-serves: POST /api/bongos/box/ensure, then poll GET /api/bongos/box/terminal
404
- # -> open the url, basic-auth as user 'otb' with the returned credential, run: claude
405
- # -> then /remote-control to drive the box from the Claude app (ADR 0040)
406
-
407
- # 5. When the builder is done for the day, the idle sweep parks the box:
408
- node scripts/gds/box.js sweep-idle --apply # control plane / prod cron
409
-
410
- # 6. Source materializes automatically once the builder authenticates on the box
411
- # (the box-source-fetch cron clones the rank-scoped surface). Nothing to do here.
412
-
413
- # 7. Offboarding / demotion: the credential is cut on the builder's next fetch;
414
- # reconcile then deprovisions the box (run on a schedule alongside the sweeps):
415
- node scripts/gds/box.js reconcile --apply
416
- ```
@@ -1,15 +0,0 @@
1
- # @bongos/core — installed platform dependency (not your project's CLAUDE.md)
2
-
3
- This file is a placeholder shipped inside the **@bongos/core** package. The core's own
4
- internal agent notes are intentionally omitted from the published artifact so they cannot
5
- load into your instance's context.
6
-
7
- **It does not govern this repository.** Your own root `CLAUDE.md`, your `.claude/`
8
- directory, and the live Bongos database are the source of truth for how work happens here
9
- and who may do what.
10
-
11
- You are seeing this only because an agent opened a file under `node_modules/@bongos/core/`.
12
- Nothing in here needs to be read to use the platform — run `bongos help` for commands.
13
-
14
- _To work ON the platform core itself, use the standalone @bongos/core source repository,
15
- not this vendored copy._