@bongos/core 1.19.418 → 1.19.419

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.
@@ -77,6 +77,10 @@
77
77
  "name": "closeness",
78
78
  "description": "Endpoints under `/api/bongos/closeness`."
79
79
  },
80
+ {
81
+ "name": "connections",
82
+ "description": "Endpoints under `/api/bongos/connections`."
83
+ },
80
84
  {
81
85
  "name": "copy-desk",
82
86
  "description": "Endpoints under `/api/bongos/copy-desk`."
@@ -3435,6 +3439,330 @@
3435
3439
  ]
3436
3440
  }
3437
3441
  },
3442
+ "/connections": {
3443
+ "get": {
3444
+ "operationId": "get_connections",
3445
+ "tags": [
3446
+ "connections"
3447
+ ],
3448
+ "summary": "GET /connections",
3449
+ "description": "MY accepted connections (own data, keyed on the session). Own scope, so the counterparty floors on profile_state alone: a private account you connected with appears here and in no public list (D4 §2).\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
3450
+ "x-rank": "any-builder",
3451
+ "x-source": "modules/platform-identity/routes/connections.js",
3452
+ "responses": {
3453
+ "200": {
3454
+ "description": "Success.",
3455
+ "content": {
3456
+ "application/json": {
3457
+ "schema": {
3458
+ "$ref": "#/components/schemas/GetConnectionsResponse"
3459
+ }
3460
+ }
3461
+ }
3462
+ },
3463
+ "400": {
3464
+ "$ref": "#/components/responses/BadRequest"
3465
+ },
3466
+ "401": {
3467
+ "$ref": "#/components/responses/Unauthorized"
3468
+ },
3469
+ "403": {
3470
+ "$ref": "#/components/responses/Forbidden"
3471
+ }
3472
+ },
3473
+ "security": [
3474
+ {
3475
+ "builderSession": []
3476
+ }
3477
+ ]
3478
+ },
3479
+ "post": {
3480
+ "operationId": "post_connections",
3481
+ "tags": [
3482
+ "connections"
3483
+ ],
3484
+ "summary": "POST /connections",
3485
+ "description": "ask to connect. The REQUESTER is always the session; the body names only who is being asked, and it names them by public handle.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
3486
+ "x-rank": "any-builder",
3487
+ "x-source": "modules/platform-identity/routes/connections.js",
3488
+ "requestBody": {
3489
+ "required": true,
3490
+ "content": {
3491
+ "application/json": {
3492
+ "schema": {
3493
+ "$ref": "#/components/schemas/PostConnectionsRequest"
3494
+ }
3495
+ }
3496
+ },
3497
+ "x-validated": true
3498
+ },
3499
+ "responses": {
3500
+ "200": {
3501
+ "description": "Success.",
3502
+ "content": {
3503
+ "application/json": {
3504
+ "schema": {
3505
+ "$ref": "#/components/schemas/PostConnectionsResponse"
3506
+ }
3507
+ }
3508
+ }
3509
+ },
3510
+ "400": {
3511
+ "$ref": "#/components/responses/ValidationFailed"
3512
+ },
3513
+ "401": {
3514
+ "$ref": "#/components/responses/Unauthorized"
3515
+ },
3516
+ "403": {
3517
+ "$ref": "#/components/responses/Forbidden"
3518
+ }
3519
+ },
3520
+ "security": [
3521
+ {
3522
+ "builderSession": []
3523
+ }
3524
+ ]
3525
+ }
3526
+ },
3527
+ "/connections/degree/{handle}": {
3528
+ "get": {
3529
+ "operationId": "get_connections_degree_handle",
3530
+ "tags": [
3531
+ "connections"
3532
+ ],
3533
+ "summary": "GET /connections/degree/:handle",
3534
+ "description": "the degree label between the CALLER and one handle, plus the mutuals it was computed from (own-scope read, D4 §4). Bounded by construction: degreeBetween caps every level and answers null past the caps.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
3535
+ "x-rank": "any-builder",
3536
+ "x-source": "modules/platform-identity/routes/connections.js",
3537
+ "parameters": [
3538
+ {
3539
+ "name": "handle",
3540
+ "in": "path",
3541
+ "required": true,
3542
+ "schema": {
3543
+ "type": "string"
3544
+ },
3545
+ "description": "Path parameter `handle`."
3546
+ }
3547
+ ],
3548
+ "responses": {
3549
+ "200": {
3550
+ "description": "Success.",
3551
+ "content": {
3552
+ "application/json": {
3553
+ "schema": {
3554
+ "$ref": "#/components/schemas/GetConnectionsDegreeHandleResponse"
3555
+ }
3556
+ }
3557
+ }
3558
+ },
3559
+ "400": {
3560
+ "$ref": "#/components/responses/BadRequest"
3561
+ },
3562
+ "401": {
3563
+ "$ref": "#/components/responses/Unauthorized"
3564
+ },
3565
+ "403": {
3566
+ "$ref": "#/components/responses/Forbidden"
3567
+ },
3568
+ "404": {
3569
+ "$ref": "#/components/responses/NotFound"
3570
+ }
3571
+ },
3572
+ "security": [
3573
+ {
3574
+ "builderSession": []
3575
+ }
3576
+ ]
3577
+ }
3578
+ },
3579
+ "/connections/requests": {
3580
+ "get": {
3581
+ "operationId": "get_connections_requests",
3582
+ "tags": [
3583
+ "connections"
3584
+ ],
3585
+ "summary": "GET /connections/requests",
3586
+ "description": "MY pending requests, both directions (own data).\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
3587
+ "x-rank": "any-builder",
3588
+ "x-source": "modules/platform-identity/routes/connections.js",
3589
+ "responses": {
3590
+ "200": {
3591
+ "description": "Success.",
3592
+ "content": {
3593
+ "application/json": {
3594
+ "schema": {
3595
+ "$ref": "#/components/schemas/GetConnectionsRequestsResponse"
3596
+ }
3597
+ }
3598
+ }
3599
+ },
3600
+ "400": {
3601
+ "$ref": "#/components/responses/BadRequest"
3602
+ },
3603
+ "401": {
3604
+ "$ref": "#/components/responses/Unauthorized"
3605
+ },
3606
+ "403": {
3607
+ "$ref": "#/components/responses/Forbidden"
3608
+ }
3609
+ },
3610
+ "security": [
3611
+ {
3612
+ "builderSession": []
3613
+ }
3614
+ ]
3615
+ }
3616
+ },
3617
+ "/connections/{id}": {
3618
+ "delete": {
3619
+ "operationId": "delete_connections_id",
3620
+ "tags": [
3621
+ "connections"
3622
+ ],
3623
+ "summary": "DELETE /connections/:id",
3624
+ "description": "remove an accepted connection (either side may end it) or cancel a pending request I sent. An ADDRESSEE ending a pending row is a decline, not a delete, and is 404'd here so the cooldown cannot be dodged.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
3625
+ "x-rank": "any-builder",
3626
+ "x-source": "modules/platform-identity/routes/connections.js",
3627
+ "parameters": [
3628
+ {
3629
+ "name": "id",
3630
+ "in": "path",
3631
+ "required": true,
3632
+ "schema": {
3633
+ "type": "string"
3634
+ },
3635
+ "description": "Path parameter `id`."
3636
+ }
3637
+ ],
3638
+ "responses": {
3639
+ "200": {
3640
+ "description": "Success.",
3641
+ "content": {
3642
+ "application/json": {
3643
+ "schema": {
3644
+ "$ref": "#/components/schemas/DeleteConnectionsIdResponse"
3645
+ }
3646
+ }
3647
+ }
3648
+ },
3649
+ "400": {
3650
+ "$ref": "#/components/responses/BadRequest"
3651
+ },
3652
+ "401": {
3653
+ "$ref": "#/components/responses/Unauthorized"
3654
+ },
3655
+ "403": {
3656
+ "$ref": "#/components/responses/Forbidden"
3657
+ },
3658
+ "404": {
3659
+ "$ref": "#/components/responses/NotFound"
3660
+ }
3661
+ },
3662
+ "security": [
3663
+ {
3664
+ "builderSession": []
3665
+ }
3666
+ ]
3667
+ }
3668
+ },
3669
+ "/connections/{id}/accept": {
3670
+ "post": {
3671
+ "operationId": "post_connections_id_accept",
3672
+ "tags": [
3673
+ "connections"
3674
+ ],
3675
+ "summary": "POST /connections/:id/accept",
3676
+ "description": "accept a request addressed to ME. The addressee is the session; a row belonging to anyone else simply does not match, and the 404 that follows is what stops a builder acting on someone else's connection.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
3677
+ "x-rank": "any-builder",
3678
+ "x-source": "modules/platform-identity/routes/connections.js",
3679
+ "parameters": [
3680
+ {
3681
+ "name": "id",
3682
+ "in": "path",
3683
+ "required": true,
3684
+ "schema": {
3685
+ "type": "string"
3686
+ },
3687
+ "description": "Path parameter `id`."
3688
+ }
3689
+ ],
3690
+ "responses": {
3691
+ "200": {
3692
+ "description": "Success.",
3693
+ "content": {
3694
+ "application/json": {
3695
+ "schema": {
3696
+ "$ref": "#/components/schemas/PostConnectionsIdAcceptResponse"
3697
+ }
3698
+ }
3699
+ }
3700
+ },
3701
+ "400": {
3702
+ "$ref": "#/components/responses/BadRequest"
3703
+ },
3704
+ "401": {
3705
+ "$ref": "#/components/responses/Unauthorized"
3706
+ },
3707
+ "403": {
3708
+ "$ref": "#/components/responses/Forbidden"
3709
+ },
3710
+ "404": {
3711
+ "$ref": "#/components/responses/NotFound"
3712
+ }
3713
+ },
3714
+ "security": [
3715
+ {
3716
+ "builderSession": []
3717
+ }
3718
+ ]
3719
+ }
3720
+ },
3721
+ "/connections/{id}/decline": {
3722
+ "post": {
3723
+ "operationId": "post_connections_id_decline",
3724
+ "tags": [
3725
+ "connections"
3726
+ ],
3727
+ "summary": "POST /connections/:id/decline",
3728
+ "description": "decline a request addressed to ME. 204: the requester is never told, so there is nothing to return that they could ever observe.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
3729
+ "x-rank": "any-builder",
3730
+ "x-source": "modules/platform-identity/routes/connections.js",
3731
+ "parameters": [
3732
+ {
3733
+ "name": "id",
3734
+ "in": "path",
3735
+ "required": true,
3736
+ "schema": {
3737
+ "type": "string"
3738
+ },
3739
+ "description": "Path parameter `id`."
3740
+ }
3741
+ ],
3742
+ "responses": {
3743
+ "200": {
3744
+ "description": "Success."
3745
+ },
3746
+ "400": {
3747
+ "$ref": "#/components/responses/BadRequest"
3748
+ },
3749
+ "401": {
3750
+ "$ref": "#/components/responses/Unauthorized"
3751
+ },
3752
+ "403": {
3753
+ "$ref": "#/components/responses/Forbidden"
3754
+ },
3755
+ "404": {
3756
+ "$ref": "#/components/responses/NotFound"
3757
+ }
3758
+ },
3759
+ "security": [
3760
+ {
3761
+ "builderSession": []
3762
+ }
3763
+ ]
3764
+ }
3765
+ },
3438
3766
  "/copy-desk/flags": {
3439
3767
  "post": {
3440
3768
  "operationId": "post_copy_desk_flags",
@@ -15051,6 +15379,15 @@
15051
15379
  "deleted"
15052
15380
  ]
15053
15381
  },
