@bongos/core 1.19.709 → 1.19.711

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.
Files changed (35) hide show
  1. package/.bongos-core.json +74 -34
  2. package/clients/bongos-client/README.md +1 -1
  3. package/clients/bongos-client/bongos-client.global.js +16 -0
  4. package/clients/bongos-client/index.cjs +16 -0
  5. package/clients/bongos-client/index.d.ts +24 -0
  6. package/clients/bongos-client/index.mjs +16 -0
  7. package/docs/adr/0128-provisioning-runner-privilege-and-db-auth-model.md +2 -0
  8. package/docs/adr/0145-free-hosted-project-tier-isolation-and-domain-separation.md +2 -0
  9. package/docs/adr/0281-an-instance-identity-is-its-own-unix-account-and-pg-role.md +56 -0
  10. package/docs/adr/README.md +1 -0
  11. package/docs/api/openapi.json +506 -3
  12. package/docs/api-reference.md +14 -2
  13. package/docs/module-api-changelog.md +4 -0
  14. package/docs/recipes/ops-gotchas.md +14 -0
  15. package/modules/agents/lib/answer-hold.js +91 -0
  16. package/modules/agents/lib/authoring.js +155 -0
  17. package/modules/agents/lib/fire-budget.js +109 -0
  18. package/modules/agents/lib/gate.js +64 -0
  19. package/modules/agents/routes/agents.js +494 -0
  20. package/modules/agents/spawn.js +12 -0
  21. package/modules/dev-box/app/src/vendor/bongos-client.cjs +16 -0
  22. package/modules/government/catalog.js +10 -0
  23. package/modules/government/migrations/government_013_agent_atoms.sql +66 -0
  24. package/package-lock.json +2 -2
  25. package/package.json +1 -1
  26. package/scripts/gds/agent-invoke.js +5 -27
  27. package/scripts/gds/provision-repo.js +181 -0
  28. package/scripts/gds/provision-units.js +33 -5
  29. package/scripts/gds/provision.js +35 -3
  30. package/src/bongos/pool.js +14 -0
  31. package/src/module-api.js +1 -1
  32. package/tests/agents_authoring.mjs +306 -0
  33. package/tests/agents_routes.mjs +76 -15
  34. package/tests/agents_write_routes.mjs +461 -0
  35. package/tests/provision.mjs +227 -0
@@ -29,6 +29,10 @@
29
29
  "name": "achievements",
30
30
  "description": "Endpoints under `/api/bongos/achievements`."
31
31
  },
32
+ {
33
+ "name": "agent-runs",
34
+ "description": "Endpoints under `/api/bongos/agent-runs`."
35
+ },
32
36
  {
33
37
  "name": "agents",
34
38
  "description": "Endpoints under `/api/bongos/agents`."
@@ -509,6 +513,58 @@
509
513
  ]
510
514
  }
511
515
  },
