@bongos/core 1.17.1 → 1.17.3

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 (233) hide show
  1. package/.bongos-core.json +521 -281
  2. package/.claude/skills/blocker-review/SKILL.md +1 -1
  3. package/.claude/skills/builder-claim/SKILL.md +2 -0
  4. package/.claude/skills/builder-exit/SKILL.md +2 -2
  5. package/.claude/skills/builder-redteam/SKILL.md +3 -3
  6. package/.claude/skills/builder-setup/SKILL.md +2 -2
  7. package/.claude/skills/builder-start/SKILL.md +1 -1
  8. package/.claude/skills/dev/SKILL.md +1 -1
  9. package/.claude/skills/idea-triage/SKILL.md +2 -2
  10. package/.claude/skills/ideate/SKILL.md +1 -1
  11. package/.claude/skills/merge-mode/SKILL.md +1 -1
  12. package/.claude/skills/new-project/SKILL.md +3 -0
  13. package/.claude/skills/paint/SKILL.md +1 -1
  14. package/GOVERNANCE.md +1 -1
  15. package/README.md +1 -1
  16. package/clients/bongos-client/README.md +1 -1
  17. package/clients/bongos-client/bongos-client.global.js +2 -0
  18. package/clients/bongos-client/index.cjs +2 -0
  19. package/clients/bongos-client/index.d.ts +4 -2
  20. package/clients/bongos-client/index.mjs +2 -0
  21. package/docs/adr/0001-stack-choice-single-vps.md +1 -1
  22. package/docs/adr/0008-google-chat-oauth-user-auth.md +1 -1
  23. package/docs/adr/0019-red-team-bounty-payout-table.md +3 -3
  24. package/docs/adr/0020-security-model-public-stub.md +1 -1
  25. package/docs/adr/0023-kind-multipliers-and-idea-bonus.md +1 -1
  26. package/docs/adr/0024-cloneable-repo-local-first-memory.md +1 -1
  27. package/docs/adr/0026-bfg-memory-hygiene-and-cross-builder-learning.md +4 -4
  28. package/docs/adr/0027-bfg-session-inefficiency-evaluator.md +3 -3
  29. package/docs/adr/0030-builders-hall-live-updates.md +1 -1
  30. package/docs/adr/0031-cloud-dev-environments-for-builders.md +4 -4
  31. package/docs/adr/0032-discord-mirror-of-gds.md +2 -2
  32. package/docs/adr/0034-thetes-graduated-newcomer-rank.md +1 -1
  33. package/docs/adr/0044-mediterranean-palette-replacement.md +1 -1
  34. package/docs/adr/0044-per-box-live-game-preview.md +1 -1
  35. package/docs/adr/0054-session-token-reward-cost-plus.md +10 -10
  36. package/docs/adr/0057-container-cost-ledger.md +3 -3
  37. package/docs/adr/0062-medusa-instance-model-decoupling.md +4 -4
  38. package/docs/adr/0065-agpl-nonprofit-ai-first-governance.md +2 -2
  39. package/docs/adr/0085-remove-default-per-builder-spend-cap.md +1 -1
  40. package/docs/adr/0086-goal-scoped-work-hierarchy.md +1 -1
  41. package/docs/adr/0096-require-reward-before-workable.md +9 -9
  42. package/docs/adr/0097-retroactive-reward-backfill.md +3 -3
  43. package/docs/adr/0100-two-repo-instance-model.md +1 -1
  44. package/docs/adr/0101-goal-hierarchy-phase-5-judge-membership-reward.md +3 -3
  45. package/docs/adr/0103-core-first-extraction-cloud-bongos-trunk.md +3 -3
  46. package/docs/adr/0107-module-upstreaming-contribution-to-core-catalog.md +1 -1
  47. package/docs/adr/0120-pay-on-land-and-builder-owned-rebase-gate.md +6 -6
  48. package/docs/adr/0136-update-channel-subscription-policy.md +51 -0
  49. package/docs/adr/0137-upstream-publish-policy-manual-owner-gated.md +40 -0
  50. package/docs/adr/0138-bundled-module-deps-declared-per-module.md +45 -0
  51. package/docs/adr/README.md +9 -6
  52. package/docs/api/openapi.json +65 -26
  53. package/docs/api-reference.md +3 -2
  54. package/docs/architecture.md +2 -2
  55. package/docs/branding-contract.md +5 -4
  56. package/docs/canonical-permissions.md +1 -1
  57. package/docs/design/vanilla-hall-ui-redesign-scope.md +1 -1
  58. package/docs/file-map.md +18 -17
  59. package/docs/handoff-template.md +2 -2
  60. package/docs/modules-contract.md +6 -1
  61. package/docs/onboarding/diagrams/README.md +7 -7
  62. package/docs/onboarding/drachmae-and-karma.md +18 -18
  63. package/docs/onboarding/primer.md +9 -9
  64. package/docs/recipes/audit-replay.md +2 -2
  65. package/docs/recipes/builder-box-lifecycle.md +2 -2
  66. package/docs/recipes/claude-design-loop.md +1 -1
  67. package/docs/recipes/ops-gotchas.md +1 -1
  68. package/docs/recipes/private-npm-distribution.md +17 -0
  69. package/docs/recipes/staging-environment.md +1 -1
  70. package/docs/recipes/upgrading-the-core.md +55 -0
  71. package/docs/research/cursor-competitive-analysis.md +1 -1
  72. package/docs/research/llm-model-agnostic-and-cheaper-grader.md +1 -1
  73. package/modules/discord/module.json +2 -1
  74. package/modules/economy/achievements.js +9 -1
  75. package/modules/economy/credits.js +10 -1
  76. package/modules/hall-ui/public/builders.js +137 -620
  77. package/modules/hall-ui/public/goal-inbox.js +232 -0
  78. package/modules/hall-ui/public/goals-page.js +4 -1
  79. package/modules/hall-ui/public/goals.html +8 -0
  80. package/modules/hall-ui/public/hall-kit.js +474 -0
  81. package/modules/hall-ui/public/hall-widgets.js +14 -5
  82. package/modules/hall-ui/public/index.html +3 -0
  83. package/modules/hall-ui/public/instances-panel.js +24 -3
  84. package/modules/hall-ui/public/new-project-wizard.js +7 -5
  85. package/modules/hall-ui/public/style.css +168 -0
  86. package/modules/hall-ui/public/work.css +194 -0
  87. package/modules/hall-ui/public/work.html +46 -374
  88. package/modules/hall-ui/public/work.js +137 -102
  89. package/modules/lifecycle/criterion-suggest.js +80 -0
  90. package/modules/lifecycle/db.js +42 -5
  91. package/modules/lifecycle/routes/goals.js +26 -0
  92. package/modules/lifecycle/routes/tasks.js +21 -0
  93. package/modules/provisioning/CLAUDE.md +15 -0
  94. package/modules/provisioning/migrations/provisioning_001_tables.sql +160 -0
  95. package/modules/provisioning/migrations/provisioning_002_standalone_shape.sql +11 -0
  96. package/modules/provisioning/migrations/provisioning_003_github_app_manifest.sql +95 -0
  97. package/modules/provisioning/module.json +17 -0
  98. package/modules/provisioning/onboard-plan.js +37 -0
  99. package/modules/provisioning/provisioning.js +539 -0
  100. package/modules/provisioning/rate-limit.js +35 -0
  101. package/modules/provisioning/routes/provisioning.js +607 -0
  102. package/modules/provisioning/tests/provisioning.mjs +232 -0
  103. package/modules/provisioning/ui/.gitkeep +0 -0
  104. package/modules/security/secret-scrub.js +10 -0
  105. package/package-lock.json +2 -2
  106. package/package.json +7 -2
  107. package/scripts/discord/README.md +1 -1
  108. package/scripts/gds/claim.js +98 -48
  109. package/scripts/gds/claude-materialize.js +30 -9
  110. package/scripts/gds/cutover-codemod.js +241 -0
  111. package/scripts/gds/fitness.js +54 -0
  112. package/scripts/gds/init.js +59 -3
  113. package/scripts/gds/lint-infra-exec.js +70 -0
  114. package/scripts/gds/lint-workflow-exec.js +63 -0
  115. package/scripts/gds/mirror-lineage-check.js +14 -5
  116. package/scripts/gds/mirror-redact.js +8 -0
  117. package/scripts/gds/onboard.js +4 -0
  118. package/scripts/gds/package-core.js +18 -3
  119. package/scripts/gds/publish-manifest.js +60 -2
  120. package/scripts/gds/run-routine.js +51 -13
  121. package/scripts/gds/run-unit-tests.js +4 -0
  122. package/scripts/gds/update-channel.js +186 -0
  123. package/scripts/gds/upgrade.js +116 -9
  124. package/scripts/hall-preview/README.md +103 -0
  125. package/scripts/hall-preview/fixtures/access-requests.json +1 -0
  126. package/scripts/hall-preview/fixtures/achievements.json +76 -0
  127. package/scripts/hall-preview/fixtures/analytics__builder.json +251 -0
  128. package/scripts/hall-preview/fixtures/audit-log.json +1 -0
  129. package/scripts/hall-preview/fixtures/boxes__cost-ledger.json +1 -0
  130. package/scripts/hall-preview/fixtures/builders__active-claims.json +1 -0
  131. package/scripts/hall-preview/fixtures/builders__directory.json +81 -0
  132. package/scripts/hall-preview/fixtures/builders__roster.json +1 -0
  133. package/scripts/hall-preview/fixtures/goals.json +502 -0
  134. package/scripts/hall-preview/fixtures/goals__25.json +96 -0
  135. package/scripts/hall-preview/fixtures/goals__26.json +67 -0
  136. package/scripts/hall-preview/fixtures/goals__3.json +103 -0
  137. package/scripts/hall-preview/fixtures/goals__35.json +49 -0
  138. package/scripts/hall-preview/fixtures/goals__conflicts.json +154 -0
  139. package/scripts/hall-preview/fixtures/goals__detail.json +96 -0
  140. package/scripts/hall-preview/fixtures/goals__inbox.json +5 -0
  141. package/scripts/hall-preview/fixtures/goals__requests.json +3 -0
  142. package/scripts/hall-preview/fixtures/grades__by-builder.json +1 -0
  143. package/scripts/hall-preview/fixtures/inbox.json +330 -0
  144. package/scripts/hall-preview/fixtures/instance.json +124 -0
  145. package/scripts/hall-preview/fixtures/leaderboard.json +184 -0
  146. package/scripts/hall-preview/fixtures/me.json +476 -0
  147. package/scripts/hall-preview/fixtures/me__recent-ships.json +304 -0
  148. package/scripts/hall-preview/fixtures/override-requests.json +1 -0
  149. package/scripts/hall-preview/fixtures/provisioning__fleet.json +1 -0
  150. package/scripts/hall-preview/fixtures/provisioning__fleet__cost-ledger.json +1 -0
  151. package/scripts/hall-preview/fixtures/provisioning__instances.json +1 -0
  152. package/scripts/hall-preview/fixtures/public__analytics__ecosystem.json +516 -0
  153. package/scripts/hall-preview/fixtures/public__bounty-table.json +37 -0
  154. package/scripts/hall-preview/fixtures/public__cost-summary.json +742 -0
  155. package/scripts/hall-preview/fixtures/public__estimation-drift.json +41 -0
  156. package/scripts/hall-preview/fixtures/public__grades.json +50 -0
  157. package/scripts/hall-preview/fixtures/public__progress.json +124 -0
  158. package/scripts/hall-preview/fixtures/public__recent-shipped.json +426 -0
  159. package/scripts/hall-preview/fixtures/public__repo-health.json +160 -0
  160. package/scripts/hall-preview/fixtures/public__repo-info.json +5 -0
  161. package/scripts/hall-preview/fixtures/security__docs.json +1 -0
  162. package/scripts/hall-preview/fixtures/security__reports.json +1 -0
  163. package/scripts/hall-preview/fixtures/sessions__search.json +1 -0
  164. package/scripts/hall-preview/fixtures/tasks.json +2420 -0
  165. package/scripts/hall-preview/fixtures/tasks__claimable.json +2555 -0
  166. package/scripts/hall-preview/fixtures/tasks__detail.json +111 -0
  167. package/scripts/hall-preview/fixtures/tasks__peer-votes__feed.json +330 -0
  168. package/scripts/hall-preview/fixtures/versions.json +89 -0
  169. package/scripts/hall-preview/refresh-fixtures.js +77 -0
  170. package/scripts/hall-preview/server.js +178 -0
  171. package/scripts/migrate.sh +29 -4
  172. package/src/bongos/auth.js +14 -1
  173. package/src/bongos/optimizer.js +37 -2
  174. package/src/bongos/serve-internal.js +10 -1
  175. package/src/branding.js +1 -0
  176. package/src/module-api.js +12 -1
  177. package/src/module-loader/loader.js +36 -4
  178. package/src/module-loader/manifest-schema.js +16 -1
  179. package/tests/branding.mjs +11 -0
  180. package/tests/claim_error_surface.mjs +107 -0
  181. package/tests/claude_materialize.mjs +20 -0
  182. package/tests/consumer_layout_boot.mjs +302 -0
  183. package/tests/criterion_suggest.mjs +106 -0
  184. package/tests/cutover_codemod.mjs +184 -0
  185. package/tests/device_flow_start.mjs +25 -2
  186. package/tests/hook_core_resolve.mjs +125 -0
  187. package/tests/infra_exec_paths.mjs +104 -0
  188. package/tests/init.mjs +47 -0
  189. package/tests/migrate_root_resolution.mjs +97 -0
  190. package/tests/mirror_lineage_proof.mjs +14 -0
  191. package/tests/module_loader.mjs +42 -0
  192. package/tests/module_manifest.mjs +19 -0
  193. package/tests/onboard.mjs +13 -0
  194. package/tests/package_core.mjs +8 -0
  195. package/tests/provisioning_github_app_routes.mjs +15 -0
  196. package/tests/publish_manifest.mjs +52 -1
  197. package/tests/run_routine.mjs +26 -1
  198. package/tests/run_routine_two_root.mjs +77 -0
  199. package/tests/scrubber_corpus.mjs +17 -0
  200. package/tests/update_channel.mjs +186 -0
  201. package/tests/upgrade.mjs +131 -0
  202. package/tests/workflow_exec_paths.mjs +98 -0
  203. package/docs/design/container-exterior-tiles.md +0 -49
  204. package/docs/design/container-exterior.md +0 -81
  205. package/docs/design/container-interior.md +0 -77
  206. package/docs/design/world-map-scroll.md +0 -41
  207. package/docs/recipes/claude-gemini-bongos-doors.md +0 -155
  208. package/docs/recipes/cloudbongos-staging-gate.md +0 -139
  209. package/docs/recipes/cloudbongos-standup.md +0 -111
  210. package/docs/recipes/parallel-load-results-2026-05-30.md +0 -63
  211. package/docs/recipes/parallel-load-test.md +0 -32
  212. package/docs/recipes/pixel-art-pipeline.md +0 -411
  213. package/docs/recipes/staging-data.md +0 -50
  214. package/migrations/instance/018_pms_v3_planning_seed.sql +0 -131
  215. package/migrations/instance/080_gds_v4_planning_seed.sql +0 -75
  216. package/migrations/instance/110_medusa_planning_seed.sql +0 -70
  217. package/migrations/instance/120_bongos_v1_planning_seed.sql +0 -69
  218. package/migrations/instance/144_bongos_v1_module_system_criterion.sql +0 -26
  219. package/migrations/instance/159_bongos_v1_goal_hierarchy_criterion.sql +0 -29
  220. package/migrations/instance/172_v2_world_planning_seed.sql +0 -163
  221. package/migrations/instance/173_bongos_v1_c7_core_modularized.sql +0 -33
  222. package/migrations/instance/182_gdsv4_bongos_consolidation.sql +0 -132
  223. package/migrations/instance/183_gdsv4_carryover_themed_goals.sql +0 -116
  224. package/migrations/instance/186_bongos_v1_governance_planning_seed.sql +0 -69
  225. package/scripts/gds/art-stage.js +0 -152
  226. package/server.js +0 -212
  227. package/tests/art_key.mjs +0 -121
  228. package/tests/day_cycle.mjs +0 -197
  229. package/tests/snapshot_picker.mjs +0 -233
  230. package/tests/sprite_facing.mjs +0 -35
  231. package/tests/terrain_picker.mjs +0 -298
  232. package/tests/turn_in_place.mjs +0 -280
  233. package/tests/walkability.mjs +0 -188
