@bongos/core 1.19.579 → 1.19.580
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 +60 -35
- 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 +4 -0
- package/clients/bongos-client/index.mjs +2 -0
- package/docs/adr/0260-the-application-is-the-consent-and-the-echo-is-the-gate.md +146 -0
- package/docs/adr/README.md +1 -0
- package/docs/api/openapi.json +73 -3
- package/docs/api-reference.md +3 -2
- package/docs/copy-inventory.md +21 -18
- package/docs/copy-registry.json +50 -23
- package/docs/file-map.md +1 -0
- package/docs/module-api-changelog.md +2 -0
- package/modules/dev-box/app/src/vendor/bongos-client.cjs +2 -0
- package/modules/hall-ui/public/oversight.css +7 -0
- package/modules/hall-ui/public/watch.js +75 -0
- package/modules/onboarding/routes/access-requests.js +56 -1
- package/modules/platform-identity/applicant-profile.js +158 -0
- package/modules/platform-identity/application-echo.js +50 -0
- package/modules/platform-identity/recruiter-sliver.js +19 -0
- package/modules/platform-identity/routes/sso.js +126 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/src/bongos/auth-admission.js +61 -0
- package/src/bongos/auth.js +2 -1
- package/src/module-api.js +11 -1
- package/tests/adr_renumber_integrity.mjs +84 -0
- package/tests/applicant_profile_boundary.mjs +641 -0
- package/tests/applicant_profile_read_bounds.mjs +95 -0
- package/tests/module_api.mjs +1 -0
- package/tests/recruiter_sliver_boundary.mjs +35 -2
- package/tests/watch_applications_queue.mjs +94 -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.580",
|
|
6
|
+
"core_contract": "1.19.580",
|
|
7
|
+
"source_commit": "23d256818bb7470aa5d203825abed34ab13f6fcf",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-09-
|
|
9
|
+
"built_at": "2026-09-07T18:50:35.452Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
|
-
"docs_redacted":
|
|
12
|
+
"docs_redacted": 453,
|
|
13
13
|
"agent_docs_stubbed": 24,
|
|
14
|
-
"functional_verbatim":
|
|
14
|
+
"functional_verbatim": 2051,
|
|
15
15
|
"rules": 3,
|
|
16
16
|
"gate_literals": 3,
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
|
-
"file_count":
|
|
20
|
-
"tree_sha256": "
|
|
19
|
+
"file_count": 2528,
|
|
20
|
+
"tree_sha256": "293d3e0eb2f7b0d375fde65d1b6e75816035857c06a98f69f91d0e213a21f8ad",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/blocker-review/SKILL.md",
|
|
@@ -372,12 +372,12 @@
|
|
|
372
372
|
{
|
|
373
373
|
"path": "clients/bongos-client/README.md",
|
|
374
374
|
"mode": "0000644",
|
|
375
|
-
"sha256": "
|
|
375
|
+
"sha256": "48a76d533215b8c0885fa77f59fc0e8915262db6b57029f6850692882e6e7455"
|
|
376
376
|
},
|
|
377
377
|
{
|
|
378
378
|
"path": "clients/bongos-client/bongos-client.global.js",
|
|
379
379
|
"mode": "0000644",
|
|
380
|
-
"sha256": "
|
|
380
|
+
"sha256": "c7fe87ceb18e7e1c10e629d002fb6f322ee540606c3ec9f6d38741dac7a55e0f"
|
|
381
381
|
},
|
|
382
382
|
{
|
|
383
383
|
"path": "clients/bongos-client/examples/hello-world.mjs",
|
|
@@ -387,17 +387,17 @@
|
|
|
387
387
|
{
|
|
388
388
|
"path": "clients/bongos-client/index.cjs",
|
|
389
389
|
"mode": "0000644",
|
|
390
|
-
"sha256": "
|
|
390
|
+
"sha256": "d80b05be7a602fa825f5f45e5b7f2bc002473a4731e2530df19fa3043f0c07fc"
|
|
391
391
|
},
|
|
392
392
|
{
|
|
393
393
|
"path": "clients/bongos-client/index.d.ts",
|
|
394
394
|
"mode": "0000644",
|
|
395
|
-
"sha256": "
|
|
395
|
+
"sha256": "e5cfa78c545e60771166ae4118e18f5c7bcf911db649c115c2831a028de3562b"
|
|
396
396
|
},
|
|
397
397
|
{
|
|
398
398
|
"path": "clients/bongos-client/index.mjs",
|
|
399
399
|
"mode": "0000644",
|
|
400
|
-
"sha256": "
|
|
400
|
+
"sha256": "47ae66627693378c276031754aca717ecc01e8e5038c4cf98ed844e5c03ee524"
|
|
401
401
|
},
|
|
402
402
|
{
|
|
403
403
|
"path": "clients/bongos-client/package.json",
|
|
@@ -1814,20 +1814,25 @@
|
|
|
1814
1814
|
"mode": "0000644",
|
|
1815
1815
|
"sha256": "772b9153d882f6bbd5000ee90b83e05d550ee4f04d4f5392db36e223d9a58c59"
|
|
1816
1816
|
},
|
|
1817
|
+
{
|
|
1818
|
+
"path": "docs/adr/0260-the-application-is-the-consent-and-the-echo-is-the-gate.md",
|
|
1819
|
+
"mode": "0000644",
|
|
1820
|
+
"sha256": "fa04f8d71fff8186ae975a949c045ca7481bd0f22f395cb8c7397bbe03ddd449"
|
|
1821
|
+
},
|
|
1817
1822
|
{
|
|
1818
1823
|
"path": "docs/adr/README.md",
|
|
1819
1824
|
"mode": "0000644",
|
|
1820
|
-
"sha256": "
|
|
1825
|
+
"sha256": "1fa7dcf1456d26896e5cec77026ace0f08db8bed65527dee563cd7fdd2d07525"
|
|
1821
1826
|
},
|
|
1822
1827
|
{
|
|
1823
1828
|
"path": "docs/api-reference.md",
|
|
1824
1829
|
"mode": "0000644",
|
|
1825
|
-
"sha256": "
|
|
1830
|
+
"sha256": "450613589b6b480ba2d6789731003794749c0dd972fcd8636380fa5814d68807"
|
|
1826
1831
|
},
|
|
1827
1832
|
{
|
|
1828
1833
|
"path": "docs/api/openapi.json",
|
|
1829
1834
|
"mode": "0000644",
|
|
1830
|
-
"sha256": "
|
|
1835
|
+
"sha256": "c3a329219460b2508d51009ff8f77bdc4733f3a17ad2776553dc7bce483555c4"
|
|
1831
1836
|
},
|
|
1832
1837
|
{
|
|
1833
1838
|
"path": "docs/architecture.md",
|
|
@@ -1852,12 +1857,12 @@
|
|
|
1852
1857
|
{
|
|
1853
1858
|
"path": "docs/copy-inventory.md",
|
|
1854
1859
|
"mode": "0000644",
|
|
1855
|
-
"sha256": "
|
|
1860
|
+
"sha256": "c4b6557f79a876b18cfc020ea4d45b8dfa419c9c37cab3fb2c629d85939bd75d"
|
|
1856
1861
|
},
|
|
1857
1862
|
{
|
|
1858
1863
|
"path": "docs/copy-registry.json",
|
|
1859
1864
|
"mode": "0000644",
|
|
1860
|
-
"sha256": "
|
|
1865
|
+
"sha256": "867399ce0002d6a1f509969544ad9589db6769352597471b495c66749271bb3c"
|
|
1861
1866
|
},
|
|
1862
1867
|
{
|
|
1863
1868
|
"path": "docs/design/apex-pass-2-direction.md",
|
|
@@ -2692,7 +2697,7 @@
|
|
|
2692
2697
|
{
|
|
2693
2698
|
"path": "docs/file-map.md",
|
|
2694
2699
|
"mode": "0000644",
|
|
2695
|
-
"sha256": "
|
|
2700
|
+
"sha256": "dd0684364a3ccd656f8201367e23775d578a84b385f49a901f7fc4205312b344"
|
|
2696
2701
|
},
|
|
2697
2702
|
{
|
|
2698
2703
|
"path": "docs/handoff-template.md",
|
|
@@ -2702,7 +2707,7 @@
|
|
|
2702
2707
|
{
|
|
2703
2708
|
"path": "docs/module-api-changelog.md",
|
|
2704
2709
|
"mode": "0000644",
|
|
2705
|
-
"sha256": "
|
|
2710
|
+
"sha256": "2237a99993338a480835d8cf8c62bc0925585d46d01a9859b5ba3afa108af5d1"
|
|
2706
2711
|
},
|
|
2707
2712
|
{
|
|
2708
2713
|
"path": "docs/modules-contract.md",
|
|
@@ -4102,7 +4107,7 @@
|
|
|
4102
4107
|
{
|
|
4103
4108
|
"path": "modules/dev-box/app/src/vendor/bongos-client.cjs",
|
|
4104
4109
|
"mode": "0000644",
|
|
4105
|
-
"sha256": "
|
|
4110
|
+
"sha256": "d80b05be7a602fa825f5f45e5b7f2bc002473a4731e2530df19fa3043f0c07fc"
|
|
4106
4111
|
},
|
|
4107
4112
|
{
|
|
4108
4113
|
"path": "modules/dev-box/app/test/unit.mjs",
|
|
@@ -4992,7 +4997,7 @@
|
|
|
4992
4997
|
{
|
|
4993
4998
|
"path": "modules/hall-ui/public/oversight.css",
|
|
4994
4999
|
"mode": "0000644",
|
|
4995
|
-
"sha256": "
|
|
5000
|
+
"sha256": "13d46f29e77ba9ca30ab52de2e0413f2fcb68bfec44de032730d4d135530e4ba"
|
|
4996
5001
|
},
|
|
4997
5002
|
{
|
|
4998
5003
|
"path": "modules/hall-ui/public/pair.css",
|
|
@@ -5312,7 +5317,7 @@
|
|
|
5312
5317
|
{
|
|
5313
5318
|
"path": "modules/hall-ui/public/watch.js",
|
|
5314
5319
|
"mode": "0000644",
|
|
5315
|
-
"sha256": "
|
|
5320
|
+
"sha256": "058f25fcef75b3db5410ea7fa6b09515b0b7fddc64a7698c1a1c44e753c8a651"
|
|
5316
5321
|
},
|
|
5317
5322
|
{
|
|
5318
5323
|
"path": "modules/hall-ui/public/watch.states.json",
|
|
@@ -5917,7 +5922,7 @@
|
|
|
5917
5922
|
{
|
|
5918
5923
|
"path": "modules/onboarding/routes/access-requests.js",
|
|
5919
5924
|
"mode": "0000644",
|
|
5920
|
-
"sha256": "
|
|
5925
|
+
"sha256": "3c66a404347ef1b93edcf5c63a902c9561b0a3f9bd9bc6c17e8a2a1f1b5f6707"
|
|
5921
5926
|
},
|
|
5922
5927
|
{
|
|
5923
5928
|
"path": "modules/onboarding/routes/onboarding.js",
|
|
@@ -5934,10 +5939,15 @@
|
|
|
5934
5939
|
"mode": "0000644",
|
|
5935
5940
|
"sha256": "020279ae50f2f9e7a004fe571e5ca14493ad26bd22c395cc4805f759f5560b72"
|
|
5936
5941
|
},
|
|
5942
|
+
{
|
|
5943
|
+
"path": "modules/platform-identity/applicant-profile.js",
|
|
5944
|
+
"mode": "0000644",
|
|
5945
|
+
"sha256": "12c1d00965d2d386480bdf94673ab6f140c8ee6ba44a5995a2d1b9593c012d20"
|
|
5946
|
+
},
|
|
5937
5947
|
{
|
|
5938
5948
|
"path": "modules/platform-identity/application-echo.js",
|
|
5939
5949
|
"mode": "0000644",
|
|
5940
|
-
"sha256": "
|
|
5950
|
+
"sha256": "8217660dc23b57ba0fb54d6375aca9aec190c7abb1d70f9d44d4e290da272c64"
|
|
5941
5951
|
},
|
|
5942
5952
|
{
|
|
5943
5953
|
"path": "modules/platform-identity/canonical-profile.js",
|
|
@@ -6117,7 +6127,7 @@
|
|
|
6117
6127
|
{
|
|
6118
6128
|
"path": "modules/platform-identity/recruiter-sliver.js",
|
|
6119
6129
|
"mode": "0000644",
|
|
6120
|
-
"sha256": "
|
|
6130
|
+
"sha256": "4f6854050e3c7459f698c550577fa97958a573d7f1981c9de4d29417af2669c0"
|
|
6121
6131
|
},
|
|
6122
6132
|
{
|
|
6123
6133
|
"path": "modules/platform-identity/routes/connections.js",
|
|
@@ -6152,7 +6162,7 @@
|
|
|
6152
6162
|
{
|
|
6153
6163
|
"path": "modules/platform-identity/routes/sso.js",
|
|
6154
6164
|
"mode": "0000644",
|
|
6155
|
-
"sha256": "
|
|
6165
|
+
"sha256": "6921323c299eee14e2db75b2c1716e35c245f7c971e6aec57c2b339b106f4d87"
|
|
6156
6166
|
},
|
|
6157
6167
|
{
|
|
6158
6168
|
"path": "modules/platform-identity/scouting-authz.js",
|
|
@@ -7562,12 +7572,12 @@
|
|
|
7562
7572
|
{
|
|
7563
7573
|
"path": "package-lock.json",
|
|
7564
7574
|
"mode": "0000644",
|
|
7565
|
-
"sha256": "
|
|
7575
|
+
"sha256": "28db9a2f030a69e041ee0c0d277238e7f5f7be0ba8a9bd370ffc2fc644a3c1d1"
|
|
7566
7576
|
},
|
|
7567
7577
|
{
|
|
7568
7578
|
"path": "package.json",
|
|
7569
7579
|
"mode": "0000644",
|
|
7570
|
-
"sha256": "
|
|
7580
|
+
"sha256": "cbec2151e643d5ff302e5d98166488c6298669395eafe9e037a2f39ea23db5c9"
|
|
7571
7581
|
},
|
|
7572
7582
|
{
|
|
7573
7583
|
"path": "public-docs/index.html",
|
|
@@ -8982,7 +8992,7 @@
|
|
|
8982
8992
|
{
|
|
8983
8993
|
"path": "src/bongos/auth-admission.js",
|
|
8984
8994
|
"mode": "0000644",
|
|
8985
|
-
"sha256": "
|
|
8995
|
+
"sha256": "aaa113f476a1d8196324379f8fd78aab22f577455fa0378bac3200b98e6a887f"
|
|
8986
8996
|
},
|
|
8987
8997
|
{
|
|
8988
8998
|
"path": "src/bongos/auth-config.js",
|
|
@@ -9007,7 +9017,7 @@
|
|
|
9007
9017
|
{
|
|
9008
9018
|
"path": "src/bongos/auth.js",
|
|
9009
9019
|
"mode": "0000644",
|
|
9010
|
-
"sha256": "
|
|
9020
|
+
"sha256": "eeb1831ab3d033598e0bcd57c30589cac583a1259c98b2dd49fbd9bb7725e11b"
|
|
9011
9021
|
},
|
|
9012
9022
|
{
|
|
9013
9023
|
"path": "src/bongos/cascade-dispatch.js",
|
|
@@ -9272,7 +9282,7 @@
|
|
|
9272
9282
|
{
|
|
9273
9283
|
"path": "src/module-api.js",
|
|
9274
9284
|
"mode": "0000644",
|
|
9275
|
-
"sha256": "
|
|
9285
|
+
"sha256": "a3e2c3a0b4099d54d8cd5c309186feb8a0c7073074095c4d10861f71e9f36ce4"
|
|
9276
9286
|
},
|
|
9277
9287
|
{
|
|
9278
9288
|
"path": "src/module-loader/catalog.js",
|
|
@@ -9364,6 +9374,11 @@
|
|
|
9364
9374
|
"mode": "0000644",
|
|
9365
9375
|
"sha256": "125edcec8c7d7536365b9675daa1fceb9fe072a30e2a4a68ee2911cd258a8c10"
|
|
9366
9376
|
},
|
|
9377
|
+
{
|
|
9378
|
+
"path": "tests/adr_renumber_integrity.mjs",
|
|
9379
|
+
"mode": "0000644",
|
|
9380
|
+
"sha256": "bdc9d3678e12dee3f3252785ca82e06071421762c7146cbc1591d3fc2ec72180"
|
|
9381
|
+
},
|
|
9367
9382
|
{
|
|
9368
9383
|
"path": "tests/agents_spawn.mjs",
|
|
9369
9384
|
"mode": "0000644",
|
|
@@ -9424,6 +9439,16 @@
|
|
|
9424
9439
|
"mode": "0000644",
|
|
9425
9440
|
"sha256": "ac84c8d8b1e2f54ef278506570ed657e68d2da4e136968b0df31b51f943af4bb"
|
|
9426
9441
|
},
|
|
9442
|
+
{
|
|
9443
|
+
"path": "tests/applicant_profile_boundary.mjs",
|
|
9444
|
+
"mode": "0000644",
|
|
9445
|
+
"sha256": "beefe27f5f753185cf3513f6576e55ee44680760ff2d8eed4ced93d56ec92b77"
|
|
9446
|
+
},
|
|
9447
|
+
{
|
|
9448
|
+
"path": "tests/applicant_profile_read_bounds.mjs",
|
|
9449
|
+
"mode": "0000644",
|
|
9450
|
+
"sha256": "185fed67d8b3e65121af637e37392c21d4bcd892bb2b305704479f42ff5987c7"
|
|
9451
|
+
},
|
|
9427
9452
|
{
|
|
9428
9453
|
"path": "tests/application_echo.mjs",
|
|
9429
9454
|
"mode": "0000644",
|
|
@@ -11292,7 +11317,7 @@
|
|
|
11292
11317
|
{
|
|
11293
11318
|
"path": "tests/module_api.mjs",
|
|
11294
11319
|
"mode": "0000644",
|
|
11295
|
-
"sha256": "
|
|
11320
|
+
"sha256": "157d551b9a99320198f22d32ce335fa4166414dc1818d11ef23a0ea05ac80a76"
|
|
11296
11321
|
},
|
|
11297
11322
|
{
|
|
11298
11323
|
"path": "tests/module_api_lazy.mjs",
|
|
@@ -11857,7 +11882,7 @@
|
|
|
11857
11882
|
{
|
|
11858
11883
|
"path": "tests/recruiter_sliver_boundary.mjs",
|
|
11859
11884
|
"mode": "0000644",
|
|
11860
|
-
"sha256": "
|
|
11885
|
+
"sha256": "a9ad552e7060898e5c94db8af29e63d7cad568294c240c48bef78859a0558ec6"
|
|
11861
11886
|
},
|
|
11862
11887
|
{
|
|
11863
11888
|
"path": "tests/redteam_followup_kind.mjs",
|
|
@@ -12592,7 +12617,7 @@
|
|
|
12592
12617
|
{
|
|
12593
12618
|
"path": "tests/watch_applications_queue.mjs",
|
|
12594
12619
|
"mode": "0000644",
|
|
12595
|
-
"sha256": "
|
|
12620
|
+
"sha256": "52c5c056c33b0b2ee91076b29e24ea5b650a0148c603fdde5136e60d6c6e926a"
|
|
12596
12621
|
},
|
|
12597
12622
|
{
|
|
12598
12623
|
"path": "tests/web_admission_status.mjs",
|
|
@@ -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
|
+
- 359 operations across 55 resource groups
|
|
9
9
|
|
|
10
10
|
## Install
|
|
11
11
|
|
|
@@ -793,6 +793,8 @@ function createClient(opts = {}) {
|
|
|
793
793
|
"sso": {
|
|
794
794
|
// POST /sso/activity/rollup — rank: public — POST /sso/activity/rollup
|
|
795
795
|
postSsoActivityRollup: (args) => request("POST", "/sso/activity/rollup", { hasBody: true }, args),
|
|
796
|
+
// POST /sso/applicant-profile — rank: public — POST /sso/applicant-profile
|
|
797
|
+
postSsoApplicantProfile: (args) => request("POST", "/sso/applicant-profile", { hasBody: true }, args),
|
|
796
798
|
// GET /sso/authorize — rank: public — GET /sso/authorize
|
|
797
799
|
getSsoAuthorize: (args) => request("GET", "/sso/authorize", { hasBody: false }, args),
|
|
798
800
|
// POST /sso/device/poll — rank: public — POST /sso/device/poll
|
|
@@ -792,6 +792,8 @@ function createClient(opts = {}) {
|
|
|
792
792
|
"sso": {
|
|
793
793
|
// POST /sso/activity/rollup — rank: public — POST /sso/activity/rollup
|
|
794
794
|
postSsoActivityRollup: (args) => request("POST", "/sso/activity/rollup", { hasBody: true }, args),
|
|
795
|
+
// POST /sso/applicant-profile — rank: public — POST /sso/applicant-profile
|
|
796
|
+
postSsoApplicantProfile: (args) => request("POST", "/sso/applicant-profile", { hasBody: true }, args),
|
|
795
797
|
// GET /sso/authorize — rank: public — GET /sso/authorize
|
|
796
798
|
getSsoAuthorize: (args) => request("GET", "/sso/authorize", { hasBody: false }, args),
|
|
797
799
|
// POST /sso/device/poll — rank: public — POST /sso/device/poll
|
|
@@ -355,6 +355,8 @@ export interface PostSessionsRequest { session_id: string; task_ids?: number[];
|
|
|
355
355
|
export interface PostSessionsResponse { skipped?: boolean; reason?: unknown; session?: unknown; token_reward?: unknown }
|
|
356
356
|
export interface PostSsoActivityRollupRequest { client_id: string; client_secret: string; github_id: StringifiableId; credits?: number; tasks_shipped?: number; karma?: number; rank?: string; disciplines?: unknown; recent_ships?: unknown }
|
|
357
357
|
export interface PostSsoActivityRollupResponse { ok: boolean }
|
|
358
|
+
export interface PostSsoApplicantProfileRequest { client_id: string; client_secret: string; github_id: StringifiableId }
|
|
359
|
+
export interface PostSsoApplicantProfileResponse { profile: unknown }
|
|
358
360
|
export interface PostSsoDevicePollRequest { device_code: string }
|
|
359
361
|
export interface PostSsoDeviceStartRequest { origin: string }
|
|
360
362
|
export interface PostSsoMembershipCheckInRequest { client_id: string; client_secret: string; github_id: StringifiableId; github_login: string; display_name?: string; avatar_url?: string; membership_kind?: string; role?: string }
|
|
@@ -1119,6 +1121,8 @@ export interface BongosClient {
|
|
|
1119
1121
|
"sso": {
|
|
1120
1122
|
/** POST /sso/activity/rollup — rank: public */
|
|
1121
1123
|
postSsoActivityRollup(args: RequestArgs & { body: PostSsoActivityRollupRequest }): Promise<PostSsoActivityRollupResponse>;
|
|
1124
|
+
/** POST /sso/applicant-profile — rank: public */
|
|
1125
|
+
postSsoApplicantProfile(args: RequestArgs & { body: PostSsoApplicantProfileRequest }): Promise<PostSsoApplicantProfileResponse>;
|
|
1122
1126
|
/** GET /sso/authorize — rank: public */
|
|
1123
1127
|
getSsoAuthorize(args?: RequestArgs): Promise<ApiResponse>;
|
|
1124
1128
|
/** POST /sso/device/poll — rank: public */
|
|
@@ -789,6 +789,8 @@ export function createClient(opts = {}) {
|
|
|
789
789
|
"sso": {
|
|
790
790
|
// POST /sso/activity/rollup — rank: public — POST /sso/activity/rollup
|
|
791
791
|
postSsoActivityRollup: (args) => request("POST", "/sso/activity/rollup", { hasBody: true }, args),
|
|
792
|
+
// POST /sso/applicant-profile — rank: public — POST /sso/applicant-profile
|
|
793
|
+
postSsoApplicantProfile: (args) => request("POST", "/sso/applicant-profile", { hasBody: true }, args),
|
|
792
794
|
// GET /sso/authorize — rank: public — GET /sso/authorize
|
|
793
795
|
getSsoAuthorize: (args) => request("GET", "/sso/authorize", { hasBody: false }, args),
|
|
794
796
|
// POST /sso/device/poll — rank: public — POST /sso/device/poll
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# ADR 0260 — An application IS the consent, and the hub's own echo is the gate: the D7 applicant view
|
|
2
|
+
|
|
3
|
+
- **Status:** accepted
|
|
4
|
+
- **Date:** 2026-09-03
|
|
5
|
+
- **Task:** [task 1002972](https://cloudbongos.com/builders#/task/1002972) (PV7, goal 1000045 — criterion C4)
|
|
6
|
+
- **Relates to:** `docs/specs/<redacted>.md` **D7** (owner-signed 2026-08-16), [ADR 0215](<redacted>.md) (the D3 sliver this reuses), [ADR 0205](<redacted>.md) + security report 1000027 (client credentials name a project, never a person), [ADR 0201](<redacted>.md) D1.2/D1.3 (the vouch mark and the statusless echo), [ADR 0171](<redacted>.md) D4/D5 (indistinguishable nulls, no-store), [ADR 0016](<redacted>.md) (descriptive, never authority), [ADR 0083](<redacted>.md) (the doorway)
|
|
7
|
+
|
|
8
|
+
## Context
|
|
9
|
+
|
|
10
|
+
Spec D7 asks for one thing in one sentence: a reviewer looking at an application
|
|
11
|
+
sees **what the applicant's own profile rules would already show them**, plus the
|
|
12
|
+
applicant's note, **evaluated live at review time**. Public account → the public
|
|
13
|
+
profile view. Private account → the D3 sliver. No new disclosure class.
|
|
14
|
+
|
|
15
|
+
Two halves of that already existed when this task opened. The applicant's note
|
|
16
|
+
reaches the project's queue through the join relay (task 1002330,
|
|
17
|
+
`relayApplicationNote`). The D3 projection exists and is proved
|
|
18
|
+
([ADR 0215](<redacted>.md)). What did not
|
|
19
|
+
exist was the **rule** — which of the two views an applicant gets — and the
|
|
20
|
+
**path** by which a project's reviewer queue could ask the hub for it.
|
|
21
|
+
|
|
22
|
+
Three things about that path are not obvious, and each could have been got wrong
|
|
23
|
+
in a way that looked like a simplification.
|
|
24
|
+
|
|
25
|
+
## Decision
|
|
26
|
+
|
|
27
|
+
### D1 — The rule is one port composing the two EXISTING views, never a third shape
|
|
28
|
+
|
|
29
|
+
`modules/platform-identity/applicant-profile.js` answers
|
|
30
|
+
`null | { view:'public', account, cross_project } | { view:'sliver', sliver }`.
|
|
31
|
+
|
|
32
|
+
The public branch's rollup half is `getPublicProfileExtras` — the very port the
|
|
33
|
+
canonical `/u/<handle>` page calls. The private branch's projection is
|
|
34
|
+
`sliverShapeFor`, exported from `recruiter-sliver.js` — the very function the
|
|
35
|
+
recruiting route serializes. **Neither branch builds its own object**, because a
|
|
36
|
+
third five-key lookalike *is* the new disclosure class D7 forbids: free to drift
|
|
37
|
+
from the original field by field, and uncovered by the whitelist proof over it.
|
|
38
|
+
|
|
39
|
+
`tests/applicant_profile_boundary.mjs` asserts both key sets against the
|
|
40
|
+
**modules' own exported lists**, never against copies kept in the test.
|
|
41
|
+
|
|
42
|
+
### D2 — The APPLICATION is the consent, not the recruiting opt-in
|
|
43
|
+
|
|
44
|
+
This is the one place the applicant view differs from the recruiter sliver, and
|
|
45
|
+
it is the decision most likely to be "tidied" away later.
|
|
46
|
+
|
|
47
|
+
`getRecruiterSliver` floors on `recruiterSliverVisibleSql` = consent **and
|
|
48
|
+
recruiting reach**, because a recruiter browsing a directory was invited by
|
|
49
|
+
nobody. An applicant has done something a browsing recruiter has not — spec D7
|
|
50
|
+
says it in those words: **"applying is an explicit act"**.
|
|
51
|
+
|
|
52
|
+
So routing this port through `getRecruiterSliver` would be wrong *in the
|
|
53
|
+
direction that looks safe*. `recruiter_discoverable` defaults to **false** for a
|
|
54
|
+
private account, so a private builder who deliberately applied would show their
|
|
55
|
+
reviewer **nothing at all**, and criterion C4 would be satisfied by an empty box.
|
|
56
|
+
The reach opt-out governs the *recruiting surface* — `account-visibility.js` says
|
|
57
|
+
exactly that of `scoutingListableSql` — and was never a gate on a door the
|
|
58
|
+
account itself knocked on.
|
|
59
|
+
|
|
60
|
+
**What the swap does NOT relax**, and this is what keeps it from meaning
|
|
61
|
+
"applying opts you into everything":
|
|
62
|
+
|
|
63
|
+
- `accountActiveSql` still floors it — `profile_state='active'` **and**
|
|
64
|
+
`terms_accepted_at IS NOT NULL`. A provisional account and one that never
|
|
65
|
+
accepted the platform terms disclose nothing, applying or not.
|
|
66
|
+
- `hide_stats` is untouched in both branches. Applying is consent to be **seen**,
|
|
67
|
+
never consent to be **counted**.
|
|
68
|
+
- The existence axis stays off, for ADR 0215 D1's reason: the private account is
|
|
69
|
+
precisely this rule's subject.
|
|
70
|
+
|
|
71
|
+
The proof asserts the swap **in both directions on the same account** — `PRIVOUT`
|
|
72
|
+
gets an applicant view and gets `null` from `getRecruiterSliver` — so the
|
|
73
|
+
tempting refactor cannot pass quietly.
|
|
74
|
+
|
|
75
|
+
### D3 — The gate is the hub's own echo, because client credentials name a project and not a person
|
|
76
|
+
|
|
77
|
+
`POST /sso/applicant-profile` is authenticated by `client_id` + `client_secret`,
|
|
78
|
+
like its two siblings. **That authentication is not sufficient on its own**, and
|
|
79
|
+
this is the whole security argument for the route.
|
|
80
|
+
|
|
81
|
+
[ADR 0205](<redacted>.md) / security
|
|
82
|
+
report 1000027 is the lesson `/sso/membership/check-in` and `/sso/activity/rollup`
|
|
83
|
+
both had to learn: credentials prove *which project* is calling and say **nothing
|
|
84
|
+
about who the `github_id` in the body is**. Without a per-subject witness, this
|
|
85
|
+
route is a **bulk disclosure oracle** over every account on the platform —
|
|
86
|
+
private ones included, since D7's private branch is exactly the disclosure the
|
|
87
|
+
existence axis normally withholds. That is strictly worse than the rollup hole it
|
|
88
|
+
would mirror.
|
|
89
|
+
|
|
90
|
+
The witness is `platform_identity_application_echoes` — the statusless
|
|
91
|
+
"you applied to this project on this date" row from
|
|
92
|
+
[ADR 0201](<redacted>.md) D1.3. It is
|
|
93
|
+
the right witness because **the hub writes it itself**, in its own join relay,
|
|
94
|
+
only after the project answered the relayed application with a success. A client
|
|
95
|
+
cannot assert itself into that table. `applicationBacksProfileRead` is the exact
|
|
96
|
+
analogue of `membershipBacksRollup`: a boolean, and the narrowest cross-account
|
|
97
|
+
read the table has — the caller must already name both the account and the
|
|
98
|
+
project, and there is no listing by `client_id`, so a project learns nothing
|
|
99
|
+
about who applied that it was not already told at its own door.
|
|
100
|
+
|
|
101
|
+
It reuses the **same 30-day window**, and that is load-bearing rather than tidy:
|
|
102
|
+
when the echo stops matching, the view stops rendering, because the platform has
|
|
103
|
+
stopped vouching for the link between that queue row and that account.
|
|
104
|
+
|
|
105
|
+
Refusal is **404, never 403** — a 403 would confirm the `github_id` names a real
|
|
106
|
+
account. And the gate runs **before any account is read**: the proof asserts that
|
|
107
|
+
on the statement log, not on the response, because a gate that refuses *after*
|
|
108
|
+
reading has already done the disclosure work.
|
|
109
|
+
|
|
110
|
+
### D4 — LIVE means nothing is stored, at either end
|
|
111
|
+
|
|
112
|
+
Nothing about the applicant is written to the `access_requests` row and nothing is
|
|
113
|
+
cached on the project. The profile is resolved on **every render**
|
|
114
|
+
(`attachApplicantProfiles`), so an applicant who hides their stats, goes private,
|
|
115
|
+
or loses their account between applying and being reviewed is answered by the next
|
|
116
|
+
read of the queue — criterion C2's "immediately". A snapshot taken at apply time
|
|
117
|
+
would keep publishing a consent that had since been withdrawn, for as long as the
|
|
118
|
+
request sat in the queue. The hub sends `Cache-Control: no-store`; the project
|
|
119
|
+
keeps nothing.
|
|
120
|
+
|
|
121
|
+
### D5 — The queue keys on the VOUCH, never on `github_login`
|
|
122
|
+
|
|
123
|
+
`attachApplicantProfiles` resolves only rows carrying `applicant_github_id` — the
|
|
124
|
+
hub's own assertion about who filed the request (`core_230`). `POST /access-requests`
|
|
125
|
+
is the one **public unauthenticated write** on the GDS, so `github_login` is a
|
|
126
|
+
string anybody may type. Resolving a cross-project record against an unvouched
|
|
127
|
+
login is precisely the impersonation surface the vouch exists to close: file a
|
|
128
|
+
request naming a well-known builder, let the owner read that builder's excellent
|
|
129
|
+
record, and walk in. An unvouched row gets `profile: null`, always.
|
|
130
|
+
|
|
131
|
+
### D6 — The credential stays in core; the module gets a narrow port
|
|
132
|
+
|
|
133
|
+
The hub call lives in `src/bongos/auth-admission.js` beside the two other calls
|
|
134
|
+
that hold the hub client secret, and `loadIdpConfig` stays **off** the doorway —
|
|
135
|
+
putting it there would hand every module the credential that speaks for the whole
|
|
136
|
+
project. `<redacted>(githubId)` is published instead: one
|
|
137
|
+
`github_id` in, one already-bounded view out. The module can neither widen the
|
|
138
|
+
request nor re-address it.
|
|
139
|
+
|
|
140
|
+
## Consequences
|
|
141
|
+
|
|
142
|
+
- **The path is complete but DORMANT until R09 ([task 1002285](https://cloudbongos.com/builders#/task/1002285)) ships the vouch writer.** Nothing in the tree writes `applicant_github_id` today, so every queue row currently resolves `profile: null` and renders exactly as it did before. This is stated rather than glossed: the feature is wired end to end and proved, and it lights up on the day R09 lands its writer — the sequencing R08's own migration comment predicted. 1002972's declared dependency was PV3 alone; **the real dependency on R09 was missing from the graph.**
|
|
143
|
+
- The reviewer queue degrades rather than breaks: a hub outage, a self-hosted instance with no hub, and an applicant with no echo all render a row with its login, note and vouch pill and no profile block.
|
|
144
|
+
- `MAX_LIVE_PROFILE_READS = 25` caps per-render hub round-trips against a 200-row queue. It bites on vouched rows in list order, so the rows a reviewer acts on carry the profile. Raising it buys detail and costs latency; it changes nothing about what may be disclosed.
|
|
145
|
+
- `recruiter-sliver.js` now has a second first-party reader. The sole-reader guard in `tests/recruiter_sliver_boundary.mjs` was widened with a stated reason **and strengthened**: every listed reader is now checked to not *branch* on a sliver field, so the property the guard protects — descriptive data is never consulted as authority — is enforced rather than trusted.
|
|
146
|
+
- **Still not built:** the apply UI's *"your application shares: …"* disclosure line D7 names. There is no apply UI anywhere — `POST /my-projects/join` has no browser caller — so the sentence has no page to live on. It belongs with whichever task builds that surface, and is filed rather than silently dropped.
|
package/docs/adr/README.md
CHANGED
|
@@ -351,3 +351,4 @@ This keeps the decision history honest and traceable.
|
|
|
351
351
|
| 0257 | [**Auth resolves before the hall mounts anything, and a widget's boot read may never navigate** ([task 1003673](https://cloudbongos.com/builders#/task/1003673) · goal 1000063 — *The front door*). An invite-only instance could not admit its FIRST builder, and the symptom lied about where the fault was: an owner saw an empty Access-requests queue and no approve button, because **signing in does not file a request** — only the landing's *Request access* form does, and that form was unreachable. A signed-out visitor to `/builders` was bounced to GitHub, refused as a first-timer ("request access first"), and pointed back at `/builders` to be bounced again. Reproduced against `main`, not just an old pin. `builders.js` was already careful — `/me` is read `softAuth` and a 401 there renders the landing — but `DOMContentLoaded` called `mountHallWidgets()` FIRST, synchronously, and `goals.js`'s mount-time read of `/goals` goes through a `getJSON` with no `softAuth`, which answers 401 by NAVIGATING. The widget's read raced `renderLanding()` and won. **Decision: the front door settles which page this is before anything else runs** — `boot()` reads `/me` (soft), then either renders the landing and stops (nothing mounts, nothing else fetches) or mounts the hall, handing the resolved payload to `loadAll()` so the page still asks once. Widgets are member surfaces (`renderLanding` hid them all after the fact anyway), so not mounting them while signed out removes the CLASS rather than the one instance of it that was found; **a widget's own boot read may never navigate** is the belt beside it (`goals.js` reads soft and renders nothing without a session). A 401 during boot is not an instruction to go and sign in — only a user action is. `tests/hall_landing_boot.mjs` executes the real `builders.js` in a DOM stub and was confirmed to FAIL against the pre-fix file. Rejected: patching `goals.js` alone (it was merely first); making the 401 redirect soft everywhere (an expired session mid-visit SHOULD be sent to sign in — the line is boot vs user action); open enrollment as the cure (removes the gate [ADR 0050](<redacted>.md) chose deliberately instead of repairing the door).](<redacted>.md) | hall-ui / admission / front door |
|
|
352
352
|
| 0258 | [**The public CLI is a generated client-only package, and its file list is proven by running it** ([task 1003679](https://cloudbongos.com/builders#/task/1003679) · goal 1000054 — *A newcomer can build without the UI*). The core ships private as `@bongos/core` ([ADR 0108](<redacted>.md)), so a newcomer with no credential can install NOTHING and the web hall is the only way in — the owner's words: "how am I supposed to easily take on tasks as a new builder? I should be able to do everything without the UI." Open task 1002025 proposed publishing the core, which ships the SERVER and bypasses [ADR 0099](<redacted>.md)'s redaction pipeline (dormant until ~2026-11). **Decision: generate a separate public `@cloudbongos/cli` from the core and do not publish the core.** Four load-bearing parts. (1) SCOPED NAME, because npm shares one namespace between org names and unscoped packages — the bare `cloudbongos` is unpublishable *precisely because the owner owns that org*, which npm reports as "invalid" and reads like the name is taken; and a package name cannot be created on the npm website at all (it exists on first publish, which is why "Add Existing Package" answered `Forbidden`). (2) THE FILE LIST IS DECLARED AND PROVEN BY BEHAVIOUR, never computed — a static require-closure CANNOT answer this, because `src/module-api.js` is the doorway a module may only import ([ADR 0083](<redacted>.md)) and it *names* every kernel capability, so a static walk sees 33 `src/` files from `start.js` alone where runtime resolves **one** (`api-prefix.js`, which imports nothing) — the same trap as task 1003677's unsatisfiable done-when. So the test packs the tarball, installs it into an empty dir with NO repo, and runs every verb: it may fail for want of a session, never for a missing file. That caught two real holes on its first strengthened run — `clients/bongos-client/index.mjs`, reached by a dynamic `import()` no `require()` walk can see, and a hand-listed `files` array that dropped `clients/` and produced a tarball which installed cleanly then died on first use; both are now build-time refusals, and the `files` array is derived from the manifest. (3) `claim`/`ship`/`dev`/`serve`/`module`/`upgrade`/`onboard`/`doctor`/`exec`/`package-core` are ABSENT and the CLI says WHY plus the next step (`bongos shell` → a cloud box with the full CLI) — a bare `unknown command` teaches nothing, which is the exact failure being fixed. The supported journey is `login` → `start` → `shell`. (4) A REDACTION GATE fails the build closed on non-loopback IPv4 (RFC 5737 doc ranges exempt), token/key shapes, and every domain + owner login readable from the instance's own `config/branding.json` — needles come from host config so the core carries no instance identity ([ADR 0062 §7](<redacted>.md)); `cloudbongos.com` is allowlisted as public by design. No `repository` field while the core repo is private (it would 404 for every user and publish the owner's login for nothing). 31 files, one dependency (`undici`), version independent of the core's ([ADR 0161](<redacted>.md)). `@bongos/client` is VENDORED, not depended on — a public package depending on a private one is uninstallable. **The owner runs `npm publish`; a builder must not.** Task 1002025 is superseded. Rejected: publishing the core; deriving from a static closure (structurally impossible past the doorway); waiting for the mirror (dormant, and this is a build product not a source release); an unscoped name; depending on `@bongos/client`; a degraded `claim`; shipping all of `src/` to make lazy getters safe.](<redacted>.md) | cli / distribution / public surface |
|
|
353
353
|
| 0259 | [**A project’s departure from the public list is public, and the copy says so** ([task 1003672](https://cloudbongos.com/builders#/task/1003672) · goal 1000046 — *Project creation*). Decides the leak [ADR 0255](<redacted>.md) §6.2 deliberately filed rather than implied fixed. The public projects feed publishes a named list plus a count of anonymous rows, so when a project flips to stealth its named row LEAVES the identified block while the count RISES BY ONE — *“Mercury is gone, and there is one more black hole”* names it, and the moment, from a single diff of two snapshots. **Everything [ADR 0182](<redacted>.md) D2a does is defeated by it**, because the identity was never carried ON the anonymous row (`STEALTH_ANONYMOUS_KEEPS` is an allow-list precisely so a column added next year is anonymous by default) — it is carried by the ABSENCE of the named row that used to sit beside it. **Larger than the channel 0255 closed**, which needed repeated sampling and correlation; this one is exact and needs two reads. No `ORDER BY` change touches it: it is inherent in publishing a named list at all, so every fix is a product decision with a visible cost and the owner’s call, not a builder’s. **Decision: accept it, and make the copy say so.** Two reasons it is the right end of the trade and not merely the cheapest — (1) it is ALREADY the honest reading of the existing promise, since D2a made existence and count public *by design*, and a count that is public and truthful is a count whose CHANGES are public; (2) both alternatives damage the control they defend. A **delay window** means the owner clicks the privacy setting and is not private yet *without knowing it*, converting an information leak into a false belief — strictly worse, and the wrong direction for a privacy control. **Decoys** spend the count’s truthfulness (it is truthful today) on a guarantee that is still only probabilistic, and a decoy scheme needs some way to tell padding from real rows — the exact stable per-project key D2a forbids. The promise stealth actually keeps is about the project’s CONTENTS, not the TRANSITION: an anonymous row stays anonymous (no name, tagline, art, origin, or key surviving to the next snapshot); what is not private is stepping off a published list readers may keep copies of. So the copy — which read *“its name, and everything else about it, stay yours”*, every clause true and the paragraph as a whole overselling, because a reader takes it to cover the switch — now names the boundary in BOTH places an owner meets the setting (the choice’s blurb and the what-changes fold), in the plain register 0182’s interview fixed for this card. **A privacy control that oversells is worse than one that admits a limit**, the same standard 0255 §6 set for itself. **No behaviour changes** in `projects-feed.js` — the feed, the redaction allow-list and the keyed rotating permutation are decided FINISHED, not insufficient. `tests/projects_hub.mjs` pins the sentence in both surfaces (the copy IS the fix, so a future edit tightening the blurb must not quietly drop it) and re-applies the no-space-metaphor ban to it. Carries 0255 §6.3’s smaller residual so both live in one place: within one 24h epoch the permutation is stable, so same-day snapshots tell black holes apart (not name them) — accepted on the same reasoning, since the fix is a per-request permutation, which breaks pagination exactly as `ORDER BY random()` was rejected for. Guards recorded for any future attempt: no stable per-project key on an anonymous row, and no anonymous ordering an observer can predict or correlate. Rejected: the delay; decoys; dropping the named list (protects a privacy nobody asked for at the cost of the feature); and saying nothing, which is the failure mode this closes.](<redacted>.md) | platform-identity / privacy boundary / copy honesty |
|
|
354
|
+
| 0260 | [**An application IS the consent, and the hub’s own echo is the gate** ([task 1002972](https://cloudbongos.com/builders#/task/1002972) · goal 1000045, criterion C4). Privacy spec **D7** asks that a reviewer see what the applicant’s OWN profile rules would already show, evaluated LIVE at review time. The rule is ONE port composing the two EXISTING views — `getPublicProfileExtras` for a public account, `recruiter-sliver`’s own `sliverShapeFor` for a private one — because a third five-key lookalike **is** the new disclosure class D7 forbids. **The one deliberate difference from the D3 sliver:** `getRecruiterSliver` floors on recruiting REACH, and reusing it verbatim would be wrong in the direction that looks safe — `recruiter_discoverable` defaults to FALSE for a private account, so a private builder who deliberately applied would show their reviewer NOTHING and C4 would be satisfied by an empty box. D7 settles it: *“applying is an explicit act”*. The swap relaxes REACH only; `accountActiveSql` (active **and** terms accepted) and `hide_stats` still floor it, and the proof asserts both directions on the same account so the tempting refactor cannot pass quietly. **The federated route’s gate is the whole security story** ([ADR 0205](<redacted>.md) / security report 1000027): client credentials name a PROJECT and never a person, so `POST /sso/applicant-profile` authenticated alone is a bulk disclosure oracle over every account on the platform, private ones included. `applicationBacksProfileRead` demands a HUB-WRITTEN echo for exactly (this account, this client) inside the same 30-day window — a row only the hub’s own join relay creates — refusing **404, never 403**, and BEFORE any account is read (asserted on the statement log, because a gate that refuses after reading has already done the disclosure work). LIVE means nothing is stored at either end; the queue keys on the VOUCH and never on `github_login` (the one PUBLIC unauthenticated write makes an unvouched login an impersonation surface); and the hub client secret stays in core behind a narrow doorway port. **The path is complete but DORMANT until R09 ([task 1002285](https://cloudbongos.com/builders#/task/1002285)) ships the vouch writer** — nothing writes `applicant_github_id` today, and that dependency was missing from 1002972’s graph. Rejected: routing the port through `getRecruiterSliver`, a snapshot at apply time, resolving by `github_login`, and putting `loadIdpConfig` on the doorway.](<redacted>.md) | platform identity / privacy |
|
package/docs/api/openapi.json
CHANGED
|
@@ -13818,6 +13818,45 @@
|
|
|
13818
13818
|
"security": []
|
|
13819
13819
|
}
|
|
13820
13820
|
},
|
|
13821
|
+
"/sso/applicant-profile": {
|
|
13822
|
+
"post": {
|
|
13823
|
+
"operationId": "post_sso_applicant_profile",
|
|
13824
|
+
"tags": [
|
|
13825
|
+
"sso"
|
|
13826
|
+
],
|
|
13827
|
+
"summary": "POST /sso/applicant-profile",
|
|
13828
|
+
"description": "POST /sso/applicant-profile — a federated instance resolves ONE applicant's profile view for its own reviewer queue, LIVE at render (privacy spec D7, task 1002972). Server-to-server, client_id+client_secret auth, like the two routes above. Answers { profile: null | { view:'public', … } | { view: 'sliver', … } } — the D7 rule, decided entirely inside ../applicant-profile.js. WHY A POST FOR A READ. The same reason /sso/activity/rollup is one: the credentials are in the body, and a client_secret in a query string lands in access logs and proxy history. Nothing is written. THE ECHO IS THE GATE, AND IT IS THE WHOLE SECURITY ARGUMENT (ADR 0205, security report 1000027). Client credentials prove WHICH PROJECT is calling and nothing about WHO the github_id is, so authenticating the caller is not enough: without a per-subject witness this route is a bulk disclosure oracle over every account on the platform, private ones included, readable by any registered project holding only its own secret. applicationBacksProfileRead demands a HUB-WRITTEN echo for exactly (this account, this client) inside the 30-day window — a row only the hub's own join relay creates, and only after the project accepted the relayed application. A project may therefore read the profile of someone who applied TO IT, and of nobody else. 404, never 403, when the echo is absent — the /scouting/:handle posture. A 403 would confirm that the github_id names a real account, which is the disclosure the whole rule exists to withhold. An account that exists but discloses nothing (provisional, terms never accepted) answers the SAME { profile: null } as one that consented but is simply not readable, so the caller cannot tell those apart either (ADR 0171 D4). NOT AUTHORITY (ADR 0016). The answer is descriptive; the project's own admission gate reads status='invited' and never this. rank: public — authenticated by client_id + client_secret, not a builder rank. The limiter runs BEFORE the client lookup, so a refused probe costs no DB work — the accountExistenceReadRateLimit posture.\n\n**Rank:** `public` — No authentication — any caller.",
|
|
13829
|
+
"x-rank": "public",
|
|
13830
|
+
"x-source": "modules/platform-identity/routes/sso.js",
|
|
13831
|
+
"requestBody": {
|
|
13832
|
+
"required": true,
|
|
13833
|
+
"content": {
|
|
13834
|
+
"application/json": {
|
|
13835
|
+
"schema": {
|
|
13836
|
+
"$ref": "#/components/schemas/PostSsoApplicantProfileRequest"
|
|
13837
|
+
}
|
|
13838
|
+
}
|
|
13839
|
+
},
|
|
13840
|
+
"x-validated": true
|
|
13841
|
+
},
|
|
13842
|
+
"responses": {
|
|
13843
|
+
"200": {
|
|
13844
|
+
"description": "Success.",
|
|
13845
|
+
"content": {
|
|
13846
|
+
"application/json": {
|
|
13847
|
+
"schema": {
|
|
13848
|
+
"$ref": "#/components/schemas/PostSsoApplicantProfileResponse"
|
|
13849
|
+
}
|
|
13850
|
+
}
|
|
13851
|
+
}
|
|
13852
|
+
},
|
|
13853
|
+
"400": {
|
|
13854
|
+
"$ref": "#/components/responses/ValidationFailed"
|
|
13855
|
+
}
|
|
13856
|
+
},
|
|
13857
|
+
"security": []
|
|
13858
|
+
}
|
|
13859
|
+
},
|
|
13821
13860
|
"/sso/authorize": {
|
|
13822
13861
|
"get": {
|
|
13823
13862
|
"operationId": "get_sso_authorize",
|
|
@@ -21703,6 +21742,37 @@
|
|
|
21703
21742
|
"ok"
|
|
21704
21743
|
]
|
|
21705
21744
|
},
|
|
21745
|
+
"PostSsoApplicantProfileRequest": {
|
|
21746
|
+
"type": "object",
|
|
21747
|
+
"properties": {
|
|
21748
|
+
"client_id": {
|
|
21749
|
+
"type": "string",
|
|
21750
|
+
"maxLength": 200
|
|
21751
|
+
},
|
|
21752
|
+
"client_secret": {
|
|
21753
|
+
"type": "string",
|
|
21754
|
+
"maxLength": 512
|
|
21755
|
+
},
|
|
21756
|
+
"github_id": {
|
|
21757
|
+
"$ref": "#/components/schemas/StringifiableId"
|
|
21758
|
+
}
|
|
21759
|
+
},
|
|
21760
|
+
"required": [
|
|
21761
|
+
"client_id",
|
|
21762
|
+
"client_secret",
|
|
21763
|
+
"github_id"
|
|
21764
|
+
],
|
|
21765
|
+
"additionalProperties": false
|
|
21766
|
+
},
|
|
21767
|
+
"PostSsoApplicantProfileResponse": {
|
|
21768
|
+
"type": "object",
|
|
21769
|
+
"properties": {
|
|
21770
|
+
"profile": {}
|
|
21771
|
+
},
|
|
21772
|
+
"required": [
|
|
21773
|
+
"profile"
|
|
21774
|
+
]
|
|
21775
|
+
},
|
|
21706
21776
|
"PostSsoDevicePollRequest": {
|
|
21707
21777
|
"type": "object",
|
|
21708
21778
|
"properties": {
|
|
@@ -22605,9 +22675,9 @@
|
|
|
22605
22675
|
"description": "A required dependency/feature is not configured or is temporarily down."
|
|
22606
22676
|
}
|
|
22607
22677
|
},
|
|
22608
|
-
"x-endpoint-count":
|
|
22609
|
-
"x-schema-count":
|
|
22678
|
+
"x-endpoint-count": 359,
|
|
22679
|
+
"x-schema-count": 395,
|
|
22610
22680
|
"x-undocumented-bodies": 9,
|
|
22611
|
-
"x-response-schemas":
|
|
22681
|
+
"x-response-schemas": 267,
|
|
22612
22682
|
"x-generated-by": "scripts/gds/gen-api-docs.js"
|
|
22613
22683
|
}
|