516
+ "/agent-runs/{id}": {
517
+ "get": {
518
+ "operationId": "get_agent_runs_id",
519
+ "tags": [
520
+ "agent-runs"
521
+ ],
522
+ "summary": "GET /agent-runs/:id",
523
+ "description": "GET /agent-runs/:id — COLLECT THE ANSWER TO A FIRE YOU STARTED. OWN RUNS ONLY, enforced in the WHERE clause rather than after the read. The id is a sequential integer primary key, so without this any authenticated builder could walk the ledger and read another builder's held answer — text generated from THEIR free-text question, out of a repo the agent read — plus the github_login in `trigger_ref`. That is the textbook IDOR, and it is worse here than the usual shape because the interesting field is model output derived from someone else's prompt. Filtering in SQL, not in JS, is deliberate: a post-read comparison is one early `return` away from leaking, and it would still have handed the row to `hold.take()` on the way past. A run with a NULL requester (an event-triggered fire — nobody asked for it) is therefore readable by nobody here, which is correct for a route whose job is \"collect MY answer\". An operator's view of the whole ledger is a different surface with a different gate, and it is not built: when it is, it belongs behind `agent.arm`, not behind a widening of this route. See ../lib/answer-hold.js for why the text is held and not stored.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
524
+ "x-rank": "any-builder",
525
+ "x-source": "modules/agents/routes/agents.js",
526
+ "parameters": [
527
+ {
528
+ "name": "id",
529
+ "in": "path",
530
+ "required": true,
531
+ "schema": {
532
+ "type": "string"
533
+ },
534
+ "description": "Path parameter `id`."
535
+ }
536
+ ],
537
+ "responses": {
538
+ "200": {
539
+ "description": "Success.",
540
+ "content": {
541
+ "application/json": {
542
+ "schema": {
543
+ "$ref": "#/components/schemas/GetAgentRunsIdResponse"
544
+ }
545
+ }
546
+ }
547
+ },
548
+ "400": {
549
+ "$ref": "#/components/responses/BadRequest"
550
+ },
551
+ "401": {
552
+ "$ref": "#/components/responses/Unauthorized"
553
+ },
554
+ "403": {
555
+ "$ref": "#/components/responses/Forbidden"
556
+ },
557
+ "404": {
558
+ "$ref": "#/components/responses/NotFound"
559
+ }
560
+ },
561
+ "security": [
562
+ {
563
+ "builderSession": []
564
+ }
565
+ ]
566
+ }
567
+ },
512
568
  "/agents": {
513
569
  "get": {
514
570
  "operationId": "get_agents",
@@ -545,6 +601,57 @@
545
601
  "builderSession": []
546
602
  }
547
603
  ]
604
+ },
605
+ "post": {
606
+ "operationId": "post_agents",
607
+ "tags": [
608
+ "agents"
609
+ ],
610
+ "summary": "POST /agents",
611
+ "description": "POST /agents — author a definition against the DB, no file edit. It lands DISARMED, always. Creating and arming are separate acts with separate audit lines, and a create that armed would let one call both invent an agent and switch it on — the same reasoning agents-sync rule 4 applies to a commit, applied to an HTTP request.\n\n**Rank:** `metic+archon` — Metic or Archon rank (review/triage powers).\n\n**Permissions:** `agent.author` (all required).",
612
+ "x-rank": "metic+archon",
613
+ "x-source": "modules/agents/routes/agents.js",
614
+ "x-permissions": [
615
+ "agent.author"
616
+ ],
617
+ "requestBody": {
618
+ "required": false,
619
+ "content": {
620
+ "application/json": {
621
+ "schema": {
622
+ "type": "object",
623
+ "additionalProperties": true
624
+ }
625
+ }
626
+ },
627
+ "x-validated": true
628
+ },
629
+ "responses": {
630
+ "200": {
631
+ "description": "Success.",
632
+ "content": {
633
+ "application/json": {
634
+ "schema": {
635
+ "$ref": "#/components/schemas/PostAgentsResponse"
636
+ }
637
+ }
638
+ }
639
+ },
640
+ "400": {
641
+ "$ref": "#/components/responses/ValidationFailed"
642
+ },
643
+ "401": {
644
+ "$ref": "#/components/responses/Unauthorized"
645
+ },
646
+ "403": {
647
+ "$ref": "#/components/responses/Forbidden"
648
+ }
649
+ },
650
+ "security": [
651
+ {
652
+ "builderSession": []
653
+ }
654
+ ]
548
655
  }
549
656
  },
550
657
  "/agents/{name}": {
@@ -597,6 +704,297 @@
597
704
  "builderSession": []
598
705
  }
599
706
  ]