@@ -1,81 +0,0 @@
1
- # Container building — exterior structure design (V2)
2
-
3
- - **Task:** [#641](https://example.com/builders#/task/641) (V2 · product · creative). Promoted from idea_inbox [#160](https://example.com/builders#/idea/160) (builder 25 art/design brief, 2026-06-04).
4
- - **Date:** 2026-06-11
5
- - **Status:** Design locked at the structure level. Pixel-precise tile specs are [#643](https://example.com/builders#/task/643) (facade); interior is [#642](https://example.com/builders#/task/642)/[#644](https://example.com/builders#/task/644)/[#645](https://example.com/builders#/task/645); palette implementation is [#640](https://example.com/builders#/task/640) / [ADR 0044](../adr/0044-mediterranean-palette-replacement.md).
6
-
7
- ## What changes
8
-
9
- The container is no longer a single beached 4×2 object (the current `CONTAINER_LAYOUT` in
10
- `src/world/terrain.js`). It becomes a **two-storey trading house built from stacked
11
- shipping containers** — a designed building with a glass storefront, a wooden deck,
12
- an exterior staircase, and a flat rooftop. It reads as a *destination*, not salvage.
13
-
14
- The narrative survives intact: Hermes placed the containers; the Examplens stacked
15
- them into a trading house. The mythic contradiction (an *ancient* shipping container)
16
- stays — see the colour note below.
17
-
18
- ## Colour: weathered black/charcoal (decided)
19
-
20
- The original brief asked for "bold, uniform red or black — not rusted or weathered."
21
- That option was resolved **after** the brief was written: blocker [#7](https://example.com/builders#/blocker/7) (Lars, 2026-06-10,
22
- recorded as a style-guide rule) fixed the container as **weathered black/charcoal
23
- corrugated metal with rust streaks**, with faded Hermes-teal as an *accent only* (the
24
- door + the caduceus etch). The later decision wins. So the building is charcoal and
25
- weathered — it is "designed architecture" by its *shape and glass*, not by looking
26
- factory-new. ADR 0044 already constrains the replacement palette to carry enough
27
- charcoal/grey value steps for this.
28
-
29
- ## Top-down structure (32×32 tiles, FireRed building anatomy)
30
-
31
- FireRed conveys buildings top-down as **facade rows (what you'd see standing in front)
32
- topped by roof rows**. A second storey = a taller facade. The reference is the Celadon
33
- department store: tall glass-fronted facade, flat roof.
34
-
35
- The building is an **8-wide × 6-tall** visual block (up from 4×2):
36
-
37
- | Rows (top→bottom) | Band | Content |
38
- |---|---|---|
39
- | 1–2 | **Rooftop** | Flat container roof; 1-px railing along the edge tiles; a stair landing tile at the right edge where the exterior staircase arrives. Future rooftop deck props (parasol, crates) are object overlays, not part of this family. |
40
- | 3–4 | **Upper-storey facade** | Charcoal corrugated container walls with a continuous band of **large glass panels** (semi-transparent blue-grey, single white highlight stripe). Container seams visible at the stacked-unit joints. |
41
- | 5–6 | **Ground-storey facade** | Glass storefront panels flanking a centred **door** (the one Hermes-teal accent, faint caduceus etch, warm interior glow). Wall-mounted light fixtures as small details on the wall tiles either side — lit at dusk/night. |
42
-
43
- Attached, outside the 8×6 block:
44
-
45
- - **Exterior staircase** — a 1-tile-wide steel stair strip climbing the building's right
46
- side from ground to the rooftop landing. Purely visual in V2 (blocking); the rooftop
47
- becomes walkable in a later version, the art shouldn't need to change for that.
48
- - **Wooden deck** — a 2-row walkable apron in front of the ground facade, drawn as an
49
- alpha-margined object overlay over sand (the marble-path technique, [ADR 0013](../adr/<redacted>.md)),
50
- so it works on any ground. Outdoor furniture (table, two chairs) are blocking
51
- object singletons placed on the deck, off the door's walking line.
52
-
53
- ### Placement and collision
54
-
55
- Anchor at **(11, 4)** in the current 30×18 world: the building spans cols 11–18,
56
- rows 4–9 (grass behind, sand in front), the deck spans rows 10–11, and the existing
57
- marble path (col 13, rows 10–13) shifts to meet the door at col 14–15. All building
58
- tiles **block**, including the door — interaction stays "stand adjacent and face it,"
59
- exactly like the current container door. Deck tiles are walkable; furniture blocks.
60
-
61
- ## Pipeline mapping (what gets generated, later)
62
-
63
- Per [ADR 0008](../adr/<redacted>.md), coherent groups are families, not per-tile calls:
64
-
65
- 1. **`container_building_facade`** family — wall, glass-panel, door, light-fixture, and
66
- staircase tiles (replaces the 2×4 `container` family in `art/template/families.json`).
67
- 2. **`container_building_roof`** family — roof field, railing edges/corners, stair landing.
68
- 3. **`deck_wood`** — 16-autotile alpha-tile line family (same recipe as `marble_path`).
69
- 4. **Furniture singletons** (table, chair) — per-tile pipeline.
70
-
71
- **Sequencing rule: generate nothing until [#640](https://example.com/builders#/task/640) / ADR 0044 lands.** Every one of these
72
- tiles would be quantized against the old palette and re-done after the Mediterranean
73
- swap. Spec work ([#643](https://example.com/builders#/task/643)) can proceed now; generation waits.
74
-
75
- ## Engineering follow-up (not this task)
76
-
77
- The world model needs a matching change in **both** `map.js`/`terrain.js` twins
78
- (server + client): new OBJECT ids, an 8×6 building layout replacing
79
- `CONTAINER_PANELS_ROWMAJOR`/`CONTAINER_LAYOUT`, deck overlay placement, and blocking
80
- rules as above. That is an engineering task to be filed when the facade family's tile
81
- ids are final (after [#643](https://example.com/builders#/task/643)).
@@ -1,77 +0,0 @@
1
- # Container building — interior atmosphere design (V2)
2
-
3
- - **Task:** [#645](https://example.com/builders#/task/645) (V2 · product · creative). Promoted from idea_inbox [#164](https://example.com/builders#/idea/164) (builder 25 art/design brief, 2026-06-04).
4
- - **Date:** 2026-06-21
5
- - **Builds on:** [`container-exterior.md`](container-exterior.md) — this is the **interior** counterpart of that structure-level brief. The granular interior layers it calls out are the **layout** ([#642](https://example.com/builders#/task/642)) and the **floor-tile pixel spec** ([#644](https://example.com/builders#/task/644)); palette is governed by [ADR 0044](../adr/0044-mediterranean-palette-replacement.md).
6
- - **Status:** Atmosphere direction locked. Colour reconciled to ADR 0044 (below). Tile generation waits for [#640](https://example.com/builders#/task/640) / ADR 0044, same sequencing rule as the exterior.
7
-
8
- ## ⚠️ Colour: the source brief is superseded — Mediterranean palette, not Poke-Mart blue
9
-
10
- idea_inbox [#164](https://example.com/builders#/idea/164)'s brief (this task's source, 2026-06-04) built the interior on a **saturated Pokémon "Poke Mart" blue wave-scale floor** and **orange-red container walls** as the nostalgia anchor. That was written **before** the palette was locked. The authoritative decisions win (exactly as the exterior briefs defer):
11
-
12
- - **[ADR 0044](../adr/0044-mediterranean-palette-replacement.md)** (2026-06-10): the world has **one Mediterranean palette**, and it states explicitly — *"there is no 'Poke Mart interior' context that needs the temperate palette."* So the interior is **not** a palette exception; it shares the world's hues.
13
- - **Blocker [#7](https://example.com/builders#/blocker/7)** (2026-06-10, locked `style_guide.md` rule): the container body is **weathered black/charcoal corrugated metal**, not red/orange. The interior-facing walls are the same shell.
14
- - **Lars (2026-06-21, this session):** Mediterranean governs the interior. The layered *geometry* survives untouched — only the hues are corrected.
15
-
16
- Concretely (geometry preserved, hue corrected — the exact move [`container-exterior-tiles.md`](container-exterior-tiles.md) made for the facade):
17
-
18
- | Source brief's colour | Corrected (Mediterranean) |
19
- |---|---|
20
- | Saturated Poke-Mart **blue** wave floor | **Aqua / turquoise** wave-scale, from the palette's turquoise-sea steps — the *pattern* is the nostalgia anchor; the *hue* matches the sea just outside the glass |
21
- | **Orange-red** container walls | **Weathered charcoal** corrugated (blocker [#7](https://example.com/builders#/blocker/7)) — warmth comes from the *lighting*, not the paint |
22
- | Lush **forest-green** featured rug | Deep **olive / sage** rug (the palette's darkest greens) — still "rich and set apart," in-palette |
23
- | Pure-**white** shelving | **Cream / pale-stone** (warm-neutral steps); no white edge rim, 1-px darker object outline (style rules) |
24
-
25
- > **[#642](https://example.com/builders#/task/642) / [#644](https://example.com/builders#/task/644) note:** both still say "blue" floor; [#644](https://example.com/builders#/task/644)'s title already hedges to "aqua-tinted." Read their "blue" as this brief's Mediterranean turquoise/aqua — this doc is the colour authority for the interior, the way `container-exterior.md` is for the facade.
26
-
27
- ## The unified interior — a layered space
28
-
29
- Three layers on three surfaces, none fighting because each owns a different plane:
30
-
31
- 1. **Shell (walls):** raw weathered-charcoal corrugated container — the industrial bones are never hidden. Double-height: tall wall columns, a second-floor balcony railing along the top of the map, pendant lamps on cables from a high ceiling.
32
- 2. **Floor (the centerpiece):** aqua/turquoise wave-scale tile — the nostalgia geometry underfoot, in Mediterranean hue.
33
- 3. **Retail layer (objects + light):** boutique principles — items displayed like sculpture with as much negative space as product; warm directional track lighting makes the illuminated patches the brightest things in the room.
34
-
35
- You walk into a space that *feels* like a game (the floor pattern), *looks* like a container (the charcoal shell), and *shops* like a boutique (the sculptural, warm-lit display). That triple recognition in the first two seconds is the emotional design goal — unchanged by the palette correction, because it lives in **pattern, material, and light**, not in saturation.
36
-
37
- ## Architecture extracted from the references (geometry — all survives)
38
-
39
- The source brief studied four interior reference images (idea_inbox [#164](https://example.com/builders#/idea/164)); the *geometry* below is the durable takeaway, independent of hue.
40
-
41
- - **Double-height shell** (Interior1/2): two storeys of open vertical space → taller wall columns, a balcony-railing band at the top of the map, pendant lamps on cables from a high ceiling.
42
- - **Symmetrical industrial staircases** (Interior1): black-metal flights on the left/right walls — key navigation + vertical drama. Visual/blocking in V2; walkable upper floor is a later version.
43
- - **Warm pooled light** (Interior2): tall warm lamps and track lights pool on the floor — 2–3 shade steps radiating from each source. This is where *all* the warmth now comes from, since the walls are charcoal.
44
- - **Sculptural display** (interior3): freestanding display tables (gallery plinths, not Poke-Mart shelves), few items, generous spacing. Negative space ≈ product — the single biggest luxury signal.
45
- - **Featured "room within a room"** (interiorShelving1): an architecturally framed VIP zone with a deep olive/sage rug and warm ochre/gold fixtures — the inner sanctum / featured-product zone.
46
- - **Rear glass panel** (Interior1): full-width north-wall glass showing the coast beyond — ties the interior back to the Mediterranean exterior (and reuses the facade's glass language).
47
-
48
- ## Key tile assets (priority order)
49
-
50
- 1. **Aqua wave-scale floor tile** — the foundational tile; pixel spec is [#644](https://example.com/builders#/task/644).
51
- 2. **Interior container wall tile** — weathered charcoal corrugated (interior-facing variant of the exterior wall family).
52
- 3. **Cream shelving unit** — wall-mounted, pale-stone, items displayed; no white rim, 1-px outline.
53
- 4. **Track-light tile** — small dark ceiling/beam fixture + a warm cone splashing onto the surface below.
54
- 5. **Display-table object overlay** — freestanding wood top, few items, spaced.
55
- 6. **Deep olive/sage featured rug** — the premium-zone floor (alpha-tile overlay over the wave floor).
56
- 7. **Industrial pendant lamp** — hung from above, warm light source.
57
- 8. **Balcony railing** — dark metal, top of the map (second-storey read).
58
- 9. **Polished-concrete floor** — grey, smooth, for back-of-house / secondary areas.
59
- 10. **Rear glass panel** — full-width north wall, coast visible through it.
60
-
61
- ## Pipeline mapping (generate after [#640](https://example.com/builders#/task/640) / ADR 0044)
62
-
63
- Per [ADR 0008](../adr/<redacted>.md) (families, not per-tile calls):
64
-
65
- 1. **`interior_container_shell`** family — wall · balcony railing · staircase · pendant lamp.
66
- 2. **`interior_floor`** family — aqua wave-scale + polished-concrete, with the olive/sage rug as an alpha-tile overlay (the `marble_path` technique, [ADR 0013](../adr/<redacted>.md)).
67
- 3. **Display singletons** — table, shelving, track light, glass panel — per-tile.
68
-
69
- **Nothing generates until the palette swap ([#640](https://example.com/builders#/task/640) / ADR 0044) lands** — every tile would re-quantize against the old palette and be redone, the same sequencing rule the exterior brief states.
70
-
71
- ## Mood target
72
-
73
- Walk in and feel like you discovered something — not a supermarket, not an unaffordable boutique, not a video-game shop, but all three at once. The charcoal shell says *industrial secret*; the aqua wave floor says *I know this place*; the spaced, warm-lit display says *this is worth something*. The floor now reads as a **sun-bleached aqua wave** rather than an arcade-saturated blue, which is the point: it belongs to the same coast you can see through the rear glass.
74
-
75
- ## Engineering follow-up (not this task)
76
-
77
- A walkable interior needs a second world map / room and a door transition (the `container-exterior.md` entrance door leads here), plus interior OBJECT ids in **both** `map.js`/`terrain.js` twins (server + client). File as an engineering task once the interior layout ([#642](https://example.com/builders#/task/642)) and floor tile ([#644](https://example.com/builders#/task/644)) are final.
@@ -1,41 +0,0 @@
1
- # World map — the "Sealed Ruins Chart" (diegetic artifact, V2)
2
-
3
- - **Task:** [#648](https://example.com/builders#/task/648) (V2 · product). Promoted from idea_inbox [#169](https://example.com/builders#/idea/169) (builder 25 art/design brief, 2026-06-04).
4
- - **Date:** 2026-06-18
5
- - **Status:** Art + design spec ready. Two follow-ups it implies are separate tasks: the **diegetic-item mechanic** (engineering) and **tile generation** (waits on the palette swap, [#640](https://example.com/builders#/task/640)/[ADR 0044](../adr/0044-mediterranean-palette-replacement.md)). See *Follow-ups* below.
6
-
7
- ## Concept
8
-
9
- The world map is **not a UI overlay or menu** — it is a **diegetic in-game artifact**: an ancient sealed chart the player *discovers*. This reframes navigation from a game mechanic into a story beat. The in-world item name is the **"Sealed Ruins Chart"** — "sealed" is load-bearing: the map is a mystery; not everything is visible from the start.
10
-
11
- > Reference concept art (builder-25 planning input): `art/references/concept-art-original/world-map/Map Item Pop Up Concept.webp` + `Shipping container Concept.png`. **NB:** these planning images are not committed to the repo (the `art/references/concept-art-original/` tree is empty here); this written spec is the durable, self-contained source.
12
-
13
- ## Behaviour (the "diegetic" part — engineering follow-up, not this art task)
14
-
15
- - The player does **not** open a menu. They find / are given the scroll — an inventory item, like the FireRed Town Map received from a character.
16
- - Examining it plays an **unroll animation**.
17
- - It is **progressive / fog-of-war**: sealed areas begin as blank parchment and fill in as the player explores. (This is the "sealed" mystery made mechanical.)
18
-
19
- ## Scroll art spec (pixel art)
20
-
21
- - **Rollers (top + bottom):** dark mahogany brown, visible wood grain (2–3 horizontal pixel lines per plank); cylindrical implied by a lighter highlight strip along the top edge.
22
- - **Corner accents:** round emerald-green gemstone insets, 8×8px each, single light highlight pixel upper-left.
23
- - **Parchment:** aged warm tan, **not uniform** — subtle darker age/wear patches; slight edge vignette.
24
- - **Title banner:** a rectangular banner at the top, slightly darker parchment, **"SEALED RUINS CHART"** in a serif pixel font (dark brown, not black).
25
- - **Footer:** bottom roller repeats the title left + right with a centred **emblem** between — a 32×32 pixel creature (dragon / sea-monster), dark-outline style.
26
-
27
- ### Map surface — the swirling currents (what makes it feel ancient)
28
- The parchment is densely covered in hand-drawn-style arrows + curved flowing lines — **ocean-current / wind notations**, the language of old nautical charts (NOT paths):
29
- - thin 1px dark-brown curved, directional lines; arrow tips = simple 2–3px chevrons;
30
- - density ≈ one arrow/curve segment per 16×16 area over **sea**; **land** areas sparser;
31
- - this texture — organic, dense, directional — is what reads as *ancient + discovered*, not *designed*.
32
-
33
- ## Colour note (palette)
34
- The hexes in the source brief (mahogany, emerald, #D4A96A parchment) are **targets, not literals**: when generated, every pixel quantizes to the locked 64-colour palette — and that palette is being **replaced** ([#640](https://example.com/builders#/task/640) / [ADR 0044](../adr/0044-mediterranean-palette-replacement.md)). The replacement must carry warm-tan / mahogany / emerald steps for this artifact; flag if it doesn't. This is a UI/item artifact (not a world ground tile), so it is less palette-constrained than terrain, but it should still read in-world.
35
-
36
- ## Mood target
37
- The player should feel they found something they were **not supposed to find** — a real object from another time, not a game menu. First unroll = one of the most memorable moments in the game.
38
-
39
- ## Follow-ups (separate tasks, not this art spec)
40
- 1. **Diegetic-item mechanic** (engineering): inventory item + examine→unroll animation + progressive fog-of-war reveal keyed to discovered areas. File when the inventory/item system exists (relates to the V3 purchase-loop / bag work, e.g. [#635](https://example.com/builders#/task/635)).
41
- 2. **Scroll tile/sprite generation** (art pipeline): produce the scroll frame + parchment + emblem assets — **after** the palette swap ([#640](https://example.com/builders#/task/640)) so it isn't re-quantized + redone.
@@ -1,155 +0,0 @@
1
- # Recipe — stand up claudebongos.com + geminibongos.com (vanity doors → cloudbongos.com)
2
-
3
- > Brings **claudebongos.com** and **geminibongos.com** live as public "vanity
4
- > doors": each shows the exact Cloud Bongos landing **gate** (`brand/cloud-bongos/gate`),
5
- > rebranded to **"Claude Bongos" / `> claude`** (resp. **"Gemini Bongos" / `> gemini`**)
6
- > at runtime — the visitor still types `bongos`, and on enter the gate redirects
7
- > to **cloudbongos.com**, landing straight on the orbs. Same task also **promotes
8
- > the gate onto the cloudbongos.com apex** (it replaces the buddha placeholder,
9
- > which is what makes the redirect land "on the orbs"). Task
10
- > [#1746](https://example.com/builders#/task/1746).
11
-
12
- ## Architecture (what we're building)
13
-
14
- One gate bundle, three domains, a runtime switch — **no forked copies**. `space.js`
15
- reads `location.hostname`; a door rebrands the entry + redirects on enter, and the
16
- default (cloudbongos.com) reveals the orbs in place. Any future change to the gate
17
- shows up on all three automatically.
18
-
19
- | Domain | Serves | On typing `bongos` + enter |
20
- |---|---|---|
21
- | **claudebongos.com** | the gate, rebranded "Claude Bongos" / `> claude` | `→ https://cloudbongos.com/?entered=1` (orbs) |
22
- | **geminibongos.com** | the gate, rebranded "Gemini Bongos" / `> gemini` | `→ https://cloudbongos.com/?entered=1` (orbs) |
23
- | **cloudbongos.com** | the gate, "Cloud Bongos" (**was** the buddha page) | reveals the orbs in place |
24
-
25
- Hosting is the **cheapest possible**, same as the staging gate:
26
-
27
- | Thing | Value |
28
- |---|---|
29
- | Served by | Caddy `file_server`, web root `/home/lars/example/brand/cloud-bongos` |
30
- | The two doors | **pure static** — no app process, no port, no password |
31
- | cloudbongos.com apex | static gate for `/` + gate assets; **still proxies** `/api`, `/healthz`, etc. to the platform app (`:3002`) so sign-in keeps working |
32
- | TLS | **one new Cloudflare Origin Cert per new domain** — the cloudbongos cert's SANs do NOT cover claudebongos.com / geminibongos.com |
33
- | Cost | **$0/mo** — same droplet, no new process/port |
34
-
35
- Why static (not a reverse-proxy block): the gate is plain HTML/CSS/JS + PNGs.
36
- There is nothing to run. The `?entered=1` query flag is how a door hands off — the
37
- gate honors it (only on non-door hosts) to skip its own entry and open the orbs.
38
-
39
- Everything in the repo (`space.js`, the `infra/Caddyfile` blocks, the gate art)
40
- auto-deploys to the droplet checkout on merge. What is **NOT** automatic — and is
41
- this recipe — is the per-domain **DNS + Origin Cert** and the **live Caddyfile
42
- merge + reload**. Until those are done, the two domains show a TLS error (expected).
43
-
44
- ## Phase ① — DNS (Cloudflare, ~1 min per domain)
45
-
46
- Both domains are already on Cloudflare. For **each** zone (claudebongos.com,
47
- geminibongos.com):
48
-
49
- 1. Cloudflare → pick the zone → **DNS** → **Add record**.
50
- 2. Type **A**, Name **`@`**, IPv4 **`REDACTED_IP`**, Proxy status **Proxied**
51
- (orange cloud ON), TTL Auto. Save.
52
- 3. Cloudflare → **SSL/TLS** → **Overview** → set encryption mode to **Full (strict)**.
53
-
54
- (Optional: add a **`www`** A record → same IP, proxied, if you want `www.` to work.
55
- The Caddyfile blocks are apex-only today; add `www.claudebongos.com` to the site
56
- line if you add the record.)
57
-
58
- ## Phase ② — Origin Certificate (own domain → own cert)
59
-
60
- Each new domain needs its **own** Origin Cert. For **each** zone:
61
-
62
- Cloudflare → **SSL/TLS → Origin Server → Create Certificate**:
63
- - Hostnames: `claudebongos.com` **and** `*.claudebongos.com` (resp. the gemini pair)
64
- - Let Cloudflare generate the key; **copy both** the certificate and the private key.
65
-
66
- Install on the droplet (operator). Caddy runs as user `caddy` and **must be able to
67
- READ the key** — match the existing `origin.key` ownership (`640 root:caddy`). A
68
- root-only `600` key makes Caddy's reload fail (learned during the cloudbongos
69
- bring-up). The `.crt` can be world-readable.
70
-
71
- ```bash
72
- # claudebongos
73
- sudo install -m 644 -g caddy /dev/stdin /etc/caddy/claudebongos-origin.crt # paste cert, Ctrl-D
74
- sudo install -m 640 -g caddy /dev/stdin /etc/caddy/claudebongos-origin.key # paste key, Ctrl-D
75
- # geminibongos
76
- sudo install -m 644 -g caddy /dev/stdin /etc/caddy/geminibongos-origin.crt # paste cert, Ctrl-D
77
- sudo install -m 640 -g caddy /dev/stdin /etc/caddy/geminibongos-origin.key # paste key, Ctrl-D
78
- ```
79
-
80
- ## Phase ③ — apply the Caddy blocks + reload
81
-
82
- Three blocks ship in `infra/Caddyfile`: the two new **door** blocks
83
- (`claudebongos.com`, `geminibongos.com`) and the **modified `cloudbongos.com`
84
- apex** block (now serves the gate + proxies functional paths). The live
85
- `/etc/caddy/Caddyfile` has drifted slightly from the repo copy, so **do not blindly
86
- `cp` the whole file** — apply the two pieces by hand:
87
-
88
- ```bash
89
- cd /home/lars/example
90
- git pull # or wait for the auto-deploy; the brand/ bundle + infra/Caddyfile land here
91
-
92
- # 1. Append the two door blocks (idempotent: skip if already present).
93
- grep -q '^claudebongos.com {' /etc/caddy/Caddyfile || \
94
- awk '/^# ── Claude Bongos \+ Gemini Bongos — vanity DOORS/{p=1} p' infra/Caddyfile \
95
- | sed '/^# ── Cloud Bongos — STAGING/,$d' >> /etc/caddy/Caddyfile
96
- # (or just open the file and paste the two blocks from infra/Caddyfile — the
97
- # awk above copies from the DOORS comment to end-of-file.)
98
-
99
- # 2. Modify the LIVE cloudbongos.com apex block. Open the file, find the
100
- # `cloudbongos.com {` block, and replace its body — the single
101
- # `reverse_proxy 127.0.0.1:3002 { … }` — with the gate-serving body from
102
- # infra/Caddyfile (root + @hidden + @gate handle + the catch-all reverse_proxy).
103
- # Leave builders.cloudbongos.com and status.cloudbongos.com UNTOUCHED.
104
- sudo nano /etc/caddy/Caddyfile
105
-
106
- # 3. Validate (as root — it must read the 640 origin keys), then reload.
107
- sudo caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile
108
- sudo systemctl reload caddy # fails closed on a bad config
109
- ```
110
-
111
- > A bad config makes `reload` error out and **keep serving the old config** — the
112
- > live sites never go down. If a cert path is wrong, that one site fails to come
113
- > up while the others keep serving.
114
-
115
- ## Phase ④ — verify
116
-
117
- ```bash
118
- # from anywhere — the two doors:
119
- curl -sSL https://claudebongos.com/ | grep -o 'space.css\|gate' # gate HTML served ✓
120
- curl -sSL https://geminibongos.com/ | grep -o 'space.css\|gate' # gate HTML served ✓
121
- curl -sS -o /dev/null -w '%{http_code}\n' https://claudebongos.com/ # 200 (via the /gate/ redirect) ✓
122
-
123
- # cloudbongos.com apex now serves the gate, and the app still answers:
124
- curl -sSL https://cloudbongos.com/ | grep -o 'space.css\|gate' # gate served at apex ✓
125
- curl -sS https://cloudbongos.com/healthz # "ok" — app still reachable ✓
126
- ```
127
-
128
- Then open each in **Chrome or Edge** (the live-glass refraction needs Chromium
129
- `backdrop-filter` + SVG displacement; Safari falls back to a plain frost, still
130
- fine):
131
-
132
- - `https://claudebongos.com` → gate reads **"Claude Bongos" / `> claude`**. Type
133
- `bongos`, press enter → you land on **cloudbongos.com showing the orbs**.
134
- - `https://geminibongos.com` → same, **"Gemini Bongos" / `> gemini`**.
135
- - `https://cloudbongos.com` → gate reads "Cloud Bongos"; type `bongos` → orbs in place.
136
- - **Sign-in still works:** `https://builders.cloudbongos.com` → sign in with GitHub
137
- (the OAuth callback is on the apex, `/api/gds/auth/web/callback`, which the
138
- catch-all still proxies to `:3002`). If sign-in breaks, the apex `@gate`/`handle`
139
- split is wrong — recheck step ③.2.
140
-
141
- ## Rollback
142
-
143
- - **Take a door down:** delete its block from `/etc/caddy/Caddyfile`,
144
- `sudo systemctl reload caddy`, remove the Cloudflare `@` A record.
145
- - **Revert cloudbongos.com to the buddha page:** restore the apex block to the
146
- single `reverse_proxy 127.0.0.1:3002 { … }` body, reload. The buddha landing
147
- (`public-landing/index.html`) is untouched in the repo and served again
148
- immediately.
149
-
150
- ## Files this recipe uses
151
-
152
- - `infra/Caddyfile` — the two door blocks + the modified `cloudbongos.com` apex block.
153
- - `brand/cloud-bongos/gate/space.js` — the runtime hostname switch (branding +
154
- redirect + `?entered` auto-open).
155
- - `brand/cloud-bongos/` — the gate bundle + the world art it refracts (shared by all three).
@@ -1,139 +0,0 @@
1
- # Recipe — stand up staging.cloudbongos.com (password-locked landing-page preview)
2
-
3
- > Brings up **staging.cloudbongos.com** — a password-locked preview of the Cloud Bongos
4
- > landing **gate** (`brand/cloud-bongos/gate`, the cel-glass + `> cloud bongos` terminal
5
- > prototype from tasks [#1532](https://example.com/builders#/task/1532) +
6
- > [#1534](https://example.com/builders#/task/1534)). This is the future
7
- > cloudbongos.com landing page, parked behind a shared password so it can be shared for
8
- > review before it goes public. Task [#1540](https://example.com/builders#/task/1540).
9
-
10
- ## Architecture (what we're building)
11
-
12
- The **cheapest possible** host: no app process, no new port, no new certificate. Caddy
13
- **static-serves** the brand-art folder that already ships in the checkout, behind HTTP
14
- basic auth (the password lock).
15
-
16
- | Thing | Value |
17
- |---|---|
18
- | URL | `https://staging.cloudbongos.com` (redirects to `/gate/`, where the gate is served) |
19
- | Served by | Caddy `file_server`, web root `/home/lars/example/brand/cloud-bongos` |
20
- | Lock | HTTP basic auth — username `bongos` + a shared password |
21
- | TLS | the existing `*.cloudbongos.com` origin + Cloudflare edge certs (already cover it) |
22
- | Cost | **$0/mo** — same droplet, no process, no port |
23
-
24
- Why static (not a reverse-proxy block like the others): the gate is plain HTML/CSS/JS +
25
- PNGs. There is nothing to run. Caddy serves the files directly.
26
-
27
- ## Why the web root is the brand folder, not `gate/`
28
-
29
- The gate page lives at `gate/index.html` but references its art **relative to `/gate/`** —
30
- sprites **beside it** (`sprites/…`, `dispmaps/…`) and the world **one level up**
31
- (`../world/…`). So the folder that must be the web root is `brand/cloud-bongos/` (the
32
- parent), and the page must actually be served **at `/gate/`** for those relative paths to
33
- resolve. The Caddy block therefore **redirects the apex `/` → `/gate/`** (`redir / /gate/`)
34
- rather than serving the gate's HTML at the apex.
35
-
36
- > History (task 1545): the block first tried to serve the gate AT the apex via
37
- > `rewrite / /gate/index.html` plus per-folder rewrites (`/dispmaps/*` → `/gate/dispmaps/*`).
38
- > That was fragile — when the gate gained `sprites/` (task 1541) those 404'd at the apex
39
- > (the browser asked `/sprites/*`, not `/gate/sprites/*`). The redirect serves the gate at
40
- > its native path so **all** relative folders resolve, with nothing to keep in sync.
41
-
42
- ## Phase ① — DNS (Cloudflare dashboard, ~30 s)
43
-
44
- cloudbongos.com is on Cloudflare. Add **one** record:
45
-
46
- 1. Cloudflare → pick the **cloudbongos.com** zone → **DNS** → **Add record**.
47
- 2. Type **A**, Name **`staging`**, IPv4 **`REDACTED_IP`**, Proxy status **Proxied** (orange cloud ON), TTL Auto.
48
- 3. Save.
49
-
50
- No new certificate is needed — the cloudbongos origin cert is already
51
- `cloudbongos.com, *.cloudbongos.com`, and Cloudflare's edge cert covers
52
- `staging.cloudbongos.com` too.
53
-
54
- ## Phase ② — the password (droplet, one-time)
55
-
56
- The `staging.cloudbongos.com` block ships in `infra/Caddyfile` with a **dummy** bcrypt hash,
57
- so no real secret is in git. Generate the real hash and keep it handy for Phase ③:
58
-
59
- ```bash
60
- caddy hash-password --plaintext 'YOUR_PASSWORD'
61
- # → prints $2a$14$……… (copy the whole thing)
62
- ```
63
-
64
- Username is `bongos`; the password is whatever you hashed. The real hash lives **only** in
65
- the live `/etc/caddy/Caddyfile` — never committed.
66
-
67
- ## Phase ③ — apply the Caddy block + reload
68
-
69
- The `staging.cloudbongos.com` block is already in `infra/Caddyfile`. The live
70
- `/etc/caddy/Caddyfile` has drifted slightly from the repo copy (comment wording + block
71
- order), so **do not blindly `cp` the whole file** — append just the new block, swap the
72
- dummy hash for the real one, then reload:
73
-
74
- ```bash
75
- cd /home/lars/example
76
- # 1. append only the staging.cloudbongos.com block (idempotent: skip if present)
77
- grep -q 'staging.cloudbongos.com {' /etc/caddy/Caddyfile || \
78
- awk '/^# ── Cloud Bongos — STAGING/{p=1} p' infra/Caddyfile >> /etc/caddy/Caddyfile
79
-
80
- # 2. replace the dummy hash: open the file, find the staging.cloudbongos.com
81
- # block's `basic_auth { bongos … }` line, and paste the real $2a$… hash from
82
- # Phase ② in place of the dummy one.
83
- sudo nano /etc/caddy/Caddyfile # or any editor
84
-
85
- # 3. let the caddy user READ the static files. Caddy runs as user `caddy`, which
86
- # cannot traverse the 750 home dir — without this, authenticated requests get
87
- # 403. Everything under /home/lars/example is already world-readable, so
88
- # ONLY the home dir itself needs to be traversable (one-time, persists):
89
- sudo setfacl -m u:caddy:x /home/lars 2>/dev/null \
90
- || sudo chmod o+x /home/lars # fallback if the `acl` package is absent
91
- # o+x allows traverse, not listing. For strict least-privilege instead of the
92
- # broad o+x, `sudo apt-get install -y acl` first so the setfacl path is taken.
93
-
94
- # 4. validate (as root — it must read the 640 root:caddy origin key), then reload
95
- sudo caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile
96
- sudo systemctl reload caddy # fails closed on a bad config
97
- ```
98
-
99
- > A bad config makes `reload` error out and **keep serving the old config** — the live
100
- > sites never go down. Leaving the dummy hash in place is also safe: it just means no one
101
- > can log in (fail-closed) until you paste the real one.
102
-
103
- ## Phase ④ — verify
104
-
105
- ```bash
106
- # from anywhere:
107
- curl -sS -o /dev/null -w '%{http_code}\n' https://staging.cloudbongos.com/ # 401 (locked) ✓
108
- curl -sS -u bongos:YOUR_PASSWORD https://staging.cloudbongos.com/ | grep -o 'cloud bongos' # matches ✓
109
- curl -sS -o /dev/null -w '%{http_code}\n' -u bongos:YOUR_PASSWORD \
110
- https://staging.cloudbongos.com/world/assets/plates/scene-establishing.png # 200 (art loads) ✓
111
- ```
112
-
113
- Then open `https://staging.cloudbongos.com` in **Chrome or Edge** (the live-glass
114
- refraction needs Chromium `backdrop-filter` + SVG displacement; Safari falls back to a
115
- plain frost, which still looks fine). The browser shows a username/password box — enter
116
- `bongos` + the password — and the gate appears.
117
-
118
- ## Rotating / removing the password
119
-
120
- - **Change the password:** regenerate the hash (Phase ②), edit the `bongos …` line in the
121
- `staging.cloudbongos.com` block of `/etc/caddy/Caddyfile`, `sudo systemctl reload caddy`.
122
- - **Take the site down:** delete the `staging.cloudbongos.com` block from
123
- `/etc/caddy/Caddyfile`, `sudo systemctl reload caddy`, and remove the Cloudflare
124
- `staging` A record.
125
-
126
- ## Later — promote to the real public landing page
127
-
128
- When the gate is approved as the public landing page, that is a **separate task**: fold the
129
- gate into `public-landing/index.html` (served at the cloudbongos.com apex by
130
- `platform-server.js`), bring the world art it needs under a served path, and drop the basic
131
- auth. Until then, this staging block is the review surface and `public-landing/index.html`
132
- stays the minimal buddha placeholder.
133
-
134
- ## Files this recipe uses
135
-
136
- - `infra/Caddyfile` — the `staging.cloudbongos.com` block (ships with a dummy hash; the
137
- live file carries the real one).
138
- - `brand/cloud-bongos/gate/` — the gate prototype (`index.html` + `dispmaps/`).
139
- - `brand/cloud-bongos/world/assets/` — the art the gate refracts.
@@ -1,111 +0,0 @@
1
- # Recipe — stand up cloudbongos.com (Customer 0, the vanilla dogfood instance)
2
-
3
- > Brings **cloudbongos.com** live as a real Cloud Bongos instance: the vanilla brand, its own database, no game code — Cloud Bongos running on Cloud Bongos (the C8 proof, [ADR 0062](../adr/<redacted>.md) §2, task [#1374](https://example.com/builders#/task/1374) + the proof ship [#1206](https://example.com/builders#/task/1206)). Owner-only inputs are tracked in blocker [#40](https://example.com/builders#/blocker/40).
4
-
5
- ## Architecture (what we're building)
6
-
7
- Same droplet as OTB (`REDACTED_IP`), **separate process + separate database**, no new monthly cost. One checkout (`/home/lars/example`) runs **two** processes:
8
-
9
- | Instance | Entrypoint | Brand | Port | DB | Domain |
10
- |---|---|---|---|---|---|
11
- | OTB (game) | `server.js` | `config/branding.json` (default) | 3000 | `example` | example.com |
12
- | **Cloud Bongos** | `src/platform-server.js` | `config/branding.cloudbongos.json` (via `GDS_BRANDING_FILE`) | **3002** | **`cloudbongos`** | **cloudbongos.com** |
13
-
14
- The only difference between the two processes is **environment** — no divergent clone. The brand switch is `src/branding.js`'s `GDS_BRANDING_FILE` knob (task 1374). The landing page is served at the apex by `platform-server.js`; the Hall + status ride `builders.`/`status.` (host routing in `src/bongos/serve-internal.js`).
15
-
16
- ## Phase ① — DNS ✅ done
17
-
18
- Domain on Cloudflare (Free plan), nameservers switched, **SSL/TLS = Full (strict)**, three **proxied** A records → `REDACTED_IP`:
19
- `cloudbongos.com` (`@`), `builders`, `status`. (Until the origin cert + process are up, the site shows a TLS error — expected.)
20
-
21
- ## Phase ② — GitHub OAuth App (builder sign-in)
22
-
23
- GitHub → Settings → Developer settings → **OAuth Apps** → **New OAuth App**:
24
- - **Application name:** `Cloud Bongos`
25
- - **Homepage URL:** `https://cloudbongos.com`
26
- - **Authorization callback URL:** `https://cloudbongos.com/api/gds/auth/web/callback` (PINNED — `auth.js` `WEB_REDIRECT_ORIGIN`; sign-in begun on `builders.` still calls back to the apex)
27
- - **Enable Device Flow:** ✅ (the CLI login uses it)
28
-
29
- Copy the **Client ID** + generate a **Client Secret** → they go in `/etc/cloudbongos/web.env` as `CLOUDBONGOS_GITHUB_CLIENT_ID` / `CLOUDBONGOS_GITHUB_CLIENT_SECRET` (resolved as `<ENVPREFIX>_GITHUB_*`; vanilla envPrefix = `CLOUDBONGOS`).
30
-
31
- ## Phase ③ — Cloudflare Origin Certificate (own domain → own cert)
32
-
33
- cloudbongos.com is a different domain, so it needs its **own** Origin Cert (the example cert's SANs don't cover it). Cloudflare → **SSL/TLS → Origin Server → Create Certificate**:
34
- - Hostnames: `cloudbongos.com` **and** `*.cloudbongos.com`
35
- - Let Cloudflare generate the key; **copy both** the certificate and the private key.
36
-
37
- Install on the droplet (operator). **Caddy runs as user `caddy` and must be able to READ the key** — match the existing `origin.key` ownership (`640 root:caddy`). A root-only `600` key makes Caddy's reload fail and loop (the OTB site keeps serving the old config, but the new site never comes up — seen during the first bring-up). The `.crt` can be world-readable.
38
- ```bash
39
- sudo install -m 644 -g caddy /dev/stdin /etc/caddy/cloudbongos-origin.crt # paste cert, Ctrl-D
40
- sudo install -m 640 -g caddy /dev/stdin /etc/caddy/cloudbongos-origin.key # paste key, Ctrl-D
41
- ```
42
-
43
- ## Phase ④ — Droplet bring-up (operator)
44
-
45
- ```bash
46
- # 1. The instance database (peer auth via the local socket, same as OTB)
47
- createdb cloudbongos
48
-
49
- # 2. GDS-only schema (skips migrations/game/*.sql — no game tables on this instance)
50
- cd /home/lars/example
51
- PGDATABASE=cloudbongos MEDUSA_GDS_ONLY=1 ./scripts/migrate.sh
52
-
53
- # 3. Secrets file (from infra/cloudbongos.env.example)
54
- sudo mkdir -p /etc/cloudbongos
55
- sudo cp infra/cloudbongos.env.example /etc/cloudbongos/web.env
56
- sudo $EDITOR /etc/cloudbongos/web.env # fill the OAuth client id + secret
57
- sudo chmod 600 /etc/cloudbongos/web.env && sudo chown lars:lars /etc/cloudbongos/web.env
58
-
59
- # 4. systemd service (from infra/cloudbongos.service)
60
- sudo cp infra/cloudbongos.service /etc/systemd/system/cloudbongos.service
61
- sudo systemctl daemon-reload
62
- sudo systemctl enable --now cloudbongos
63
- curl -sS http://127.0.0.1:3002/healthz # → "ok"
64
-
65
- # 5. Caddy — the three cloudbongos blocks are in infra/Caddyfile; apply + reload
66
- sudo cp infra/Caddyfile /etc/caddy/Caddyfile # or merge the cloudbongos blocks in
67
- sudo systemctl reload caddy
68
- ```
69
-
70
- ## Phase ⑤ — Verify + first sign-in
71
-
72
- ```bash
73
- curl -sS https://cloudbongos.com/healthz # "ok" (through Cloudflare now)
74
- curl -sS https://cloudbongos.com/ | grep -o 'cloud bongos' # the landing
75
- ```
76
- - `https://cloudbongos.com` → the buddha landing.
77
- - `https://builders.cloudbongos.com` → the Hall. **Sign in with GitHub.** Because the pack sets `firstAdmin: "example-owner"`, that first sign-in auto-seats example-owner as **Owner/Archon** on the (empty) cloudbongos DB — no manual step (`auth.js` `maybeSeatFirstAdmin`).
78
- - `https://status.cloudbongos.com` → the dashboard.
79
-
80
- ## Deploy automation (important — updated for the dedicated box)
81
-
82
- > **This changed with [ADR 0126](../adr/<redacted>.md).** cloudbongos.com no longer runs co-tenant on the OTB box. It runs on its **own dedicated droplet** (`REDACTED_IP`, checkout `/home/lars/cloudbongos`) and tracks `main` via a **pull-based `systemd` timer** — [ADR 0129](../adr/<redacted>.md), task [#2064](https://example.com/builders#/task/2064). The co-hosting box's CI deploy no longer touches it. (The historical "wire `sudo systemctl restart cloudbongos` into `~/deploy.sh`" note applied only while it was a co-tenant.)
83
-
84
- Once the box exists, the deploy is [`scripts/deploy/deploy-cloudbongos.sh`](../../scripts/deploy/deploy-cloudbongos.sh) fired by [`infra/cloudbongos-deploy.timer`](../../infra/cloudbongos-deploy.timer) every ~2 min: `git fetch` + reset `origin/main` → `npm ci` (if the lockfile changed) → `PGDATABASE=cloudbongos MEDUSA_GDS_ONLY=1 ./scripts/migrate.sh` → `sudo systemctl restart cloudbongos` → 3002 healthz. After the first install it **self-heals** (each run refreshes `/home/lars/deploy.sh` + the units from the checkout).
85
-
86
- **One-time bootstrap** (operator, over SSH to the box — the box can't install its own first deploy automation). Run once the deploy files are on `main`:
87
- ```bash
88
- # Executed as a STABLE copy outside the checkout (so `git reset --hard` can't
89
- # corrupt the running script — see ADR 0129):
90
- sudo -u lars install -m 0755 /home/lars/cloudbongos/scripts/deploy/deploy-cloudbongos.sh /home/lars/deploy.sh
91
- sudo install -m 0644 /home/lars/cloudbongos/infra/cloudbongos-deploy.service /etc/systemd/system/
92
- sudo install -m 0644 /home/lars/cloudbongos/infra/cloudbongos-deploy.timer /etc/systemd/system/
93
- sudo systemctl daemon-reload
94
- sudo systemctl enable --now cloudbongos-deploy.timer
95
- # Verify: one forced run brings the box current + smokes the chain
96
- sudo -u lars FORCE_DEPLOY=1 /home/lars/deploy.sh
97
- systemctl list-timers cloudbongos-deploy.timer --no-pager
98
- curl -sS https://cloudbongos.com/healthz # → "ok"
99
- ```
100
-
101
- ## The C8 proof (task 1206) — ✅ DONE 2026-06-21
102
-
103
- With the instance live, **one real task** went through the full lifecycle **on cloudbongos.com** — claim → grade (the real 4-worker panel, PASS) → merge → ship, credits awarded — carrying zero game code: **cloudbongos task [#1](https://example.com/builders#/task/1)** (under version CB-V1), the `GDS_MODULES_FILE` fix that made Customer-0 advertise its modules correctly all-off. That shipped task is the C8 acceptance ("the GDS builds itself"). Full record + the operational mechanics for driving cloudbongos.com's GDS from the CLI: [2026-06-21 — C8 self-build proof](../session-logs/<redacted>.md). Closes task [#1206](https://example.com/builders#/task/1206).
104
-
105
- ## Files this recipe uses
106
-
107
- - `config/branding.cloudbongos.json` — the customer-0 brand pack (deltas over `config/branding.neutral.json`).
108
- - `src/branding.js` — the `GDS_BRANDING_FILE` selector.
109
- - `src/platform-server.js` — GDS-only entrypoint + the apex landing mount.
110
- - `public-landing/index.html` — the landing page (mascot at `public-landing/buddha.png`).
111
- - `infra/cloudbongos.service`, `infra/cloudbongos.env.example`, `infra/Caddyfile` (the cloudbongos blocks).