@bongos/core 1.19.1081 → 1.20.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 +528 -193
- package/.claude/skills/planning-session/SKILL.md +6 -2
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +32 -0
- package/clients/bongos-client/index.cjs +32 -0
- package/clients/bongos-client/index.d.ts +51 -6
- package/clients/bongos-client/index.mjs +32 -0
- package/docs/adr/0099-delayed-redacted-mirror-export.md +5 -1
- package/docs/adr/0111-instance-hosting-provisioning-module.md +1 -0
- package/docs/adr/0120-pay-on-land-and-builder-owned-rebase-gate.md +17 -0
- package/docs/adr/0176-private-repo-deploy-keys.md +2 -0
- package/docs/adr/0310-a-speciality-offers-skills-and-the-adopter-chooses-them.md +1 -1
- package/docs/adr/0348-the-web-tier-may-look-read-only-at-what-an-owners-render-key-can-see.md +53 -0
- package/docs/adr/0349-a-version-preview-is-a-sandboxed-child-the-web-tier-launches.md +106 -0
- package/docs/adr/0350-the-hub-holds-a-per-project-write-deploy-key-so-a-hosted-upgrade-reaches-github.md +103 -0
- package/docs/adr/README.md +3 -0
- package/docs/api/openapi.json +851 -19
- package/docs/api-reference.md +33 -9
- package/docs/architecture.md +6 -2
- package/docs/copy-inventory.md +620 -553
- package/docs/copy-registry.json +1467 -820
- package/docs/file-map.md +2 -0
- package/docs/module-api-changelog.md +8 -0
- package/docs/modules-contract.md +1 -0
- package/docs/page-inventory.json +38 -4
- package/docs/page-readings.json +1666 -1558
- package/migrations/core_260_goals_working_area.sql +63 -0
- package/migrations/core_261_grade_attempts_verdicts.sql +24 -0
- package/migrations/core_262_drop_builder_box_blocked.sql +33 -0
- package/modules/agents/lib/validate.js +43 -0
- package/modules/autonomy/gauge.js +38 -2
- package/modules/grading/grader-subagent.js +37 -2
- package/modules/grading/grader-workers/reader.js +65 -5
- package/modules/hall-ui/public/approval-queue.css +33 -7
- package/modules/hall-ui/public/approval-queue.js +9 -2
- package/modules/hall-ui/public/atlas.html +1 -1
- package/modules/hall-ui/public/blockers.html +1 -1
- package/modules/hall-ui/public/board-room.html +1 -1
- package/modules/hall-ui/public/brand-holes.js +121 -0
- package/modules/hall-ui/public/collab.html +1 -1
- package/modules/hall-ui/public/collab.js +1 -1
- package/modules/hall-ui/public/copy-desk.html +1 -1
- package/modules/hall-ui/public/deploy.html +6 -1
- package/modules/hall-ui/public/deploy.js +22 -8
- package/modules/hall-ui/public/diagrams.html +1 -1
- package/modules/hall-ui/public/dom-utils.js +20 -0
- package/modules/hall-ui/public/drachmae.html +1 -1
- package/modules/hall-ui/public/fleet.html +1 -1
- package/modules/hall-ui/public/gate.html +1 -1
- package/modules/hall-ui/public/goal-inbox.js +1 -1
- package/modules/hall-ui/public/goals.html +1 -1
- package/modules/hall-ui/public/government.html +1 -1
- package/modules/hall-ui/public/idea.html +1 -1
- package/modules/hall-ui/public/idea.js +28 -0
- package/modules/hall-ui/public/ideas.html +1 -1
- package/modules/hall-ui/public/ideas.js +19 -3
- package/modules/hall-ui/public/index.html +5 -1
- package/modules/hall-ui/public/modules.html +1 -1
- package/modules/hall-ui/public/primer.html +1 -1
- package/modules/hall-ui/public/profile-nudge.js +21 -4
- package/modules/hall-ui/public/profile.html +1 -1
- package/modules/hall-ui/public/profile.js +1 -1
- package/modules/hall-ui/public/project-settings.html +1 -1
- package/modules/hall-ui/public/ranks.html +1 -1
- package/modules/hall-ui/public/roadmap.html +1 -1
- package/modules/hall-ui/public/roster.html +1 -1
- package/modules/hall-ui/public/sessions.html +1 -1
- package/modules/hall-ui/public/settings.html +19 -30
- package/modules/hall-ui/public/settings.js +24 -177
- package/modules/hall-ui/public/settings.states.json +1 -1
- package/modules/hall-ui/public/shell.js +4 -2
- package/modules/hall-ui/public/studio.css +14 -8
- package/modules/hall-ui/public/studio.html +5 -4
- package/modules/hall-ui/public/task.html +1 -1
- package/modules/hall-ui/public/thinking.css +6 -3
- package/modules/hall-ui/public/thinking.html +1 -1
- package/modules/hall-ui/public/tweak-editor-lib.js +82 -1
- package/modules/hall-ui/public/tweak-editor.css +142 -11
- package/modules/hall-ui/public/tweak-editor.html +25 -9
- package/modules/hall-ui/public/tweak-editor.js +243 -10
- package/modules/hall-ui/public/watch.html +1 -1
- package/modules/hall-ui/public/work.html +1 -1
- package/modules/hall-ui/records/approval-queue.md +6 -0
- package/modules/hall-ui/records/tweak-editor.md +6 -0
- package/modules/ideas/ratify.js +36 -8
- package/modules/ideas/routes/ratify-goal.js +6 -0
- package/modules/lifecycle/db-analytics.js +79 -10
- package/modules/lifecycle/db-claims.js +48 -1
- package/modules/lifecycle/db-deps-criteria.js +94 -40
- package/modules/lifecycle/db-goals.js +4 -1
- package/modules/lifecycle/db-grade.js +9 -1
- package/modules/lifecycle/db-relevance-flags.js +183 -0
- package/modules/lifecycle/db-versions.js +4 -3
- package/modules/lifecycle/db.js +19 -0
- package/modules/lifecycle/done-when.js +5 -3
- package/modules/lifecycle/est-advisory.js +31 -4
- package/modules/lifecycle/goal-task-relevance-advisory.js +153 -0
- package/modules/lifecycle/goal-task-relevance-judge.js +226 -0
- package/modules/lifecycle/migrations/lifecycle_014_goal_task_relevance_flags.sql +82 -0
- package/modules/lifecycle/module.json +2 -1
- package/modules/lifecycle/routes/claims.js +8 -1
- package/modules/lifecycle/routes/goal-task-relevance.js +72 -0
- package/modules/lifecycle/routes/goals.js +1 -1
- package/modules/lifecycle/routes/tasks.js +27 -24
- package/modules/lifecycle/routes/visuals.js +9 -0
- package/modules/lifecycle/ship-preflight.js +7 -11
- package/modules/lifecycle/task-visuals.js +109 -5
- package/modules/npm-release/module.json +2 -1
- package/modules/npm-release/preview/commands.js +81 -0
- package/modules/npm-release/preview/divert.js +95 -0
- package/modules/npm-release/preview/env.js +78 -0
- package/modules/npm-release/preview/proxy.js +107 -0
- package/modules/npm-release/preview/runtime.js +83 -0
- package/modules/npm-release/preview/supervisor.js +175 -0
- package/modules/npm-release/public/work.js +65 -1
- package/modules/npm-release/routes/preview.js +133 -0
- package/modules/provisioning/app-status.js +83 -0
- package/modules/provisioning/module.json +2 -1
- package/modules/provisioning/pollers/app-liveness.js +110 -0
- package/modules/provisioning/render-lookup.js +68 -0
- package/modules/provisioning/render-standup.js +44 -1
- package/modules/provisioning/routes/render-standup.js +81 -6
- package/modules/provisioning/starter-bundles.js +4 -1
- package/modules/public-landing/public/account-private.states.json +14 -0
- package/modules/public-landing/public/account.html +513 -0
- package/modules/public-landing/public/account.probes.json +37 -0
- package/modules/public-landing/public/account.states.json +16 -0
- package/modules/public-landing/public/index.html +5 -2
- package/modules/public-landing/public/projects.html +661 -18
- package/modules/public-landing/public/projects.states.json +8 -1
- package/modules/render-deploy/deploys.js +203 -0
- package/modules/render-deploy/migrations/render_deploy_001_app.sql +22 -0
- package/modules/render-deploy/module.json +25 -0
- package/modules/render-deploy/public/deploy.css +10 -0
- package/modules/render-deploy/public/deploy.js +249 -0
- package/modules/render-deploy/render.js +56 -0
- package/modules/render-deploy/routes/act.js +88 -0
- package/modules/render-deploy/routes/app.js +65 -0
- package/modules/render-deploy/routes/history.js +38 -0
- package/modules/specialities/db.js +27 -8
- package/modules/specialities/migrations/specialities_003_skills.sql +46 -0
- package/modules/specialities/routes/specialities.js +51 -3
- package/modules/specialities/skills.js +94 -0
- package/modules/specialities/specialities.js +56 -9
- package/modules/ui-design/kit/fixtures/me-cross-project-private.json +12 -0
- package/modules/ui-design/kit/fixtures/me__cross-project.json +15 -0
- package/modules/ui-design/kit/fixtures/provisioning-instance-render.json +39 -0
- package/modules/ui-design/kit/lib.js +3 -1
- package/modules/ui-design/kit/serve.js +96 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +169 -0
- package/scripts/gds/agents-sync.js +13 -3
- package/scripts/gds/autobongos-run.js +80 -2
- package/scripts/gds/autobongos-service.cmd +12 -0
- package/scripts/gds/copy-apply.js +14 -0
- package/scripts/gds/dev-box-guard.js +3 -2
- package/scripts/gds/fitness.js +9 -0
- package/scripts/gds/grade-correlation-audit.js +42 -12
- package/scripts/gds/grade-replay.js +1 -1
- package/scripts/gds/provision-core-upgrade.js +38 -7
- package/scripts/gds/provision-pin-key.js +176 -0
- package/scripts/gds/provision-render.js +62 -31
- package/scripts/gds/provision-teardown.js +7 -2
- package/scripts/gds/provision-units.js +31 -0
- package/scripts/gds/provision.js +10 -10
- package/scripts/gds/publish-manifest.js +1 -0
- package/scripts/gds/render-api.js +19 -0
- package/scripts/gds/render-payload.js +39 -65
- package/scripts/gds/run-unit-tests.js +10 -0
- package/scripts/gds/ship-finish.js +19 -20
- package/scripts/gds/smoke-dependencies.sh +37 -8
- package/scripts/gds/status.js +12 -4
- package/scripts/gds/upgrade.js +2 -2
- package/scripts/public-mirror-export.js +7 -1
- package/src/bongos/module-scope-map.js +13 -0
- package/src/bongos/serve-internal.js +3 -1
- package/src/module-api.js +10 -1
- package/src/platform-server.js +39 -0
- package/tests/account_privacy_flags.mjs +10 -6
- package/tests/agents_authoring.mjs +15 -1
- package/tests/agents_sync.mjs +106 -5
- package/tests/agents_validate.mjs +45 -0
- package/tests/api_path_404.mjs +6 -0
- package/tests/autobongos_cadence.mjs +7 -0
- package/tests/autobongos_loop.mjs +136 -1
- package/tests/autonomy_gauge.mjs +62 -0
- package/tests/blocker_hall_live_proof.mjs +275 -0
- package/tests/claim_gate_ci_unblock.mjs +116 -0
- package/tests/claim_gate_rebase.mjs +15 -3
- package/tests/collab_page.mjs +10 -0
- package/tests/conductor_main_e2e.mjs +97 -0
- package/tests/core_262_drop_box_blocked_db.mjs +143 -0
- package/tests/core_upgrade_runner.mjs +1 -0
- package/tests/dependency_writes_atomic.mjs +191 -0
- package/tests/deploy_page_projects.mjs +2 -1
- package/tests/effective_visibility_predicate.mjs +15 -0
- package/tests/est_advisory.mjs +27 -0
- package/tests/goal_map_page.mjs +26 -0
- package/tests/goal_task_relevance.mjs +518 -0
- package/tests/goal_working_area.mjs +146 -0
- package/tests/grade_attempts.mjs +61 -0
- package/tests/grade_attribution.mjs +23 -0
- package/tests/grade_correlation_audit.mjs +54 -1
- package/tests/grader_reader_lens.mjs +55 -2
- package/tests/grader_root_outage.mjs +6 -1
- package/tests/grader_subagent_tools_arg.mjs +102 -0
- package/tests/hall_approval_queue.mjs +54 -2
- package/tests/hall_settings_world.mjs +3 -1
- package/tests/hall_tweak_editor.mjs +316 -11
- package/tests/hub_account_page.mjs +394 -0
- package/tests/idea_detail_page.mjs +64 -0
- package/tests/idea_goal_ratification.mjs +64 -0
- package/tests/idea_spark_hall.mjs +60 -9
- package/tests/ideator_full_idea_shapes_space_proof.mjs +1 -1
- package/tests/landing_page.mjs +6 -3
- package/tests/module-scope-map.mjs +31 -8
- package/tests/module_api.mjs +1 -0
- package/tests/module_loader.mjs +1 -1
- package/tests/nav_permission_atoms.mjs +1 -1
- package/tests/no_phantom_mirror_workflow.mjs +24 -0
- package/tests/npm_release_preview_commands.mjs +89 -0
- package/tests/npm_release_preview_divert.mjs +164 -0
- package/tests/npm_release_preview_env.mjs +100 -0
- package/tests/npm_release_preview_proxy.mjs +162 -0
- package/tests/npm_release_preview_routes.mjs +208 -0
- package/tests/npm_release_preview_supervisor.mjs +203 -0
- package/tests/pin_write_key.mjs +383 -0
- package/tests/planning_session_skill.mjs +27 -0
- package/tests/platform_boot.mjs +71 -0
- package/tests/profile_nudge_links.mjs +86 -0
- package/tests/profile_ui_cross_project.mjs +1 -1
- package/tests/projects_hub.mjs +2 -2
- package/tests/projects_hub_app_status.mjs +249 -0
- package/tests/projects_hub_app_step.mjs +282 -52
- package/tests/projects_hub_render_connect.mjs +295 -0
- package/tests/provision_render.mjs +121 -28
- package/tests/provision_settings_apply.mjs +62 -0
- package/tests/provisioning_app_status.mjs +204 -0
- package/tests/provisioning_render_route.mjs +126 -6
- package/tests/render_check.mjs +2 -1
- package/tests/render_deploy.mjs +495 -0
- package/tests/rot_inbox.mjs +8 -0
- package/tests/ship_preflight.mjs +61 -8
- package/tests/smoke_dependencies_witness.mjs +46 -0
- package/tests/speciality_skills.mjs +214 -0
- package/tests/studio_room_height.mjs +70 -0
- package/tests/task_detail_includes.mjs +10 -0
- package/tests/task_visuals_instance_root.mjs +150 -0
- package/tests/tweak_batch_apply.mjs +21 -0
- package/tests/ui_design_kit.mjs +36 -4
- package/tests/upgrade.mjs +1 -1
- package/tests/upgrade_persist_pin.mjs +24 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# ADR 0349 — A version preview is a sandboxed child the web tier launches, reached by a cookie
|
|
2
|
+
|
|
3
|
+
- **Status:** accepted
|
|
4
|
+
- **Date:** 2026-09-29
|
|
5
|
+
- **Task:** [task 1004300](https://cloudbongos.com/builders#/task/1004300) (goal 1000090 — BONGOS-V2); split out of [task 1002622](https://cloudbongos.com/builders#/task/1002622)
|
|
6
|
+
- **Deciders:** Lars (owner, Archon) approved both decisions below on 2026-09-29, in chat with the orchestrator: "Yes to cookie routing and web tier, build 1004300." Claude wrote the record.
|
|
7
|
+
- **Amends:** [ADR 0293](0293-the-owners-deploy-door-is-an-intent-the-control-plane-drains.md) D1, for this one feature only.
|
|
8
|
+
- **Related:** [ADR 0016](0016-trust-boundary-server-enforced-permissions.md) (authority is resolved per request) · [ADR 0043](0043-git-ssh-trust-boundary-and-rank-floor-on-permission-paths.md) (the control plane is a separate privilege domain) · [ADR 0151](0151-governance-permissions-as-atom-ranks-as-roles.md) (`requirePermission`) · [`docs/modules-contract.md`](../modules-contract.md) (the `request.divert` seam)
|
|
9
|
+
|
|
10
|
+
## Context
|
|
11
|
+
|
|
12
|
+
The owner asked for the version history on `/deploy` to open the hall **as it was at that
|
|
13
|
+
version**, with a banner saying which version it is. A release-notes page was rejected: the
|
|
14
|
+
point is to see, and to run a version's migrations against real data before that version goes
|
|
15
|
+
live. So a preview is a second Bongos process, running an installed copy of that core against
|
|
16
|
+
a copy of the live database.
|
|
17
|
+
|
|
18
|
+
Two questions had no safe default.
|
|
19
|
+
|
|
20
|
+
**How does a browser reach it?** The hall's pages call absolute `/api/bongos/...` URLs about
|
|
21
|
+
133 times across `modules/*/public`, and the hall is routed on the Host header
|
|
22
|
+
(`src/bongos/serve-internal.js`, `isBuildersHost`). Serving a preview under a `/preview/<v>/`
|
|
23
|
+
path would break both. A second origin would need its own GitHub OAuth app, an owner step.
|
|
24
|
+
|
|
25
|
+
**Who launches it?** ADR 0293 D1 keeps the web tier shell-less: it asks, and the control-plane
|
|
26
|
+
runner acts. That fits a privileged act like moving a pin. A preview is unprivileged by
|
|
27
|
+
design, and routing every start and stop through the intent queue would add a timer's latency
|
|
28
|
+
to something a person is waiting on.
|
|
29
|
+
|
|
30
|
+
## Decision
|
|
31
|
+
|
|
32
|
+
### D1 — Route by cookie, through an optional `request.divert` seam (owner)
|
|
33
|
+
|
|
34
|
+
`GET /api/bongos/npm-release/preview/enter?v=X` sets `bongos_preview=X` (HttpOnly, SameSite=Lax,
|
|
35
|
+
Path=/). A kernel middleware, mounted before any surface, asks an **optional** provider of the
|
|
36
|
+
`request.divert` seam whether to take the request. With no provider it is one map lookup and
|
|
37
|
+
`next()`: an instance without the npm-release module is served exactly as before. A provider
|
|
38
|
+
that throws is treated as absent for that request.
|
|
39
|
+
|
|
40
|
+
The npm-release provider forwards a request to the preview only when **all** hold:
|
|
41
|
+
|
|
42
|
+
- the cookie names the version now running;
|
|
43
|
+
- the path is not sign-in (`/api/bongos/auth/*`) and not the module's own preview routes, so
|
|
44
|
+
signing in and **leaving** a preview always reach the live hall;
|
|
45
|
+
- there is no `Authorization` header (a CLI or API call is never a preview);
|
|
46
|
+
- the caller is signed in and holds `core.pin.move`, resolved by the kernel's own
|
|
47
|
+
`requireBuilder` and `requirePermission` gates on **every** forwarded request, uncached
|
|
48
|
+
(ADR 0016). A refusal clears the cookie and serves the live hall.
|
|
49
|
+
|
|
50
|
+
The proxy keeps the Host header, passes server-sent-events streams through unbuffered, strips
|
|
51
|
+
the steering cookie from what the preview sees, and injects the banner after `<body>` on HTML
|
|
52
|
+
responses, so a version older than any banner code still shows it.
|
|
53
|
+
|
|
54
|
+
The session cookie is forwarded: the preview's database is a copy, so the same session rows
|
|
55
|
+
exist in it and no second sign-in is needed. Anything done there stays in the copy.
|
|
56
|
+
|
|
57
|
+
**Rejected:** a `/preview/<v>/` path (breaks 133 absolute URLs and Host routing); a second
|
|
58
|
+
origin (an OAuth app per preview host, and cookies that no longer carry).
|
|
59
|
+
|
|
60
|
+
### D2 — The web tier launches the preview, as a sandboxed child (owner; amends ADR 0293 D1)
|
|
61
|
+
|
|
62
|
+
The npm-release module's supervisor installs the version, copies the database, migrates the
|
|
63
|
+
copy and spawns the child itself. This is the exception ADR 0293 D1 refuses for the deploy
|
|
64
|
+
door, granted here because a preview is not the deploy door: it changes nothing live.
|
|
65
|
+
|
|
66
|
+
What keeps that exception narrow:
|
|
67
|
+
|
|
68
|
+
- **The child's environment is built from nothing.** An allow-list of `PATH`, `NODE_ENV`,
|
|
69
|
+
`PGHOST`, `PGPORT`, `PGUSER`, `PGPASSWORD`, plus its own `PORT`/`HOST` on 127.0.0.1. `HOME`
|
|
70
|
+
is an empty scratch directory, so the Discord webhook file is not found. Every `GITHUB_*`,
|
|
71
|
+
`DISCORD_*` and AI key is absent because nothing carries it, not because it was removed.
|
|
72
|
+
`CHAT_DRY_RUN=1`; nine pollers off (`PUBLISH_RECONCILER_DISABLED`, `ROUTINE_TIMERS_DISABLED`,
|
|
73
|
+
`BOARD_EXPIRY_DISABLED`, `SEARCH_INDEX_SWEEP_DISABLED`, `SESSION_UPLOAD_HEALTH_DISABLED`,
|
|
74
|
+
`SESSION_REWARD_RECONCILE_DISABLED`, `PROVISIONING_LIVENESS_DISABLED`,
|
|
75
|
+
`PROVISIONING_CATALOG_BACKFILL_DISABLED`, `CONFLICT_RESOLVE_DISABLED`); modules `discord`,
|
|
76
|
+
`agents`, `provisioning` and `npm-release` off.
|
|
77
|
+
- **The copy is a dump and a restore, never a template copy.** `pg_dump -Fc` then `pg_restore
|
|
78
|
+
--no-owner` into `bongos_preview`. A template copy needs no other connection on the source,
|
|
79
|
+
and the live pool holds some. `PGDATABASE` is pinned to the copy and `DATABASE_URL` blanked
|
|
80
|
+
for the migration, so nothing can be applied to the live database.
|
|
81
|
+
- **Commands run without a shell.** A version is `^\d+\.\d+\.\d+$` before it reaches any
|
|
82
|
+
command line.
|
|
83
|
+
- **One preview at a time; 20 minutes idle stops it** and drops the copy.
|
|
84
|
+
- **Only published versions**, no older than the running version minus 30 published versions.
|
|
85
|
+
- **Nothing here holds a control-plane credential.** The web tier already cannot read
|
|
86
|
+
`/etc/cloudbongos/box.env` (ADR 0327 §5); the child gets less than the web tier has.
|
|
87
|
+
|
|
88
|
+
### D3 — Accepted residual risks, for now
|
|
89
|
+
|
|
90
|
+
- The child connects to Postgres as the same role as the live hall (`PGUSER`), so a hostile
|
|
91
|
+
published core could open the live database. It is our own published package, built from
|
|
92
|
+
this repository by CI, and a preview is limited to `core.pin.move` holders; a per-preview
|
|
93
|
+
database role is the next step, not built here.
|
|
94
|
+
- **No egress firewall yet** (owner default). The env allow-list means the child holds no
|
|
95
|
+
outward credentials; it can still make anonymous outbound requests. `systemd-run
|
|
96
|
+
IPAddressDeny` is the follow-up and needs the droplet.
|
|
97
|
+
- A preview started by the live process is stopped when that process exits, but the copy is
|
|
98
|
+
dropped only by the next start or stop. It is one fixed name, so it cannot pile up.
|
|
99
|
+
|
|
100
|
+
## Consequences
|
|
101
|
+
|
|
102
|
+
- One new optional seam, `request.divert`, listed in `docs/modules-contract.md`.
|
|
103
|
+
- No migration. No schema. The provider is registered only where npm-release is on.
|
|
104
|
+
- **Part B is unverified until run on the droplet:** the `lars` role has CREATEDB, the unit's
|
|
105
|
+
`MemoryMax` fits a second Node process plus a restore, the dump time, one preview end to end,
|
|
106
|
+
and live row counts before and after. Until then the code is implemented, not verified.
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# ADR 0350 — The hub holds a per-project WRITE deploy key, so a hosted project's upgraded pin always reaches its GitHub repo
|
|
2
|
+
|
|
3
|
+
- **Status:** accepted
|
|
4
|
+
- **Date:** 2026-09-29
|
|
5
|
+
- **Task:** [task 1004291](https://cloudbongos.com/builders#/task/1004291) (follow-up to task 1004065)
|
|
6
|
+
- **Deciders:** the owner chose this option on 2026-09-25 (a per-project write key, over keeping the owner's GitHub access longer, or leaving it as is); the shared-account default below was stated in the task and is the safe default, not a new ruling; Claude wrote the record.
|
|
7
|
+
- **Amends:** [ADR 0176](0176-private-repo-deploy-keys.md) (which said the deploy key is read-only and that pushes ride the owner's token) · **Related:** [ADR 0281](0281-an-instance-identity-is-its-own-unix-account-and-pg-role.md) (a project's own account and database) · [ADR 0155](0155-adopt-private-repo-widen-oauth-scope.md) (the owner's token)
|
|
8
|
+
|
|
9
|
+
## Context
|
|
10
|
+
|
|
11
|
+
When a hosted project's core is upgraded, the pin is committed in the project's checkout and
|
|
12
|
+
the hub's runner pushes it to the owner's GitHub repo (task 1004065, `pushUpgradePin`). That
|
|
13
|
+
push needs a credential that can WRITE. There were two, and neither lasts:
|
|
14
|
+
|
|
15
|
+
- the owner's token is stored for one hour after they sign in with repository access
|
|
16
|
+
(`GITHUB_TOKEN_TTL_MS`), and the scaffold path deletes it after use, so an upgrade days later
|
|
17
|
+
normally finds none;
|
|
18
|
+
- the checkout's own `origin` is the read-only pull key of ADR 0176 (or the shared alias key),
|
|
19
|
+
which cannot write the owner's repo.
|
|
20
|
+
|
|
21
|
+
So most upgrades still ended "GitHub was not updated". The box and the repo split, and the
|
|
22
|
+
box's local commit stopped its fast-forward pull from taking the owner's new work (the
|
|
23
|
+
hermeslines-marketing shape).
|
|
24
|
+
|
|
25
|
+
## Decision
|
|
26
|
+
|
|
27
|
+
**D1. A second deploy key per hosted repo, registered writable.** Minted with
|
|
28
|
+
`ssh-keygen -t ed25519`, registered with `read_only: false` under its own title
|
|
29
|
+
`cloudbongos-pin-<slug>`, distinct from the ADR 0176 pull key `cloudbongos-deploy-<slug>`. The pull key
|
|
30
|
+
stays exactly as it was and stays read-only. Registering a deploy key needs repository admin
|
|
31
|
+
for the owner, the same requirement the pull key already has. Public repos get one too (they
|
|
32
|
+
have no per-repo key today).
|
|
33
|
+
|
|
34
|
+
**D2. Where it lives and who can read it.** Outside every project checkout, in a directory of
|
|
35
|
+
the runner's own account: `PROVISION_PIN_KEY_DIR`, default `~/.config/cloudbongos/pin-keys/`,
|
|
36
|
+
directory mode 0700, key mode 0600, born under `umask 077`, and the modes are read back and
|
|
37
|
+
the key deleted if they are not exactly 700/600. It is never written into the project's
|
|
38
|
+
`.git`, its unix home (`/var/lib/bongos-<slug>`) or anywhere the project's account
|
|
39
|
+
(ADR 0281) can read.
|
|
40
|
+
|
|
41
|
+
**D3. When it is minted.** At standup (scaffold and adopt), right after the ADR 0176 wiring, and
|
|
42
|
+
on any later pass that holds a fresh owner token and finds no key: `pushUpgradePin` mints it
|
|
43
|
+
before pushing. So an existing project needs the owner to reconnect GitHub with repository
|
|
44
|
+
access once; the next upgrade then sets up lasting access. Minting is best effort and never
|
|
45
|
+
fails a standup or an upgrade.
|
|
46
|
+
|
|
47
|
+
**D4. How it is used.** `pushUpgradePin` tries, in order: the write key, then the owner's token
|
|
48
|
+
(when fresh), then the checkout's own `origin`. The write key is passed for that one push only
|
|
49
|
+
(`-c core.sshCommand="ssh -F /dev/null -i <key> -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new"`
|
|
50
|
+
against `git@github.com:<owner>/<repo>.git`), with hooks off, no prompt, never forced. A push the
|
|
51
|
+
key made that GitHub rejects as non-fast-forward is reported as "behind" and nothing else is
|
|
52
|
+
tried, because the credential worked and the history is what stopped it.
|
|
53
|
+
|
|
54
|
+
**D5. Revoked when the project leaves.** Teardown, and therefore disconnect (which runs the
|
|
55
|
+
teardown first), deletes the `cloudbongos-pin-<slug>` registration by title next to the pull
|
|
56
|
+
key's, and removes the local key whatever else happened. With no usable owner token the
|
|
57
|
+
registration cannot be removed by us; the log names it so the owner can remove it in their repo
|
|
58
|
+
settings.
|
|
59
|
+
|
|
60
|
+
**D6. A project with no account of its own is refused a key (the recorded default).** A project
|
|
61
|
+
still running as the shared app user (the `resolveRunAs` fallback, no `bongos-<slug>`
|
|
62
|
+
account) has the SAME uid as the runner and could read the runner's key, so no write key is
|
|
63
|
+
minted for it, and the deploy page reports "needs its own account first" (repo note code
|
|
64
|
+
`needs_account`) instead of offering a reconnect that could not help. The check is fail-closed:
|
|
65
|
+
only a positive `id -u bongos-<slug>` counts; an inconclusive probe refuses. If the owner wants a
|
|
66
|
+
shared-uid project to hold a write key anyway, that is a new decision recorded here, not a
|
|
67
|
+
silent one.
|
|
68
|
+
|
|
69
|
+
**D7. The deploy page.** The `no_access` note and the reconnect link now say that reconnecting
|
|
70
|
+
sets up lasting access, no longer "about an hour".
|
|
71
|
+
|
|
72
|
+
## What a compromise can and cannot do
|
|
73
|
+
|
|
74
|
+
- **The runner account** (the hub's app user) already holds the database, the owner tokens and the
|
|
75
|
+
process manager, so it can already do everything the write key can. The key adds, for that
|
|
76
|
+
one account, the ability to push to each hosted owner repo without a live token — one repo per
|
|
77
|
+
key, revocable individually from the owner's repo settings.
|
|
78
|
+
- **A project's own process** (its `bongos-<slug>` account, ADR 0281) can read the read-only pull
|
|
79
|
+
key, exactly as before, and can NOT read the write key: wrong owner, directory 0700. It
|
|
80
|
+
therefore cannot push code into the owner's repo, which is the point of keeping the key
|
|
81
|
+
outside the checkout. A project on the shared account can, which is why D6 refuses it a key.
|
|
82
|
+
- **GitHub or the network** learn only the public half. A leaked write key can push to that one
|
|
83
|
+
repository until the owner deletes it in their settings; it cannot read other repos, and it
|
|
84
|
+
is a per-repo deploy key, not an account token.
|
|
85
|
+
|
|
86
|
+
## Alternatives rejected
|
|
87
|
+
|
|
88
|
+
- **Keep the owner's token longer.** A long-lived account-wide token in the hub is a bigger
|
|
89
|
+
prize than a per-repo key, and it still lapses when the owner revokes the OAuth grant.
|
|
90
|
+
- **Leave it as is.** Most upgrades keep ending in a split that blocks the owner's own pushes.
|
|
91
|
+
- **Make the pull key writable.** It lives in the checkout, readable by the project's own
|
|
92
|
+
account, so a compromised project could push code to the owner's repo.
|
|
93
|
+
|
|
94
|
+
## Consequences
|
|
95
|
+
|
|
96
|
+
- Existing projects get the key on the next runner pass after the owner reconnects GitHub with
|
|
97
|
+
repository access; until then the deploy page still reports "GitHub was not updated" with the
|
|
98
|
+
corrected copy.
|
|
99
|
+
- Reconciling an already-split repo (hermeslines-marketing) and auto-rebasing a pin onto newer
|
|
100
|
+
GitHub commits stay out of scope; both are still reported, not resolved.
|
|
101
|
+
- Code: `scripts/gds/provision-pin-key.js`, `pushUpgradePin` in
|
|
102
|
+
`scripts/gds/provision-core-upgrade.js`, the revoke in `scripts/gds/provision-teardown.js`,
|
|
103
|
+
the copy in `modules/hall-ui/public/deploy.js`; tests in `tests/pin_write_key.mjs`.
|
package/docs/adr/README.md
CHANGED
|
@@ -439,3 +439,6 @@ This keeps the decision history honest and traceable.
|
|
|
439
439
|
| 0345 | [**We host every project's hall, and its app deploys where the owner chooses** ([task 1004349](https://cloudbongos.com/builders#/task/1004349), goal 1000106 — working area 1, owner Lars). Reverses ADR 0323 §2 and the hall half of ADR 0327. **D1 (owner):** every project's Builders Hall runs on our shared server; `cloud-host` now means "hosted by us". **D2 (owner):** the hall's address is ours or the owner's own domain. **D3 (owner):** the setup wizard asks where the APP deploys; Render only at launch; choosing it creates the app on the owner's own Render account with ADR 0327's borrowed key. **D4 (owner):** "decide later" is allowed. **D5 (owner):** no project is moved. **D6 (measured):** ADR 0145 Finding 1 is live — the hub cookie is `Domain=.cloudbongos.com` and a hall loads modules from its own repo — so our-address halls need task 1004357; an owner domain is safe. **D7 (measured):** at the ceiling the create route refuses `box_full` up front, and the resize is not a prerequisite. Task 1004184 re-scoped, task 1004185 on hold.](0345-we-host-every-projects-hall-and-its-app-deploys-where-the-owner-chooses.md) | hosting / provisioning / security |
|
|
440
440
|
| 0346 | [**Dev boxes are retired; a builder builds from their own checkout** ([task 1003898](https://cloudbongos.com/builders#/task/1003898), goal 1000120 — owner Lars). Records the whole removal, link by link, the owner's three rulings of 2026-09-13 (drop the data, delete `.devcontainer/`, remove `bongos box`/`shell`/`code`), what was kept on purpose (server-mediated publish, the CLI downloads, `secret-box`, `do-api`, the declared-source rule), and the retirement guards that still name the box. Supersedes 0031, 0044, 0045, 0053, 0057, 0059, 0071, 0072 (code staleness), 0123, 0144, 0145 (desktop-app branding), 0148, 0193, 0277; supersedes in part 0035, 0046, 0052, 0055, 0072 (Bongos app), 0151 §3.](0346-dev-boxes-are-retired.md) | builder environment / removal |
|
|
441
441
|
| 0347 | [**Every store module ships a how-to: a file in the module, a publish gate, and a Docs score shown on its own** ([task 1004364](https://cloudbongos.com/builders#/task/1004364), goal 1000091 — working area 5, owner Will). Amends ADR 0343 D1. **D1-D2 (owner):** the how-to is `HOWTO.md` at the module root, frozen with the version by the tarball hashes, with five required sections (what it does; install and enable; how to use it, with one worked example; configuration; limits and known issues). **D3:** an optional `howto.artifactUrl` Claude artifact shown as "also available as", never graded. **D4:** `bongos module publish` and the store refuse a version with a missing file or empty section, naming it; store versions only. **D5 (owner):** a Docs part scored 0–100 per version, day one, shown beside the parts but NOT in the D3 average, floor or ranking; "pending"/"not scored" never zero; price never an input. **D6 (owner):** one Sonnet 5 call per version, 5¢ ceiling.](0347-every-store-module-ships-a-how-to.md) | modules / store / quality |
|
|
442
|
+
| 0348 | [**The web tier may look, read-only, at what an owner's Render key can see** ([task 1004352](https://cloudbongos.com/builders#/task/1004352), goal 1000106 — working area 1, owner Lars). Amends ADR 0111 §2. **D1 (owner):** one route, `POST /provisioning/render/lookup`, may call Render from the web tier — GET only (workspaces; web services in one named workspace), the owner's key held for that request and never stored, logged or returned, every service checked to carry the workspace asked about, capped per builder. Every write stays in the runner. **D2 (owner):** a key sent during a Create rides the project's own `provision` run; the runner sets the app up on Render straight after the project is up and clears the key either way.](0348-the-web-tier-may-look-read-only-at-what-an-owners-render-key-can-see.md) | provisioning / security / render |
|
|
443
|
+
| 0349 | [**A version preview is a sandboxed child the web tier launches, reached by a cookie** ([task 1004300](https://cloudbongos.com/builders#/task/1004300), goal 1000090 — BONGOS-V2, owner Lars, approved 2026-09-29). Amends ADR 0293 D1 for this feature only. **D1 (owner):** route by a `bongos_preview` cookie through an optional `request.divert` seam, not a path; forward only with the cookie, off the sign-in and preview paths, with no Bearer, and with `core.pin.move` re-checked on every request. **D2 (owner):** the npm-release module launches the preview itself, with a deny-by-default environment, a dump-and-restore copy of the database (never a template copy), one at a time, 20-minute idle stop, published versions within 30 of the running one. **D3:** the same database role and no egress firewall are accepted for now.](0349-a-version-preview-is-a-sandboxed-child-the-web-tier-launches.md) | modules / npm-release / deploy |
|
|
444
|
+
| 0350 | [**The hub holds a per-project WRITE deploy key, so a hosted project's upgraded pin always reaches its GitHub repo** ([task 1004291](https://cloudbongos.com/builders#/task/1004291), follow-up to task 1004065). Amends ADR 0176. The owner's token lasts one hour and the checkout's own key is read-only, so most hosted upgrades ended "GitHub was not updated". **D1:** a second deploy key per repo, registered `read_only: false` as `cloudbongos-pin-<slug>`, the pull key untouched. **D2:** kept outside every checkout in the runner's own directory (0700 dir, 0600 key, modes verified), unreadable by the project's account. **D3:** minted at standup and whenever a fresh owner token finds none, so reconnecting GitHub once is enough. **D4:** `pushUpgradePin` tries write key, then owner token, then origin, never forced. **D5:** revoked on teardown and disconnect. **D6:** a project on the shared app user is refused a key: "needs its own account first". **D7:** /deploy says reconnecting sets up lasting access.](0350-the-hub-holds-a-per-project-write-deploy-key-so-a-hosted-upgrade-reaches-github.md) | provisioning / security |
|