707
+ },
708
+ "patch": {
709
+ "operationId": "patch_agents_name",
710
+ "tags": [
711
+ "agents"
712
+ ],
713
+ "summary": "PATCH /agents/:name",
714
+ "description": "PATCH /agents/:name — edit IN PLACE. One row per name, before and after. The patch is merged over the stored row and the WHOLE definition is re-validated (authoring.mergeForValidation says why), against the EDITOR's live rank — which re-stamps author_rank downward if a lower-ranked builder edits, and re-runs the scope wall against that new rank.\n\n**Rank:** `metic+archon` — Metic or Archon rank (review/triage powers).\n\n**Permissions:** `agent.author` (all required).",
715
+ "x-rank": "metic+archon",
716
+ "x-source": "modules/agents/routes/agents.js",
717
+ "x-permissions": [
718
+ "agent.author"
719
+ ],
720
+ "parameters": [
721
+ {
722
+ "name": "name",
723
+ "in": "path",
724
+ "required": true,
725
+ "schema": {
726
+ "type": "string"
727
+ },
728
+ "description": "Path parameter `name`."
729
+ }
730
+ ],
731
+ "requestBody": {
732
+ "required": false,
733
+ "content": {
734
+ "application/json": {
735
+ "schema": {
736
+ "type": "object",
737
+ "additionalProperties": true
738
+ }
739
+ }
740
+ },
741
+ "x-validated": true
742
+ },
743
+ "responses": {
744
+ "200": {
745
+ "description": "Success.",
746
+ "content": {
747
+ "application/json": {
748
+ "schema": {
749
+ "$ref": "#/components/schemas/PatchAgentsNameResponse"
750
+ }
751
+ }
752
+ }
753
+ },
754
+ "400": {
755
+ "$ref": "#/components/responses/ValidationFailed"
756
+ },
757
+ "401": {
758
+ "$ref": "#/components/responses/Unauthorized"
759
+ },
760
+ "403": {
761
+ "$ref": "#/components/responses/Forbidden"
762
+ },
763
+ "404": {
764
+ "$ref": "#/components/responses/NotFound"
765
+ }
766
+ },
767
+ "security": [
768
+ {
769
+ "builderSession": []
770
+ }
771
+ ]
772
+ },
773
+ "delete": {
774
+ "operationId": "delete_agents_name",
775
+ "tags": [
776
+ "agents"
777
+ ],
778
+ "summary": "DELETE /agents/:name",
779
+ "description": "DELETE /agents/:name — only a db-authored definition, and only ever one. The LEDGER is untouched: agents_runs.definition_id is ON DELETE SET NULL and carries a denormalized agent_name, so what this agent spent and did survives the definition it pointed at (the schema header says why).\n\n**Rank:** `metic+archon` — Metic or Archon rank (review/triage powers).\n\n**Permissions:** `agent.author` (all required).",
780
+ "x-rank": "metic+archon",
781
+ "x-source": "modules/agents/routes/agents.js",
782
+ "x-permissions": [
783
+ "agent.author"
784
+ ],
785
+ "parameters": [
786
+ {
787
+ "name": "name",
788
+ "in": "path",
789
+ "required": true,
790
+ "schema": {
791
+ "type": "string"
792
+ },
793
+ "description": "Path parameter `name`."
794
+ }
795
+ ],
796
+ "responses": {
797
+ "200": {
798
+ "description": "Success.",
799
+ "content": {
800
+ "application/json": {
801
+ "schema": {
802
+ "$ref": "#/components/schemas/DeleteAgentsNameResponse"
803
+ }
804
+ }
805
+ }
806
+ },
807
+ "400": {
808
+ "$ref": "#/components/responses/BadRequest"
809
+ },
810
+ "401": {
811
+ "$ref": "#/components/responses/Unauthorized"
812
+ },
813
+ "403": {
814
+ "$ref": "#/components/responses/Forbidden"
815
+ },
816
+ "404": {
817
+ "$ref": "#/components/responses/NotFound"
818
+ }
819
+ },
820
+ "security": [
821
+ {
822
+ "builderSession": []
823
+ }
824
+ ]
825
+ }
826
+ },
827
+ "/agents/{name}/disable": {
828
+ "post": {
829
+ "operationId": "post_agents_name_disable",
830
+ "tags": [
831
+ "agents"
832
+ ],
833
+ "summary": "POST /agents/:name/disable",
834
+ "description": "POST /agents/:name/disable — and THIS is what stops dispatch. Two halves, because there are two ways an agent fires. The event dispatch index is partial (`WHERE enabled`), so an event agent switched off stops being selected at all; an on-demand agent has no selection step, so gate.js refuses it at invoke. Off means off on both paths, which is the property this route exists to give an operator.\n\n**Rank:** `metic+archon` — Metic or Archon rank (review/triage powers).\n\n**Permissions:** `agent.arm` (all required).",
835
+ "x-rank": "metic+archon",
836
+ "x-source": "modules/agents/routes/agents.js",
837
+ "x-permissions": [
838
+ "agent.arm"
839
+ ],
840
+ "parameters": [
841
+ {
842
+ "name": "name",
843
+ "in": "path",
844
+ "required": true,
845
+ "schema": {
846
+ "type": "string"
847
+ },
848
+ "description": "Path parameter `name`."
849
+ }
850
+ ],
851
+ "responses": {
852
+ "200": {
853
+ "description": "Success.",
854
+ "content": {
855
+ "application/json": {
856
+ "schema": {
857
+ "$ref": "#/components/schemas/PostAgentsNameDisableResponse"
858
+ }
859
+ }
860
+ }
861
+ },
862
+ "400": {
863
+ "$ref": "#/components/responses/BadRequest"
864
+ },
865
+ "401": {
866
+ "$ref": "#/components/responses/Unauthorized"
867
+ },
868
+ "403": {
869
+ "$ref": "#/components/responses/Forbidden"
870
+ },
871
+ "404": {
872
+ "$ref": "#/components/responses/NotFound"
873
+ }
874
+ },
875
+ "security": [
876
+ {
877
+ "builderSession": []
878
+ }
879
+ ]
880
+ }
881
+ },
882
+ "/agents/{name}/enable": {
883
+ "post": {
884
+ "operationId": "post_agents_name_enable",
885
+ "tags": [
886
+ "agents"
887
+ ],
888
+ "summary": "POST /agents/:name/enable",
889
+ "description": "POST /agents/:name/enable — THE OPERATOR ACT. This is the one write that works on a FILE-sourced definition, and it is the whole reason a committed agent can ever run: agents-sync deliberately never arms anything (its rule 4), and carries an operator's `enabled` forward untouched on every later deploy. So arming here is durable, and landing a commit still cannot arm anything. Two refusals it cannot talk its way past, both enforced by the schema as well as here: a flagged definition stays disarmed, and a definition with no author_rank has no authority to act with.\n\n**Rank:** `metic+archon` — Metic or Archon rank (review/triage powers).\n\n**Permissions:** `agent.arm` (all required).",
890
+ "x-rank": "metic+archon",
891
+ "x-source": "modules/agents/routes/agents.js",
892
+ "x-permissions": [
893
+ "agent.arm"
894
+ ],
895
+ "parameters": [
896
+ {
897
+ "name": "name",
898
+ "in": "path",
899
+ "required": true,
900
+ "schema": {
901
+ "type": "string"
902
+ },
903
+ "description": "Path parameter `name`."
904
+ }
905
+ ],
906
+ "responses": {
907
+ "200": {
908
+ "description": "Success.",
909
+ "content": {
910
+ "application/json": {
911
+ "schema": {
912
+ "$ref": "#/components/schemas/PostAgentsNameEnableResponse"
913
+ }
914
+ }
915
+ }
916
+ },
917
+ "400": {
918
+ "$ref": "#/components/responses/BadRequest"
919
+ },
920
+ "401": {
921
+ "$ref": "#/components/responses/Unauthorized"
922
+ },
923
+ "403": {
924
+ "$ref": "#/components/responses/Forbidden"
925
+ },
926
+ "404": {
927
+ "$ref": "#/components/responses/NotFound"
928
+ }
929
+ },
930
+ "security": [
931
+ {
932
+ "builderSession": []
933
+ }
934
+ ]
935
+ }
936
+ },
937
+ "/agents/{name}/invoke": {
938
+ "post": {
939
+ "operationId": "post_agents_name_invoke",
940
+ "tags": [
941
+ "agents"
942
+ ],
943
+ "summary": "POST /agents/:name/invoke",
944
+ "description": "POST /agents/:name/invoke — fire an on-demand agent. 202, never 200. A fire takes 60-120 seconds and costs about a dollar, which is past the edge proxy's patience — so the ledger row IS the response and the run continues behind it. Collect the answer from GET /agent-runs/:id. SELF-GATED at requireBuilder, deliberately, matching the reads: which agents exist at all is an operator decision, and a v1 agent is hard-read-only. The thing that reasoning does NOT bound is SPEND, so a per-builder ceiling does (../lib/fire-budget.js). It is a blast radius, not a budget: the real accounting is agents_runs.cost_usd through the shared LLM cost cache.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
945
+ "x-rank": "any-builder",
946
+ "x-source": "modules/agents/routes/agents.js",
947
+ "parameters": [
948
+ {
949
+ "name": "name",
950
+ "in": "path",
951
+ "required": true,
952
+ "schema": {
953
+ "type": "string"
954
+ },
955
+ "description": "Path parameter `name`."
956
+ }
957
+ ],
958
+ "requestBody": {
959
+ "required": false,
960
+ "content": {
961
+ "application/json": {
962
+ "schema": {
963
+ "$ref": "#/components/schemas/PostAgentsNameInvokeRequest"
964
+ }
965
+ }
966
+ },
967
+ "x-validated": true
968
+ },
969
+ "responses": {
970
+ "200": {
971
+ "description": "Success.",
972
+ "content": {
973
+ "application/json": {
974
+ "schema": {
975
+ "$ref": "#/components/schemas/PostAgentsNameInvokeResponse"
976
+ }
977
+ }
978
+ }
979
+ },
980
+ "400": {
981
+ "$ref": "#/components/responses/ValidationFailed"
982
+ },
983
+ "401": {
984
+ "$ref": "#/components/responses/Unauthorized"
985
+ },
986
+ "403": {
987
+ "$ref": "#/components/responses/Forbidden"
988
+ },
989
+ "404": {
990
+ "$ref": "#/components/responses/NotFound"
991
+ }
992
+ },
993
+ "security": [
994
+ {
995
+ "builderSession": []
996
+ }
997
+ ]
600
998
  }
601
999
  },