15382
+ "DeleteConnectionsIdResponse": {
15383
+ "type": "object",
15384
+ "properties": {
15385
+ "outcome": {}
15386
+ },
15387
+ "required": [
15388
+ "outcome"
15389
+ ]
15390
+ },
15054
15391
  "DeleteDependenciesResponse": {
15055
15392
  "type": "object",
15056
15393
  "properties": {
@@ -15485,6 +15822,49 @@
15485
15822
  "why"
15486
15823
  ]
15487
15824
  },
15825
+ "GetConnectionsDegreeHandleResponse": {
15826
+ "type": "object",
15827
+ "properties": {
15828
+ "handle": {},
15829
+ "degree": {},
15830
+ "mutuals": {},
15831
+ "mutual_count": {},
15832
+ "truncated": {}
15833
+ },
15834
+ "required": [
15835
+ "handle",
15836
+ "degree",
15837
+ "mutuals",
15838
+ "mutual_count",
15839
+ "truncated"
15840
+ ]
15841
+ },
15842
+ "GetConnectionsRequestsResponse": {
15843
+ "type": "object",
15844
+ "properties": {
15845
+ "incoming": {},
15846
+ "outgoing": {},
15847
+ "incoming_count": {},
15848
+ "outgoing_count": {}
15849
+ },
15850
+ "required": [
15851
+ "incoming",
15852
+ "outgoing",
15853
+ "incoming_count",
15854
+ "outgoing_count"
15855
+ ]
15856
+ },
15857
+ "GetConnectionsResponse": {
15858
+ "type": "object",
15859
+ "properties": {
15860
+ "connections": {},
15861
+ "count": {}
15862
+ },
15863
+ "required": [
15864
+ "connections",
15865
+ "count"
15866
+ ]
15867
+ },
15488
15868
  "GetDiscordChannelsPlanResponse": {
15489
15869
  "type": "object",
15490
15870
  "properties": {
@@ -17932,6 +18312,41 @@
17932
18312
  "reward"
17933
18313
  ]
17934
18314
  },
