@bongos/core 1.19.157 → 1.19.159
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 +43 -33
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +2 -0
- package/clients/bongos-client/index.cjs +2 -0
- package/clients/bongos-client/index.d.ts +3 -0
- package/clients/bongos-client/index.mjs +2 -0
- package/docs/adr/0172-editable-rank-roles-substrate-axis.md +45 -0
- package/docs/adr/README.md +1 -0
- package/docs/api/openapi.json +77 -5
- package/docs/api-reference.md +4 -3
- package/docs/canonical-permissions.md +16 -2
- package/modules/governance/catalog.js +91 -12
- package/modules/governance/db.js +30 -0
- package/modules/governance/routes/governance.js +106 -24
- package/modules/hall-ui/public/governance.css +3 -0
- package/modules/hall-ui/public/governance.html +1 -0
- package/modules/hall-ui/public/governance.js +110 -31
- package/modules/platform-identity/platform-identity.js +31 -12
- package/modules/platform-identity/tests/platform-identity.mjs +18 -1
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/scripts/gds/run-unit-tests.js +1 -1
- package/src/bongos/route-rank-check.js +1 -0
- package/src/module-api.js +5 -1
- package/tests/governance_abuse_matrix.mjs +78 -11
- package/tests/governance_catalog.mjs +51 -0
- package/tests/governance_routes.mjs +72 -12
- package/tests/scouting_rollup_fanout.mjs +113 -0
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.
|
|
6
|
-
"core_contract": "1.19.
|
|
7
|
-
"source_commit": "
|
|
5
|
+
"core_version": "1.19.159",
|
|
6
|
+
"core_contract": "1.19.159",
|
|
7
|
+
"source_commit": "bb03edbdf692de7f7e5a0f93e86cb6b9b8c47804",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-08-16T16:
|
|
9
|
+
"built_at": "2026-08-16T16:34:56.328Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
|
-
"docs_redacted":
|
|
12
|
+
"docs_redacted": 305,
|
|
13
13
|
"agent_docs_stubbed": 21,
|
|
14
|
-
"functional_verbatim":
|
|
14
|
+
"functional_verbatim": 1257,
|
|
15
15
|
"rules": 3,
|
|
16
16
|
"gate_literals": 3,
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
|
-
"file_count":
|
|
20
|
-
"tree_sha256": "
|
|
19
|
+
"file_count": 1583,
|
|
20
|
+
"tree_sha256": "12635cda248f239af4ee99dedef72a321b7af7a827734f397701b7fc4f6a7794",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/blocker-review/SKILL.md",
|
|
@@ -347,12 +347,12 @@
|
|
|
347
347
|
{
|
|
348
348
|
"path": "clients/bongos-client/README.md",
|
|
349
349
|
"mode": "0000644",
|
|
350
|
-
"sha256": "
|
|
350
|
+
"sha256": "c8143e170b5ac87e7cdd243eb83edb8b22ad252382c86544f96e7a38311e0380"
|
|
351
351
|
},
|
|
352
352
|
{
|
|
353
353
|
"path": "clients/bongos-client/bongos-client.global.js",
|
|
354
354
|
"mode": "0000644",
|
|
355
|
-
"sha256": "
|
|
355
|
+
"sha256": "dd11d5decd14af95cc23c2bef8dfe8c477132fb07d671682b228107fe69d7ea4"
|
|
356
356
|
},
|
|
357
357
|
{
|
|
358
358
|
"path": "clients/bongos-client/examples/hello-world.mjs",
|
|
@@ -362,17 +362,17 @@
|
|
|
362
362
|
{
|
|
363
363
|
"path": "clients/bongos-client/index.cjs",
|
|
364
364
|
"mode": "0000644",
|
|
365
|
-
"sha256": "
|
|
365
|
+
"sha256": "257ac54c66f7969090065a34c29e2874fa6f9d111b6e75f0510ab1a2fcb3ef17"
|
|
366
366
|
},
|
|
367
367
|
{
|
|
368
368
|
"path": "clients/bongos-client/index.d.ts",
|
|
369
369
|
"mode": "0000644",
|
|
370
|
-
"sha256": "
|
|
370
|
+
"sha256": "bca3cfecb85aa504c645ef360eb2370e72c417e34d171b40936b6e01d0d94e95"
|
|
371
371
|
},
|
|
372
372
|
{
|
|
373
373
|
"path": "clients/bongos-client/index.mjs",
|
|
374
374
|
"mode": "0000644",
|
|
375
|
-
"sha256": "
|
|
375
|
+
"sha256": "4889c6c70c1f58d90006800cf2bd363b4c2cc546c69042a1faae5106c16aab03"
|
|
376
376
|
},
|
|
377
377
|
{
|
|
378
378
|
"path": "clients/bongos-client/package.json",
|
|
@@ -1334,20 +1334,25 @@
|
|
|
1334
1334
|
"mode": "0000644",
|
|
1335
1335
|
"sha256": "85e5261934dffea5c36fea620a1cef2c472381ba650d45dcf5f005ab6abf554e"
|
|
1336
1336
|
},
|
|
1337
|
+
{
|
|
1338
|
+
"path": "docs/adr/0172-editable-rank-roles-substrate-axis.md",
|
|
1339
|
+
"mode": "0000644",
|
|
1340
|
+
"sha256": "0bfe5ce46139bf9ee236868317cc80ee10eb8747d5606d0dd0fe9f2580ea5177"
|
|
1341
|
+
},
|
|
1337
1342
|
{
|
|
1338
1343
|
"path": "docs/adr/README.md",
|
|
1339
1344
|
"mode": "0000644",
|
|
1340
|
-
"sha256": "
|
|
1345
|
+
"sha256": "6956721fd794123ff5c51909fcee4bd03e1f1c455765ed8805fcf0fd94d92986"
|
|
1341
1346
|
},
|
|
1342
1347
|
{
|
|
1343
1348
|
"path": "docs/api-reference.md",
|
|
1344
1349
|
"mode": "0000644",
|
|
1345
|
-
"sha256": "
|
|
1350
|
+
"sha256": "600c7a58bb59803bea046d379bf1f9c42a03a1c5f05514f4b2edac3b71956b12"
|
|
1346
1351
|
},
|
|
1347
1352
|
{
|
|
1348
1353
|
"path": "docs/api/openapi.json",
|
|
1349
1354
|
"mode": "0000644",
|
|
1350
|
-
"sha256": "
|
|
1355
|
+
"sha256": "0b2a7a1534b1bc4b92b3a2e939e5d5e6a7b8907fa0cd7513e5a3a6311d4318e1"
|
|
1351
1356
|
},
|
|
1352
1357
|
{
|
|
1353
1358
|
"path": "docs/architecture.md",
|
|
@@ -1367,7 +1372,7 @@
|
|
|
1367
1372
|
{
|
|
1368
1373
|
"path": "docs/canonical-permissions.md",
|
|
1369
1374
|
"mode": "0000644",
|
|
1370
|
-
"sha256": "
|
|
1375
|
+
"sha256": "c31eb04a6c76b8a234259cc4796a053433ebf4228af34afb7dfd6460c3df8d64"
|
|
1371
1376
|
},
|
|
1372
1377
|
{
|
|
1373
1378
|
"path": "docs/design-contract.md",
|
|
@@ -2867,7 +2872,7 @@
|
|
|
2867
2872
|
{
|
|
2868
2873
|
"path": "modules/governance/catalog.js",
|
|
2869
2874
|
"mode": "0000644",
|
|
2870
|
-
"sha256": "
|
|
2875
|
+
"sha256": "d40eb5268e7ab16be01078ace7177bb611afaa0b2d0952e7d8afeacbf6eed5eb"
|
|
2871
2876
|
},
|
|
2872
2877
|
{
|
|
2873
2878
|
"path": "modules/governance/config.js",
|
|
@@ -2877,7 +2882,7 @@
|
|
|
2877
2882
|
{
|
|
2878
2883
|
"path": "modules/governance/db.js",
|
|
2879
2884
|
"mode": "0000644",
|
|
2880
|
-
"sha256": "
|
|
2885
|
+
"sha256": "9b8fd44a94c2f563eeac75bfe271cb52a52e22ad6df36a6985460c224445d858"
|
|
2881
2886
|
},
|
|
2882
2887
|
{
|
|
2883
2888
|
"path": "modules/governance/migrations/governance_001_tables.sql",
|
|
@@ -2942,7 +2947,7 @@
|
|
|
2942
2947
|
{
|
|
2943
2948
|
"path": "modules/governance/routes/governance.js",
|
|
2944
2949
|
"mode": "0000644",
|
|
2945
|
-
"sha256": "
|
|
2950
|
+
"sha256": "ecc1ab37c0f3558f1294358fa2e039947d620d538a9aae08d73ce7e695a03773"
|
|
2946
2951
|
},
|
|
2947
2952
|
{
|
|
2948
2953
|
"path": "modules/governance/routes/role-sync.js",
|
|
@@ -3182,17 +3187,17 @@
|
|
|
3182
3187
|
{
|
|
3183
3188
|
"path": "modules/hall-ui/public/governance.css",
|
|
3184
3189
|
"mode": "0000644",
|
|
3185
|
-
"sha256": "
|
|
3190
|
+
"sha256": "f51e53cba5b1e1853de2682ca4909530925547691af93ccc772aa2e3c3598833"
|
|
3186
3191
|
},
|
|
3187
3192
|
{
|
|
3188
3193
|
"path": "modules/hall-ui/public/governance.html",
|
|
3189
3194
|
"mode": "0000644",
|
|
3190
|
-
"sha256": "
|
|
3195
|
+
"sha256": "605a6456c163cbff579d3f742a7da6f54e487b4764e33890c216f65dbd3be92b"
|
|
3191
3196
|
},
|
|
3192
3197
|
{
|
|
3193
3198
|
"path": "modules/hall-ui/public/governance.js",
|
|
3194
3199
|
"mode": "0000644",
|
|
3195
|
-
"sha256": "
|
|
3200
|
+
"sha256": "8cfd83ad23a36a66546ed50493312ed9334b58e55df982895ed049272378c191"
|
|
3196
3201
|
},
|
|
3197
3202
|
{
|
|
3198
3203
|
"path": "modules/hall-ui/public/hall-kit.js",
|
|
@@ -3777,7 +3782,7 @@
|
|
|
3777
3782
|
{
|
|
3778
3783
|
"path": "modules/platform-identity/platform-identity.js",
|
|
3779
3784
|
"mode": "0000644",
|
|
3780
|
-
"sha256": "
|
|
3785
|
+
"sha256": "3d20563833a9daf20d14745f2ce1c5fe530fc918eac03427185998ff9b67beed"
|
|
3781
3786
|
},
|
|
3782
3787
|
{
|
|
3783
3788
|
"path": "modules/platform-identity/pollers/code-sweep.js",
|
|
@@ -3812,7 +3817,7 @@
|
|
|
3812
3817
|
{
|
|
3813
3818
|
"path": "modules/platform-identity/tests/platform-identity.mjs",
|
|
3814
3819
|
"mode": "0000644",
|
|
3815
|
-
"sha256": "
|
|
3820
|
+
"sha256": "727fc8c7533eec1e4af477582bdd6428e9842b930b3cc898bb8a13ddead83ec3"
|
|
3816
3821
|
},
|
|
3817
3822
|
{
|
|
3818
3823
|
"path": "modules/platform-identity/ui/.gitkeep",
|
|
@@ -4187,12 +4192,12 @@
|
|
|
4187
4192
|
{
|
|
4188
4193
|
"path": "package-lock.json",
|
|
4189
4194
|
"mode": "0000644",
|
|
4190
|
-
"sha256": "
|
|
4195
|
+
"sha256": "a5c34ddb4aced104656a9e6210d64de0326743b6f66fda7992a44a95378e42c3"
|
|
4191
4196
|
},
|
|
4192
4197
|
{
|
|
4193
4198
|
"path": "package.json",
|
|
4194
4199
|
"mode": "0000644",
|
|
4195
|
-
"sha256": "
|
|
4200
|
+
"sha256": "2b3da2ee3ca98ac2af38a25c4608e5755cd0953427afc818b96773082ec7d9db"
|
|
4196
4201
|
},
|
|
4197
4202
|
{
|
|
4198
4203
|
"path": "public-docs/index.html",
|
|
@@ -5177,7 +5182,7 @@
|
|
|
5177
5182
|
{
|
|
5178
5183
|
"path": "scripts/gds/run-unit-tests.js",
|
|
5179
5184
|
"mode": "0000644",
|
|
5180
|
-
"sha256": "
|
|
5185
|
+
"sha256": "5e7030e617e4db066136556a43cf030078b0d042ee480c9b7245eea05ab83776"
|
|
5181
5186
|
},
|
|
5182
5187
|
{
|
|
5183
5188
|
"path": "scripts/gds/sandbox-stage.js",
|
|
@@ -5652,7 +5657,7 @@
|
|
|
5652
5657
|
{
|
|
5653
5658
|
"path": "src/bongos/route-rank-check.js",
|
|
5654
5659
|
"mode": "0000644",
|
|
5655
|
-
"sha256": "
|
|
5660
|
+
"sha256": "1802c2589279319abafbda1c3519dff39a43cb52d160b4c4610c6d6f3d79affd"
|
|
5656
5661
|
},
|
|
5657
5662
|
{
|
|
5658
5663
|
"path": "src/bongos/routes.js",
|
|
@@ -5787,7 +5792,7 @@
|
|
|
5787
5792
|
{
|
|
5788
5793
|
"path": "src/module-api.js",
|
|
5789
5794
|
"mode": "0000644",
|
|
5790
|
-
"sha256": "
|
|
5795
|
+
"sha256": "7d48fba3da62904e7f98a55a4617fa4c67d47e9184f83eb3c5ddbb1ca973efd3"
|
|
5791
5796
|
},
|
|
5792
5797
|
{
|
|
5793
5798
|
"path": "src/module-loader/catalog.js",
|
|
@@ -6692,12 +6697,12 @@
|
|
|
6692
6697
|
{
|
|
6693
6698
|
"path": "tests/governance_abuse_matrix.mjs",
|
|
6694
6699
|
"mode": "0000644",
|
|
6695
|
-
"sha256": "
|
|
6700
|
+
"sha256": "8dd44b580b4962e819cc3b0bb5973460fe8bfdba467576af3c51c51219f728cb"
|
|
6696
6701
|
},
|
|
6697
6702
|
{
|
|
6698
6703
|
"path": "tests/governance_catalog.mjs",
|
|
6699
6704
|
"mode": "0000644",
|
|
6700
|
-
"sha256": "
|
|
6705
|
+
"sha256": "d0556eb45a319f27824a6b32722bfc241f1969b64449bfcc64314e04706b46a3"
|
|
6701
6706
|
},
|
|
6702
6707
|
{
|
|
6703
6708
|
"path": "tests/governance_claim_eligibility.mjs",
|
|
@@ -6767,7 +6772,7 @@
|
|
|
6767
6772
|
{
|
|
6768
6773
|
"path": "tests/governance_routes.mjs",
|
|
6769
6774
|
"mode": "0000644",
|
|
6770
|
-
"sha256": "
|
|
6775
|
+
"sha256": "ca9951a6c270e327034f61fb133ad735badc10a88275b35e64294ed9afeecab4"
|
|
6771
6776
|
},
|
|
6772
6777
|
{
|
|
6773
6778
|
"path": "tests/governance_seed.mjs",
|
|
@@ -7519,6 +7524,11 @@
|
|
|
7519
7524
|
"mode": "0000644",
|
|
7520
7525
|
"sha256": "b499ea62cdc1a615e565ce7b0379b2cab83b9aaa8098361f94d3eb93ebc7f927"
|
|
7521
7526
|
},
|
|
7527
|
+
{
|
|
7528
|
+
"path": "tests/scouting_rollup_fanout.mjs",
|
|
7529
|
+
"mode": "0000644",
|
|
7530
|
+
"sha256": "1b58003a1fac89b05acfc3287e529a9e17e2b7a6ec48259b5700f6cc837cc554"
|
|
7531
|
+
},
|
|
7522
7532
|
{
|
|
7523
7533
|
"path": "tests/scrubber_corpus.mjs",
|
|
7524
7534
|
"mode": "0000644",
|
|
@@ -5,7 +5,7 @@ A **generated**, zero-dependency typed client for the Bongos API — produced fr
|
|
|
5
5
|
by hand; it regenerates when the spec changes, so it can never drift from the routes.
|
|
6
6
|
|
|
7
7
|
- API version: **v1** (served at `/api/bongos/v1`)
|
|
8
|
-
-
|
|
8
|
+
- 271 operations across 45 resource groups
|
|
9
9
|
|
|
10
10
|
## Install
|
|
11
11
|
|
|
@@ -355,6 +355,8 @@ function createClient(opts = {}) {
|
|
|
355
355
|
deleteGovernanceRolesRoleKeyPermissionsPermissionKey: (args) => request("DELETE", "/governance/roles/{roleKey}/permissions/{permissionKey}", { hasBody: false }, args),
|
|
356
356
|
// PUT /governance/roles/{roleKey}/permissions/{permissionKey} — rank: archon — PUT /governance/roles/:roleKey/permissions/:permissionKey
|
|
357
357
|
putGovernanceRolesRoleKeyPermissionsPermissionKey: (args) => request("PUT", "/governance/roles/{roleKey}/permissions/{permissionKey}", { hasBody: true }, args),
|
|
358
|
+
// POST /governance/roles/{roleKey}/reset — rank: archon — POST /governance/roles/:roleKey/reset
|
|
359
|
+
postGovernanceRolesRoleKeyReset: (args) => request("POST", "/governance/roles/{roleKey}/reset", { hasBody: true }, args),
|
|
358
360
|
},
|
|
359
361
|
"grades": {
|
|
360
362
|
// GET /grades/by-builder — rank: metic+archon — GET /grades/by-builder
|
|
@@ -354,6 +354,8 @@ function createClient(opts = {}) {
|
|
|
354
354
|
deleteGovernanceRolesRoleKeyPermissionsPermissionKey: (args) => request("DELETE", "/governance/roles/{roleKey}/permissions/{permissionKey}", { hasBody: false }, args),
|
|
355
355
|
// PUT /governance/roles/{roleKey}/permissions/{permissionKey} — rank: archon — PUT /governance/roles/:roleKey/permissions/:permissionKey
|
|
356
356
|
putGovernanceRolesRoleKeyPermissionsPermissionKey: (args) => request("PUT", "/governance/roles/{roleKey}/permissions/{permissionKey}", { hasBody: true }, args),
|
|
357
|
+
// POST /governance/roles/{roleKey}/reset — rank: archon — POST /governance/roles/:roleKey/reset
|
|
358
|
+
postGovernanceRolesRoleKeyReset: (args) => request("POST", "/governance/roles/{roleKey}/reset", { hasBody: true }, args),
|
|
357
359
|
},
|
|
358
360
|
"grades": {
|
|
359
361
|
// GET /grades/by-builder — rank: metic+archon — GET /grades/by-builder
|
|
@@ -220,6 +220,7 @@ export interface PostGoalsResponse { ok: boolean; goal: unknown; succeeds_goal_i
|
|
|
220
220
|
export interface PostGovernanceAssignmentsRequest { builder_id: number; role_key: string }
|
|
221
221
|
export interface PostGovernanceRolesRequest { role_key: string; label: string; description?: string }
|
|
222
222
|
export interface PostGovernanceRolesResponse { role: unknown }
|
|
223
|
+
export interface PostGovernanceRolesRoleKeyResetResponse { ok: boolean; role_key: unknown; seeded_rank: unknown; permission_count: unknown }
|
|
223
224
|
export interface PostInboxIdRatifyRequest { notes_md?: string }
|
|
224
225
|
export interface PostInboxRequest { title: string; body_md?: string; kind?: string; suggested_version?: string; suggested_priority?: number }
|
|
225
226
|
export interface PostInboxResponse { idea: unknown; open_count: unknown; kind_auto: unknown }
|
|
@@ -591,6 +592,8 @@ export interface BongosClient {
|
|
|
591
592
|
deleteGovernanceRolesRoleKeyPermissionsPermissionKey(args?: RequestArgs): Promise<DeleteGovernanceRolesRoleKeyPermissionsPermissionKeyResponse>;
|
|
592
593
|
/** PUT /governance/roles/{roleKey}/permissions/{permissionKey} — rank: archon */
|
|
593
594
|
putGovernanceRolesRoleKeyPermissionsPermissionKey(args?: RequestArgs): Promise<PutGovernanceRolesRoleKeyPermissionsPermissionKeyResponse>;
|
|
595
|
+
/** POST /governance/roles/{roleKey}/reset — rank: archon */
|
|
596
|
+
postGovernanceRolesRoleKeyReset(args?: RequestArgs): Promise<PostGovernanceRolesRoleKeyResetResponse>;
|
|
594
597
|
};
|
|
595
598
|
"grades": {
|
|
596
599
|
/** GET /grades/by-builder — rank: metic+archon */
|
|
@@ -351,6 +351,8 @@ export function createClient(opts = {}) {
|
|
|
351
351
|
deleteGovernanceRolesRoleKeyPermissionsPermissionKey: (args) => request("DELETE", "/governance/roles/{roleKey}/permissions/{permissionKey}", { hasBody: false }, args),
|
|
352
352
|
// PUT /governance/roles/{roleKey}/permissions/{permissionKey} — rank: archon — PUT /governance/roles/:roleKey/permissions/:permissionKey
|
|
353
353
|
putGovernanceRolesRoleKeyPermissionsPermissionKey: (args) => request("PUT", "/governance/roles/{roleKey}/permissions/{permissionKey}", { hasBody: true }, args),
|
|
354
|
+
// POST /governance/roles/{roleKey}/reset — rank: archon — POST /governance/roles/:roleKey/reset
|
|
355
|
+
postGovernanceRolesRoleKeyReset: (args) => request("POST", "/governance/roles/{roleKey}/reset", { hasBody: true }, args),
|
|
354
356
|
},
|
|
355
357
|
"grades": {
|
|
356
358
|
// GET /grades/by-builder — rank: metic+archon — GET /grades/by-builder
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# 0172 — The sub-Archon rank roles are owner-editable; the Archon role is not; the substrate keys are non-grantable to everything
|
|
2
|
+
|
|
3
|
+
- **Status:** Accepted
|
|
4
|
+
- **Date:** 2026-08-16
|
|
5
|
+
- **Task:** [#1002961](https://cloudbongos.com/builders#/task/1002961) (goal 1000030 — configurable roles & permissions)
|
|
6
|
+
- **Amends:** ADR 0151 §2 (the "four seeded rank-roles are immutable through this API" wall — the GRANT half only)
|
|
7
|
+
- **Related:** ADR 0016 (trust boundary — server-enforced, uncached, per request), 0043 (git/ssh trust boundary + the rank floor on protected paths), 0157 (the archon→metic delegation of nineteen operational keys)
|
|
8
|
+
|
|
9
|
+
## Context
|
|
10
|
+
|
|
11
|
+
The governance tab (BV1.R110/R111) let an Archon build custom roles and toggle their operational permissions, but rendered all four **rank roles** — Newcomer/`xenos`, Contributor/`thetes`, Trusted/`metic`, Owner/`archon` — read-only. The owner asked why, and then asked for them to be editable.
|
|
12
|
+
|
|
13
|
+
Three facts made this cheaper and narrower than the read-only rendering suggested:
|
|
14
|
+
|
|
15
|
+
1. **The rank roles' grants were already ordinary rows.** `<redacted>.sql` writes them into `governance_role_permissions` exactly like a custom role's. The immutability was a route-level refusal (`loadMutableRole` → `seeded_role_immutable`) plus a disabled toggle — policy, not structure. Nothing had to be remodelled to make them editable.
|
|
16
|
+
2. **The `system` flag was carrying two different meanings.** ADR 0151 used one boolean for "trust-boundary power," but the twelve `system:true` keys split cleanly in two. Seven are **in-app**: a real route reads them through `requirePermission`, so a grant row genuinely confers them (`builder.rank.set`, `version.close`, `task.confirm.grade_bypass`, `dependency.cross_tier.manage`, `goal.scope.manage`, `governance.manage`, `gate_approval.decide`). Three are **substrate**: `deploy.push_main`, `path.protected.modify`, `infra.ssh` are enforced entirely outside the app by the pre-push hook, `main-audit.js`, the grader pre-pass and credential placement (ADR 0043) — `tests/governance_abuse_matrix.mjs` V10 asserts *no route consumes them* at all. Two are **principal** keys, held by an exact `builders.system_role` identity and by no role ever.
|
|
17
|
+
3. **A grant row for a substrate key is inert.** It resolves honestly and admits nothing (abuse matrix V8/V10/V11). So the interesting question was never "is it dangerous to allow" but "would allowing it be *true*" — and it would not be.
|
|
18
|
+
|
|
19
|
+
The owner was presented with the choice and chose to delegate the in-app Archon powers as well as the operational ones, having been told the escalation consequence explicitly.
|
|
20
|
+
|
|
21
|
+
## Decision
|
|
22
|
+
|
|
23
|
+
**1. The three sub-Archon rank roles are grant-editable; the Archon rank role is not.** `loadMutableRole` splits into `loadGrantEditableRole` (refuses only `seeded_rank === 'archon'`, as `archon_role_immutable`) and `loadAssignableRole` (unchanged). Keeping the Archon role frozen is not symmetry for its own sake — it is the **lockout guard**. `governance.manage` floors at archon, so the Archon rank role is the only role holding it by seed; if that role were editable, a sequence of clicks in this tab could revoke the permission needed to reach this tab. Frozen, no such sequence exists, and the escape hatch (Decision 4) never needs to be reachable without one.
|
|
24
|
+
|
|
25
|
+
**2. `substrate` becomes a declared axis in the catalog, and substrate keys are non-grantable to every role, forever.** `modules/governance/catalog.js` marks the three keys `substrate: true`, asserted at load to imply `system: true`, `floor: 'archon'`, and not-a-principal. This makes the class **data** rather than the hardcoded `SUBSTRATE_KEYS` array the abuse matrix used to carry, so a fourth substrate key is covered by every abuse vector the day it lands. The wall itself is the honesty rule: offering a toggle that writes a row conferring nothing would make the tab lie about the system's shape, which is a worse failure than the missing capability.
|
|
26
|
+
|
|
27
|
+
**3. Grantability becomes per-role: `catalog.isGrantableTo(key, role)`.** Four answers — never (substrate, principal); nothing (the Archon role); everything-but-those (a sub-Archon rank role); operational only (a custom role). `isGrantable(key)` survives unchanged as the narrow custom-role question, which is what `config.js` strips against and what the R96 config vector asserts.
|
|
28
|
+
|
|
29
|
+
**4. Custom roles are NOT widened.** A custom role keeps the operational-only wall. The asymmetry is deliberate and is the reason this delegation is bounded: a rank role is the owner's own coarse ladder, moved only by a rank write (`PATCH /builders/:id/rank`, itself `builder.rank.set`), while a custom role is ad-hoc and assignable to any builder by anyone holding `governance.manage`. Widening both would hand the second group the first group's lever in one step.
|
|
30
|
+
|
|
31
|
+
**5. Rank-role ASSIGNMENT stays rank-derived — unchanged.** `seeded_role_assignment_forbidden` still refuses assigning any seeded rank role, because assignment comes from `builders.rank` via the R95b `syncRankRole`. A second writer here would fork authority truth and be silently undone on the next rank write.
|
|
32
|
+
|
|
33
|
+
**6. A reset escape hatch: `POST /governance/roles/:roleKey/reset`.** Restores a rank role to `catalog.seedForRank(seeded_rank)` — the same floor derivation `governance_002` seeded from, so it cannot drift from the catalog even after later floor edits. It exists because a rank's grant set is ~60 rows: without it, a drifted rank has no practical way back, and "what does Trusted mean by default" becomes a question only a migration can answer. Written in one transaction (a crash between the delete and the insert would leave every builder at that rank holding nothing), filtered through `isGrantableTo` so reset can never write what the toggle refuses, and refused on a custom role (`role_has_no_default`) — a custom role's "default" is the empty set it was created with, and clearing grants under the name *restore* would be a destructive verb in a restorative costume.
|
|
34
|
+
|
|
35
|
+
## Consequences
|
|
36
|
+
|
|
37
|
+
**The escalation chain is now open by owner decision, and that is the point.** A rank role granted `governance.manage` can grant its own rank `builder.rank.set`, and thereby reach every in-app Archon power. The owner was shown this consequence in the words "anyone you give role-editing to can then grant themselves the rest" and chose it. It is Archon-set policy, not an oversight — and it stops at the substrate: no amount of in-app escalation produces a `git push origin main`, a protected-path edit below the Metic floor, or an ssh session, because ADR 0043's enforcers read the rank ladder and never consult this database.
|
|
38
|
+
|
|
39
|
+
**ADR 0151's parity guarantee now describes the seed, not the live system.** R103's "role(rank) reproduces `requireRank(rank)` exactly" remains true of `RANK_ROLE_SEED` and is still asserted (`tests/governance_seed.mjs`, `governance_require_permission.mjs` — both read the derived constant). It is no longer true of a *live* instance whose owner has edited a rank, and it was never meant to be after that point: making the ladder configurable is the goal, and `requireRank` is the legacy gate the R104–R109 migration is retiring anyway.
|
|
40
|
+
|
|
41
|
+
**One-boolean grantability is gone from the tab's rendering.** `GET /governance/permissions` now returns `substrate` alongside `system` and `grantable`, and the hall tab groups rows into Operational / Owner powers / <redacted>, computing the lock per selected role. A client that reads only `grantable` would under-render the rank-role case; nothing else consumes that endpoint today.
|
|
42
|
+
|
|
43
|
+
**A drifted rank role is invisible to the catalog.** Nothing warns that `metic`'s live grants differ from `seedForRank('metic')` — the tab shows what is, not what the ladder shipped. If that becomes confusing, the follow-up is a "differs from default" badge on the role list, cheap to add on top of the same two arrays; it was left out here because reset (Decision 6) already answers the operational need.
|
|
44
|
+
|
|
45
|
+
**Rejected: allowing the substrate keys on a rank role behind a warning.** It would produce a toggle that succeeds, persists, and delegates nothing — the failure mode of a permissions UI that is worst for exactly the reader who trusts it. Rejected: making the Archon role editable with a "you may lock yourself out" confirm — a confirm is not a guard, and the recovery for a real lockout is a hand-written SQL statement on the droplet.
|
package/docs/adr/README.md
CHANGED
|
@@ -243,6 +243,7 @@ This keeps the decision history honest and traceable.
|
|
|
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
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 |
|
|
246
|
+
| 0172 | [**The sub-Archon rank roles are owner-editable; the Archon role is not; the substrate keys are non-grantable to everything** ([task 1002961](https://cloudbongos.com/builders#/task/1002961), goal 1000030). Amends ADR 0151 §2's "the four seeded rank-roles are immutable through this API" wall — the GRANT half only. Their grants were always ordinary `governance_role_permissions` rows (the `governance_002` seed writes them); the read-only rendering was route policy, not structure. Three walls now, where there was one: the **Archon** rank-role stays frozen as the LOCKOUT GUARD (it is the only role holding `governance.manage` by seed, so freezing it means no click sequence can revoke the permission needed to reach this tab); the three **sub-Archon** rank-roles become grant-editable, the seven **in-app** Archon powers included (owner's explicit choice, escalation consequence stated); and `substrate` becomes a declared catalog axis — `deploy.push_main`/`path.protected.modify`/`infra.ssh` are non-grantable to EVERY role forever, because ADR 0043 enforces them out of band, so a grant row confers nothing and the toggle would be the UI lying. Grantability goes per-role (`isGrantableTo(key, role)`); `isGrantable(key)` survives as the narrow custom-role question config.js strips against. **Custom roles are deliberately NOT widened** — a rank-role moves only by a rank write, a custom role is assignable by anyone holding `governance.manage`. Assignment stays rank-derived (R95b) untouched. Adds `POST /governance/roles/:roleKey/reset` (one transaction, filtered through `isGrantableTo`, refused on a custom role) because a drifted ~60-row rank has no way back by clicking. Rejected: substrate keys behind a warning (a toggle that succeeds and delegates nothing), an editable Archon role behind a confirm (a confirm is not a guard; real recovery is hand-written SQL on the droplet).](<redacted>.md) | governance / permissions |
|
|
246
247
|
| 0171 | [**Profile ↔ cross-project rollup: the join runs in-process on the hub; consent is fail-closed account state; the hide flag lives on the account, enforced in the read path** ([task 1002277](https://cloudbongos.com/builders#/task/1002277), R01 of goal 1000045). The C1 cross-project profile reads the platform-identity rollup through the module's **declared-but-never-registered `platform-identity.hub` port**: the module registers a real provider and core `GET /builders/:id/profile` `resolveOptional`s it beside `reward` — zero network, `null` (clean degrade) on every non-hub instance. Federated instances **link out** instead of inlining (amends profile-page spec Decision 1: an instance-side copy is either stale-on-hide or a per-view hub call). Consent: `profile_state` on `platform_identity_accounts`, **fail-closed** — `active` iff touched by a hub-session `/sso/authorize` (device-poll/check-in create `provisional`; backfill = all provisional, lazy upgrade; no core-`builders` join — type-unsafe vs `system:*` ids and a module-migration boundary breach), so the P03 owner decision can only ever widen, never claw back. The C2 hide flag is `hide_stats` on the account (NOT per-instance `behavior_prefs`), enforced **inside** the module's read functions — `listScoutingRollup` itself gains the `active AND NOT hide_stats` predicate so the live Metic-floor `/people`+`/scouting` surfaces honor it by construction. Hub profile responses go `no-store` (hide must beat the 60s cache). Publishing is gated on hardening the self-reported rollup (membership-row requirement, rank-vocab validation, disciplines caps, check-in→rollup sequencing). Found + filed: the `LATERAL unnest` fan-out inflating rollup sums (task 1002911), the unreserved `u` slug (task 1002912), the missing platform T&C artifact (task 1002913), criterion amendments (task 1002914). Rejected: instance→hub per-view pull (always-on dependency), hub→instance mirroring (stale-on-hide), storing the flag in builder-settings, backfilling `active` from hub `builders` rows.](<redacted>.md) | platform identity / profiles / privacy |
|
|
247
248
|
| 0170 | [**The featured projection tells time honestly: `created_at` in, `updated_at` out, owner from the project's own archon** ([task 1002778](https://cloudbongos.com/builders#/task/1002778), goal 1000061). The hub card's metadata pass added `created_at` to the public `/projects/featured` projection, labelled **Listed** — it is the moment the hub catalogued the row, never when the project began, and the C6 card test bans the word "Created". Two recorded deviations: (1) the task brief said `updated_at` was ready to add, but the column is bumped by every client re-registration AND the Archon's feature toggle, so as a public "last updated" it would report curation noise as project activity — it may sort, never project, and the test pins the SELECT list itself (`SELECT *` banned: the route is CORS-open rank:public and a star would leak `owner_github_id`/`status`). (2) The owner fact reads the project's OWN leaderboard archon row (login + avatar, type-checked and capped) from the payload project-stats already fetches — zero extra requests, the 2-fetch pin intact, the advisory `owner_github_id` column still unread per the design contract. Multi-archon: top-credits archon is named; a fallen-off-the-page archon renders as no owner shown, never a wrong one. Rejected: projecting `updated_at` as asked (it lies), reading `owner_github_id` (advisory, contract-banned), a third per-origin fetch for activity recency (moves the test pin for a fact nobody asked for yet).](<redacted>.md) | platform identity / public surface |
|
|
248
249
|
|
package/docs/api/openapi.json
CHANGED
|
@@ -5252,7 +5252,7 @@
|
|
|
5252
5252
|
"governance"
|
|
5253
5253
|
],
|
|
5254
5254
|
"summary": "GET /governance/permissions",
|
|
5255
|
-
"description": "governance.manage — the permission catalog + principals, straight from code (the catalog is CODE, not a table); no DB read.
|
|
5255
|
+
"description": "governance.manage — the permission catalog + principals, straight from code (the catalog is CODE, not a table); no DB read. Principals are read-only by construction. Three flags the tab renders its locks from, and the tab must read all three rather than just `grantable`, because since ADR 0172 grantability depends on WHICH role is selected: • `grantable` — the CUSTOM-role answer (system:false). Unchanged meaning. • `system` — an in-app Archon power: locked on a custom role, togglable on a sub-Archon rank-role. • `substrate` — locked EVERYWHERE, on every role, forever (ADR 0043).\n\n**Rank:** `archon` — Archon only (rank and identity management + the escalation keys — the trust boundary).\n\n**Permissions:** `governance.manage` (all required).",
|
|
5256
5256
|
"x-rank": "archon",
|
|
5257
5257
|
"x-source": "modules/governance/routes/governance.js",
|
|
5258
5258
|
"x-permissions": [
|
|
@@ -5384,7 +5384,7 @@
|
|
|
5384
5384
|
"governance"
|
|
5385
5385
|
],
|
|
5386
5386
|
"summary": "PUT /governance/roles/:roleKey/permissions/:permissionKey",
|
|
5387
|
-
"description": "governance.manage — grant one
|
|
5387
|
+
"description": "governance.manage — grant one permission to a role (the toggle's ON half). The order of refusals is the authority order: role exists → role is not the Archon one → the key exists → the key is grantable TO THIS ROLE.\n\n**Rank:** `archon` — Archon only (rank and identity management + the escalation keys — the trust boundary).\n\n**Permissions:** `governance.manage` (all required).",
|
|
5388
5388
|
"x-rank": "archon",
|
|
5389
5389
|
"x-source": "modules/governance/routes/governance.js",
|
|
5390
5390
|
"x-permissions": [
|
|
@@ -5503,6 +5503,61 @@
|
|
|
5503
5503
|
]
|
|
5504
5504
|
}
|
|
5505
5505
|
},
|
|
5506
|
+
"/governance/roles/{roleKey}/reset": {
|
|
5507
|
+
"post": {
|
|
5508
|
+
"operationId": "post_governance_roles_roleKey_reset",
|
|
5509
|
+
"tags": [
|
|
5510
|
+
"governance"
|
|
5511
|
+
],
|
|
5512
|
+
"summary": "POST /governance/roles/:roleKey/reset",
|
|
5513
|
+
"description": "governance.manage — restore a SEEDED rank-role to its catalog default (ADR 0172). The counterweight to making the rank-roles editable: a rank's grant set is ~60 rows, so an owner who drifts one has no practical way back by clicking, and \"what does Trusted mean by default\" is a question only the catalog can answer. Restores catalog.seedForRank(role.seeded_rank) — the same derivation governance_002 seeded from, so the answer cannot drift from the floors even after later catalog edits. Deliberately NOT offered for a CUSTOM role: a custom role has no catalog default to return to (its \"default\" is the empty set it was created with), so the honest verb there is revoking the grants, which the toggle already does.\n\n**Rank:** `archon` — Archon only (rank and identity management + the escalation keys — the trust boundary).\n\n**Permissions:** `governance.manage` (all required).",
|
|
5514
|
+
"x-rank": "archon",
|
|
5515
|
+
"x-source": "modules/governance/routes/governance.js",
|
|
5516
|
+
"x-permissions": [
|
|
5517
|
+
"governance.manage"
|
|
5518
|
+
],
|
|
5519
|
+
"parameters": [
|
|
5520
|
+
{
|
|
5521
|
+
"name": "roleKey",
|
|
5522
|
+
"in": "path",
|
|
5523
|
+
"required": true,
|
|
5524
|
+
"schema": {
|
|
5525
|
+
"type": "string"
|
|
5526
|
+
},
|
|
5527
|
+
"description": "Path parameter `roleKey`."
|
|
5528
|
+
}
|
|
5529
|
+
],
|
|
5530
|
+
"responses": {
|
|
5531
|
+
"200": {
|
|
5532
|
+
"description": "Success.",
|
|
5533
|
+
"content": {
|
|
5534
|
+
"application/json": {
|
|
5535
|
+
"schema": {
|
|
5536
|
+
"$ref": "#/components/schemas/PostGovernanceRolesRoleKeyResetResponse"
|
|
5537
|
+
}
|
|
5538
|
+
}
|
|
5539
|
+
}
|
|
5540
|
+
},
|
|
5541
|
+
"400": {
|
|
5542
|
+
"$ref": "#/components/responses/BadRequest"
|
|
5543
|
+
},
|
|
5544
|
+
"401": {
|
|
5545
|
+
"$ref": "#/components/responses/Unauthorized"
|
|
5546
|
+
},
|
|
5547
|
+
"403": {
|
|
5548
|
+
"$ref": "#/components/responses/Forbidden"
|
|
5549
|
+
},
|
|
5550
|
+
"404": {
|
|
5551
|
+
"$ref": "#/components/responses/NotFound"
|
|
5552
|
+
}
|
|
5553
|
+
},
|
|
5554
|
+
"security": [
|
|
5555
|
+
{
|
|
5556
|
+
"builderSession": []
|
|
5557
|
+
}
|
|
5558
|
+
]
|
|
5559
|
+
}
|
|
5560
|
+
},
|
|
5506
5561
|
"/grades/by-builder": {
|
|
5507
5562
|
"get": {
|
|
5508
5563
|
"operationId": "get_grades_by_builder",
|
|
@@ -15316,6 +15371,23 @@
|
|
|
15316
15371
|
"role"
|
|
15317
15372
|
]
|
|
15318
15373
|
},
|
|
15374
|
+
"PostGovernanceRolesRoleKeyResetResponse": {
|
|
15375
|
+
"type": "object",
|
|
15376
|
+
"properties": {
|
|
15377
|
+
"ok": {
|
|
15378
|
+
"type": "boolean"
|
|
15379
|
+
},
|
|
15380
|
+
"role_key": {},
|
|
15381
|
+
"seeded_rank": {},
|
|
15382
|
+
"permission_count": {}
|
|
15383
|
+
},
|
|
15384
|
+
"required": [
|
|
15385
|
+
"ok",
|
|
15386
|
+
"role_key",
|
|
15387
|
+
"seeded_rank",
|
|
15388
|
+
"permission_count"
|
|
15389
|
+
]
|
|
15390
|
+
},
|
|
15319
15391
|
"PostInboxIdRatifyRequest": {
|
|
15320
15392
|
"type": "object",
|
|
15321
15393
|
"properties": {
|
|
@@ -17036,9 +17108,9 @@
|
|
|
17036
17108
|
"description": "A required dependency/feature is not configured or is temporarily down."
|
|
17037
17109
|
}
|
|
17038
17110
|
},
|
|
17039
|
-
"x-endpoint-count":
|
|
17040
|
-
"x-schema-count":
|
|
17111
|
+
"x-endpoint-count": 271,
|
|
17112
|
+
"x-schema-count": 304,
|
|
17041
17113
|
"x-undocumented-bodies": 8,
|
|
17042
|
-
"x-response-schemas":
|
|
17114
|
+
"x-response-schemas": 206,
|
|
17043
17115
|
"x-generated-by": "scripts/gds/gen-api-docs.js"
|
|
17044
17116
|
}
|
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. 271 endpoints across 44 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/<redacted>.md)): `public` < `any-builder` < `metic+archon` < `archon`.
|
|
@@ -214,7 +214,7 @@ Base path: `/api/bongos`. Ranks (enforced server-side, [ADR 0016](adr/<redacted>
|
|
|
214
214
|
| GET | `/api/bongos/goals/inbox` | `any-builder` | — | GET /goals/inbox — the caller's private-goals inbox (task 1952 / ADR 0112): { invitations: invites addressed to me (accept/decline); join… |
|
|
215
215
|
| POST | `/api/bongos/goals/reorder` | `metic+archon` | `version_id`, `ordered_ids` | POST /goals/reorder — Archon drag-to-reorder: set the priority order of every goal WITHIN one version (task 1760). |
|
|
216
216
|
|
|
217
|
-
## `governance` (
|
|
217
|
+
## `governance` (9)
|
|
218
218
|
|
|
219
219
|
| Method | Path | Rank | Body | Description |
|
|
220
220
|
|---|---|---|---|---|
|
|
@@ -224,8 +224,9 @@ Base path: `/api/bongos`. Ranks (enforced server-side, [ADR 0016](adr/<redacted>
|
|
|
224
224
|
| GET | `/api/bongos/governance/permissions` | `archon` | — | governance.manage — the permission catalog + principals, straight from code (the catalog is CODE, not a table); no DB read. |
|
|
225
225
|
| GET | `/api/bongos/governance/roles` | `archon` | — | governance.manage — the full role list with each role's grants. |
|
|
226
226
|
| POST | `/api/bongos/governance/roles` | `archon` | `role_key`, `label`, `description` | governance.manage — create a CUSTOM role (is_system false, no seeded_rank — the seeded four exist only via governance_002). |
|
|
227
|
-
| PUT | `/api/bongos/governance/roles/:roleKey/permissions/:permissionKey` | `archon` | — | governance.manage — grant one
|
|
227
|
+
| PUT | `/api/bongos/governance/roles/:roleKey/permissions/:permissionKey` | `archon` | — | governance.manage — grant one permission to a role (the toggle's ON half). |
|
|
228
228
|
| DELETE | `/api/bongos/governance/roles/:roleKey/permissions/:permissionKey` | `archon` | — | governance.manage — revoke a grant (the toggle's OFF half). |
|
|
229
|
+
| POST | `/api/bongos/governance/roles/:roleKey/reset` | `archon` | — | governance.manage — restore a SEEDED rank-role to its catalog default (ADR 0172). |
|
|
229
230
|
|
|
230
231
|
## `grades` (1)
|
|
231
232
|
|
|
@@ -147,8 +147,22 @@ or missing ranks fail closed.
|
|
|
147
147
|
past the failed grade, approve their own red gate — so keeping rank Archon-only depends on keeping
|
|
148
148
|
these Archon-only too. **Any future delegation must re-check that chain.**
|
|
149
149
|
- plus `version.close` (paired with rank as the powers that never delegate), the cross-tier
|
|
150
|
-
`POST|DELETE /api/gds/dependencies` (`system: true`
|
|
151
|
-
|
|
150
|
+
`POST|DELETE /api/gds/dependencies` (`system: true`), and the substrate floors
|
|
151
|
+
(`deploy.push_main` / `path.protected.modify` / `infra.ssh`, ADR 0043).
|
|
152
|
+
|
|
153
|
+
> **Amended by [ADR 0172](adr/<redacted>.md) (owner decision, 2026-08-16).**
|
|
154
|
+
> The paragraphs above describe the SEEDED ladder, which is still what a fresh instance boots with — but
|
|
155
|
+
> they are no longer a guarantee about a running one. The owner made the three sub-Archon **rank roles**
|
|
156
|
+
> editable from the governance tab and chose to include the seven in-app Archon powers, `governance.manage`
|
|
157
|
+
> and `builder.rank.set` among them. So "no system permission is grantable below archon" now reads:
|
|
158
|
+
> *no system permission is grantable to a CUSTOM role, and none is seeded below archon* — an Archon may
|
|
159
|
+
> nonetheless hand one to a rank role deliberately. The four-step escalation chain described above is
|
|
160
|
+
> therefore **open by decision, not by accident**: whoever holds `governance.manage` can reach the rest.
|
|
161
|
+
> Two things that did NOT move, and that bound the blast radius: the **Archon** rank-role is immutable
|
|
162
|
+
> (nothing clickable revokes the permission needed to reach the tab), and the three **substrate** keys stay
|
|
163
|
+
> non-grantable to every role — ADR 0043's enforcers read the rank ladder and never consult this database,
|
|
164
|
+
> so no amount of in-app escalation produces a push to `main`, a protected-path edit, or an ssh session.
|
|
165
|
+
> `builders.rank` remains the only writer of who holds a rank role (R95b).
|
|
152
166
|
|
|
153
167
|
Earlier one-power delegations that ADR 0157 generalises: **task authoring** — `POST /api/gds/tasks`,
|
|
154
168
|
`PATCH /api/gds/tasks/:id` (incl. the `newcomer_friendly` flag), `POST /api/gds/tasks/:id/promote`, and the
|