602
1000
  "/analytics/builder/{id}": {
@@ -16689,6 +17087,19 @@
16689
17087
  },
16690
17088
  "description": "The inner error object carried by every non-2xx response."
16691
17089
  },
17090
+ "DeleteAgentsNameResponse": {
17091
+ "type": "object",
17092
+ "properties": {
17093
+ "ok": {
17094
+ "type": "boolean"
17095
+ },
17096
+ "deleted": {}
17097
+ },
17098
+ "required": [
17099
+ "ok",
17100
+ "deleted"
17101
+ ]
17102
+ },
16692
17103
  "DeleteBoxSshKeyIdResponse": {
16693
17104
  "type": "object",
16694
17105
  "properties": {
@@ -16870,6 +17281,19 @@
16870
17281
  "achievements"
16871
17282
  ]
16872
17283
  },
17284
+ "GetAgentRunsIdResponse": {
17285
+ "type": "object",
17286
+ "properties": {
17287
+ "ok": {
17288
+ "type": "boolean"
17289
+ },
17290
+ "run": {}
17291
+ },
17292
+ "required": [
17293
+ "ok",
17294
+ "run"
17295
+ ]
17296
+ },
16873
17297
  "GetAgentsNameResponse": {
16874
17298
  "type": "object",
16875
17299
  "properties": {
@@ -18515,6 +18939,19 @@
18515
18939
  "request"
18516
18940
  ]
18517
18941
  },