18315
+ "PostConnectionsIdAcceptResponse": {
18316
+ "type": "object",
18317
+ "properties": {
18318
+ "status": {}
18319
+ },
18320
+ "required": [
18321
+ "status"
18322
+ ]
18323
+ },
18324
+ "PostConnectionsRequest": {
18325
+ "type": "object",
18326
+ "properties": {
18327
+ "handle": {
18328
+ "type": "string",
18329
+ "minLength": 1,
18330
+ "maxLength": 39
18331
+ },
18332
+ "note": {
18333
+ "type": "string"
18334
+ }
18335
+ },
18336
+ "required": [
18337
+ "handle"
18338
+ ],
18339
+ "additionalProperties": false
18340
+ },
18341
+ "PostConnectionsResponse": {
18342
+ "type": "object",
18343
+ "properties": {
18344
+ "status": {}
18345
+ },
18346
+ "required": [
18347
+ "status"
18348
+ ]
18349
+ },
17935
18350
  "PostCopyDeskFlagsIdCloseRequest": {
17936
18351
  "type": "object",
17937
18352
  "properties": {
@@ -20847,9 +21262,9 @@
20847
21262
  "description": "A required dependency/feature is not configured or is temporarily down."
20848
21263
  }
20849
21264
  },
20850
- "x-endpoint-count": 330,
20851
- "x-schema-count": 365,
21265
+ "x-endpoint-count": 337,
21266
+ "x-schema-count": 372,
20852
21267
  "x-undocumented-bodies": 9,
