@bongos/core 1.25.12 → 1.25.14
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 +106 -51
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +6 -0
- package/clients/bongos-client/index.cjs +6 -0
- package/clients/bongos-client/index.d.ts +13 -4
- package/clients/bongos-client/index.mjs +6 -0
- package/docs/adr/0328-a-hosted-projects-authority-is-its-membership-row-not-its-owner-column.md +1 -1
- package/docs/adr/0407-a-project-grant-is-made-by-the-owner-not-witnessed-at-sign-in.md +124 -0
- package/docs/adr/README.md +1 -0
- package/docs/api/openapi.json +198 -11
- package/docs/api-reference.md +7 -4
- package/docs/copy-inventory.md +28 -34
- package/docs/copy-registry.json +41 -125
- package/docs/file-map.md +1 -1
- package/docs/module-api-changelog.md +4 -0
- package/docs/page-inventory.json +1 -0
- package/docs/page-readings.json +3 -3
- package/modules/front-door-guide/claude.js +11 -7
- package/modules/front-door-guide/context.js +54 -0
- package/modules/front-door-guide/db.js +79 -6
- package/modules/front-door-guide/guide.js +18 -3
- package/modules/front-door-guide/migrations/front_door_guide_002_plan_and_account.sql +37 -0
- package/modules/front-door-guide/migrations/front_door_guide_003_starting_point.sql +12 -0
- package/modules/front-door-guide/module.json +7 -3
- package/modules/front-door-guide/plan.js +102 -0
- package/modules/front-door-guide/pollers/retention-sweep.js +67 -0
- package/modules/front-door-guide/routes/front-door-guide-plan.js +145 -0
- package/modules/front-door-guide/routes/front-door-guide.js +36 -33
- package/modules/front-door-guide/settings.js +4 -0
- package/modules/front-door-guide/starting-point.js +55 -0
- package/modules/hall-ui/public/genesis-home.js +29 -2
- package/modules/hall-ui/public/goals-page.js +3 -29
- package/modules/hall-ui/public/index.html +2 -1
- package/modules/hall-ui/public/task-lib.js +45 -0
- package/modules/hall-ui/public/work.js +3 -47
- package/modules/lifecycle/kickoff-seed.js +9 -5
- package/modules/onboarding/genesis-home.js +1 -1
- package/modules/provisioning/module.json +2 -1
- package/modules/provisioning/seams.js +10 -0
- package/package-lock.json +306 -3
- package/package.json +4 -2
- package/release-notes.json +24 -0
- package/scripts/bongos/package-core.js +4 -2
- package/scripts/bongos/run-unit-tests.js +4 -0
- package/src/bongos/module-scope-map.js +2 -0
- package/src/module-api.js +1 -1
- package/tests/cutover_codemod.mjs +1 -1
- package/tests/front_door_guide_db.mjs +115 -0
- package/tests/front_door_guide_existing.mjs +238 -0
- package/tests/front_door_guide_plan.mjs +344 -0
- package/tests/genesis_home.mjs +80 -2
- package/tests/package_core.mjs +11 -7
- package/tests/pruned_lockfile_integrity.mjs +4 -2
- package/tests/status_page_no_dead_uptime.mjs +1 -1
- package/tests/task_lib.mjs +46 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# ADR 0407 — A hosted project's grant is made by its owner, not witnessed at sign-in
|
|
2
|
+
|
|
3
|
+
- **Status:** accepted — the owner accepted this design as written on 2026-11-13, including its three answers below (no archon-made grants; `operate` = reads, the variable-name list and restart; the sign-in default stays `member`). Design only: nothing here widens a gate. The gate stays owner-or-archon until [task 1004161](https://cloudbongos.com/builders#/task/1004161) builds what is written here.
|
|
4
|
+
- **Date:** 2026-11-13
|
|
5
|
+
- **Task:** [task 1004753](https://cloudbongos.com/builders#/task/1004753) (goal 1000106, check `wa1-members-owner-granted`)
|
|
6
|
+
- **Amends:** [ADR 0328](0328-a-hosted-projects-authority-is-its-membership-row-not-its-owner-column.md) — replaces its suspended Phase 2 and re-states its Decision 3 (what a refused caller is told).
|
|
7
|
+
- **Follow-on tasks (updated 2026-11-13, in the task records):** [task 1004161](https://cloudbongos.com/builders#/task/1004161) is rewritten to build §§1–4 and the attack tests, and is taken off hold with an explicit promote the moment this task lands (promote refuses while this task is unshipped, and nothing else clears a park); [task 1004162](https://cloudbongos.com/builders#/task/1004162) is re-checked against §6 (a third refusal, and it now owns the membership port).
|
|
8
|
+
- **Related:** [ADR 0016](0016-trust-boundary-server-enforced-permissions.md) (authority is read live from the database on every request), [ADR 0083](0083-modular-architecture-strangler-build-plan.md) (modules cooperate through ports), [ADR 0141](<redacted>.md) / [0205](0205-federated-checkin-needs-the-hubs-own-signin-witness.md) (the sign-in witness), [ADR 0151](0151-governance-permissions-as-atom-ranks-as-roles.md) §3 (the ownership axis), [ADR 0176](0176-private-repo-deploy-keys.md) / task 1004194 (repo-private stays owner-only because it uses the owner's GitHub token).
|
|
9
|
+
|
|
10
|
+
## Context
|
|
11
|
+
|
|
12
|
+
ADR 0328 Phase 2 would have admitted a `member` row of `<redacted>` onto the hosted-project routes. Its 2026-10-16 amendment suspended that, and every fact behind the suspension was re-checked against the code for this ADR (2026-11-13):
|
|
13
|
+
|
|
14
|
+
- `recordFederatedSignin` (`modules/platform-identity/platform-identity.js`) inserts `membership_kind = 'member'` with `signin_verified_at = now()` on every completed hub sign-in, and turns a `pending` row into `member`. Its own comment calls it "the only way a membership row is born".
|
|
15
|
+
- It is called from `POST /sso/token` and `POST /sso/device/poll` (`modules/platform-identity/routes/sso.js`). `POST /sso/device/start` takes only a typed `origin` and no session; it refuses only an unknown, inactive or secretless client. So any GitHub user can get a witnessed `member` row on any credentialed project, and the project's own server is never asked.
|
|
16
|
+
- `POST /sso/membership/check-in` (`touchMembership`) lets the **project's server** set any kind, `owner` included, on a row that already has a sign-in witness. On the instance side, `membershipKind` (`src/bongos/auth-admission.js`) reports `owner` for the first admin and `member` for everyone else, and with `admission.policy: open` that is every GitHub sign-up.
|
|
17
|
+
|
|
18
|
+
So no value of `membership_kind` means "the owner trusts this person with the hosting". `member` means "has signed in here"; `owner` means "this project's server says so". Neither is a fact the hub itself decided.
|
|
19
|
+
|
|
20
|
+
**What the gate guards today.** `requireProjectOwner` (`modules/provisioning/route-helpers.js`, task 1004159) admits the row's `owner_builder_id`, or a caller whose live rank is `archon`; everyone else gets `404 instance_not_found`. It is mounted on 14 routes. The task's "14 routes" is accurate for that helper, but **28 routes** take a hosted project's `:id`. The other 14 check inline or by permission:
|
|
21
|
+
|
|
22
|
+
| Gate today | Routes |
|
|
23
|
+
|---|---|
|
|
24
|
+
| `requireProjectOwner` (owner or archon) — 14 | `GET …/:id`; `PATCH …/:id/detail`; `GET …/:id/invite-suggestions`; `GET`/`PATCH …/:id/settings`; `POST …/:id/teardown`; `POST …/:id/restart`; `POST …/:id/domain`; `POST …/:id/github-app` (all `routes/provisioning.js`); `POST …/:id/disconnect` (`routes/disconnect.js`); `GET`/`POST …/:id/env-manifest`, `PATCH`/`DELETE …/:id/env-manifest/:varId` (`routes/env-manifest.js`) |
|
|
25
|
+
| inline, owner or archon — 4 | `GET`/`PATCH …/:id/look`, `PUT`/`DELETE …/:id/logo` (`routes/look.js`) |
|
|
26
|
+
| inline, owner only — 6 | `POST …/:id/demo` (`routes/demo.js`); `POST …/:id/github-app/pick-up` (`routes/github-setup.js`); `GET`/`POST …/:id/render` (`routes/render-standup.js`); `POST …/:id/repo-private` (`routes/repo-private.js`) |
|
|
27
|
+
| owner or `core.pin.move` — 3 | `GET`/`POST …/:id/core-upgrade`, `POST …/:id/update-channel` (`routes/core-upgrade.js`) |
|
|
28
|
+
| `provisioning.fleet.manage` only — 2 | `POST …/:id/force-teardown`; `POST …/:id/update-subscription` |
|
|
29
|
+
|
|
30
|
+
## Decision
|
|
31
|
+
|
|
32
|
+
### 1. The grant is its own row, in the provisioning module, with one writer
|
|
33
|
+
|
|
34
|
+
A new table, `provisioning_project_grants` (migration `<redacted>.sql`, additive, in the module's own namespace):
|
|
35
|
+
|
|
36
|
+
| Column | Meaning |
|
|
37
|
+
|---|---|
|
|
38
|
+
| `id` | bigint identity |
|
|
39
|
+
| `instance_id` | `provisioning_instances.id`, `ON DELETE CASCADE` (disconnect deletes the row, so its grants go with it) |
|
|
40
|
+
| `grantee_builder_id` | the hub `builders.id` of the person granted — the same key as `owner_builder_id`, so the gate compares like with like |
|
|
41
|
+
| `level` | `text NOT NULL CHECK (level IN ('operate'))` — one level today (§3); a second level is an ADR amendment, never a free string |
|
|
42
|
+
| `granted_by_builder_id`, `granted_at` | who made it, when |
|
|
43
|
+
| `revoked_at`, `revoked_by_builder_id` | null while live; a revoke is a stamp, never a delete, so the record survives |
|
|
44
|
+
|
|
45
|
+
A partial unique index on `(instance_id, grantee_builder_id) WHERE revoked_at IS NULL` allows one live grant per person per project; re-granting after a revoke makes a new row.
|
|
46
|
+
|
|
47
|
+
**The single writer** is a new file `modules/provisioning/grants.js` (`grantProjectAccess`, `revokeProjectAccess`, `liveGrant`), called only by the grant routes in §2. Nothing else may insert or update the table; a test greps for any other `INSERT INTO provisioning_project_grants` / `UPDATE provisioning_project_grants`.
|
|
48
|
+
|
|
49
|
+
**Why here and not in `<redacted>`.** ADR 0328 rejected a separate provisioning table because a second copy of membership would drift. That reason is gone: the membership table does not hold grants, and its writers (sign-in, check-in, invites) are exactly the paths that must never create one. Keeping the grant beside `owner_builder_id` also means the gate reads both facts in one query in the module that owns them, with no cross-module port in the admission path, so "the port is down" can never decide who gets in.
|
|
50
|
+
|
|
51
|
+
### 2. Who may make and remove a grant
|
|
52
|
+
|
|
53
|
+
- **Create — the project's owner only, from their own hub session.** `POST /provisioning/instances/:id/grants { github_login }`, gated `requireBuilder` + `requireProjectOwner(loadInstance, { archonOverride: false })`. The login is resolved **server-side** to an existing, active hub builder; no such builder → `404 grantee_not_found` ("they need to sign in to the hub once first"). The reply names the person found (display name and picture), because a GitHub login can change hands. The owner cannot grant themselves. At most 25 live grants per project (`409 grant_limit`).
|
|
54
|
+
- **An archon cannot create a grant** on someone else's project. An archon already has fleet-support reach through the override; a grant would let them hand an owner's project to a third person without the owner, and in doubt we refuse. (Owner choice — see *Open questions*.)
|
|
55
|
+
- **Revoke** — `DELETE /provisioning/instances/:id/grants/:grantId`: the owner; the grantee themselves (leaving); or an archon. A revoke only ever removes access, so the archon door is a safety lever, not a widening.
|
|
56
|
+
- **List** — `GET /provisioning/instances/:id/grants`: owner or archon (the existing gate). A grantee sees their own grant on the project read, not the list.
|
|
57
|
+
|
|
58
|
+
**How the hub learns of the grant without trusting the visitor:** it does not learn it from anyone. The row is born on the hub from the owner's authenticated session, checked against `owner_builder_id` on the hub's own database. Nothing the visitor sends, nothing a project's server reports at check-in, and no sign-in can create, change or extend it. The gate never reads `<redacted>` to admit anyone.
|
|
59
|
+
|
|
60
|
+
### 3. What a grantee may do — a short allow-list, opt-in per route
|
|
61
|
+
|
|
62
|
+
The one level, `operate`, is "help keep it running": look at the project and restart it, and maintain the list of environment variable names. Everything that changes what the project *is*, who can see it, where it lives, or that acts with the owner's own accounts stays owner-only.
|
|
63
|
+
|
|
64
|
+
| Route | Grantee (`operate`) |
|
|
65
|
+
|---|---|
|
|
66
|
+
| `GET …/:id` | ✅ read — the projection drops `owner_monthly_usd` (the owner's own bill) and `github_install` (an action on the owner's GitHub) |
|
|
67
|
+
| `GET …/:id/settings` | ✅ read |
|
|
68
|
+
| `GET …/:id/env-manifest` | ✅ read — names and purposes only; the platform holds no values (ADR 0323 §6) |
|
|
69
|
+
| `POST …/:id/env-manifest`, `PATCH`/`DELETE …/:id/env-manifest/:varId` | ✅ write — declaring a variable name changes no running behaviour; the manifest exists for builder sessions to read and keep current |
|
|
70
|
+
| `POST …/:id/restart` | ✅ write — recovers a stuck project; changes no data, costs nothing |
|
|
71
|
+
| `teardown`, `disconnect`, `domain`, `github-app`, `github-app/pick-up`, `repo-private`, `render` (GET and POST), `demo` | ❌ owner only (plus the archon override where it exists today). Teardown and disconnect destroy; domain moves the address; github-app, pick-up, repo-private and render act with the owner's own GitHub or Render account |
|
|
72
|
+
| `PATCH …/:id/settings`, `PATCH …/:id/detail`, look and logo (4) | ❌ owner only — who can see and join the project, and how it presents itself, is the owner's call |
|
|
73
|
+
| `GET …/:id/invite-suggestions` | ❌ owner only — inviting is the owner's lever |
|
|
74
|
+
| `core-upgrade` (2), `update-channel` | ❌ unchanged (owner or `core.pin.move`) |
|
|
75
|
+
| `force-teardown`, `update-subscription` | ❌ unchanged (fleet permission) |
|
|
76
|
+
| the grant routes (§2) | ❌ except a grantee revoking their own grant |
|
|
77
|
+
|
|
78
|
+
**Mechanism — opt-in, never default.** `requireProjectOwner` gains one option, `{ grantee: 'operate' }`. Without it (every route today, and every route added later) a grant admits nothing. With it, the gate admits the owner, then a live `operate` grant for `req.builder.id`, then the archon override, in that order, and sets `req.projectDoor` to `owner` · `grantee` · `archon`. The grant is read inside the gate on every request (no caching, ADR 0016), so a revoke takes effect on the grantee's next request. A failed grant read **denies** — the gate's existing `failFrom(res, err, 'instance_read_failed')`, never a fall-through to "admit". The inline-gated routes are not converted by this design; they stay owner-only by not having the option.
|
|
79
|
+
|
|
80
|
+
### 4. The audit actor
|
|
81
|
+
|
|
82
|
+
`projectActor(req)` reads `req.projectDoor`: `api:self` (owner), `api:grantee:<builderId>` (grantee), `api:archon:<builderId>` (archon override). A grantee who is also an archon is recorded as `grantee`, because the owner's grant is what let them in. The grant routes write `provisioning_events` rows `grant-made` and `grant-revoked`, whose `detail` names the grantee's builder id and level; their actor is the same three-way label (a grantee leaving is `api:grantee:<id>`).
|
|
83
|
+
|
|
84
|
+
### 5. The hub's sign-in default stays `member`
|
|
85
|
+
|
|
86
|
+
The task asked whether sign-in should stop writing `member`. **This design keeps it**, because once nothing grants on it, the word carries no authority, and changing it reaches three working features for no security gain:
|
|
87
|
+
|
|
88
|
+
- *My Projects* (`listMembershipsForAccount`) lists every non-`pending` row; a `visitor` default would still list them, but would make the existing project list say "visitor" for people who genuinely build there.
|
|
89
|
+
- The activity-rollup precheck (`membershipBacksRollup`) accepts any witnessed non-`pending` row; unchanged either way.
|
|
90
|
+
- Invites turn `pending` into `member` at sign-in; a `visitor` default would need its own rule for accepted invites.
|
|
91
|
+
|
|
92
|
+
The word is documented as "has signed in here" (ADR 0328's amendment already says so). If the owner later wants the label changed for clarity, that is its own task, not a security fix.
|
|
93
|
+
|
|
94
|
+
### 6. What a refused caller is told (replaces ADR 0328 Decision 3; task 1004162)
|
|
95
|
+
|
|
96
|
+
- **No grant and no membership row** → `404 instance_not_found`, as today. A stranger learns nothing.
|
|
97
|
+
- **Not yet a member of the project** (a `visitor` or `pending` row, no grant) → `403 project_membership_required` with the apply path, as task 1004162 already specifies: a `visitor` is pointed to `POST /my-projects/join` (the shipped ADR 0141 / 0201 / 0247 join flow); a `pending` row is told their request is already on file and the owner decides. The message also says that managing the hosting is something the owner gives separately, so joining is not mistaken for that.
|
|
98
|
+
- **A member of the project (a `member` or `owner` kind row) without a grant** → `403 project_access_required`: "Only the project's owner, and people the owner has given access, can manage its hosting. Ask the owner to give you access." There is no self-serve request for hosting access.
|
|
99
|
+
- **How the row is read.** Through a port, `platform-identity.projectMembershipKind` as ADR 0328 named it, and only after the gate has already refused. If the port is unregistered, throws, or answers an unknown value, the answer is the `404`: on doubt, say less. This read only picks the wording of a refusal and can never admit anyone.
|
|
100
|
+
- **A grantee on a route without the opt-in** → `403 owner_only`, "Only the project's owner can do this." They already know the project exists.
|
|
101
|
+
|
|
102
|
+
## Consequences
|
|
103
|
+
|
|
104
|
+
- A team can share one hosted project's day-to-day running, once task 1004161 lands, without the owner handing over teardown, the address or their own accounts.
|
|
105
|
+
- The device-flow attack in the task is closed by construction: running the device flow writes a membership row, and nothing reads that row to admit. Task 1004161's tests must show a GitHub user who only ran the device flow is refused on **all 28** routes, with a member row present.
|
|
106
|
+
- One new table, one new file, three routes, and one gate option. One extra indexed read per request on the 7 opt-in routes, only when the caller is not the owner.
|
|
107
|
+
- An owner can lock themselves out of nothing: a grantee can never revoke the owner, change the owner, or make grants.
|
|
108
|
+
- **Ownership transfer** has no route today. Whatever adds one must decide what happens to live grants; this ADR does not.
|
|
109
|
+
- The owner needs a place to give access. Task 1004161 builds the API and gate; the manage page's "People with access" card may be split into its own task at claim time, but the grant is not usable by a non-technical owner until it exists.
|
|
110
|
+
|
|
111
|
+
## Alternatives considered
|
|
112
|
+
|
|
113
|
+
- **Admit `member` rows that a project's check-in reported** (ADR 0328 Phase 2). Rejected: the sign-in default is `member`, and check-in is the project's own word, which on an open project is every sign-up.
|
|
114
|
+
- **A `granted` kind inside `<redacted>`.** Rejected: that table's writers include check-in, which can set any kind; a grant must have one writer and no outside reporter.
|
|
115
|
+
- **Let an archon make grants.** Rejected for now (doubt → refuse); the owner can ask for it.
|
|
116
|
+
- **One grant level that admits every route the archon override admits.** Rejected: it would hand teardown and disconnect to anyone the owner added for a restart.
|
|
117
|
+
- **A self-serve "request hosting access" queue.** Not built: it adds a new inbox and a new notification for a request a person can simply make to the owner.
|
|
118
|
+
|
|
119
|
+
## Owner decisions (2026-11-13)
|
|
120
|
+
|
|
121
|
+
1. The design is accepted as written.
|
|
122
|
+
2. An archon may not give access on someone else's project (§2).
|
|
123
|
+
3. A grantee's level, `operate`, is the project read, the settings read, the variable-name list and restart (§3).
|
|
124
|
+
4. Separately, the owner ruled that who may open a project's pages at all is chosen by its founders during genesis, not fixed by a platform default. That is outside this ADR (it is the project's page door, not its hosting) and is filed as an idea for the genesis work.
|
package/docs/adr/README.md
CHANGED
|
@@ -522,3 +522,4 @@ These 20 numbers are each shared by exactly two files. They are **accepted histo
|
|
|
522
522
|
| 0404 | [**A party is a room, and rooms register as kinds** ([task 1005220](https://cloudbongos.com/builders#/task/1005220), goal 1000148). Any group that talks together is a room: a free-form party (cap 8), a chain party (ADR 0400), a scoping session (cap 40, owned by `lifecycle`) or a decision room (cap 40, `government`). Each owner contributes its kind to the `room.kinds` contribution point (a port is single-provider, so four owners would fail the boot) with `members`/`isMember`/optional `invite`/`accept`, and fires `room.opened`/`room.member.joined`/`room.member.left`/`room.closed`; consumers never read an owner's tables and reconcile against `members()`. Free-form party: one per builder, leader handover to the longest-standing member, disband on last leave. A chain room follows an open `chain_party` row. Unlinked players get everything but voice. V3 polls (3 s, cursor + 304, paused when hidden, per-builder limiter) rather than pushing through `/live`. With discord off, no voice key or voice state; chat still works. A room is never authority. Rejected: one port, a port per kind, a rooms module reading other modules' tables, a second party roster, push via `/live`, speaking indicators.](0404-a-party-is-a-room-and-rooms-register-as-kinds.md) | game frame / modules / Discord |
|
|
523
523
|
| 0405 | [**Each character has one colour, and the visor wears it** ([task 1005218](https://cloudbongos.com/builders#/task/1005218), goal 1000142; owner 2026-11-12). The Artisan is ember (the accent), the Artist violet, the Patron gold, each mixed from the hall’s tokens and none typed in. In game mode the visor’s lines, name, picture rim and accent are the held character’s colour, and your picture on the glass is your character wearing the visor. The characters are redrawn at 64 by 64 as two-animal aliens, drawn by a script.](0405-each-character-has-one-colour-and-the-visor-wears-it.md) | hall / game frame |
|
|
524
524
|
| 0406 | [**Every project gets a sandbox that follows main, and live moves only when a person promotes** ([task 1005288](https://cloudbongos.com/builders#/task/1005288), goal 1000106; owner 2026-11-13). One repository, no sandbox branch: a second Render web service on the free plan auto-deploys every landing on `main`, and the live service stops auto-deploying and takes only the commit a person promotes (with the Render key typed in for that request). Fake data and test keys by default; lent real keys should be read-only, behind an outbound gate that refuses sends, charges and writes. Render's free database expires in 30 days, so the sandbox gets a second database in the app's paid one. A project with no app gets no hosted sandbox; Cloud Bongos runs the same rule over its release pin.](0406-every-project-gets-a-sandbox-that-follows-main-and-live-moves-only-when-a-person-promotes.md) | provisioning / hosting |
|
|
525
|
+
| 0407 | [**A hosted project's grant is made by its owner, not witnessed at sign-in** ([task 1004753](https://cloudbongos.com/builders#/task/1004753), goal 1000106; accepted by the owner 2026-11-13). Replaces ADR 0328's suspended Phase 2: no `membership_kind` admits anyone, because sign-in writes `member` for any GitHub user (the device flow included) and check-in is the project's own word. A new `provisioning_project_grants` row, one writer (`grants.js`), created only by the project's owner from their hub session (no archon create; owner, grantee or archon may revoke), read live in `requireProjectOwner` on routes that opt in with `{ grantee: 'operate' }`: the project read, settings read, env-manifest (names only) and restart. Teardown, disconnect, domain, the GitHub/Render/repo-private actions, settings and look writes stay owner-only. Audit actor `api:grantee:<id>`. The sign-in default stays `member`. A visitor or pending applicant gets 403 `project_membership_required` with the join path; a member without a grant gets 403 `project_access_required` (ask the owner); a stranger keeps the 404.](0407-a-project-grant-is-made-by-the-owner-not-witnessed-at-sign-in.md) | provisioning / permissions |
|
package/docs/api/openapi.json
CHANGED
|
@@ -7946,25 +7946,81 @@
|
|
|
7946
7946
|
"description": "Path parameter `id`."
|
|
7947
7947
|
}
|
|
7948
7948
|
],
|
|
7949
|
+
"responses": {
|
|
7950
|
+
"200": {
|
|
7951
|
+
"description": "Success."
|
|
7952
|
+
},
|
|
7953
|
+
"400": {
|
|
7954
|
+
"$ref": "#/components/responses/BadRequest"
|
|
7955
|
+
},
|
|
7956
|
+
"404": {
|
|
7957
|
+
"$ref": "#/components/responses/NotFound"
|
|
7958
|
+
}
|
|
7959
|
+
},
|
|
7960
|
+
"security": []
|
|
7961
|
+
}
|
|
7962
|
+
},
|
|
7963
|
+
"/front-door-guide/conversations/{id}/attach": {
|
|
7964
|
+
"post": {
|
|
7965
|
+
"operationId": "post_front_door_guide_conversations_id_attach",
|
|
7966
|
+
"tags": [
|
|
7967
|
+
"front-door-guide"
|
|
7968
|
+
],
|
|
7969
|
+
"summary": "POST /front-door-guide/conversations/:id/attach",
|
|
7970
|
+
"description": "the visitor has just signed in; the chat moves onto their account.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
|
|
7971
|
+
"x-rank": "any-builder",
|
|
7972
|
+
"x-source": "modules/front-door-guide/routes/front-door-guide-plan.js",
|
|
7973
|
+
"parameters": [
|
|
7974
|
+
{
|
|
7975
|
+
"name": "id",
|
|
7976
|
+
"in": "path",
|
|
7977
|
+
"required": true,
|
|
7978
|
+
"schema": {
|
|
7979
|
+
"type": "string"
|
|
7980
|
+
},
|
|
7981
|
+
"description": "Path parameter `id`."
|
|
7982
|
+
}
|
|
7983
|
+
],
|
|
7984
|
+
"requestBody": {
|
|
7985
|
+
"required": true,
|
|
7986
|
+
"content": {
|
|
7987
|
+
"application/json": {
|
|
7988
|
+
"schema": {
|
|
7989
|
+
"$ref": "#/components/schemas/PostFrontDoorGuideConversationsIdAttachRequest"
|
|
7990
|
+
}
|
|
7991
|
+
}
|
|
7992
|
+
},
|
|
7993
|
+
"x-validated": true
|
|
7994
|
+
},
|
|
7949
7995
|
"responses": {
|
|
7950
7996
|
"200": {
|
|
7951
7997
|
"description": "Success.",
|
|
7952
7998
|
"content": {
|
|
7953
7999
|
"application/json": {
|
|
7954
8000
|
"schema": {
|
|
7955
|
-
"$ref": "#/components/schemas/
|
|
8001
|
+
"$ref": "#/components/schemas/PostFrontDoorGuideConversationsIdAttachResponse"
|
|
7956
8002
|
}
|
|
7957
8003
|
}
|
|
7958
8004
|
}
|
|
7959
8005
|
},
|
|
7960
8006
|
"400": {
|
|
7961
|
-
"$ref": "#/components/responses/
|
|
8007
|
+
"$ref": "#/components/responses/ValidationFailed"
|
|
8008
|
+
},
|
|
8009
|
+
"401": {
|
|
8010
|
+
"$ref": "#/components/responses/Unauthorized"
|
|
8011
|
+
},
|
|
8012
|
+
"403": {
|
|
8013
|
+
"$ref": "#/components/responses/Forbidden"
|
|
7962
8014
|
},
|
|
7963
8015
|
"404": {
|
|
7964
8016
|
"$ref": "#/components/responses/NotFound"
|
|
7965
8017
|
}
|
|
7966
8018
|
},
|
|
7967
|
-
"security": [
|
|
8019
|
+
"security": [
|
|
8020
|
+
{
|
|
8021
|
+
"builderSession": []
|
|
8022
|
+
}
|
|
8023
|
+
]
|
|
7968
8024
|
}
|
|
7969
8025
|
},
|
|
7970
8026
|
"/front-door-guide/conversations/{id}/messages": {
|
|
@@ -8013,6 +8069,90 @@
|
|
|
8013
8069
|
"security": []
|
|
8014
8070
|
}
|
|
8015
8071
|
},
|
|
8072
|
+
"/front-door-guide/conversations/{id}/plan": {
|
|
8073
|
+
"post": {
|
|
8074
|
+
"operationId": "post_front_door_guide_conversations_id_plan",
|
|
8075
|
+
"tags": [
|
|
8076
|
+
"front-door-guide"
|
|
8077
|
+
],
|
|
8078
|
+
"summary": "POST /front-door-guide/conversations/:id/plan",
|
|
8079
|
+
"description": "the visitor has no account yet.\n\n**Rank:** `public` — No authentication — any caller.",
|
|
8080
|
+
"x-rank": "public",
|
|
8081
|
+
"x-source": "modules/front-door-guide/routes/front-door-guide-plan.js",
|
|
8082
|
+
"parameters": [
|
|
8083
|
+
{
|
|
8084
|
+
"name": "id",
|
|
8085
|
+
"in": "path",
|
|
8086
|
+
"required": true,
|
|
8087
|
+
"schema": {
|
|
8088
|
+
"type": "string"
|
|
8089
|
+
},
|
|
8090
|
+
"description": "Path parameter `id`."
|
|
8091
|
+
}
|
|
8092
|
+
],
|
|
8093
|
+
"requestBody": {
|
|
8094
|
+
"required": true,
|
|
8095
|
+
"content": {
|
|
8096
|
+
"application/json": {
|
|
8097
|
+
"schema": {
|
|
8098
|
+
"$ref": "#/components/schemas/PostFrontDoorGuideConversationsIdPlanRequest"
|
|
8099
|
+
}
|
|
8100
|
+
}
|
|
8101
|
+
},
|
|
8102
|
+
"x-validated": true
|
|
8103
|
+
},
|
|
8104
|
+
"responses": {
|
|
8105
|
+
"200": {
|
|
8106
|
+
"description": "Success."
|
|
8107
|
+
},
|
|
8108
|
+
"400": {
|
|
8109
|
+
"$ref": "#/components/responses/ValidationFailed"
|
|
8110
|
+
},
|
|
8111
|
+
"404": {
|
|
8112
|
+
"$ref": "#/components/responses/NotFound"
|
|
8113
|
+
}
|
|
8114
|
+
},
|
|
8115
|
+
"security": []
|
|
8116
|
+
}
|
|
8117
|
+
},
|
|
8118
|
+
"/front-door-guide/mine": {
|
|
8119
|
+
"get": {
|
|
8120
|
+
"operationId": "get_front_door_guide_mine",
|
|
8121
|
+
"tags": [
|
|
8122
|
+
"front-door-guide"
|
|
8123
|
+
],
|
|
8124
|
+
"summary": "GET /front-door-guide/mine",
|
|
8125
|
+
"description": "my newest chat and plan (the creation wizard's pre-fill reads it).\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
|
|
8126
|
+
"x-rank": "any-builder",
|
|
8127
|
+
"x-source": "modules/front-door-guide/routes/front-door-guide-plan.js",
|
|
8128
|
+
"responses": {
|
|
8129
|
+
"200": {
|
|
8130
|
+
"description": "Success.",
|
|
8131
|
+
"content": {
|
|
8132
|
+
"application/json": {
|
|
8133
|
+
"schema": {
|
|
8134
|
+
"$ref": "#/components/schemas/GetFrontDoorGuideMineResponse"
|
|
8135
|
+
}
|
|
8136
|
+
}
|
|
8137
|
+
}
|
|
8138
|
+
},
|
|
8139
|
+
"400": {
|
|
8140
|
+
"$ref": "#/components/responses/BadRequest"
|
|
8141
|
+
},
|
|
8142
|
+
"401": {
|
|
8143
|
+
"$ref": "#/components/responses/Unauthorized"
|
|
8144
|
+
},
|
|
8145
|
+
"403": {
|
|
8146
|
+
"$ref": "#/components/responses/Forbidden"
|
|
8147
|
+
}
|
|
8148
|
+
},
|
|
8149
|
+
"security": [
|
|
8150
|
+
{
|
|
8151
|
+
"builderSession": []
|
|
8152
|
+
}
|
|
8153
|
+
]
|
|
8154
|
+
}
|
|
8155
|
+
},
|
|
8016
8156
|
"/front-door-guide/status": {
|
|
8017
8157
|
"get": {
|
|
8018
8158
|
"operationId": "get_front_door_guide_status",
|
|
@@ -33721,19 +33861,17 @@
|
|
|
33721
33861
|
"count"
|
|
33722
33862
|
]
|
|
33723
33863
|
},
|
|
33724
|
-
"
|
|
33864
|
+
"GetFrontDoorGuideMineResponse": {
|
|
33725
33865
|
"type": "object",
|
|
33726
33866
|
"properties": {
|
|
33727
33867
|
"ok": {
|
|
33728
33868
|
"type": "boolean"
|
|
33729
33869
|
},
|
|
33730
|
-
"conversation": {}
|
|
33731
|
-
"transcript": {}
|
|
33870
|
+
"conversation": {}
|
|
33732
33871
|
},
|
|
33733
33872
|
"required": [
|
|
33734
33873
|
"ok",
|
|
33735
|
-
"conversation"
|
|
33736
|
-
"transcript"
|
|
33874
|
+
"conversation"
|
|
33737
33875
|
]
|
|
33738
33876
|
},
|
|
33739
33877
|
"GetGithubOrgsOrgResponse": {
|
|
@@ -38246,6 +38384,32 @@
|
|
|
38246
38384
|
},
|
|
38247
38385
|
"additionalProperties": false
|
|
38248
38386
|
},
|
|
38387
|
+
"PostFrontDoorGuideConversationsIdAttachRequest": {
|
|
38388
|
+
"type": "object",
|
|
38389
|
+
"properties": {
|
|
38390
|
+
"token": {
|
|
38391
|
+
"type": "string",
|
|
38392
|
+
"maxLength": 64
|
|
38393
|
+
}
|
|
38394
|
+
},
|
|
38395
|
+
"required": [
|
|
38396
|
+
"token"
|
|
38397
|
+
],
|
|
38398
|
+
"additionalProperties": false
|
|
38399
|
+
},
|
|
38400
|
+
"PostFrontDoorGuideConversationsIdAttachResponse": {
|
|
38401
|
+
"type": "object",
|
|
38402
|
+
"properties": {
|
|
38403
|
+
"ok": {
|
|
38404
|
+
"type": "boolean"
|
|
38405
|
+
},
|
|
38406
|
+
"conversation": {}
|
|
38407
|
+
},
|
|
38408
|
+
"required": [
|
|
38409
|
+
"ok",
|
|
38410
|
+
"conversation"
|
|
38411
|
+
]
|
|
38412
|
+
},
|
|
38249
38413
|
"PostFrontDoorGuideConversationsIdMessagesRequest": {
|
|
38250
38414
|
"type": "object",
|
|
38251
38415
|
"properties": {
|
|
@@ -38260,6 +38424,9 @@
|
|
|
38260
38424
|
"website": {
|
|
38261
38425
|
"type": "string",
|
|
38262
38426
|
"maxLength": 500
|
|
38427
|
+
},
|
|
38428
|
+
"starting_point": {
|
|
38429
|
+
"type": "string"
|
|
38263
38430
|
}
|
|
38264
38431
|
},
|
|
38265
38432
|
"required": [
|
|
@@ -38268,12 +38435,32 @@
|
|
|
38268
38435
|
],
|
|
38269
38436
|
"additionalProperties": false
|
|
38270
38437
|
},
|
|
38438
|
+
"PostFrontDoorGuideConversationsIdPlanRequest": {
|
|
38439
|
+
"type": "object",
|
|
38440
|
+
"properties": {
|
|
38441
|
+
"token": {
|
|
38442
|
+
"type": "string",
|
|
38443
|
+
"maxLength": 64
|
|
38444
|
+
},
|
|
38445
|
+
"website": {
|
|
38446
|
+
"type": "string",
|
|
38447
|
+
"maxLength": 500
|
|
38448
|
+
}
|
|
38449
|
+
},
|
|
38450
|
+
"required": [
|
|
38451
|
+
"token"
|
|
38452
|
+
],
|
|
38453
|
+
"additionalProperties": false
|
|
38454
|
+
},
|
|
38271
38455
|
"PostFrontDoorGuideConversationsRequest": {
|
|
38272
38456
|
"type": "object",
|
|
38273
38457
|
"properties": {
|
|
38274
38458
|
"website": {
|
|
38275
38459
|
"type": "string",
|
|
38276
38460
|
"maxLength": 500
|
|
38461
|
+
},
|
|
38462
|
+
"starting_point": {
|
|
38463
|
+
"type": "string"
|
|
38277
38464
|
}
|
|
38278
38465
|
},
|
|
38279
38466
|
"additionalProperties": false
|
|
@@ -44165,9 +44352,9 @@
|
|
|
44165
44352
|
"description": "A required dependency/feature is not configured or is temporarily down."
|
|
44166
44353
|
}
|
|
44167
44354
|
},
|
|
44168
|
-
"x-endpoint-count":
|
|
44169
|
-
"x-schema-count":
|
|
44355
|
+
"x-endpoint-count": 709,
|
|
44356
|
+
"x-schema-count": 745,
|
|
44170
44357
|
"x-undocumented-bodies": 12,
|
|
44171
|
-
"x-response-schemas":
|
|
44358
|
+
"x-response-schemas": 468,
|
|
44172
44359
|
"x-generated-by": "scripts/bongos/gen-api-docs.js"
|
|
44173
44360
|
}
|
package/docs/api-reference.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# Bongos API reference
|
|
4
4
|
|
|
5
|
-
> **Generated from the live route files** — the route file is authoritative.
|
|
5
|
+
> **Generated from the live route files** — the route file is authoritative. 709 endpoints across 120 route files.
|
|
6
6
|
> Machine-readable spec: [`docs/api/openapi.json`](api/openapi.json) (OpenAPI 3.1). Rendered docs site: **`/docs`** (e.g. `cloudbongos.com/docs`).
|
|
7
7
|
|
|
8
8
|
Base path: `/api/bongos`. Ranks (enforced server-side, [ADR 0016](adr/0016-trust-boundary-server-enforced-permissions.md)): `public` < `any-builder` < `metic+archon` < `archon`.
|
|
@@ -328,13 +328,16 @@ Base path: `/api/bongos`. Ranks (enforced server-side, [ADR 0016](adr/0016-trust
|
|
|
328
328
|
| POST | `/api/bongos/founding/stages/:stage/close` | `any-builder` | — | task 1005121: one call. |
|
|
329
329
|
| POST | `/api/bongos/founding/start` | `any-builder` | — | |
|
|
330
330
|
|
|
331
|
-
## `front-door-guide` (
|
|
331
|
+
## `front-door-guide` (7)
|
|
332
332
|
|
|
333
333
|
| Method | Path | Rank | Body | Description |
|
|
334
334
|
|---|---|---|---|---|
|
|
335
|
-
| POST | `/api/bongos/front-door-guide/conversations` | `public` | `website` | start a chat. |
|
|
335
|
+
| POST | `/api/bongos/front-door-guide/conversations` | `public` | `website`, `starting_point` | start a chat. |
|
|
336
336
|
| GET | `/api/bongos/front-door-guide/conversations/:id` | `public` | — | the transcript, for the plan and the sign-up hand-off. |
|
|
337
|
-
| POST | `/api/bongos/front-door-guide/conversations/:id/
|
|
337
|
+
| POST | `/api/bongos/front-door-guide/conversations/:id/attach` | `any-builder` | `token` | the visitor has just signed in; the chat moves onto their account. |
|
|
338
|
+
| POST | `/api/bongos/front-door-guide/conversations/:id/messages` | `public` | `token`, `text`, `website`, `starting_point` | one visitor message, one guide reply. |
|
|
339
|
+
| POST | `/api/bongos/front-door-guide/conversations/:id/plan` | `public` | `token`, `website` | the visitor has no account yet. |
|
|
340
|
+
| GET | `/api/bongos/front-door-guide/mine` | `any-builder` | — | my newest chat and plan (the creation wizard's pre-fill reads it). |
|
|
338
341
|
| GET | `/api/bongos/front-door-guide/status` | `public` | — | the landing asks before it offers the chat. |
|
|
339
342
|
|
|
340
343
|
## `github` (8)
|