18942
+ "PatchAgentsNameResponse": {
18943
+ "type": "object",
18944
+ "properties": {
18945
+ "ok": {
18946
+ "type": "boolean"
18947
+ },
18948
+ "agent": {}
18949
+ },
18950
+ "required": [
18951
+ "ok",
18952
+ "agent"
18953
+ ]
18954
+ },
18518
18955
  "PatchBoxesBuilderIdBlockRequest": {
18519
18956
  "type": "object",
18520
18957
  "properties": {
@@ -19239,6 +19676,72 @@
19239
19676
  "ok"
19240
19677
  ]
19241
19678
  },
19679
+ "PostAgentsNameDisableResponse": {
19680
+ "type": "object",
19681
+ "properties": {
19682
+ "ok": {
19683
+ "type": "boolean"
19684
+ },
19685
+ "agent": {}
19686
+ },
19687
+ "required": [
19688
+ "ok",
19689
+ "agent"
19690
+ ]
19691
+ },
19692
+ "PostAgentsNameEnableResponse": {
19693
+ "type": "object",
19694
+ "properties": {
19695
+ "ok": {
19696
+ "type": "boolean"
19697
+ },
19698
+ "agent": {}
19699
+ },
19700
+ "required": [
19701
+ "ok",
19702
+ "agent"
19703
+ ]
19704
+ },
19705
+ "PostAgentsNameInvokeRequest": {
19706
+ "type": "object",
19707
+ "properties": {
19708
+ "input": {
19709
+ "type": "string",
19710
+ "maxLength": 8192
19711
+ }
19712
+ },
19713
+ "additionalProperties": false
19714
+ },
19715
+ "PostAgentsNameInvokeResponse": {
19716
+ "type": "object",
19717
+ "properties": {
19718
+ "ok": {
19719
+ "type": "boolean"
19720
+ },
19721
+ "run_id": {},
19722
+ "poll": {},
19723
+ "message": {}
19724
+ },
19725
+ "required": [
19726
+ "ok",
19727
+ "run_id",
19728
+ "poll",
19729
+ "message"
19730
+ ]
19731
+ },
19732
+ "PostAgentsResponse": {
19733
+ "type": "object",
19734
+ "properties": {
19735
+ "ok": {
19736
+ "type": "boolean"
19737
+ },
19738
+ "agent": {}
19739
+ },
19740
+ "required": [
19741
+ "ok",
19742
+ "agent"
19743
+ ]
19744
+ },
19242
19745
  "PostAuthAppPairApproveRequest": {
19243
19746
  "type": "object",
19244
19747
  "properties": {
@@ -23116,9 +23619,9 @@
23116
23619
  "description": "A required dependency/feature is not configured or is temporarily down."
23117
23620
  }
23118
23621
  },