20853
- "x-response-schemas": 247,
21268
+ "x-response-schemas": 253,
20854
21269
  "x-generated-by": "scripts/gds/gen-api-docs.js"
20855
21270
  }
@@ -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. 330 endpoints across 53 route files.
5
+ > **Generated from the live route files** — the route file is authoritative. 337 endpoints across 54 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`.
@@ -153,6 +153,18 @@ Base path: `/api/bongos`. Ranks (enforced server-side, [ADR 0016](adr/<redacted>
153
153
  | GET | `/api/bongos/closeness/weights` | `any-builder` | — | the MODEL is not private, and publishing it is the whole point of the weights being data. |
154
154
  | GET | `/api/bongos/closeness/with/:builderId` | `any-builder` | — | One pair — "why am I close to THEM", asked directly rather than by paging the graph. |
155
155
 
156
+ ## `connections` (7)
157
+
158
+ | Method | Path | Rank | Body | Description |
159
+ |---|---|---|---|---|
160
+ | GET | `/api/bongos/connections` | `any-builder` | — | MY accepted connections (own data, keyed on the session). |
161
+ | POST | `/api/bongos/connections` | `any-builder` | `handle`, `note` | ask to connect. |
162
+ | DELETE | `/api/bongos/connections/:id` | `any-builder` | — | remove an accepted connection (either side may end it) or cancel a pending request I sent. |
163
+ | POST | `/api/bongos/connections/:id/accept` | `any-builder` | — | accept a request addressed to ME. |
164
+ | POST | `/api/bongos/connections/:id/decline` | `any-builder` | — | decline a request addressed to ME. |
165
+ | GET | `/api/bongos/connections/degree/:handle` | `any-builder` | — | the degree label between the CALLER and one handle, plus the mutuals it was computed from (own-scope read, D4 §4). |
166
+ | GET | `/api/bongos/connections/requests` | `any-builder` | — | MY pending requests, both directions (own data). |
167
+
156
168
  ## `copy-desk` (4)
157
169
 
158
170
  | Method | Path | Rank | Body | Description |
@@ -1273,5 +1273,7 @@ is load-bearing: the script throws rather than guess if it is missing, and
1273
1273
  landed since 1.19.416 with no explicit bump. run 33263505319. (task 1002620)
1274
1274
  1.19.418 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
1275
1275
  landed since 1.19.417 with no explicit bump. run 33264139544. (task 1002620)
1276
+ 1.19.419 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
1277
+ landed since 1.19.418 with no explicit bump. run 33265382684. (task 1002620)
1276
1278
  ---------------------------------------------------------------------------
1277
1279
  ```
