@bongos/core 1.17.1 → 1.17.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.bongos-core.json +244 -264
- package/.claude/skills/blocker-review/SKILL.md +1 -1
- package/.claude/skills/builder-claim/SKILL.md +2 -0
- package/.claude/skills/builder-exit/SKILL.md +2 -2
- package/.claude/skills/builder-redteam/SKILL.md +3 -3
- package/.claude/skills/builder-setup/SKILL.md +2 -2
- package/.claude/skills/builder-start/SKILL.md +1 -1
- package/.claude/skills/dev/SKILL.md +1 -1
- package/.claude/skills/idea-triage/SKILL.md +2 -2
- package/.claude/skills/ideate/SKILL.md +1 -1
- package/.claude/skills/merge-mode/SKILL.md +1 -1
- package/.claude/skills/new-project/SKILL.md +3 -0
- package/.claude/skills/paint/SKILL.md +1 -1
- package/GOVERNANCE.md +1 -1
- package/README.md +1 -1
- package/docs/adr/0001-stack-choice-single-vps.md +1 -1
- package/docs/adr/0008-google-chat-oauth-user-auth.md +1 -1
- package/docs/adr/0019-red-team-bounty-payout-table.md +3 -3
- package/docs/adr/0020-security-model-public-stub.md +1 -1
- package/docs/adr/0023-kind-multipliers-and-idea-bonus.md +1 -1
- package/docs/adr/0024-cloneable-repo-local-first-memory.md +1 -1
- package/docs/adr/0026-bfg-memory-hygiene-and-cross-builder-learning.md +4 -4
- package/docs/adr/0027-bfg-session-inefficiency-evaluator.md +3 -3
- package/docs/adr/0030-builders-hall-live-updates.md +1 -1
- package/docs/adr/0031-cloud-dev-environments-for-builders.md +4 -4
- package/docs/adr/0032-discord-mirror-of-gds.md +2 -2
- package/docs/adr/0034-thetes-graduated-newcomer-rank.md +1 -1
- package/docs/adr/0044-mediterranean-palette-replacement.md +1 -1
- package/docs/adr/0044-per-box-live-game-preview.md +1 -1
- package/docs/adr/0054-session-token-reward-cost-plus.md +10 -10
- package/docs/adr/0057-container-cost-ledger.md +3 -3
- package/docs/adr/0062-medusa-instance-model-decoupling.md +4 -4
- package/docs/adr/0065-agpl-nonprofit-ai-first-governance.md +2 -2
- package/docs/adr/0085-remove-default-per-builder-spend-cap.md +1 -1
- package/docs/adr/0086-goal-scoped-work-hierarchy.md +1 -1
- package/docs/adr/0096-require-reward-before-workable.md +9 -9
- package/docs/adr/0097-retroactive-reward-backfill.md +3 -3
- package/docs/adr/0100-two-repo-instance-model.md +1 -1
- package/docs/adr/0101-goal-hierarchy-phase-5-judge-membership-reward.md +3 -3
- package/docs/adr/0103-core-first-extraction-cloud-bongos-trunk.md +3 -3
- package/docs/adr/0107-module-upstreaming-contribution-to-core-catalog.md +1 -1
- package/docs/adr/0120-pay-on-land-and-builder-owned-rebase-gate.md +6 -6
- package/docs/adr/0136-update-channel-subscription-policy.md +51 -0
- package/docs/adr/0137-upstream-publish-policy-manual-owner-gated.md +40 -0
- package/docs/adr/0138-bundled-module-deps-declared-per-module.md +45 -0
- package/docs/adr/README.md +9 -6
- package/docs/architecture.md +2 -2
- package/docs/branding-contract.md +5 -4
- package/docs/canonical-permissions.md +1 -1
- package/docs/design/vanilla-hall-ui-redesign-scope.md +1 -1
- package/docs/file-map.md +18 -17
- package/docs/handoff-template.md +2 -2
- package/docs/modules-contract.md +6 -1
- package/docs/onboarding/diagrams/README.md +7 -7
- package/docs/onboarding/drachmae-and-karma.md +18 -18
- package/docs/onboarding/primer.md +9 -9
- package/docs/recipes/audit-replay.md +2 -2
- package/docs/recipes/builder-box-lifecycle.md +2 -2
- package/docs/recipes/claude-design-loop.md +1 -1
- package/docs/recipes/ops-gotchas.md +1 -1
- package/docs/recipes/private-npm-distribution.md +17 -0
- package/docs/recipes/staging-environment.md +1 -1
- package/docs/recipes/upgrading-the-core.md +55 -0
- package/docs/research/cursor-competitive-analysis.md +1 -1
- package/docs/research/llm-model-agnostic-and-cheaper-grader.md +1 -1
- package/modules/discord/module.json +2 -1
- package/modules/economy/achievements.js +9 -1
- package/modules/economy/credits.js +10 -1
- package/modules/hall-ui/public/builders.js +71 -4
- package/modules/hall-ui/public/goal-inbox.js +191 -0
- package/modules/hall-ui/public/hall-widgets.js +14 -1
- package/modules/hall-ui/public/index.html +3 -0
- package/modules/hall-ui/public/new-project-wizard.js +1 -0
- package/modules/hall-ui/public/style.css +43 -0
- package/modules/provisioning/CLAUDE.md +15 -0
- package/modules/provisioning/migrations/provisioning_001_tables.sql +160 -0
- package/modules/provisioning/migrations/provisioning_002_standalone_shape.sql +11 -0
- package/modules/provisioning/migrations/provisioning_003_github_app_manifest.sql +95 -0
- package/modules/provisioning/module.json +17 -0
- package/modules/provisioning/onboard-plan.js +37 -0
- package/modules/provisioning/provisioning.js +539 -0
- package/modules/provisioning/rate-limit.js +35 -0
- package/modules/provisioning/routes/provisioning.js +607 -0
- package/modules/provisioning/tests/provisioning.mjs +232 -0
- package/modules/provisioning/ui/.gitkeep +0 -0
- package/modules/security/secret-scrub.js +10 -0
- package/package-lock.json +2 -2
- package/package.json +7 -2
- package/scripts/discord/README.md +1 -1
- package/scripts/gds/claim.js +98 -48
- package/scripts/gds/claude-materialize.js +30 -9
- package/scripts/gds/cutover-codemod.js +241 -0
- package/scripts/gds/fitness.js +54 -0
- package/scripts/gds/init.js +59 -3
- package/scripts/gds/lint-infra-exec.js +70 -0
- package/scripts/gds/lint-workflow-exec.js +63 -0
- package/scripts/gds/mirror-lineage-check.js +14 -5
- package/scripts/gds/mirror-redact.js +8 -0
- package/scripts/gds/onboard.js +4 -0
- package/scripts/gds/package-core.js +18 -3
- package/scripts/gds/publish-manifest.js +60 -2
- package/scripts/gds/run-routine.js +51 -13
- package/scripts/gds/run-unit-tests.js +4 -0
- package/scripts/gds/update-channel.js +186 -0
- package/scripts/gds/upgrade.js +116 -9
- package/src/bongos/auth.js +14 -1
- package/src/bongos/serve-internal.js +10 -1
- package/src/branding.js +1 -0
- package/src/module-api.js +12 -1
- package/src/module-loader/loader.js +36 -4
- package/src/module-loader/manifest-schema.js +16 -1
- package/tests/branding.mjs +11 -0
- package/tests/claim_error_surface.mjs +107 -0
- package/tests/claude_materialize.mjs +20 -0
- package/tests/consumer_layout_boot.mjs +302 -0
- package/tests/cutover_codemod.mjs +184 -0
- package/tests/device_flow_start.mjs +25 -2
- package/tests/hook_core_resolve.mjs +125 -0
- package/tests/infra_exec_paths.mjs +104 -0
- package/tests/init.mjs +47 -0
- package/tests/mirror_lineage_proof.mjs +14 -0
- package/tests/module_loader.mjs +42 -0
- package/tests/module_manifest.mjs +19 -0
- package/tests/onboard.mjs +13 -0
- package/tests/package_core.mjs +8 -0
- package/tests/provisioning_github_app_routes.mjs +15 -0
- package/tests/publish_manifest.mjs +52 -1
- package/tests/run_routine.mjs +26 -1
- package/tests/run_routine_two_root.mjs +77 -0
- package/tests/scrubber_corpus.mjs +17 -0
- package/tests/update_channel.mjs +186 -0
- package/tests/upgrade.mjs +131 -0
- package/tests/workflow_exec_paths.mjs +98 -0
- package/docs/design/container-exterior-tiles.md +0 -49
- package/docs/design/container-exterior.md +0 -81
- package/docs/design/container-interior.md +0 -77
- package/docs/design/world-map-scroll.md +0 -41
- package/docs/recipes/claude-gemini-bongos-doors.md +0 -155
- package/docs/recipes/cloudbongos-staging-gate.md +0 -139
- package/docs/recipes/cloudbongos-standup.md +0 -111
- package/docs/recipes/parallel-load-results-2026-05-30.md +0 -63
- package/docs/recipes/parallel-load-test.md +0 -32
- package/docs/recipes/pixel-art-pipeline.md +0 -411
- package/docs/recipes/staging-data.md +0 -50
- package/migrations/instance/018_pms_v3_planning_seed.sql +0 -131
- package/migrations/instance/080_gds_v4_planning_seed.sql +0 -75
- package/migrations/instance/110_medusa_planning_seed.sql +0 -70
- package/migrations/instance/120_bongos_v1_planning_seed.sql +0 -69
- package/migrations/instance/144_bongos_v1_module_system_criterion.sql +0 -26
- package/migrations/instance/159_bongos_v1_goal_hierarchy_criterion.sql +0 -29
- package/migrations/instance/172_v2_world_planning_seed.sql +0 -163
- package/migrations/instance/173_bongos_v1_c7_core_modularized.sql +0 -33
- package/migrations/instance/182_gdsv4_bongos_consolidation.sql +0 -132
- package/migrations/instance/183_gdsv4_carryover_themed_goals.sql +0 -116
- package/migrations/instance/186_bongos_v1_governance_planning_seed.sql +0 -69
- package/scripts/gds/art-stage.js +0 -152
- package/server.js +0 -212
- package/tests/art_key.mjs +0 -121
- package/tests/day_cycle.mjs +0 -197
- package/tests/snapshot_picker.mjs +0 -233
- package/tests/sprite_facing.mjs +0 -35
- package/tests/terrain_picker.mjs +0 -298
- package/tests/turn_in_place.mjs +0 -280
- package/tests/walkability.mjs +0 -188
|
@@ -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).
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
# Parallel-load test results — 2026-05-30 ([#272](https://example.com/builders#/task/272), criterion C6)
|
|
2
|
-
|
|
3
|
-
> **Harness:** [`tests/parallel-load.mjs`](../../tests/parallel-load.mjs) · **Procedure:** [parallel-load-test.md](parallel-load-test.md) · **Target:** seeded staging ([#262](https://example.com/builders#/task/262)) at `http://127.0.0.1:3001` on the droplet (direct, bypassing Cloudflare).
|
|
4
|
-
|
|
5
|
-
This is the live run that closes **[#272](https://example.com/builders#/task/272) (V3.R53)** and satisfies criterion **C6 — parallel-safe-under-load**. It exercises the deterministic claim concurrency audited in [#269](https://example.com/builders#/task/269) (the `claimTask()` transaction + `uniq_active_claim_per_task` index) against a real running server under concurrent load.
|
|
6
|
-
|
|
7
|
-
## Bottom line
|
|
8
|
-
|
|
9
|
-
**Across both runs: ZERO `server_error` (5xx), ZERO races, ZERO anomalies.** Every claim resolved to exactly one of: `success`, `collision` (409 — exactly-one-wins), or `throttled` (429 — the rate limiter cleanly shedding excess writes). Staging's journal logged **0** 5xx responses over the full window. C6's bar — "all claims succeed or 409 cleanly; no 500s, no race conditions" — holds, with the added finding that excess write load is shed cleanly via 429 rather than ever degrading into a race or crash.
|
|
10
|
-
|
|
11
|
-
## Run A — sustained 30-minute run (the [#272](https://example.com/builders#/task/272) acceptance run)
|
|
12
|
-
|
|
13
|
-
`--workers 5 --duration 1800 --work-ms 150` (natural claim→work→release→repeat loop across distinct tasks):
|
|
14
|
-
|
|
15
|
-
| metric | value |
|
|
16
|
-
|---|---|
|
|
17
|
-
| wall | 1800.1s (30 min) |
|
|
18
|
-
| claim attempts | 144 |
|
|
19
|
-
| success | 13 |
|
|
20
|
-
| collision (409, exactly-one-wins) | 8 (5.6%) |
|
|
21
|
-
| throttled (429) | 123 |
|
|
22
|
-
| **server_error (5xx)** | **0** |
|
|
23
|
-
| client_error / other | 0 / 0 |
|
|
24
|
-
| anomalies | 0 |
|
|
25
|
-
| staging 5xx in journal | 0 |
|
|
26
|
-
|
|
27
|
-
The system stayed healthy (`/healthz` 200) for the entire 30 minutes with no degradation. Low attempt volume is expected: the per-IP rate limiter (below) caps writes, and releases that get throttled leave tasks `active`, draining the claimable pool — so workers spend most cycles idling on an empty queue rather than hammering. That's the system behaving correctly under its own backpressure.
|
|
28
|
-
|
|
29
|
-
## Run B — high-intensity contention burst (forces collisions)
|
|
30
|
-
|
|
31
|
-
`--workers 5 --duration 25 --work-ms 100 --contend` (all 5 workers race the SAME task — the canonical "two builders, one task" case):
|
|
32
|
-
|
|
33
|
-
| metric | value |
|
|
34
|
-
|---|---|
|
|
35
|
-
| claim attempts | 2733 |
|
|
36
|
-
| success | 8 |
|
|
37
|
-
| collision (409, exactly-one-wins) | 17 |
|
|
38
|
-
| throttled (429) | 2708 |
|
|
39
|
-
| **server_error (5xx)** | **0** |
|
|
40
|
-
| anomalies | 0 |
|
|
41
|
-
|
|
42
|
-
This is the direct demonstration of the C6 invariant: when multiple workers race the same task row, **exactly one wins and the rest get a clean 409 `ALREADY_CLAIMED`** — never two winners, never a 5xx.
|
|
43
|
-
|
|
44
|
-
## The rate limiter (a relevant, healthy finding)
|
|
45
|
-
|
|
46
|
-
`src/bongos/middleware/rate-limit.js` enforces **30 writes / 60s per-IP** and **60 writes / 60s per-session** on write methods (GET is unlimited). Because all simulated workers run from the droplet's single IP, the per-IP limit binds first, so most write attempts under load return **429** (`throttled` above). For a parallel-safety test this is a *positive* result: under sustained overload the server sheds excess writes cleanly (429 + `Retry-After`) instead of racing or 500ing. To exercise the claim logic at higher volume in a future run, raise the limit on staging or distribute workers across IPs.
|
|
47
|
-
|
|
48
|
-
## Harness fixes made while running this (committed under [#272](https://example.com/builders#/task/272))
|
|
49
|
-
|
|
50
|
-
Running against the real server surfaced live-path bugs the self-test (mock) couldn't:
|
|
51
|
-
|
|
52
|
-
- **claimable parsing:** the harness read `.data.tasks`, but `GET /tasks/claimable` returns `{ claimable: [...] }` → it saw 0 tasks and never claimed. Fixed to read `.claimable` (with `.tasks` fallback).
|
|
53
|
-
- **429 classification:** 429 was bucketed as `other` / flagged as a release anomaly. Added a dedicated `throttled` outcome (clean) so the report and the pass/fail conclusion are accurate.
|
|
54
|
-
- **`--contend` mode:** added so workers can be pointed at one task to demonstrate exactly-one-wins collisions on demand (Run B).
|
|
55
|
-
|
|
56
|
-
## Reproduce
|
|
57
|
-
|
|
58
|
-
```bash
|
|
59
|
-
# On the droplet (staging seeded per #262, service running, alphanumeric token minted):
|
|
60
|
-
cd /home/lars/example-staging
|
|
61
|
-
BASE_URL=http://127.0.0.1:3001 TOKEN=<redacted> node tests/parallel-load.mjs --workers 5 --duration 1800 --work-ms 150
|
|
62
|
-
# add --contend to force same-task collisions
|
|
63
|
-
```
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
# Parallel-load test — exercising claim concurrency under N builders
|
|
2
|
-
|
|
3
|
-
> **Harness:** [`tests/parallel-load.mjs`](../../tests/parallel-load.mjs) (built under prep task [#472](https://example.com/builders#/task/472)).
|
|
4
|
-
> **Live run:** criterion **C6** task **[#272](https://example.com/builders#/task/272)** (V3.R53) — a 30-min, 5-builder run against **staging**, gated on **[#262](https://example.com/builders#/task/262)** (staging data seeding).
|
|
5
|
-
|
|
6
|
-
The harness spawns N concurrent simulated "builder" loops that hammer the GDS claim lifecycle (claim → simulated work → release → repeat) and classifies every claim outcome into `success` / `collision` (clean 409/refusal) / `server_error` (the anomaly we hunt) / `client_error` / `other`. It validates the deterministic claim concurrency C6 requires — the same `claimTask()` transaction + `uniq_active_claim_per_task` index audited in [multi-builder-merge.md](multi-builder-merge.md).
|
|
7
|
-
|
|
8
|
-
## Self-test (CI-safe, no network)
|
|
9
|
-
|
|
10
|
-
```bash
|
|
11
|
-
node tests/parallel-load.mjs # defaults: 5 workers, 60s
|
|
12
|
-
node tests/parallel-load.mjs --workers 5 --duration 3 --work-ms 50
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
With no `BASE_URL`, the harness drives an **in-process mock server** whose claim semantics mirror the DB (one active claim per task; touches-overlap → conflict). The board deliberately has two tasks sharing a path so collisions are *forced*, then asserts: ≥1 success, ≥1 collision, **zero** server_error, no invariant violation, and that attempts == sum of classes. Exit 0 = harness accounting proven correct. This runs in CI without a droplet.
|
|
16
|
-
|
|
17
|
-
## Live run against staging ([#272](https://example.com/builders#/task/272) — once [#262](https://example.com/builders#/task/262) has seeded staging)
|
|
18
|
-
|
|
19
|
-
```bash
|
|
20
|
-
# Get builder tokens (one or more simulated builders share the load).
|
|
21
|
-
# NEVER point this at production — it mutates real claim/task state. The
|
|
22
|
-
# harness refuses a prod example.com host unless you pass --i-know.
|
|
23
|
-
BASE_URL=https://staging.example.com \
|
|
24
|
-
TOKENS=$tokA,$tokB,$tokC,$tokD,$tokE \
|
|
25
|
-
node tests/parallel-load.mjs --workers 5 --duration 1800
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
`#272` acceptance: run 30 min (`--duration 1800`); **every claim resolves to `success` or a clean `collision` — zero `server_error`, no races**; the report's throughput + collision-rate are the deliverable. A single anomaly (any 5xx, thrown error, or invariant violation) exits 1 and fails the run.
|
|
29
|
-
|
|
30
|
-
## Why the split (prep [#472](https://example.com/builders#/task/472) vs live [#272](https://example.com/builders#/task/272))
|
|
31
|
-
|
|
32
|
-
`#272`'s done-when explicitly requires a seeded staging environment, which `#262` (backlog) provides. Rather than block the runnable harness behind infra, [#472](https://example.com/builders#/task/472) built + self-validated the harness now; [#272](https://example.com/builders#/task/272) stays the live-staging run. When [#262](https://example.com/builders#/task/262) ships, point the harness at staging per above to close [#272](https://example.com/builders#/task/272) and satisfy C6.
|