23119
- "x-endpoint-count": 365,
23120
- "x-schema-count": 403,
23622
+ "x-endpoint-count": 372,
23623
+ "x-schema-count": 411,
23121
23624
  "x-undocumented-bodies": 9,
23122
- "x-response-schemas": 272,
23625
+ "x-response-schemas": 279,
23123
23626
  "x-generated-by": "scripts/gds/gen-api-docs.js"
23124
23627
  }
@@ -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. 365 endpoints across 59 route files.
5
+ > **Generated from the live route files** — the route file is authoritative. 372 endpoints across 59 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`.
@@ -23,12 +23,24 @@ Base path: `/api/bongos`. Ranks (enforced server-side, [ADR 0016](adr/<redacted>
23
23
  |---|---|---|---|---|
24
24
  | GET | `/api/bongos/achievements` | `any-builder` | — | All seeded achievements — lets /builders render locked + unlocked side by side. |
25
25
 
26
- ## `agents` (2)
26
+ ## `agent-runs` (1)
27
+
28
+ | Method | Path | Rank | Body | Description |
29
+ |---|---|---|---|---|
30
+ | GET | `/api/bongos/agent-runs/:id` | `any-builder` | — | GET /agent-runs/:id — COLLECT THE ANSWER TO A FIRE YOU STARTED. |
31
+
32
+ ## `agents` (8)
27
33
 
28
34
  | Method | Path | Rank | Body | Description |
29
35
  |---|---|---|---|---|
30
36
  | GET | `/api/bongos/agents` | `any-builder` | — | GET /agents — the whole registry. |
37
+ | POST | `/api/bongos/agents` | `metic+archon` | validated | POST /agents — author a definition against the DB, no file edit. |
31
38
  | GET | `/api/bongos/agents/:name` | `any-builder` | — | GET /agents/:name — one definition, persona included. |
39
+ | PATCH | `/api/bongos/agents/:name` | `metic+archon` | validated | PATCH /agents/:name — edit IN PLACE. |
40
+ | DELETE | `/api/bongos/agents/:name` | `metic+archon` | — | DELETE /agents/:name — only a db-authored definition, and only ever one. |
41
+ | POST | `/api/bongos/agents/:name/disable` | `metic+archon` | — | POST /agents/:name/disable — and THIS is what stops dispatch. |
42
+ | POST | `/api/bongos/agents/:name/enable` | `metic+archon` | — | POST /agents/:name/enable — THE OPERATOR ACT. |
43
+ | POST | `/api/bongos/agents/:name/invoke` | `any-builder` | `input` | POST /agents/:name/invoke — fire an on-demand agent. |
32
44
 
33
45
  ## `analytics` (1)
34
46
 
@@ -1877,5 +1877,9 @@ is load-bearing: the script throws rather than guess if it is missing, and
1877
1877
  landed since 1.19.707 with no explicit bump. run 34742245823. (task 1002620)
1878
1878
  1.19.709 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
1879
1879
  landed since 1.19.708 with no explicit bump. run 34743928060. (task 1002620)
1880
+ 1.19.710 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
1881
+ landed since 1.19.709 with no explicit bump. run 34744369035. (task 1002620)
1882
+ 1.19.711 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
1883
+ landed since 1.19.710 with no explicit bump. run 34744940181. (task 1002620)
1880
1884
  ---------------------------------------------------------------------------
1881
1885
  ```