@@ -263,6 +263,22 @@ function createClient(opts = {}) {
263
263
  // GET /closeness/with/{builderId} — rank: any-builder — GET /closeness/with/:builderId
264
264
  getClosenessWithBuilderId: (args) => request("GET", "/closeness/with/{builderId}", { hasBody: false }, args),
265
265
  },
266
+ "connections": {
267
+ // GET /connections — rank: any-builder — GET /connections
268
+ getConnections: (args) => request("GET", "/connections", { hasBody: false }, args),
269
+ // POST /connections — rank: any-builder — POST /connections
270
+ postConnections: (args) => request("POST", "/connections", { hasBody: true }, args),
271
+ // DELETE /connections/{id} — rank: any-builder — DELETE /connections/:id
272
+ deleteConnectionsId: (args) => request("DELETE", "/connections/{id}", { hasBody: false }, args),
273
+ // POST /connections/{id}/accept — rank: any-builder — POST /connections/:id/accept
274
+ postConnectionsIdAccept: (args) => request("POST", "/connections/{id}/accept", { hasBody: true }, args),
275
+ // POST /connections/{id}/decline — rank: any-builder — POST /connections/:id/decline
276
+ postConnectionsIdDecline: (args) => request("POST", "/connections/{id}/decline", { hasBody: true }, args),
277
+ // GET /connections/degree/{handle} — rank: any-builder — GET /connections/degree/:handle
278
+ getConnectionsDegreeHandle: (args) => request("GET", "/connections/degree/{handle}", { hasBody: false }, args),
279
+ // GET /connections/requests — rank: any-builder — GET /connections/requests
280
+ getConnectionsRequests: (args) => request("GET", "/connections/requests", { hasBody: false }, args),
281
+ },
266
282
  "copyDesk": {
267
283
  // POST /copy-desk/flags — rank: any-builder — POST /copy-desk/flags
268
284
  postCopyDeskFlags: (args) => request("POST", "/copy-desk/flags", { hasBody: true }, args),
@@ -0,0 +1,58 @@
1
+ // modules/platform-identity/account-visibility.js — THE effective-visibility
2
+ // predicate for a platform account, published in both forms so a route and a
3
+ // query cannot drift (ADR 0202 D4; the ADR 0182 `publishableSql` precedent).
4
+ //
5
+ // WHAT IT ANSWERS: "may the world see that this account exists at all?" —
6
+ // profile_state = 'active' (consent: a hub session has touched the account,
7
+ // ADR 0171 D3) AND account_visibility = 'public' (existence: the privacy-spec
8
+ // D1 axis). Both floors, ANDed, in one place.
9
+ //
10
+ // WHY ITS OWN FILE. ADR 0202 D4 requires connections to consume ONE shared
11
+ // reader rather than grow a connections-flavoured variant — "adding a second
12
+ // predicate is the drift ADR 0194 D7 exists to prevent". The reader the ADR
13
+ // names belongs to task 1002967, which has not shipped; platform-identity.js is
14
+ // already at the file-size ratchet. So the definition lands here, alone and
15
+ // tiny, and 1002967 ADOPTS it rather than writing a second one.
16
+ //
17
+ // WHAT IT IS NOT. Not a rollup gate: hide_stats suppresses NUMBERS, not
18
+ // existence, and is applied by the rollup readers (ADR 0171 D2), never here.
19
+ // Not authority: an account being visible confers nothing (ADR 0016).
20
+ //
21
+ // Fail-closed: a missing/absent account row is invisible, never visible
22
+ // (ADR 0192 §6 / ADR 0194 §2).
23
+ 'use strict';
24
+
25
+ // The JS twin. `account` is any row carrying profile_state + account_visibility;
26
+ // anything falsy (absent row, unreadable account) answers false.
27
+ function isAccountVisible(account) {
28
+ if (!account) return false;
29
+ return account.profile_state === 'active' && account.account_visibility === 'public';
30
+ }
31
+
32
+ // The SAME predicate as a SQL fragment, for the reads that filter in the
33
+ // database. `alias` qualifies the columns for a joined query (e.g.
34
+ // accountVisibleSql('pa')); omit it for an unaliased single-table query.
35
+ function accountVisibleSql(alias) {
36
+ // The alias is INTERPOLATED, so it is the one thing here that could carry SQL.
37
+ // Callers pass a literal, but a helper that builds SQL should refuse anything
38
+ // that is not a bare identifier rather than trust every future caller.
39
+ if (alias && !/^[A-Za-z_][A-Za-z0-9_]*$/.test(String(alias))) {
40
+ throw new Error('accountVisibleSql: alias must be a bare SQL identifier');
41
+ }
42
+ const q = alias ? `${alias}.` : '';
43
+ return `(${q}profile_state = 'active' AND ${q}account_visibility = 'public')`;
44
+ }
45
+
46
+ // The CONSENT floor on its own — active, without the existence axis. A private
47
+ // account is still a real participant in its OWN scope: ADR 0202 D4 §2 says its
48
+ // connections appear in its own list and in the counterparty's own list, and an
49
+ // incoming request must name who is asking or it cannot be decided. Those
50
+ // own-scope reads floor on this; every PUBLIC read floors on accountVisibleSql.
51
+ function accountActiveSql(alias) {
52
+ if (alias && !/^[A-Za-z_][A-Za-z0-9_]*$/.test(String(alias))) {
53
+ throw new Error('accountActiveSql: alias must be a bare SQL identifier');
54
+ }
55
+ return `(${alias ? `${alias}.` : ''}profile_state = 'active')`;
56
+ }
57
+
58
+ module.exports = { isAccountVisible, accountVisibleSql, accountActiveSql };