@bongos/core 1.19.100 → 1.19.102

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.bongos-core.json CHANGED
@@ -2,22 +2,22 @@
2
2
  "artifact": "bongos-core",
3
3
  "manifest_schema": 1,
4
4
  "generator": "scripts/gds/package-core.js",
5
- "core_version": "1.19.100",
6
- "core_contract": "1.19.100",
7
- "source_commit": "7b80b662d17af4b1be962ab5d755604ad8a8723f",
5
+ "core_version": "1.19.102",
6
+ "core_contract": "1.19.102",
7
+ "source_commit": "484851c87018d5e1be475e9964f1215eaf50e4b1",
8
8
  "source_ref": "HEAD",
9
- "built_at": "2026-08-13T22:46:41.158Z",
9
+ "built_at": "2026-08-13T23:21:54.630Z",
10
10
  "redaction": {
11
11
  "model": "docs-redacted+functional-verbatim",
12
- "docs_redacted": 299,
12
+ "docs_redacted": 301,
13
13
  "agent_docs_stubbed": 21,
14
14
  "functional_verbatim": 1232,
15
15
  "rules": 3,
16
16
  "gate_literals": 3,
17
17
  "gate": "passed"
18
18
  },
19
- "file_count": 1552,
20
- "tree_sha256": "9a68e40f31c8eeee4090d108b208194fa3338756974d81dcc6a967456a3715b7",
19
+ "file_count": 1554,
20
+ "tree_sha256": "5858094e2d3e98c087659218b83067785b4a98e0ee29e6f26c06dc354d8eaeea",
21
21
  "files": [
22
22
  {
23
23
  "path": ".claude/skills/blocker-review/SKILL.md",
@@ -342,7 +342,7 @@
342
342
  {
343
343
  "path": "bin/bongos.js",
344
344
  "mode": "0000644",
345
- "sha256": "b976a94f0879fd4fe0cdc1eb82209b446cdc7a39186ea1300f4b535bec8f1d00"
345
+ "sha256": "36208a41a60e3a21356ca9f81abdff9203837ff024b3d15c88a26bee9cce371c"
346
346
  },
347
347
  {
348
348
  "path": "clients/bongos-client/README.md",
@@ -1319,10 +1319,15 @@
1319
1319
  "mode": "0000644",
1320
1320
  "sha256": "8bdad4b4333cbb7b38f66e4dddab08675dfc0a431d21c0be6a31ca4afd8c498f"
1321
1321
  },
1322
+ {
1323
+ "path": "docs/adr/0169-bongos-serve-server-bring-up.md",
1324
+ "mode": "0000644",
1325
+ "sha256": "fe1c4b38b9b3eef60ecd00ecc35eb6086690350a022d2c376d1432c95c9452f7"
1326
+ },
1322
1327
  {
1323
1328
  "path": "docs/adr/README.md",
1324
1329
  "mode": "0000644",
1325
- "sha256": "fad8c85a5e99cb222616afcdc2efaa139f3795e1a71f13f9f6fac2cf0bacddef"
1330
+ "sha256": "529d997cf39669fe80bf0fb9a02c546adbb6ba139d4f8dad8de4f59932c47a95"
1326
1331
  },
1327
1332
  {
1328
1333
  "path": "docs/api-reference.md",
@@ -1597,7 +1602,7 @@
1597
1602
  {
1598
1603
  "path": "docs/recipes/instance-deploy.md",
1599
1604
  "mode": "0000644",
1600
- "sha256": "207d05ae2b1c7ecc78de3f55e4cc7b46d69c99f3ecc39c53708a2d214de95c19"
1605
+ "sha256": "92a10c52ddbda73541b4f5cdad6b3b564d2e3dd9905c9b9fc0a22c3e56aea7b9"
1601
1606
  },
1602
1607
  {
1603
1608
  "path": "docs/recipes/instance-first-run.md",
@@ -1607,7 +1612,7 @@
1607
1612
  {
1608
1613
  "path": "docs/recipes/local-dev.md",
1609
1614
  "mode": "0000644",
1610
- "sha256": "ae824e62522122efc200ba4fc82bef3baa2ee461c4113798491f1d895567fb69"
1615
+ "sha256": "ac7b71a9dfd550d78355de9e8b758173a5917ecdb1567599a33b16e9d112bc62"
1611
1616
  },
1612
1617
  {
1613
1618
  "path": "docs/recipes/managed-settings-remote-control.md",
@@ -1664,6 +1669,11 @@
1664
1669
  "mode": "0000644",
1665
1670
  "sha256": "a44bb6dcf12bac7e104eab82862e8c0a690376652466c898cd1ab6f8e0e9c270"
1666
1671
  },
1672
+ {
1673
+ "path": "docs/recipes/server-bring-up.md",
1674
+ "mode": "0000644",
1675
+ "sha256": "e5f9e1a9f2337e80ed4ff94d60c96941f50ff30339a35585458dfd8de6e66c5f"
1676
+ },
1667
1677
  {
1668
1678
  "path": "docs/recipes/ship-and-regrade.md",
1669
1679
  "mode": "0000644",
@@ -4132,12 +4142,12 @@
4132
4142
  {
4133
4143
  "path": "package-lock.json",
4134
4144
  "mode": "0000644",
4135
- "sha256": "0d50d0845267723058ce6795117975622643fe75f7fc8314bcc1e47a5660fd7e"
4145
+ "sha256": "c83bac76021e0d2fae2092fabdb4cd4614af07caf2dab896b07ff1ecbcdd1409"
4136
4146
  },
4137
4147
  {
4138
4148
  "path": "package.json",
4139
4149
  "mode": "0000644",
4140
- "sha256": "fb9adb2ceb26de87deae215aa38c85b6408a5d7b8555a090482357044bd938eb"
4150
+ "sha256": "3d78882106612f2950816b2c7849605ae097a8a8b41fda1b6e1dc0e9d056bc96"
4141
4151
  },
4142
4152
  {
4143
4153
  "path": "public-docs/index.html",
@@ -4377,7 +4387,7 @@
4377
4387
  {
4378
4388
  "path": "public-landing/assets/cosmos.css",
4379
4389
  "mode": "0000644",
4380
- "sha256": "5f39814ab40306bc3fc5d4160119b85e2134f778dd62a7a6e6183e36a3ed193e"
4390
+ "sha256": "1fdc2c4e3d58ae089a71bade6aa3f5f3259447b243eacdc75b8a91797ea065e7"
4381
4391
  },
4382
4392
  {
4383
4393
  "path": "public-landing/buddha.png",
@@ -4397,7 +4407,7 @@
4397
4407
  {
4398
4408
  "path": "public-landing/projects.html",
4399
4409
  "mode": "0000644",
4400
- "sha256": "5fd5fe20f1cb0186fd4ae69a7124a3305703952c7ac9662421793449b7ba8e26"
4410
+ "sha256": "081e903fcb943e4facda8e83c9247a31602bf7750d5d7740353c97a4ccbcf1e2"
4401
4411
  },
4402
4412
  {
4403
4413
  "path": "public-landing/world/assets/clouds/cloud-island-large.png",
@@ -4697,12 +4707,12 @@
4697
4707
  {
4698
4708
  "path": "scripts/gds/dev-lib.js",
4699
4709
  "mode": "0000644",
4700
- "sha256": "b0978faa2f77b123eee002f649b11f139cea948ce8189d20b07d9cf73011ce37"
4710
+ "sha256": "b6c3cf99b050644088507fd5365ced7db5a1c05178ce850e3d28ac7fab9d9476"
4701
4711
  },
4702
4712
  {
4703
4713
  "path": "scripts/gds/dev.js",
4704
4714
  "mode": "0000644",
4705
- "sha256": "7600e581106b99535b055898f965693554813e8112eb701c4a018627fa867925"
4715
+ "sha256": "79effdf07519d26153f6fe846fd052cd4a37fa1e25b6b1a278146c9d8bcf6766"
4706
4716
  },
4707
4717
  {
4708
4718
  "path": "scripts/gds/diagnose-achievements.js",
@@ -5707,7 +5717,7 @@
5707
5717
  {
5708
5718
  "path": "src/module-api.js",
5709
5719
  "mode": "0000644",
5710
- "sha256": "4ac4d646de8c683999e2767139efbcee06b0f05d5435707e281bca8498081aa8"
5720
+ "sha256": "36d78bc9f5db096873e060769bd9b9fd24b65d5a2141e65e57adfdd30df81342"
5711
5721
  },
5712
5722
  {
5713
5723
  "path": "src/module-loader/catalog.js",
@@ -5972,7 +5982,7 @@
5972
5982
  {
5973
5983
  "path": "tests/bongos_cli.mjs",
5974
5984
  "mode": "0000644",
5975
- "sha256": "e017ee497f5aaf041cf18d8bc2b12faed4e3cd71418e14d55eee630ebb4282d3"
5985
+ "sha256": "20d7d86da8e03ede2004bdc3dda589670977fca0240d56cd3677fc08fbb2de03"
5976
5986
  },
5977
5987
  {
5978
5988
  "path": "tests/bongos_dispatch.mjs",
@@ -6302,7 +6312,7 @@
6302
6312
  {
6303
6313
  "path": "tests/dev_lib.mjs",
6304
6314
  "mode": "0000644",
6305
- "sha256": "eaa112b0b6765a01ec18dfee3bad7c6f6d16e59c0b8c497b82fab0de3a2f2d20"
6315
+ "sha256": "a4611e9a8aa7ff2e1dbd1c91e84496e55cd6b07ece2f73bd62e152c118b94348"
6306
6316
  },
6307
6317
  {
6308
6318
  "path": "tests/devbox_artifact_gate.mjs",
@@ -7227,7 +7237,7 @@
7227
7237
  {
7228
7238
  "path": "tests/projects_hub.mjs",
7229
7239
  "mode": "0000644",
7230
- "sha256": "9495982aa30c4e845ab879a571be1d6753e39c3f1e3b2629ae9b41feeaf471cc"
7240
+ "sha256": "6a59da0cfbf5d2be741ed26f3350a39d49a64b6f1dffb89f69666af7ba938dd1"
7231
7241
  },
7232
7242
  {
7233
7243
  "path": "tests/provision.mjs",
package/bin/bongos.js CHANGED
@@ -91,6 +91,9 @@ const VERBS = {
91
91
 
92
92
  init: { script: 'init.js', group: 'instance', summary: 'Scaffold a new instance (config + first version + kickoff tasks)' },
93
93
  dev: { script: 'dev.js', group: 'instance', summary: 'Run THIS instance locally in one command (Postgres + migrate + server + sign-in)', selfHelp: true },
94
+ // Same script, same boot path, --server pinned on: a real host supervises with
95
+ // systemd, binds per the public origin, and never opens a browser (task 1002398).
96
+ serve: { script: 'dev.js', prefixArgs: ['--server'], group: 'instance', summary: 'Run THIS instance on a REAL HOST (systemd service + health check)', selfHelp: true },
94
97
  onboard: { script: 'onboard.js', group: 'instance', summary: 'Guided zero-to-live walkthrough: init → scaffold → provision → OAuth → verify → brand' },
95
98
  module: { script: 'module.js', group: 'instance', summary: 'Scaffold a new module (bongos module new <key>)', selfHelp: true },
96
99
  upgrade: { script: 'upgrade.js', group: 'instance', summary: 'Pull the core to a new version (bump pin→install→migrate→materialize→restart); bare = module-compat pre-check', selfHelp: true },
@@ -0,0 +1,52 @@
1
+ # 0169 — `bongos serve`: the `bongos dev` boot path, on a real host
2
+
3
+ - **Status:** Accepted
4
+ - **Date:** 2026-08-13
5
+ - **Task:** [#1002398](https://cloudbongos.com/builders#/task/1002398) (goal 1000052 — first-run onboarding), depends on [#1002397](https://cloudbongos.com/builders#/task/1002397).
6
+ - **Extends:** [ADR 0149](<redacted>.md) (`bongos dev`, the local launcher). Sibling of [ADR 0108 §4](<redacted>.md) / [`instance-deploy.md`](../recipes/instance-deploy.md) (the *pinned-core consumer* deploy) and `provision.js` (the *control-plane* cloud path).
7
+
8
+ ## Context
9
+
10
+ ADR 0149 shipped `bongos dev`: one idempotent command that stands an instance up **on the owner's laptop**. The obvious next question — "great, now put it on a server" — had no equally good answer:
11
+
12
+ | Path to a real host | Why it didn't close the gap |
13
+ |---|---|
14
+ | Docker ([`self-host.md`](../recipes/self-host.md)) | serves the **vanilla** brand on `:3000`; matching the repo's brand + port needs a compose override |
15
+ | `bongos onboard` → `provision.js` | provisions a **whole droplet** through the control plane — needs a public domain plus DigitalOcean/Cloudflare tokens |
16
+ | [`instance-deploy.md`](../recipes/instance-deploy.md) | describes the right shape, but the systemd unit is a **hand-written prose step** ("create a systemd unit for the instance…") — no script in the core writes one |
17
+
18
+ So an owner with a plain VPS — the most ordinary hosting story there is — was back to hand-assembly, and hand-assembly is where ADR 0149's three silent mismatches (port, DB name, socket) came from in the first place.
19
+
20
+ The boot path itself is *already right* for a server: deps → Postgres → createdb → migrate → env → server → health is exactly what a host needs. Only four **decisions inside it** are laptop-shaped.
21
+
22
+ ## Decision
23
+
24
+ Ship **`bongos serve`** — the same script, the same six steps, with `--server` pinned on (`bongos dev --server` is the same thing). Not a second boot path: a **mode** on the existing one, so the two can't drift.
25
+
26
+ Four decisions change, each derived rather than asked:
27
+
28
+ 1. **Supervision — a systemd unit, not a pid file.** The laptop's detached-with-a-pid-file process is right for a laptop and wrong for a host: it dies on reboot, has no restart policy, and logs to `/tmp`. Server mode writes `/etc/systemd/system/bongos-<db>.service` (`Restart=always`, `WantedBy=multi-user.target`), `daemon-reload`s, and `enable --now`s it — so the host's own supervisor owns the process. Where systemd is absent (a container, a BSD, an operator without root) it **falls back** to the pid-managed process with a loud warning rather than refusing.
29
+ 2. **Bind address — derived from the origin.** `https://…` means something already terminates TLS in front of us, so bind `127.0.0.1`; plain `http://` on a public hostname means nothing is in front yet, so bind `0.0.0.0`. `--bind` (or `HOST`) still wins. An operator never has to reason about it, and the common misconfiguration — an app bound to `0.0.0.0` behind a proxy, publicly reachable on its raw port — can't happen by default.
30
+ 3. **A fresh Postgres cluster gets bootstrapped.** `apt-get install postgresql` creates exactly one role — `postgres`, peer-auth only — so a deploy user's `createdb` fails with `role "deploy" does not exist`. That reads like a broken install and is really an unbootstrapped cluster. On that specific error we create the login role + its database as the postgres superuser (`sudo -u postgres`, or `su postgres -c` for root on a sudo-less image). This is the fresh-Debian wall, and it is the one step most likely to end a first server run.
31
+ 4. **Health means the *public* origin, not loopback.** The loopback probe proves the app; on a host the interesting failures are DNS, the proxy, and TLS — all of which sit outside it. Server mode probes `publicOrigin/healthz` too and, when it can't be reached, says which layer is missing. It is **advisory**: DNS legitimately may not be wired yet, so this warns and still exits 0.
32
+
33
+ Plus: no auto-login (a host has no browser — it prints `bongos login <origin>` instead).
34
+
35
+ **Secrets go to an `EnvironmentFile`, never `Environment=`.** The unit carries only the eight keys the boot path resolves; `PGPASSWORD` and the GitHub OAuth creds go to a root-owned `0600` `/etc/bongos/<service>.env`, created once and never overwritten. Two reasons: `systemctl show` exposes `Environment=` to any user on the box, and the laptop path's `buildEnv()` spreads the *caller's whole environment* — baking a builder's shell (tokens included) into a root-owned file. `unitEnvironment()` is a deliberately separate, allowlisted function for exactly that reason.
36
+
37
+ As with ADR 0149, every decision above is a **pure, injectable function** in `dev-lib.js` (`deriveBindHost`, `systemdPlan`, `pgStartPlan`, `pgBootstrapPlan`, `renderSystemdUnit`, `unitEnvironment`, `parseSub`); `dev.js` only executes them. `tests/dev_lib.mjs` proves all of it with no host, no spawn, and no network.
38
+
39
+ ## Alternatives considered
40
+
41
+ - **A separate `deploy.sh`** — would have duplicated the boot path, which is precisely how the laptop and server paths would drift. Rejected: the whole value is that it's the *same* six steps.
42
+ - **Docker on the host** — real, and still the right answer for a container platform, but it makes the instance's own brand/port a compose-override exercise and puts a container runtime between an owner and a $5 VPS. Kept as the parallel path.
43
+ - **Always bind `0.0.0.0`** — simpler to explain, but publishes the raw app port on every host that has a proxy in front. Rejected on that alone.
44
+ - **Refuse without systemd** — cleaner contract, but would fail on containers and BSDs where the pid-managed fallback works fine. Rejected in favour of a warning.
45
+
46
+ ## Consequences
47
+
48
+ - A plain VPS goes from `git clone` to a supervised, reboot-surviving instance with `npm install` then `bongos serve` — the same two commands as the laptop.
49
+ - `bongos serve stop` **stops and disables** the unit, so "stopped" survives a reboot; `bongos serve` re-enables it. Asymmetric with `bongos dev stop` (which only kills a process) because on a host the two verbs mean different things.
50
+ - The unit is a *starting point*, not a hardened production profile — no `ProtectSystem`, no resource limits, no log shipping. An operator with opinions should edit it; a re-run rewrites it, so persistent customisation belongs in a systemd drop-in (`/etc/systemd/system/bongos-<db>.service.d/`).
51
+ - TLS/proxy/DNS stay **out of scope** — deliberately. Those are the host's business and vary per operator; `bongos serve` tells you when they're the missing piece and points at [`server-bring-up.md`](../recipes/server-bring-up.md).
52
+ - Windows is still not a native path (migrations need `bash`), unchanged from ADR 0149.
@@ -242,5 +242,6 @@ This keeps the decision history honest and traceable.
242
242
  | 0167 | [**The module catalog: `source` (which tree ships it) and `provenance` (who built it) are two axes** ([task 1001772](https://cloudbongos.com/builders#/task/1001772) — the last task of goal 1000029). Closes [ADR 0107](<redacted>.md) §2/§3: a browsable catalog of enable-able core modules, each with author + origin credit, in the hall AND via `bongos module list --catalog`. The bug it fixes: the Modules tab derived its own/core-loaded split from the manifest's `origin` alone, but attribution is OPTIONAL and core's own modules declare none (nothing upstreamed them) — so on a split instance every core module read as *built here* and the catalog you could opt into read as empty. One field was answering two questions. Fix: a shared model (`src/module-loader/catalog.js`) that both the hall route and the CLI build from, carrying `source` — read STRUCTURALLY from the loader's discovery root ([ADR 0108](<redacted>.md) §1: core package vs instance tree), falling back on coincident roots to whether `package.json` names the core — beside the unchanged `provenance` credit axis. Plus `credit: {author, origin, inferred}`, which fills a core module's blank attribution with the platform name and MARKS it inferred (`*` in the CLI, a hoverable `<abbr>` in the hall) so it never reads as a claim the author typed. The tab now sections by `source` ("Module catalog" / "Built by this project"), gains a search box, and draws "Submit for review" from instance-source modules only. Rejected: stamping author/origin into 15 core manifests (repetitive, and module 16 forgets), overloading `origin` (destroys the credit axis), inferring from `default:true` (§6 makes accepted catalog modules default-OFF — exactly backwards), a DB catalog table (premature; the manifests already are the catalog).](<redacted>.md) | modules / upstreaming |
243
243
  | 0168 | [**The scaffold vendors the hub's RUNNING core (`npm pack` of the installed package); registry-default deferred to the go-public step** ([task 1002706](https://cloudbongos.com/builders#/task/1002706); gap 13 of the project-creation audit). Two collided facts: children were born pinned to a frozen `file:vendor/bongos-core-1.19.2.tgz` (installedCoreTarball reuses the hub's own file: pin — task 1002275 — and the hub's pin sat at 1.19.2), and the task-1002621 registry cutover (hub pin now `"@bongos/core": "1.19.56"`, verified on the box) made installedCoreTarball return null — dropping the scaffold into the package-core.js source rebuild that ABORTS on a consumer instance (empty core set → empty child repo, the 1002275 failure resurrected). Fix: a three-rung ladder — file:-pin reuse → NEW `installedCorePackDir()` + `npm pack` of the hub's installed core (child pins the SAME, CURRENT core, stays file:-pinned, owner needs no token) → source rebuild. The brief's "make registry the default now that the package is public" was verified FALSE (both npm scopes 404 anonymously; ADR 0134 keeps them private until go-public) — a registry-default child would be born UNINSTALLABLE for a tokenless owner. Registry-default is deferred to the go-public step (idea 1000700); the gap-13 "flip the box env" ops leg is explicitly a no-op until then; child freshness rides this ladder + the hub-pin lane (task 1002708). Rejected: registry-default now, dropping NPM_TOKEN, vendoring from the npm cacache.](<redacted>.md) | provisioning / distribution |
244
244
  | 0167 | [**The /create gate trust link defaults to the AGPL-3.0 license text, not the npm package page** ([task 1002705](https://cloudbongos.com/builders#/task/1002705); gap 1 of the project-creation audit). The gate's "open source" link pointed at a nonexistent repo (404); the task brief's suggested default — the npm package page — is ALSO dead for the gate's actual audience (signed-out visitors): `@cloudbongos/core` and `@bongos/core` both 404 anonymously (private scopes, ADR 0134), and the ADR 0099 public mirror stays dormant until ~2026-11. The license text is the one live, instance-neutral artifact behind the claim today. The browsable-code upgrade is the owner's filed choice (idea 1000700): public repo now (forfeits the ADR 0065 §4 redaction window) / public npm package / wait for the mirror. Rejected: the npm link (verified dead), flipping the repo public (not a builder's call), plain unlinked text (less information, same cost).](<redacted>.md) | public landing / governance |
245
+ | 0169 | [**`bongos serve`: the `bongos dev` boot path, on a real host** ([task 1002398](https://cloudbongos.com/builders#/task/1002398), goal 1000052). [ADR 0149](<redacted>.md) gave an owner one command to stand an instance up on their laptop; "now put it on a server" had no equally good answer — Docker serves the *vanilla* brand on `:3000`, `bongos onboard` provisions a whole droplet through the control plane, and [`instance-deploy.md`](../recipes/instance-deploy.md) left the systemd unit as a hand-written prose step. The six steps (deps → Postgres → createdb → migrate → env → server → health) were already right for a host; only four *decisions inside them* were laptop-shaped. So: a `--server` **mode** on the same script (`bongos serve`), never a second boot path, so the two can't drift. (1) **Supervision** — a systemd unit (`Restart=always`, `WantedBy=multi-user.target`) instead of a pid file that dies on reboot, falling back to the pid-managed process where systemd is absent rather than refusing. (2) **Bind address, derived from the origin** — `https://…` means a proxy terminates TLS, so bind `127.0.0.1`; plain `http://` on a public host means nothing is in front, so bind `0.0.0.0`. The common misconfiguration (app publicly reachable on its raw port behind a proxy) becomes impossible by default. (3) **Peer-auth bootstrap** — a fresh `apt-get install postgresql` cluster has only the `postgres` role, so a deploy user's `createdb` fails with `role "deploy" does not exist`; that reads like a broken install and is really an unbootstrapped cluster, so we create the login role as the superuser. (4) **Health means the PUBLIC origin** — the loopback probe proves the app; DNS/proxy/TLS are where a host actually fails, so it probes the advertised origin too and names the missing layer (advisory — DNS may legitimately not be wired). Secrets go to a root-owned `0600` `EnvironmentFile`, never `Environment=` (`systemctl show` leaks those, and the laptop path's `buildEnv()` spreads the caller's whole shell). Every decision is a pure injectable in `dev-lib.js`, proved in `tests/dev_lib.mjs` with no host. Rejected: a separate `deploy.sh` (drift — the point is the *same* six steps), Docker-on-the-host (makes brand+port a compose override), always binding `0.0.0.0` (exposes the raw port), refusing without systemd (breaks containers/BSD).](<redacted>.md) | onboarding / deploy |
245
246
 
246
247
  > ⚠️ **Numbering collision (known):** two ADRs share `0008`. The numbers were assigned in parallel sessions before the lock could be enforced. The files are kept under their existing filenames to preserve git history, but the table disambiguates them as `0008-a` / `0008-b`. New ADRs should resume at `0040` to avoid further collisions. (`0024` is also a real on-disk duplicate: the cloneable-repo/local-first-memory ADR and the multi-agent-system-architecture ADR were both numbered `0024` in parallel; both keep their filenames, disambiguated as `0024-a` / `0024-b` like `0008`. Likewise `0025` ended up with two on-disk files — the offsite-backup ADR and the structured-criterion↔task-link ADR — both kept under their existing filenames and disambiguated as `0025-a` / `0025-b`; separately, the BFG ADR had also claimed `0025` on 2026-05-30 and yielded to `0026`. A third happened 2026-06-06: `0036` was claimed in parallel by the builders-subdomain ADR ([#726](https://example.com/builders#/task/726)) and this Discord-channels ADR ([#727](https://example.com/builders#/task/727)); the Discord-channels ADR yielded to `0037`. A fourth landed 2026-06-10: `0044` was claimed in parallel by the palette-replacement ADR and the per-box game-preview ADR ([#897](https://example.com/builders#/task/897)); both keep their filenames, disambiguated as `0044-a` / `0044-b` like `0008`. A fifth was caught pre-land 2026-06-13: `0050` was claimed in parallel by the device-flow-admission ADR ([#1000](https://example.com/builders#/task/1000)) and the sandbox-for-everyone ADR ([#1001](https://example.com/builders#/task/1001)); the latter yielded to `0052` before merging, so there is no `0050` duplicate. A sixth was caught pre-land 2026-06-21: the tool-agnostic-design-layer ADR ([#1375](https://example.com/builders#/task/1375)) first collided at `0079` with the value-gated-autonomous-engagement ADR, then at `0080` with the llm-cache-api-transport ADR (both part of the 0075–0080 LLM-cost-reduction series, landed on `main` in parallel); it yielded to `0081` before merging, so there is no `0079`/`0080` design-layer duplicate. A seventh landed 2026-06-23: `0084` was claimed in parallel by the auto-derived-per-task-rank-floor ADR and the remove-default-spend-cap ADR (task 1499); the rank-floor ADR landed on `main` first, so the spend-cap ADR yielded to `0085`, leaving no `0084` duplicate. An eighth landed 2026-06-27: `0095` was claimed in parallel by the borrowed-memory-and-retrieval-concepts ADR (PR #459, landed first) and the cross-agent-context-management ADR ([#1651](https://example.com/builders#/task/1651), PR #460); both keep their filenames, disambiguated as `0095-a` / `0095-b` like `0008`. These two are companions — `0095-a` adopts the memory/retrieval concepts and explicitly defers the grep-vs-semantic question to `0095-b`.)
@@ -33,5 +33,6 @@
33
33
 
34
34
  1. Install the script as the droplet's `~/deploy.sh` (the CI deploy key is forced-command-locked to `~/deploy.sh` — ADR 0043 — so it stays hand-installed, like `deploy-prod.sh`).
35
35
  2. Create a systemd unit for the instance: `ExecStart=/usr/bin/node <instanceRoot>/node_modules/@bongos/core/src/platform-server.js`, `WorkingDirectory=<instanceRoot>`, `Environment=PORT=… HOST=127.0.0.1 <PREFIX>_INSTANCE_ROOT=<instanceRoot>`.
36
+ **`bongos serve` now writes exactly this unit for you** (plus the DB/migrate/health legs) — see [`server-bring-up.md`](server-bring-up.md) / [ADR 0169](../adr/<redacted>.md). Step 4 of the flow above ("restart the platform") is `systemctl restart bongos-<db>` once it has.
36
37
  3. Set `INSTANCE_SERVICE=<unit>` + `PORT=<port>` in the deploy environment.
37
38
  4. Front it with TLS and point the origins at the real domain (see [self-host.md](self-host.md)).
@@ -79,3 +79,6 @@ the Client ID + secret to `~/.config/<configDir>/gds-github.json` as
79
79
  servers don't collide.
80
80
  - **Prefer the cloud?** `bongos box` + `bongos code` give you a remote dev box instead — no
81
81
  local database or server to run.
82
+ - **Ready for a real host?** `bongos serve` runs this *same* boot path on a server —
83
+ systemd-supervised, bind address derived from your public origin, no browser needed. See
84
+ [`server-bring-up.md`](server-bring-up.md) ([ADR 0169](../adr/<redacted>.md)).
@@ -0,0 +1,112 @@
1
+ # Recipe — putting your instance on a real host (`bongos serve`)
2
+
3
+ > [ADR 0169](../adr/<redacted>.md), task [#1002398](https://cloudbongos.com/builders#/task/1002398). The server half of [`local-dev.md`](local-dev.md) / [ADR 0149](../adr/<redacted>.md): the **same** boot path `bongos dev` runs on your laptop, on a VPS.
4
+
5
+ ## The short version
6
+
7
+ On the host, in your instance's checkout:
8
+
9
+ ```bash
10
+ npm install
11
+ bongos serve
12
+ ```
13
+
14
+ That's it. It is idempotent — re-run it after every `git pull`.
15
+
16
+ ## What you need first
17
+
18
+ - A Linux host (Debian/Ubuntu assumed below) with **Node ≥ 22**, **Postgres**, and **bash**.
19
+ - Your instance repo cloned on it, with `config/branding.json` pointing `domains.publicOrigin` at the origin you'll actually serve.
20
+ - Either root, or a user with **passwordless sudo** — `bongos serve` uses `sudo -n` so it fails fast instead of hanging a deploy on a hidden password prompt. Without it, run `sudo bongos serve`.
21
+
22
+ ```bash
23
+ sudo apt-get install -y postgresql # provides psql/createdb/pg_isready too
24
+ ```
25
+
26
+ ## What it does
27
+
28
+ The six steps from `bongos dev`, with the four host-shaped decisions made for you (ADR 0169):
29
+
30
+ 1. **Dependencies** — `npm ci` (or `npm install`) if this checkout has none.
31
+ 2. **Postgres running** — `systemctl start postgresql`, falling back to `service` then `pg_ctl`, then polls until it accepts connections.
32
+ 3. **Database + role** — creates the DB, and on a fresh cluster creates your **login role** first. See the peer-auth note below; this is the step most first server runs die on.
33
+ 4. **Migrations** — the core `migrate.sh`, with `PGDATABASE` exported for both it and the server (the ADR 0149 mismatch).
34
+ 5. **The server, supervised by systemd** — writes `/etc/systemd/system/bongos-<db>.service`, enables it (survives reboot) and starts it (`Restart=always`).
35
+ 6. **Health** — on the bind address, *and* on your public origin.
36
+
37
+ ## Where things land
38
+
39
+ | Thing | Path |
40
+ |---|---|
41
+ | systemd unit | `/etc/systemd/system/bongos-<db>.service` |
42
+ | secrets (0600, root-owned) | `/etc/bongos/bongos-<db>.env` |
43
+ | logs | `journalctl -u bongos-<db> -f` |
44
+
45
+ ```bash
46
+ bongos serve status # service state, health, and whether the public origin answers
47
+ bongos serve stop # stop AND disable (stays down across a reboot)
48
+ bongos serve # re-enables and restarts
49
+ ```
50
+
51
+ ## The bind address is derived — don't fight it
52
+
53
+ `bongos serve` reads `domains.publicOrigin` and picks:
54
+
55
+ | Your `publicOrigin` | It binds | Because |
56
+ |---|---|---|
57
+ | `https://app.example.com` | `127.0.0.1` | something terminates TLS in front — the proxy reaches you on loopback |
58
+ | `http://app.example.com:3006` | `0.0.0.0` | nothing is in front yet — you're serving it directly |
59
+ | `http://localhost:3006` | `127.0.0.1` | a local-shaped origin |
60
+
61
+ Override with `--bind <addr>` if your topology is unusual. **The `https` case is the one to get right**: set your origin to `https://…` *before* the first `bongos serve`, and the app is never publicly exposed on its raw port.
62
+
63
+ ## The fresh-Postgres wall (peer auth)
64
+
65
+ A brand-new `apt-get install postgresql` cluster has exactly **one** role — `postgres` — reachable only by peer auth as the `postgres` OS user. So your deploy user's `createdb` fails with:
66
+
67
+ ```
68
+ createdb: error: ... role "deploy" does not exist
69
+ ```
70
+
71
+ That reads like a broken Postgres and is really an unbootstrapped one. `bongos serve` detects this specific failure and fixes it — `CREATE ROLE "deploy" LOGIN CREATEDB` plus the database, run as the postgres superuser. If it can't (no root, no sudo), it says so; the manual equivalent is:
72
+
73
+ ```bash
74
+ sudo -u postgres psql -c 'CREATE ROLE "deploy" LOGIN CREATEDB'
75
+ sudo -u postgres createdb -O deploy <dbname>
76
+ ```
77
+
78
+ ## Sign-in on a host
79
+
80
+ There's no browser on a server, so `bongos serve` never auto-signs-in. It prints the command to run **from your own machine**:
81
+
82
+ ```bash
83
+ bongos login https://app.example.com
84
+ ```
85
+
86
+ If sign-in isn't configured yet, it walks you through the GitHub OAuth app and tells you to put the credentials in `/etc/bongos/bongos-<db>.env` (not `~/.config` — the service runs as its own user, often with an empty home), then `systemctl restart bongos-<db>`.
87
+
88
+ ## "The app is healthy but my origin isn't answering"
89
+
90
+ Expected, and `bongos serve` says which layer is missing. It proves the app on the bind address; DNS, the reverse proxy, and TLS are yours:
91
+
92
+ - **Bound to `127.0.0.1`** — you need a proxy forwarding your origin to `127.0.0.1:<port>`. Any of Caddy (easiest — automatic TLS), nginx + certbot, or Cloudflare Tunnel.
93
+ - **Bound to `0.0.0.0`** — you need DNS pointing at this host and the port open in the firewall (`ufw allow <port>`).
94
+
95
+ ## Customising the unit
96
+
97
+ A re-run **rewrites** the unit, so don't hand-edit it. Use a systemd drop-in, which survives:
98
+
99
+ ```bash
100
+ sudo systemctl edit bongos-<db> # writes /etc/systemd/system/bongos-<db>.service.d/override.conf
101
+ ```
102
+
103
+ The shipped unit is a sound starting point, not a hardened production profile — no `ProtectSystem`, no resource limits, no log shipping. Add what your threat model wants there.
104
+
105
+ ## Relation to the other deploy paths
106
+
107
+ | Path | When |
108
+ |---|---|
109
+ | **`bongos serve`** (this) | you have a host and want your instance on it |
110
+ | [`instance-deploy.md`](instance-deploy.md) | a consumer instance on a **pinned core** — `git pull` → `npm ci` → migrate → restart (this is the restart it's describing) |
111
+ | [`self-host.md`](self-host.md) | Docker/container platforms |
112
+ | `bongos onboard` | you have **no** host yet and want the control plane to provision one |
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.19.100",
3
+ "version": "1.19.102",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.19.100",
9
+ "version": "1.19.102",
10
10
  "license": "AGPL-3.0-or-later",
11
11
  "dependencies": {
12
12
  "express": "^4.21.2",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.19.100",
3
+ "version": "1.19.102",
4
4
  "description": "Cloud Bongos — the AI-first build platform core (GDS + platform surfaces + module system), installed as a versioned dependency (ADR 0108).",
5
5
  "license": "AGPL-3.0-or-later",
6
6
  "main": "src/platform-server.js",
@@ -540,14 +540,10 @@ p.sec-p{font-size:var(--fs-lede);font-weight:500;color:var(--ink-soft);line-heig
540
540
  box-shadow:0 0 0 3px color-mix(in srgb,var(--acc) 22%,transparent);}
541
541
  .tbl .ptitle{font-family:var(--font-display);font-weight:700;font-size:15px;color:var(--ink);}
542
542
  .tbl .ptag{font-size:12.5px;color:var(--ink-faint);font-weight:600;}
543
- .tbl .num{font-family:var(--font-mono);font-weight:500;}
544
543
  .tbl .pill{display:inline-block;font-family:var(--font-mono);font-size:11px;padding:3px 10px;
545
544
  border-radius:999px;background:var(--accent-soft);color:var(--accent-deep);}
546
545
  :root[data-mode="dark"] .tbl .pill{color:var(--sun-lit);}
547
546
  @media (prefers-color-scheme: dark){:root:not([data-mode="light"]) .tbl .pill{color:var(--sun-lit);}}
548
- .tbl .tags{display:flex;gap:6px;flex-wrap:wrap;}
549
- .tbl .tags span{font-family:var(--font-mono);font-size:10.5px;padding:2px 8px;border-radius:999px;
550
- border:1px solid var(--line);color:var(--ink-faint);}
551
547
  .tbl td.right{text-align:right;}
552
548
  /* Nothing in an action or status cell may wrap: "Manage →" breaking after the
553
549
  space and the chip's recency dropping to a second line both read as a broken
@@ -557,7 +553,6 @@ p.sec-p{font-size:var(--fs-lede);font-weight:500;color:var(--ink-soft);line-heig
557
553
  .tbl .colStatus{width:1%;white-space:nowrap;}
558
554
  .tbl .colGo{width:1%;white-space:nowrap;padding-left:8px;}
559
555
  .tbl .colName{width:auto;}
560
- @media (max-width:780px){.tbl .colTags,.tbl .colType{display:none;}}
561
556
 
562
557
  /* ── the project card ──
563
558
  An overlay INSIDE the map, not a destination: opening a project must not cost
@@ -182,9 +182,6 @@ try{var _m=localStorage.getItem('cb-color-mode');if(_m==='light'||_m==='dark')do
182
182
  <thead>
183
183
  <tr>
184
184
  <th class="colName" data-sort="name" aria-sort="ascending">Project</th>
185
- <th class="right" data-sort="builders">Builders</th>
186
- <th class="colType">Type</th>
187
- <th class="colTags">Tags</th>
188
185
  <th class="right colStatus">Status</th>
189
186
  <th class="colGo"></th>
190
187
  </tr>
@@ -199,9 +196,6 @@ try{var _m=localStorage.getItem('cb-color-mode');if(_m==='light'||_m==='dark')do
199
196
  <td class="colName"><span class="pname"><span class="pdot" style="--acc:var(--accent)"></span>
200
197
  <span><span class="ptitle" data-brand-product-inline>Cloud Bongos</span><span id="selfPill"></span><br>
201
198
  <span class="ptag">this platform</span></span></span></td>
202
- <td class="right num">—</td>
203
- <td class="colType">—</td>
204
- <td class="colTags">—</td>
205
199
  <td class="right colStatus"><span class="chip calm"><span class="cdot"></span>—</span></td>
206
200
  <td class="colGo"><a class="btn small ghost" href="#" data-self-open>Open →</a></td>
207
201
  </tr>
@@ -628,11 +622,6 @@ try{var _m=localStorage.getItem('cb-color-mode');if(_m==='light'||_m==='dark')do
628
622
  accentOf(p) + '"></span>' +
629
623
  '<span><span class="ptitle">' + esc(p.name) + '</span>' + pill + '<br>' +
630
624
  '<span class="ptag">' + esc(p.tagline || '') + '</span></span></span></td>' +
631
- /* the public projection carries no builder count, type or tags — an
632
- em dash is the truthful cell, an invented number is not */
633
- '<td class="right num">—</td>' +
634
- '<td class="colType">—</td>' +
635
- '<td class="colTags">—</td>' +
636
625
  /* liveness on a catalog row is NOT probed by this surface. A chip that
637
626
  said "live" here would claim more than anything verified (C6); the
638
627
  verification overlay on owned orbs is its own task. */