@@ -25,6 +25,20 @@ Organized by topic, not chronology. New entries go in the matching section; crea
25
25
  - **Why it bites:** The `pg` library's defaults assume a standard TCP+password setup, which is the most common configuration but not the cheapest one for a single-machine deploy. Peer auth is more secure and frictionless once you know the socket path. The error message points at SASL, not at the host setting, which buries the real cause.
26
26
  - **Where this lives in the codebase:** [`src/db.js`](../../src/db.js) has the explicit socket host.
27
27
 
28
+ ### Re-provisioning an EXISTING instance moves its `HOME`, orphaning any stored builder secrets
29
+
30
+ - **Symptom:** after a re-provision, an instance that had builder API keys stored can no longer decrypt them. Nothing errors at startup — the instance comes up healthy and simply behaves as though no key was ever saved.
31
+ - **Root cause:** task [1003369](https://cloudbongos.com/builders#/task/1003369) ([ADR 0281](../adr/<redacted>.md)) gives each instance its own unix account with `HOME=/var/lib/bongos-<slug>`. [`src/bongos/secret-box.js`](../../src/bongos/secret-box.js) keeps its master key at `~/.config/<configDir>/builder-secret.key`, so the key an instance wrote under the OLD shared home (`/home/lars/.config/…`) is no longer on its `HOME` path. `provisionMasterKey()` finds nothing, mints a fresh key, and every previously encrypted secret is now undecryptable ciphertext.
32
+ - **Who this hits:** only a **re-provision of an instance that has already stored secrets**. A fresh standup has nothing to lose, and an instance whose `web.env` sets `BUILDER_SECRET_KEY` is unaffected — that env wins over the file.
33
+ - **Fix (before the re-provision, one command as root):**
34
+ ```bash
35
+ install -D -o bongos-<slug> -g bongos-<slug> -m 600 \
36
+ /home/lars/.config/<configDir>/builder-secret.key \
37
+ /var/lib/bongos-<slug>/.config/<configDir>/builder-secret.key
38
+ ```
39
+ `<configDir>` is the instance’s `branding.configDir` (vanilla: `cloudbongos`). Do it **first** — `provisionMasterKey()` writes with the `wx` flag and refuses to clobber, so a key placed after the instance has already minted its own will not be picked up.
40
+ - **Why it bites:** the failure is silent on both ends. The old key is not deleted, the new one is valid, and the only visible symptom is secrets that quietly stopped working.
41
+
28
42
  ---
29
43
 
30
44
  ## Colyseus