@bongos/core 1.19.790 → 1.19.792
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 +76 -41
- package/.claude/skills/builder-reauth/SKILL.md +5 -3
- package/README.md +1 -1
- package/docs/adr/0206-own-scoped-session-revocation-no-absolute-ceiling.md +2 -0
- package/docs/adr/0294-cli-sessions-do-not-expire.md +56 -0
- package/docs/adr/README.md +1 -0
- package/docs/copy-inventory.md +90 -90
- package/docs/copy-registry.json +109 -109
- package/docs/module-api-changelog.md +4 -0
- package/migrations/core_243_cli_sessions_never_expire.sql +58 -0
- package/modules/hall-ui/public/hall-kit.js +7 -65
- package/modules/hall-ui/public/hall-render.js +1 -1
- package/modules/hall-ui/public/hall-visual.js +85 -0
- package/modules/hall-ui/public/index.html +1 -0
- package/modules/hall-ui/public/profile.html +1 -0
- package/modules/hall-ui/public/profile.js +2 -2
- package/modules/hall-ui/public/settings.js +5 -2
- package/modules/hall-ui/public/studio.css +1 -1
- package/modules/hall-ui/public/studio.html +1 -0
- package/modules/hall-ui/public/studio.js +4 -3
- package/modules/hall-ui/public/style.css +0 -16
- package/modules/hall-ui/public/task.html +1 -0
- package/modules/hall-ui/public/task.js +10 -13
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/scripts/gds/accessibility-replay.js +195 -0
- package/scripts/gds/box-auth-check.js +1 -1
- package/scripts/gds/cli-lib.js +2 -2
- package/scripts/gds/reauth.js +9 -7
- package/scripts/gds/run-unit-tests.js +7 -0
- package/src/bongos/auth-github.js +2 -0
- package/src/bongos/db-kernel.js +29 -11
- package/src/bongos/db.js +33 -24
- package/src/bongos/routes/auth.js +3 -2
- package/src/module-api.js +1 -1
- package/tests/accessibility_replay.mjs +201 -0
- package/tests/auth_session_expiry_db.mjs +183 -0
- package/tests/auth_session_ttl.mjs +124 -11
- package/tests/fixtures/accessibility-replay.json +93 -0
- package/tests/hall_studio_world.mjs +19 -11
- package/tests/task_visuals.mjs +91 -27
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.792",
|
|
6
|
+
"core_contract": "1.19.792",
|
|
7
|
+
"source_commit": "7d0308f082bb516dafaf779e6e8955df07aac1f0",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-09-
|
|
9
|
+
"built_at": "2026-09-17T00:25:50.991Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
|
-
"docs_redacted":
|
|
12
|
+
"docs_redacted": 486,
|
|
13
13
|
"agent_docs_stubbed": 25,
|
|
14
|
-
"functional_verbatim":
|
|
14
|
+
"functional_verbatim": 2179,
|
|
15
15
|
"rules": 3,
|
|
16
16
|
"gate_literals": 3,
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
|
-
"file_count":
|
|
20
|
-
"tree_sha256": "
|
|
19
|
+
"file_count": 2690,
|
|
20
|
+
"tree_sha256": "82a4277b3d647e6c4e975f1a9b4c6316a2177aa583ebaba8a37aab51a552694e",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/ask-for-help/SKILL.md",
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
{
|
|
78
78
|
"path": ".claude/skills/builder-reauth/SKILL.md",
|
|
79
79
|
"mode": "0000644",
|
|
80
|
-
"sha256": "
|
|
80
|
+
"sha256": "41527df350a69c001dd9a1a2d07767338fa216af58bea5a2019c2261d2b69aaa"
|
|
81
81
|
},
|
|
82
82
|
{
|
|
83
83
|
"path": ".claude/skills/builder-redteam/SKILL.md",
|
|
@@ -337,7 +337,7 @@
|
|
|
337
337
|
{
|
|
338
338
|
"path": "README.md",
|
|
339
339
|
"mode": "0000644",
|
|
340
|
-
"sha256": "
|
|
340
|
+
"sha256": "39267c71403ae6fa77551c88773e6ff744032b6ae2d9bfa002573c0c520a148a"
|
|
341
341
|
},
|
|
342
342
|
{
|
|
343
343
|
"path": "bin/bongos.js",
|
|
@@ -1522,7 +1522,7 @@
|
|
|
1522
1522
|
{
|
|
1523
1523
|
"path": "docs/adr/0206-own-scoped-session-revocation-no-absolute-ceiling.md",
|
|
1524
1524
|
"mode": "0000644",
|
|
1525
|
-
"sha256": "
|
|
1525
|
+
"sha256": "f8654e155205d9566aab8a103b6fbdb6100e23ddba3c74bb417955cef997e0f0"
|
|
1526
1526
|
},
|
|
1527
1527
|
{
|
|
1528
1528
|
"path": "docs/adr/0207-connections-implementation-departures-and-honest-limits.md",
|
|
@@ -1954,10 +1954,15 @@
|
|
|
1954
1954
|
"mode": "0000644",
|
|
1955
1955
|
"sha256": "fb72a972f472cfea4c214dc73c7c5e811670d4eb2bbbc8d397495c8756daa625"
|
|
1956
1956
|
},
|
|
1957
|
+
{
|
|
1958
|
+
"path": "docs/adr/0294-cli-sessions-do-not-expire.md",
|
|
1959
|
+
"mode": "0000644",
|
|
1960
|
+
"sha256": "32d1b4184b6ba494bacb915990f0ba2770535e25c5646c86a2d91d9cc05c93ed"
|
|
1961
|
+
},
|
|
1957
1962
|
{
|
|
1958
1963
|
"path": "docs/adr/README.md",
|
|
1959
1964
|
"mode": "0000644",
|
|
1960
|
-
"sha256": "
|
|
1965
|
+
"sha256": "5659dd04d00f3ac4b3087ec41d24d7eea6f9d3ad349cd68b4405b612c8238feb"
|
|
1961
1966
|
},
|
|
1962
1967
|
{
|
|
1963
1968
|
"path": "docs/api-reference.md",
|
|
@@ -1992,12 +1997,12 @@
|
|
|
1992
1997
|
{
|
|
1993
1998
|
"path": "docs/copy-inventory.md",
|
|
1994
1999
|
"mode": "0000644",
|
|
1995
|
-
"sha256": "
|
|
2000
|
+
"sha256": "ec93785011f13f27444bd8ccfe9fe24da8cd125cd1b440bb974b78ebe1b28533"
|
|
1996
2001
|
},
|
|
1997
2002
|
{
|
|
1998
2003
|
"path": "docs/copy-registry.json",
|
|
1999
2004
|
"mode": "0000644",
|
|
2000
|
-
"sha256": "
|
|
2005
|
+
"sha256": "76dfd0c0caaedc010f832b44a3acef437f7352e86090cc46f7ce1edb15b6f192"
|
|
2001
2006
|
},
|
|
2002
2007
|
{
|
|
2003
2008
|
"path": "docs/design/apex-pass-2-direction.md",
|
|
@@ -2522,7 +2527,7 @@
|
|
|
2522
2527
|
{
|
|
2523
2528
|
"path": "docs/module-api-changelog.md",
|
|
2524
2529
|
"mode": "0000644",
|
|
2525
|
-
"sha256": "
|
|
2530
|
+
"sha256": "a0c0e1aafc81738b39d52423b76ebbc23bfecd478ddee519c341d25a265a1e93"
|
|
2526
2531
|
},
|
|
2527
2532
|
{
|
|
2528
2533
|
"path": "docs/modules-contract.md",
|
|
@@ -3604,6 +3609,11 @@
|
|
|
3604
3609
|
"mode": "0000644",
|
|
3605
3610
|
"sha256": "e52821124b99bd844c8323f2de0221db3fafd92c0c6e2227838cadf7d987676a"
|
|
3606
3611
|
},
|
|
3612
|
+
{
|
|
3613
|
+
"path": "migrations/core_243_cli_sessions_never_expire.sql",
|
|
3614
|
+
"mode": "0000644",
|
|
3615
|
+
"sha256": "8e3e2db2ab3b7775bddf12f3a7ec3ed74477334b6388487ecd919cd61a62b0ba"
|
|
3616
|
+
},
|
|
3607
3617
|
{
|
|
3608
3618
|
"path": "modules/agents/CLAUDE.md",
|
|
3609
3619
|
"mode": "0000644",
|
|
@@ -4872,12 +4882,17 @@
|
|
|
4872
4882
|
{
|
|
4873
4883
|
"path": "modules/hall-ui/public/hall-kit.js",
|
|
4874
4884
|
"mode": "0000644",
|
|
4875
|
-
"sha256": "
|
|
4885
|
+
"sha256": "b0647793327eb27b7d460ecb97a39b835ecedf4754910def1e69867bf87070f7"
|
|
4876
4886
|
},
|
|
4877
4887
|
{
|
|
4878
4888
|
"path": "modules/hall-ui/public/hall-render.js",
|
|
4879
4889
|
"mode": "0000644",
|
|
4880
|
-
"sha256": "
|
|
4890
|
+
"sha256": "ab4db906c2510541a77f76a3d8a8440f4e78ffa6a4064315a4b4b7849f93c4a8"
|
|
4891
|
+
},
|
|
4892
|
+
{
|
|
4893
|
+
"path": "modules/hall-ui/public/hall-visual.js",
|
|
4894
|
+
"mode": "0000644",
|
|
4895
|
+
"sha256": "9b0aba27fe62541e2f103a14708b0de1aa6dfd1339f59b9b426eac8a565e5d7a"
|
|
4881
4896
|
},
|
|
4882
4897
|
{
|
|
4883
4898
|
"path": "modules/hall-ui/public/hall-widgets.js",
|
|
@@ -4947,7 +4962,7 @@
|
|
|
4947
4962
|
{
|
|
4948
4963
|
"path": "modules/hall-ui/public/index.html",
|
|
4949
4964
|
"mode": "0000644",
|
|
4950
|
-
"sha256": "
|
|
4965
|
+
"sha256": "60fd4302c50d016f3a77abda01aa26cda31a0d68eb04c47790252d7f584cad5a"
|
|
4951
4966
|
},
|
|
4952
4967
|
{
|
|
4953
4968
|
"path": "modules/hall-ui/public/index.states.json",
|
|
@@ -5082,12 +5097,12 @@
|
|
|
5082
5097
|
{
|
|
5083
5098
|
"path": "modules/hall-ui/public/profile.html",
|
|
5084
5099
|
"mode": "0000644",
|
|
5085
|
-
"sha256": "
|
|
5100
|
+
"sha256": "4ac2a618bb28875dac4e9f027946956dbee2eb0e2716237c74f889491334f6c7"
|
|
5086
5101
|
},
|
|
5087
5102
|
{
|
|
5088
5103
|
"path": "modules/hall-ui/public/profile.js",
|
|
5089
5104
|
"mode": "0000644",
|
|
5090
|
-
"sha256": "
|
|
5105
|
+
"sha256": "7d3b0dac460b02fcfae3a108c9191f7a6be5f1eb3473a93d37502222ff961083"
|
|
5091
5106
|
},
|
|
5092
5107
|
{
|
|
5093
5108
|
"path": "modules/hall-ui/public/profile.states.json",
|
|
@@ -5222,7 +5237,7 @@
|
|
|
5222
5237
|
{
|
|
5223
5238
|
"path": "modules/hall-ui/public/settings.js",
|
|
5224
5239
|
"mode": "0000644",
|
|
5225
|
-
"sha256": "
|
|
5240
|
+
"sha256": "fd553ac7c0ca9c74526647388e42afc18fc7c1b9f2cfc70c493f78343ba7c393"
|
|
5226
5241
|
},
|
|
5227
5242
|
{
|
|
5228
5243
|
"path": "modules/hall-ui/public/settings.states.json",
|
|
@@ -5242,17 +5257,17 @@
|
|
|
5242
5257
|
{
|
|
5243
5258
|
"path": "modules/hall-ui/public/studio.css",
|
|
5244
5259
|
"mode": "0000644",
|
|
5245
|
-
"sha256": "
|
|
5260
|
+
"sha256": "6dccbf9dcdcef09851d0fe53a15e41e28d7909d67d131f1a7fcfe5db09d28417"
|
|
5246
5261
|
},
|
|
5247
5262
|
{
|
|
5248
5263
|
"path": "modules/hall-ui/public/studio.html",
|
|
5249
5264
|
"mode": "0000644",
|
|
5250
|
-
"sha256": "
|
|
5265
|
+
"sha256": "ea0051dba4a0e4e2ebde1ab1dcaeaedac697d41f89df35526286beb018553025"
|
|
5251
5266
|
},
|
|
5252
5267
|
{
|
|
5253
5268
|
"path": "modules/hall-ui/public/studio.js",
|
|
5254
5269
|
"mode": "0000644",
|
|
5255
|
-
"sha256": "
|
|
5270
|
+
"sha256": "201e0b69b0be0ce88b85ee7404c2fe2d13b839b1ae5f44ddde65f6c22b73b975"
|
|
5256
5271
|
},
|
|
5257
5272
|
{
|
|
5258
5273
|
"path": "modules/hall-ui/public/studio.states.json",
|
|
@@ -5262,7 +5277,7 @@
|
|
|
5262
5277
|
{
|
|
5263
5278
|
"path": "modules/hall-ui/public/style.css",
|
|
5264
5279
|
"mode": "0000644",
|
|
5265
|
-
"sha256": "
|
|
5280
|
+
"sha256": "e06903836d2a31be222aef1fda880f6a68ab86f99667fae69b77b1fc3b118c08"
|
|
5266
5281
|
},
|
|
5267
5282
|
{
|
|
5268
5283
|
"path": "modules/hall-ui/public/task-detail.js",
|
|
@@ -5282,12 +5297,12 @@
|
|
|
5282
5297
|
{
|
|
5283
5298
|
"path": "modules/hall-ui/public/task.html",
|
|
5284
5299
|
"mode": "0000644",
|
|
5285
|
-
"sha256": "
|
|
5300
|
+
"sha256": "5d8a37f1d69506824339063f9f9f3b1af1f9569ca28738e638eaa98a19e86ea8"
|
|
5286
5301
|
},
|
|
5287
5302
|
{
|
|
5288
5303
|
"path": "modules/hall-ui/public/task.js",
|
|
5289
5304
|
"mode": "0000644",
|
|
5290
|
-
"sha256": "
|
|
5305
|
+
"sha256": "015737c6c7e0da732ad5611d049aa3f09f4c868fbf2056d1495a991c5086a1dd"
|
|
5291
5306
|
},
|
|
5292
5307
|
{
|
|
5293
5308
|
"path": "modules/hall-ui/public/task.states.json",
|
|
@@ -7677,12 +7692,12 @@
|
|
|
7677
7692
|
{
|
|
7678
7693
|
"path": "package-lock.json",
|
|
7679
7694
|
"mode": "0000644",
|
|
7680
|
-
"sha256": "
|
|
7695
|
+
"sha256": "a4f7542d3fd24e6d64fc20658f5339458a3ec0e38cccb5b12959b40babedf580"
|
|
7681
7696
|
},
|
|
7682
7697
|
{
|
|
7683
7698
|
"path": "package.json",
|
|
7684
7699
|
"mode": "0000644",
|
|
7685
|
-
"sha256": "
|
|
7700
|
+
"sha256": "fbda0210622f719253cab72cd3ab108ef90efa5d1357c4df20afa076e15add1f"
|
|
7686
7701
|
},
|
|
7687
7702
|
{
|
|
7688
7703
|
"path": "public-docs/index.html",
|
|
@@ -7734,6 +7749,11 @@
|
|
|
7734
7749
|
"mode": "0000644",
|
|
7735
7750
|
"sha256": "67e9a8dead2b0ea75f6011fd687a3db56a12df945a77f430eec2602a0ecac07c"
|
|
7736
7751
|
},
|
|
7752
|
+
{
|
|
7753
|
+
"path": "scripts/gds/accessibility-replay.js",
|
|
7754
|
+
"mode": "0000644",
|
|
7755
|
+
"sha256": "5a71ebbccd2afe9c0e116dcb879c59bf62091b47dcfa5eebaa42ca1082be3350"
|
|
7756
|
+
},
|
|
7737
7757
|
{
|
|
7738
7758
|
"path": "scripts/gds/address-park-sweep.sh",
|
|
7739
7759
|
"mode": "0000644",
|
|
@@ -7887,7 +7907,7 @@
|
|
|
7887
7907
|
{
|
|
7888
7908
|
"path": "scripts/gds/box-auth-check.js",
|
|
7889
7909
|
"mode": "0000644",
|
|
7890
|
-
"sha256": "
|
|
7910
|
+
"sha256": "c24c66d306b2904692bddfad0ea3cb023f8532beb3d674a2a81e6fed0eeb9bd5"
|
|
7891
7911
|
},
|
|
7892
7912
|
{
|
|
7893
7913
|
"path": "scripts/gds/box-connect-lib.js",
|
|
@@ -7982,7 +8002,7 @@
|
|
|
7982
8002
|
{
|
|
7983
8003
|
"path": "scripts/gds/cli-lib.js",
|
|
7984
8004
|
"mode": "0000644",
|
|
7985
|
-
"sha256": "
|
|
8005
|
+
"sha256": "88b8d2359743238922d73797807dd2f545b1a68c731d8a4766d7210196ad48fc"
|
|
7986
8006
|
},
|
|
7987
8007
|
{
|
|
7988
8008
|
"path": "scripts/gds/client-baseurl-guard.js",
|
|
@@ -8617,7 +8637,7 @@
|
|
|
8617
8637
|
{
|
|
8618
8638
|
"path": "scripts/gds/reauth.js",
|
|
8619
8639
|
"mode": "0000644",
|
|
8620
|
-
"sha256": "
|
|
8640
|
+
"sha256": "5368ed9a43034b060451e54f21f885c216fab17cf731d7981d2681aeae890507"
|
|
8621
8641
|
},
|
|
8622
8642
|
{
|
|
8623
8643
|
"path": "scripts/gds/recall.js",
|
|
@@ -8692,7 +8712,7 @@
|
|
|
8692
8712
|
{
|
|
8693
8713
|
"path": "scripts/gds/run-unit-tests.js",
|
|
8694
8714
|
"mode": "0000644",
|
|
8695
|
-
"sha256": "
|
|
8715
|
+
"sha256": "c7ea33433208b0835808028fb81853509d97331b919a70314e1a02718e8ca0c0"
|
|
8696
8716
|
},
|
|
8697
8717
|
{
|
|
8698
8718
|
"path": "scripts/gds/sandbox-stage.js",
|
|
@@ -9277,7 +9297,7 @@
|
|
|
9277
9297
|
{
|
|
9278
9298
|
"path": "src/bongos/auth-github.js",
|
|
9279
9299
|
"mode": "0000644",
|
|
9280
|
-
"sha256": "
|
|
9300
|
+
"sha256": "e2afb2d5f5459ed307be4e6e2d57cbd283b97794f8d1a99f4df5673000270681"
|
|
9281
9301
|
},
|
|
9282
9302
|
{
|
|
9283
9303
|
"path": "src/bongos/auth-idp.js",
|
|
@@ -9302,12 +9322,12 @@
|
|
|
9302
9322
|
{
|
|
9303
9323
|
"path": "src/bongos/db-kernel.js",
|
|
9304
9324
|
"mode": "0000644",
|
|
9305
|
-
"sha256": "
|
|
9325
|
+
"sha256": "e9a566e63b95225e3ee15331424af831875f75b5ea977c73f077bb50f46c4c6b"
|
|
9306
9326
|
},
|
|
9307
9327
|
{
|
|
9308
9328
|
"path": "src/bongos/db.js",
|
|
9309
9329
|
"mode": "0000644",
|
|
9310
|
-
"sha256": "
|
|
9330
|
+
"sha256": "7ebe731cacb9d7cb72af9e8310ccefc9d902b9e613c45839bc1555b5bb71cd5a"
|
|
9311
9331
|
},
|
|
9312
9332
|
{
|
|
9313
9333
|
"path": "src/bongos/github-repos.js",
|
|
@@ -9447,7 +9467,7 @@
|
|
|
9447
9467
|
{
|
|
9448
9468
|
"path": "src/bongos/routes/auth.js",
|
|
9449
9469
|
"mode": "0000644",
|
|
9450
|
-
"sha256": "
|
|
9470
|
+
"sha256": "4723bdcca92e442d73517df92463f58d16f0773d4167d8661a75fd9a7a61392d"
|
|
9451
9471
|
},
|
|
9452
9472
|
{
|
|
9453
9473
|
"path": "src/bongos/routes/backup.js",
|
|
@@ -9557,7 +9577,7 @@
|
|
|
9557
9577
|
{
|
|
9558
9578
|
"path": "src/module-api.js",
|
|
9559
9579
|
"mode": "0000644",
|
|
9560
|
-
"sha256": "
|
|
9580
|
+
"sha256": "51bcfd1a4eb31a189085c07cdcc46dd1b9d923c9335021ea3dcb54c6c55e01c6"
|
|
9561
9581
|
},
|
|
9562
9582
|
{
|
|
9563
9583
|
"path": "src/module-loader/catalog.js",
|
|
@@ -9619,6 +9639,11 @@
|
|
|
9619
9639
|
"mode": "0000644",
|
|
9620
9640
|
"sha256": "7229451a9a9cb74049b7fea808458423d8a2754c213e6974593811cd1b4322b1"
|
|
9621
9641
|
},
|
|
9642
|
+
{
|
|
9643
|
+
"path": "tests/accessibility_replay.mjs",
|
|
9644
|
+
"mode": "0000644",
|
|
9645
|
+
"sha256": "23b0d20a03c51684d99cb45b56fb7bb3c17be42174d63c38442013a9878dc443"
|
|
9646
|
+
},
|
|
9622
9647
|
{
|
|
9623
9648
|
"path": "tests/account_existence_rate_limit.mjs",
|
|
9624
9649
|
"mode": "0000644",
|
|
@@ -9884,10 +9909,15 @@
|
|
|
9884
9909
|
"mode": "0000644",
|
|
9885
9910
|
"sha256": "acbce017f7111b67c556db645de7b88fb5f39829755fa6c9378f300c8fc62a15"
|
|
9886
9911
|
},
|
|
9912
|
+
{
|
|
9913
|
+
"path": "tests/auth_session_expiry_db.mjs",
|
|
9914
|
+
"mode": "0000644",
|
|
9915
|
+
"sha256": "880cbfa0304ab037db980f22471a2b46bcdbad0e7dca57af359402dbb068d6a4"
|
|
9916
|
+
},
|
|
9887
9917
|
{
|
|
9888
9918
|
"path": "tests/auth_session_ttl.mjs",
|
|
9889
9919
|
"mode": "0000644",
|
|
9890
|
-
"sha256": "
|
|
9920
|
+
"sha256": "e4c3309248873ac816ecf9111cc08e4c9e3e6c318a2e925434f1cc490c71b734"
|
|
9891
9921
|
},
|
|
9892
9922
|
{
|
|
9893
9923
|
"path": "tests/auth_web_recovery.mjs",
|
|
@@ -10739,6 +10769,11 @@
|
|
|
10739
10769
|
"mode": "0000644",
|
|
10740
10770
|
"sha256": "f1b1a1ea6ab9979dfc4e865404c85a92e64a17b29eb1e8838d9fbe01191b8e3d"
|
|
10741
10771
|
},
|
|
10772
|
+
{
|
|
10773
|
+
"path": "tests/fixtures/accessibility-replay.json",
|
|
10774
|
+
"mode": "0000644",
|
|
10775
|
+
"sha256": "8fd3ae3efc2a2c7f47e605925fec881c76dabe1fe8242ca67354b0a6a346e5e6"
|
|
10776
|
+
},
|
|
10742
10777
|
{
|
|
10743
10778
|
"path": "tests/fixtures/session-506-digest.json",
|
|
10744
10779
|
"mode": "0000644",
|
|
@@ -11327,7 +11362,7 @@
|
|
|
11327
11362
|
{
|
|
11328
11363
|
"path": "tests/hall_studio_world.mjs",
|
|
11329
11364
|
"mode": "0000644",
|
|
11330
|
-
"sha256": "
|
|
11365
|
+
"sha256": "28bbcdeb27e87ee3abd5e9ba1e68013723b043297d54cf561284b47e5e64b6e5"
|
|
11331
11366
|
},
|
|
11332
11367
|
{
|
|
11333
11368
|
"path": "tests/hall_tokens.mjs",
|
|
@@ -13167,7 +13202,7 @@
|
|
|
13167
13202
|
{
|
|
13168
13203
|
"path": "tests/task_visuals.mjs",
|
|
13169
13204
|
"mode": "0000644",
|
|
13170
|
-
"sha256": "
|
|
13205
|
+
"sha256": "cb15ca74b2f4abc5da542a3c08d6b2aa30f122f0ddf22cffa2f3d24f4ddb2000"
|
|
13171
13206
|
},
|
|
13172
13207
|
{
|
|
13173
13208
|
"path": "tests/terms_acceptance.mjs",
|
|
@@ -4,11 +4,13 @@ description: >-
|
|
|
4
4
|
Refresh an expired Bongos CLI session the UI-first way — Settings, copy the re-issue one-liner, this runs it. Triggers: "/builder-reauth", "re-auth", "refresh my session", "my Bongos session expired", "re-issue my CLI token", or any /builder-* failing with "Bongos CLI session is no longer valid".
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
You are refreshing the current builder's Bongos CLI session. CLI bearer tokens
|
|
7
|
+
You are refreshing the current builder's Bongos CLI session. CLI bearer tokens **do not expire** — since [ADR 0294](../../../docs/adr/0294-cli-sessions-do-not-expire.md) a session lasts until it is revoked. So a rejected token means something killed it: the builder (or an Archon) revoked it, a demotion swept it, or an auth-changing deploy invalidated it. This skill is the **fast path back in**: browser clicks plus a single copy/paste, instead of the slower terminal Device Flow.
|
|
8
8
|
|
|
9
|
-
## Why expiry
|
|
9
|
+
## Why there is no expiry (so you can explain it if asked)
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
There used to be a 24h idle TTL, for auth-token hygiene (GDS-V3 criterion **C9**): the CLI token sits in a plaintext file (`~/.config/cloudbongos/bongos-session.json`, mode 0600), so bounding a leaked or abandoned token seemed worth the friction. It wasn't. Because the TTL refreshed on every use, a thief's own traffic kept a stolen token alive indefinitely — the only person a timer ever signed out was the legitimate holder, who had stepped away. [ADR 0294](../../../docs/adr/0294-cli-sessions-do-not-expire.md) removed it.
|
|
12
|
+
|
|
13
|
+
What actually bounds a leaked token is **revocation**, and it is faster than any clock: the builder kills it themselves from Settings → Active sessions or "Sign out everywhere" ([ADR 0206](../../../docs/adr/0206-own-scoped-session-revocation-no-absolute-ceiling.md)). Rank changes were never the TTL's job either — demotion calls `revokeAllSessionsForBuilder` synchronously and rank is read fresh from the DB on every request ([ADR 0016](../../../docs/adr/0016-trust-boundary-server-enforced-permissions.md)).
|
|
12
14
|
|
|
13
15
|
## The flow (guided paste)
|
|
14
16
|
|
package/README.md
CHANGED
|
@@ -73,7 +73,7 @@ We keep secrets (`.env` files, API keys, private keys) out of the repo with two
|
|
|
73
73
|
|
|
74
74
|
## If your CLI starts 401'ing
|
|
75
75
|
|
|
76
|
-
GDS CLI calls (`node scripts/gds/start.js`, `claim.js`, `ship.js`, etc.) authenticate against your instance's GDS host using a session token stored under `~/.config/<configDir
|
|
76
|
+
GDS CLI calls (`node scripts/gds/start.js`, `claim.js`, `ship.js`, etc.) authenticate against your instance's GDS host using a session token stored under `~/.config/<configDir>/` (the neutral core uses `cloudbongos`; an instance may pin its own). CLI sessions do not expire — they last until revoked ([ADR 0294](docs/adr/0294-cli-sessions-do-not-expire.md)) — so a 401 means the token was revoked (by you from Settings → Active sessions, or by an Archon), swept by a rank demotion, or invalidated by an auth-changing deploy.
|
|
77
77
|
|
|
78
78
|
`apiCall` in `scripts/gds/cli-lib.js` detects HTTP 401 and exits with a clear recovery message rather than letting the failure cascade. When you see it:
|
|
79
79
|
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
- **Task:** task 1003341 (goal 1000045 — public builder profiles + account security, criterion `i-control-my-own-access-and-exposure`)
|
|
7
7
|
- **Interacts with:** [ADR 0016](0016-trust-boundary-server-enforced-permissions.md) (the DB enforces; a session is checked fresh per request, never cached), [ADR 0053](0053-scoped-dev-box-session.md) / [ADR 0151](0151-governance-permissions-as-atom-ranks-as-roles.md) §3 (the session-scope registry — scope is capability on the token, orthogonal to rank), migration `035_auth_token_hygiene.sql` (the 24h TTL + refresh-on-use it introduced, and the deliberate backfill grace that came with it).
|
|
8
8
|
|
|
9
|
+
> **Amended by [ADR 0294](0294-cli-sessions-do-not-expire.md) (2026-09-16).** This ADR declined to add an absolute ceiling *on top of* the 24h sliding TTL, and that judgement stands unchanged. What changed afterwards is the sliding TTL itself: ADR 0294 removed it from CLI sessions entirely, on the same reasoning this file states below — refresh-on-use meant the timer only ever fired on the legitimate holder, never on a leak. So where the text below says an idle session expires after 24h, read: CLI sessions do not expire at all, and the own-scoped revoke shipped here is now the only bound on a leaked bearer rather than the faster of two.
|
|
10
|
+
|
|
9
11
|
## Context
|
|
10
12
|
|
|
11
13
|
`db-kernel.js lookupSession` rewrites `expires_at = now() + 24h` on every authenticated request. That is refresh-on-use, and for a session in daily use it means the TTL never arrives: a CLI bearer sitting in `~/.config/otb/gds-session.json` is, in practice, immortal. If it leaks — committed to a repo, left on a shared dev box, copied off a stolen laptop — the holder keeps it alive simply by using it.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# 0294 — CLI sessions do not expire; revocation is the whole remedy
|
|
2
|
+
|
|
3
|
+
- **Status:** Accepted
|
|
4
|
+
- **Date:** 2026-09-16
|
|
5
|
+
- **Deciders:** The owner, who asked for the CLI timeout removed entirely after a three-week gap signed him out, and accepted the stated security consequence.
|
|
6
|
+
- **Task:** task 1003956 (goal 1000110 — Working area 2: account creation, management & community)
|
|
7
|
+
- **Interacts with:** [ADR 0206](0206-own-scoped-session-revocation-no-absolute-ceiling.md) (the own-scoped revoke this leans on, and the ceiling it declined to build), [ADR 0016](0016-trust-boundary-server-enforced-permissions.md) (a session is resolved fresh per request, never cached), migrations `035_auth_token_hygiene.sql` (introduced the TTL) and `core_243_cli_sessions_never_expire.sql` (clears it).
|
|
8
|
+
|
|
9
|
+
## Context
|
|
10
|
+
|
|
11
|
+
A `bongos` sign-in died after 24 hours of not being used. `db-kernel.js createSession` defaulted a null `expiresAt` to `now() + SESSION_TTL_MS`, and `lookupSession` rewrote `expires_at = now() + 24h` on every authenticated request. An actively-used session therefore never lapsed; an idle one did, and the builder came back to `Your Bongos CLI session is no longer valid` blocking every `/builder-*` command until they re-authed through the browser.
|
|
12
|
+
|
|
13
|
+
This is the third time the number has been argued. V3.R38 (task #259, migration 035) set it to 1 hour. V3.R90 (task 1000400) widened it to 24 hours with the note that 1h "kicked builders out over lunch". Each round shortened the nuisance and left the mechanism alone. The owner hit it again on 2026-09-16 after a three-week gap and asked for the timeout gone rather than widened a fourth time.
|
|
14
|
+
|
|
15
|
+
The argument for keeping it was always auth-token hygiene (GDS-V3 criterion C9): the token sits in a plaintext file at mode 0600, so a leaked or abandoned copy should not live forever. **That argument does not survive contact with refresh-on-use.** A leaked token is, by definition, a token somebody is using — and every use rolled the expiry forward another 24 hours. The timer could only ever fire on a session that had gone quiet, which is the legitimate holder stepping away, not the thief. The control was aimed at attackers and landed exclusively on users. ADR 0206 had already written down half of this ("a CLI bearer sitting in the session file is, in practice, immortal"); what it did not do is draw the conclusion that a control which cannot reach its target is not worth its cost.
|
|
16
|
+
|
|
17
|
+
What genuinely bounds a leaked bearer arrived with ADR 0206 itself: `GET /me/sessions`, `POST /me/sessions/revoke`, `POST /me/sessions/revoke-all`, and the hall's "Sign out everywhere" — no rank required, no Archon to find. A person who knows their token leaked gets it dead in one request. That is strictly faster than any TTL, which at best kills it the following day.
|
|
18
|
+
|
|
19
|
+
## Decision
|
|
20
|
+
|
|
21
|
+
**CLI sessions carry no expiry. They last until revoked.**
|
|
22
|
+
|
|
23
|
+
1. `createSession` records `expires_at = NULL` when the caller names no expiry. `lookupSession` already treated NULL as never-expires, so this is a one-line default change rather than new machinery. Every call site that passes `null` is `source: 'cli'` — the CLI device flow, the federated CLI sign-in, the hall's token re-issue, and the dev-box pair.
|
|
24
|
+
2. `lookupSession` still bumps `last_used_at` on every request, but rolls `expires_at` forward **only for a row that already has one**:
|
|
25
|
+
|
|
26
|
+
```sql
|
|
27
|
+
expires_at = CASE WHEN expires_at IS NULL THEN NULL
|
|
28
|
+
ELSE now() + ($2::int * INTERVAL '1 millisecond') END
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Without the `CASE`, the first request after sign-in would mint the very timeout this removes.
|
|
32
|
+
3. Migration `core_243` clears `expires_at` on live, unrevoked `source='cli'` rows, so the change reaches sessions that already exist instead of charging every builder one last forced re-auth on deploy day. This is migration 035's grace inverted, for the same reason 035 gave: do not sign out the people holding sessions when the policy moves.
|
|
33
|
+
|
|
34
|
+
**Browser sessions are untouched.** They pass an explicit 30-day `expiresAt`, so the new default never reaches them, and `core_243`'s `source = 'cli'` filter leaves their rows alone. `SESSION_TTL_MS` survives as the roll-forward window for rows that have an expiry — it is no longer a default.
|
|
35
|
+
|
|
36
|
+
### Rejected
|
|
37
|
+
|
|
38
|
+
- **Widen the TTL again (7 days, 30 days).** The fourth iteration of a mechanism whose first three failed for the same structural reason. Any window loose enough to stop annoying builders is far too loose to bound a leak, and refresh-on-use means it still never fires on the attacker.
|
|
39
|
+
- **Keep the TTL and make re-auth cheaper.** Already tried: ADR-era tasks 1000401 and 1000504 built the one-click hall re-issue and `/builder-reauth` precisely to soften this. The friction got smaller and the interruption stayed. Removing the interruption is what was asked for.
|
|
40
|
+
- **An absolute ceiling on `created_at`.** ADR 0206 rejected it and its reasoning holds unchanged: a cap signs out every dev box and long-running session on a timer, for a threat the revoke answers faster. Nothing here revives it.
|
|
41
|
+
- **Drop `expires_at` from the schema.** The column is load-bearing for web sessions and still honoured by `lookupSession`. Removing it would force browser expiry into some other mechanism for no gain.
|
|
42
|
+
- **A blanket `UPDATE builder_sessions SET expires_at = NULL`.** Would hand browser cookies an immortality nobody decided on — and irreversibly, since `lookupSession` only rolls an expiry it can already see, so a web row cleared by mistake stays cleared. The `source = 'cli'` filter is the safety property of that migration.
|
|
43
|
+
|
|
44
|
+
## Consequences
|
|
45
|
+
|
|
46
|
+
- A builder signs in once and stays signed in. Coming back after a weekend, a holiday, or three weeks costs nothing.
|
|
47
|
+
- **A leaked CLI token is valid until somebody revokes it.** This is the real cost and it was accepted explicitly. In practice it is a smaller change than it sounds: under refresh-on-use a leaked token in active use was already immortal, so what actually changes is the *abandoned* copy — one on a decommissioned laptop or an old dev box — which previously went stale on its own and now does not.
|
|
48
|
+
- The remedy is self-serve and immediate (ADR 0206). The compounding half of that decision now carries more weight: the session list shows `created_at` beside `last_used_at`, so a session nobody recognises is something a person can see and kill. That visibility was a nice-to-have beside a timer; it is now the primary control.
|
|
49
|
+
- Nothing about rank or deactivation changes. `setBuilderRank` still calls `revokeAllSessionsForBuilder` on every demotion, and `requireBuilder` still refuses a deactivated builder on the hot path regardless of a live token. Neither consults `expires_at` — the TTL was never load-bearing for C7.
|
|
50
|
+
- Criterion C9's idle-session hygiene claim is no longer met by a timer, and should be read as met by revocation plus visibility instead.
|
|
51
|
+
|
|
52
|
+
## When to revisit
|
|
53
|
+
|
|
54
|
+
- **Sessions start being issued to principals nobody will notice a leak on** — machine identities, CI runners, embedded devices. ADR 0206 flagged this and it is the strongest argument for a cap; such a principal wants a short, non-sliding expiry set explicitly at creation, which the `expiresAt` parameter already supports without changing this default.
|
|
55
|
+
- **A compliance or hosting requirement names a maximum session age.** Then it belongs on `created_at` as a true ceiling, per ADR 0206, with the grace treatment `core_243` and 035 both gave.
|
|
56
|
+
- **The session list shows long-dead sessions accumulating faster than people prune them.** That is the visibility half failing, and it is now the only half.
|
package/docs/adr/README.md
CHANGED
|
@@ -385,3 +385,4 @@ This keeps the decision history honest and traceable.
|
|
|
385
385
|
| 0291 | [**The accessibility dimension scores the transcript, so it lives in the session evaluator, not the grader panel** ([task 1002516](https://cloudbongos.com/builders#/task/1002516), clause 4 of criterion `accessibility-fluency-profile`). The task and its sibling task 1003937 both point at `modules/grading/`, but a v4 worker is handed `{ task, valueSummary, diff, changedFiles, … }` and **no transcript** — it judges the change a session produced, never how the builder was spoken to while producing it. The transcript lives in `session_records`, so the dimension does too: `modules/sessions/accessibility.js`, a pure scorer over the digest's per-turn detail, scored SERVER-SIDE at upload (a score the uploader could supply is a score nobody can read; and the SessionEnd hook's 6-second watchdog rules out a second round trip for the profile). Three signals, exactly the three the criterion names. **Profile-relative, and a sanctioned signal is not scored at all** — `engineer` waives jargon + framing, `comfortable` waives terminal-first, `mixed` widens rather than waives, and an all-waived session scores `null`, not a number, so a profile-sanctioned reply cannot score low even in principle; `verbosity` is not an input and every rate is per-1000-characters, so brevity is arithmetically incapable of lowering it. Advisory, gating nothing (ADR 0241's shape). The profile crosses the module boundary as DATA — the route resolves the `builder-settings` kernel port, never a sibling require (ADR 0083), with a test as the tripwire against a value rename. Detectors deliberately undercount (word-bounded jargon, after a substring match fired `ORM` on "format"; a click-path table of UIs that actually exist), because a metric that punishes correct work is what the task itself calls actively harmful. Does NOT answer task 1003937, whose copy/voice lens judges a DIFF and still needs the owner on cost, gate-or-advisory, and what standard to judge against.](0291-the-accessibility-dimension-scores-the-transcript-not-the-diff.md) | grading / sessions / accessibility |
|
|
386
386
|
| 0292 | [**Both install paths stay, and vendoring turns out to be the production one** ([task 1003879](https://cloudbongos.com/builders#/task/1003879), goal 1000090 — public-npm **gate 6**, the decision row held back from gate 5's mechanical cleanup because both candidates were large deletions that could as easily be mistakes). Gate 6 framed the **vendored-tarball path** (~124 refs across `upgrade.js`/`init.js`/`provision-repo.js`/`package-core.js`) as insurance for hosts that cannot reach npm, to be weighed against the AGPL self-host promise. Read that way it loses. **That is not what the code does with it:** `provision-repo.js` resolves the core for every wizard-created project as "a tarball built from THIS CHECKOUT, file:-pinned", with `--registry-core` as the opt-out — vendoring is the production provisioning path, not the fallback, and deleting it breaks project creation. `resolveVendorableCoreTarball`'s three-rung ladder (reuse the control plane's own pinned tarball → pack the core it has INSTALLED → rebuild from source) exists to answer one question a registry install cannot express: *what is the control plane actually running?* Rungs 1 and 2 each name the bug they fixed — empty new repos when the runner is itself a consumer (task 1002275), and "the frozen-old-tarball births" (task 1002706). The owner's input — a checkout routinely runs ahead of the registry — holds, though not via `main`: since publish-on-merge, 136 of the last 300 `main` commits are release bumps, releases land every 9–51 min, and `main`'s `CORE_VERSION` equalled the registry `latest` (1.19.785) when this was written. Provisioning does not provision from `main`; it provisions from the control plane's own tree, and an unmerged branch or a deliberately-held pin exists on the registry at NO version, ever. **Supersedes ADR 0134's "vendored is the default until the registry path is proven end-to-end" clause:** the condition is MET twice over — with a token 2026-07-08 (task 2118 / blocker 71) and tokenless since [ADR 0283](0283-the-core-publishes-publicly-because-access-was-never-the-thing-protecting-it.md) (`npm pack @bongos/core` from a clean `HOME` fetches 25.5 MB) — and the default stands anyway, for load-bearing reasons rather than for want of proof. Costs accepted with open eyes: ~124 refs, a second install mode every scaffold/upgrade/provision path must keep straight, a Windows-only test failure ([task 1003839](https://cloudbongos.com/builders#/task/1003839)), `refreshStandaloneCorePin`. **Candidate 2 — `@cloudbongos/cli` — also STAYS, on a NEW justification:** task 1003872 was right that the original reason (reaching the board without a private-registry credential) is gone, so it needs one of its own, and the measurement supplies it — 32 files / 0.5 MB / `undici`, versus the core's 2,611 files / 46.4 MB / `express`+`pg`+`pino`. Collapsing it would make `npx` a 46 MB download dragging in a web server and a Postgres driver to print a task list. **Unpublishing was never on the table** (public at 0.1.3, strangers may have installed it); [ADR 0258](0258-the-public-cli-is-a-generated-client-package-not-the-published-core.md) stands with its *rationale* amended — a deliberately small entry point, no longer a private-core workaround. The one real rot the flip left is rung 2's stale "still-private registry package" clause, a comment. **Gate 6 closes keep/keep, no removal task.** Reopens if provisioning ever stops meaning "match the control plane".](0292-both-install-paths-stay-and-vendoring-is-the-production-one.md) | distribution / provisioning |
|
|
387
387
|
| 0293 | [**The owner's deploy door is an INTENT the control plane drains, not a web tier with a shell** ([task 1003159](https://cloudbongos.com/builders#/task/1003159), goal 1000090). The owner, twice in a month: *"I don't think I can make anything live"*, then *"this is above my paygrade"*. Moving a core pin is gate 3 of the release pipeline and the ONLY act that makes shipped work visible to users — and it was reachable from exactly one place: a shell on the control-plane box holding an ssh key. No route, no hall control. An agent could not close the loop either, because the permission classifier blocks a mutating `sudo -u lars` over ssh BY DESIGN — so the gate had no owner-reachable control AND no agent-reachable one, which is a structural mismatch with a platform whose premise is that agents build and the human holds the gates. Measured cost the day it was written: cloudbongos.com served 1.19.754 while 1.19.757, carrying a shipped fix to the claimable-task feed, sat published and unreachable. **Decision: the door is an INTENT the control-plane runner drains** — the seam [ADR 0111](0111-instance-hosting-provisioning-module.md) already built and that provision/teardown/domain-attach/settings-apply/restart already ride; `enqueueIntent` already carries `requested_by`, so the actor requirement is met by the existing shape rather than bolted on. The web tier gains no sudo, no ssh key, no new capability — it gains the ability to ASK. Rejected: giving the web tier sudo (ADR 0043 makes the control plane a separate privilege domain precisely so compromising the HTTP-reachable process does not hand over the box), and a second bespoke queue (another set of claim/settle/retry semantics to get right, when this one's failure modes are already understood in production). **The control plane is enrolled as a SELF ROW** because `provisioning_intents.instance_id` is NOT NULL and the box has never had one — which is exactly why the door had nothing to point at. That same row puts a teardown button one query away from the platform, so `hosting_shape='control-plane'` is a distinct value every destructive leg can refuse BY NAME, and the rule is an ALLOW-list (`core-upgrade`, `restart`) rather than a deny-list of today's destructive actions: a deny-list would admit tomorrow's by omission, and "permitted by omission" here means the platform deleting itself. A partial unique index makes a second self row impossible. Rejected: a nullable `instance_id` meaning "the control plane" (an implicit encoding that reads as a bug at every later call site and silently widens every query assuming the FK holds). **The target version comes from the published registry, never the request** — not tidiness: the release lane mints a git tag BEFORE the npm publish completes (measured 8–13 minutes on 2026-09-16 against ~100s recorded earlier, learnings 1000263/1000266), so a door trusting "whatever main says" would routinely queue a version that does not exist and die on `npm error notarget`. **Two-step preview-then-confirm**, the owner's own call: auto-rollback already guards the bad-release case, this guards the different one — the wrong button. **Archon-gated server-side, no caching** (ADR 0016), with `core_241` giving the ledger an actor and a source, because a privileged action with an unattributed row is not acceptable and the table carried only a free-text note. **Truth is the BOX's `/version`, not the apex** (the apex sits behind pre-launch basic_auth and its HTML rides a CDN, so an apex read reports a stale answer with total confidence). Self-restart is safe for a stated reason rather than an assumed one: the runner and the web service are DIFFERENT systemd units, and a running process keeps the code it already loaded, so an upgrade rewriting the runner's own files takes effect next tick, never mid-run. Tenant instances ride the same queue and are a filed follow-up. What it does NOT do: make deploys automatic — ADR 0137's three gates stand, and this only gives the third a handle.](0293-the-owners-deploy-door-is-an-intent-the-control-plane-drains.md) | deploy / authority / provisioning |
|
|
388
|
+
| 0294 | [**CLI sessions do not expire; revocation is the whole remedy** ([task 1003956](https://cloudbongos.com/builders#/task/1003956), goal 1000110 — owner request 2026-09-16, after a three-week gap signed him out). A `bongos` sign-in died after 24h idle: `createSession` defaulted a null `expiresAt` to `now()+SESSION_TTL_MS` and `lookupSession` rewrote `expires_at = now()+24h` on every request. **This is the third argument about the number and the first about the mechanism** — V3.R38 ([task #259](https://cloudbongos.com/builders#/task/259), migration 035) set 1h, V3.R90 ([task 1000400](https://cloudbongos.com/builders#/task/1000400)) widened it to 24h because 1h "kicked builders out over lunch", and each round shortened the nuisance while leaving the mechanism alone. The hygiene case for keeping it (criterion C9 — bound a leaked token in the mode-0600 plaintext session file) **does not survive contact with refresh-on-use**: a leaked token is by definition one somebody is using, and every use rolled the expiry forward another day, so the timer could only ever fire on a session gone quiet — the legitimate holder stepping away. The control was aimed at attackers and landed exclusively on users. [ADR 0206](0206-own-scoped-session-revocation-no-absolute-ceiling.md) had already written half of this down ("a CLI bearer in daily use is, in practice, immortal") without drawing the conclusion. Decision: **CLI sessions carry no expiry and last until revoked.** `createSession` records `expires_at = NULL` when the caller names none — every such call site is `source: 'cli'` (device flow, federated sign-in, hall re-issue, dev-box pair) — and `lookupSession` still bumps `last_used_at` but rolls `expires_at` forward only via `CASE WHEN expires_at IS NULL THEN NULL ELSE …`; **without that CASE the first request after sign-in re-mints the very timeout this removes.** Migration `core_243` clears `expires_at` on live unrevoked `source='cli'` rows, so the change reaches existing sessions instead of charging every builder one last forced re-auth on deploy day — migration 035's grace inverted, for the reason 035 gave. **Browser sessions are untouched** (they pass an explicit 30 days, so the new default never reaches them, and the migration's `source='cli'` filter is its whole safety property — `lookupSession` only rolls an expiry it can already see, so a web row cleared by mistake stays cleared forever). `SESSION_TTL_MS` survives as the roll-forward window for rows that have an expiry, no longer as a default. The accepted cost, stated plainly: **a leaked CLI token is valid until revoked** — smaller in practice than it sounds, since under refresh-on-use an actively-used leak was already immortal, so what changes is the *abandoned* copy on a dead laptop. The remedy is ADR 0206's own-scoped revoke, immediate where a TTL is next-day, and that ADR's visibility half (`created_at` beside `last_used_at`) is now the primary control rather than a nice-to-have. Nothing about rank moves: `setBuilderRank` still sweeps on demotion and `requireBuilder` still refuses a deactivated builder on the hot path, neither consulting `expires_at`. Rejected: widening the TTL a fourth time; keeping it and making re-auth cheaper (already built — [task 1000401](https://cloudbongos.com/builders#/task/1000401), [task 1000504](https://cloudbongos.com/builders#/task/1000504) — the friction shrank, the interruption stayed); an absolute `created_at` ceiling (ADR 0206's reasoning holds unchanged); dropping the column (load-bearing for web); a blanket `UPDATE … SET expires_at = NULL`. Revisit for machine principals nobody will notice a leak on, a compliance maximum, or long-dead sessions accumulating faster than people prune them.](0294-cli-sessions-do-not-expire.md) | auth / account security / CLI |
|