@askexenow/exe-os 0.9.362 → 0.9.365

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 (450) hide show
  1. package/deploy/compose/.env.customer.example +46 -2
  2. package/deploy/compose/.env.example +49 -2
  3. package/deploy/compose/cloudflared/config.yml.example +15 -8
  4. package/deploy/compose/docker-compose.yml +240 -34
  5. package/deploy/compose/generate-env.ts +88 -2
  6. package/deploy/compose/status.sh +30 -0
  7. package/deploy/compose/uptime-check.sh +39 -2
  8. package/deploy/compose/wiki-schema-guard.sh +302 -0
  9. package/deploy/stack-manifests/v0.9.json +232 -2
  10. package/dist/.dist-verified.json +3 -3
  11. package/dist/{active-agent-BPNEBKHI.js → active-agent-C6DPH43L.js} +10 -5
  12. package/dist/{active-agent-3FV5V7SS.js → active-agent-MGF7J3HB.js} +10 -5
  13. package/dist/agent-defs-generator-X7TVMPVB.js +240 -0
  14. package/dist/{agentic-ontology-RCVCJ6WB.js → agentic-ontology-CARDCG37.js} +1 -1
  15. package/dist/artifact-backup-767STHSW.js +209 -0
  16. package/dist/artifact-provenance-C7HQ267L.js +13 -0
  17. package/dist/{backfill-gate-LRX77SUD.js → backfill-gate-VX2RZTQV.js} +2 -2
  18. package/dist/{backfill-metadata-Y3DGVCK5.js → backfill-metadata-ANCJA6LA.js} +7 -7
  19. package/dist/{background-jobs-Z6KHCWES.js → background-jobs-LTPWCMA3.js} +2 -2
  20. package/dist/{backup-verify-TZPPUCLU.js → backup-verify-F7BHVEF7.js} +27 -14
  21. package/dist/{behavior-injection-JMGGWCWG.js → behavior-injection-XQ4B7DCF.js} +13 -12
  22. package/dist/{behaviors-TT6CMEKC.js → behaviors-R2IE3PEN.js} +12 -11
  23. package/dist/bin/age-ontology-load.js +2 -2
  24. package/dist/bin/agentic-ontology-backfill.js +9 -9
  25. package/dist/bin/agentic-reflection-backfill.js +10 -10
  26. package/dist/bin/agentic-semantic-label.js +9 -9
  27. package/dist/bin/backfill-conversations.js +10 -9
  28. package/dist/bin/backfill-responses.js +10 -9
  29. package/dist/bin/backfill-vectors.js +14 -13
  30. package/dist/bin/bulk-sync-postgres.js +23 -19
  31. package/dist/bin/cc-doctor.js +11 -8
  32. package/dist/bin/cleanup-stale-review-tasks.js +26 -24
  33. package/dist/bin/cli.js +29 -102
  34. package/dist/bin/customer-readiness.js +3 -1
  35. package/dist/bin/deferred-daemon-restart.js +115 -103
  36. package/dist/bin/exe-agent-config.js +5 -5
  37. package/dist/bin/exe-agent.js +14 -14
  38. package/dist/bin/exe-assign.js +12 -11
  39. package/dist/bin/exe-boot.js +60 -51
  40. package/dist/bin/exe-call.js +7 -7
  41. package/dist/bin/exe-cloud.js +26 -22
  42. package/dist/bin/exe-config-dump.js +5 -5
  43. package/dist/bin/exe-dispatch.js +30 -24
  44. package/dist/bin/exe-doctor.js +10 -9
  45. package/dist/bin/exe-export-behaviors.js +14 -13
  46. package/dist/bin/exe-forget.js +9 -9
  47. package/dist/bin/exe-gateway.js +9 -9
  48. package/dist/bin/exe-grant.js +5 -5
  49. package/dist/bin/exe-healthcheck.js +13 -8
  50. package/dist/bin/exe-heartbeat.js +36 -29
  51. package/dist/bin/exe-kill.js +30 -28
  52. package/dist/bin/exe-launch-agent.js +41 -37
  53. package/dist/bin/exe-mcp.js +5 -5
  54. package/dist/bin/exe-memory-reads.js +7 -7
  55. package/dist/bin/exe-new-employee.js +12 -11
  56. package/dist/bin/exe-os-mcp-proxy.js +5 -5
  57. package/dist/bin/exe-pending-messages.js +27 -25
  58. package/dist/bin/exe-pending-notifications.js +26 -24
  59. package/dist/bin/exe-pending-reviews.js +27 -25
  60. package/dist/bin/exe-rename.js +7 -7
  61. package/dist/bin/exe-review.js +29 -27
  62. package/dist/bin/exe-search.js +8 -8
  63. package/dist/bin/exe-session-cleanup.js +31 -29
  64. package/dist/bin/exe-settings.js +24 -20
  65. package/dist/bin/exe-start-codex.js +36 -21
  66. package/dist/bin/exe-start-opencode.js +15 -14
  67. package/dist/bin/exe-status.js +28 -25
  68. package/dist/bin/exe-support.js +5 -4
  69. package/dist/bin/exe-team.js +12 -6
  70. package/dist/bin/exe-verify-running-images.js +145 -0
  71. package/dist/bin/exe-watchdog.js +16 -6
  72. package/dist/bin/git-sweep.js +27 -25
  73. package/dist/bin/graph-backfill.js +10 -9
  74. package/dist/bin/graph-export.js +8 -8
  75. package/dist/bin/import-history.js +13 -11
  76. package/dist/bin/install.js +119 -107
  77. package/dist/bin/intercom-check.js +5 -5
  78. package/dist/bin/mcp-sessions.js +2 -2
  79. package/dist/bin/orchestration-metrics.js +6 -6
  80. package/dist/bin/postgres-agentic-reflection-backfill.js +4 -4
  81. package/dist/bin/postgres-agentic-semantic-backfill.js +3 -3
  82. package/dist/bin/pre-publish.js +1 -1
  83. package/dist/bin/reconcile-backlog.js +2 -2
  84. package/dist/bin/registry-proxy.js +1 -1
  85. package/dist/bin/scan-tasks.js +26 -24
  86. package/dist/bin/setup.js +4 -3
  87. package/dist/bin/shard-migrate.js +8 -8
  88. package/dist/bin/stack-update.js +87 -38
  89. package/dist/bin/vps-health-gate.js +1 -1
  90. package/dist/{boot-generation-XABYKF5P.js → boot-generation-X45BSVZY.js} +5 -5
  91. package/dist/{boot-review-sections-2X5TFIGU.js → boot-review-sections-OBVRXXSF.js} +25 -23
  92. package/dist/{branch-hygiene-Z4IBWAOX.js → branch-hygiene-HFJ3HA62.js} +17 -10
  93. package/dist/{branding-7MZDHTKN.js → branding-GH43W3PU.js} +2 -2
  94. package/dist/build-manifest.json +1189 -1049
  95. package/dist/{capability-cards-O5UXDOOA.js → capability-cards-57FURUMR.js} +4 -4
  96. package/dist/{capacity-monitor-IPXM726A.js → capacity-monitor-QEL62JDO.js} +27 -25
  97. package/dist/{catchup-brief-EBIGDXOK.js → catchup-brief-PMXTCASA.js} +30 -28
  98. package/dist/{chunk-UO5QJ2RX.js → chunk-22YQBVMO.js} +4 -4
  99. package/dist/chunk-2ADYTOHQ.js +189 -0
  100. package/dist/{chunk-NO5KHKKB.js → chunk-2CWS374F.js} +1 -1
  101. package/dist/{chunk-NAHCSQWT.js → chunk-2ESB5SDP.js} +19 -7
  102. package/dist/{chunk-CY5NVDTH.js → chunk-2FYIMTQE.js} +9 -5
  103. package/dist/{chunk-SC6WDF3Q.js → chunk-2RQT7TED.js} +2 -2
  104. package/dist/{chunk-UWEFIIEG.js → chunk-3CIMDFXG.js} +1 -1
  105. package/dist/{chunk-E7PL3T2U.js → chunk-3JNVB5LG.js} +1 -1
  106. package/dist/{chunk-7SY2NOMQ.js → chunk-3THNS2T7.js} +1 -1
  107. package/dist/{chunk-XJIRUTGJ.js → chunk-45EQTS7B.js} +1 -1
  108. package/dist/{chunk-YZUHSV24.js → chunk-4LWBC7CM.js} +1 -1
  109. package/dist/{chunk-DKRDU2AX.js → chunk-4QKKLK3C.js} +14 -14
  110. package/dist/{chunk-WP2TGH5R.js → chunk-52TIQJEQ.js} +2 -2
  111. package/dist/{chunk-LG37UBBE.js → chunk-5CBGH6RA.js} +5 -5
  112. package/dist/{chunk-W3LKIQJ2.js → chunk-6GQYPJPG.js} +22 -16
  113. package/dist/{chunk-I3SF6QGV.js → chunk-6Q263OZE.js} +1 -1
  114. package/dist/{chunk-XSYQ5EPE.js → chunk-7C4FUNVO.js} +11 -11
  115. package/dist/{chunk-P7CV4SHQ.js → chunk-7Y6AQWVT.js} +21 -10
  116. package/dist/{chunk-NGDXBN3M.js → chunk-A6PDF4BO.js} +2 -2
  117. package/dist/chunk-A7XLR3K7.js +45 -0
  118. package/dist/{chunk-7H3AZNQ7.js → chunk-AGQAGOQN.js} +9 -9
  119. package/dist/{chunk-VDMFVUSY.js → chunk-ANPE3TT3.js} +3 -3
  120. package/dist/{chunk-NE4QI2GX.js → chunk-BATQKSLI.js} +2 -2
  121. package/dist/{chunk-AOMFYUPT.js → chunk-BEHLIRRJ.js} +2 -2
  122. package/dist/{chunk-6EM6USV7.js → chunk-BNHAZ65R.js} +42 -50
  123. package/dist/{chunk-HLU52KTO.js → chunk-BRLS5OLZ.js} +257 -36
  124. package/dist/{chunk-QCJT265O.js → chunk-BY7WWPRM.js} +1 -1
  125. package/dist/{chunk-Z67DIJKT.js → chunk-C3UAQ3JG.js} +1 -1
  126. package/dist/{chunk-MZYNW5OY.js → chunk-CDUB5Q6J.js} +1 -1
  127. package/dist/{chunk-5H3RHWRE.js → chunk-CKG6CVKI.js} +1 -1
  128. package/dist/{chunk-WH2VVEGD.js → chunk-CMBF5WCL.js} +56 -2
  129. package/dist/{chunk-JYKZVAS6.js → chunk-CPILYY2K.js} +1 -1
  130. package/dist/{chunk-XTGR3LMF.js → chunk-CW2UCKHS.js} +2 -2
  131. package/dist/{chunk-32FEF5BW.js → chunk-CWBFOAGM.js} +2 -2
  132. package/dist/{chunk-V5I5N7NC.js → chunk-DEFSN2FQ.js} +3 -3
  133. package/dist/{chunk-HAXCUCJC.js → chunk-DJK45U7B.js} +1 -1
  134. package/dist/{chunk-MZZN5GFC.js → chunk-DQ5KHWFD.js} +1 -1
  135. package/dist/{chunk-SLBOKYYQ.js → chunk-DWAQOEXL.js} +2 -2
  136. package/dist/{chunk-T2RZU7JA.js → chunk-DYWGIPVP.js} +7 -7
  137. package/dist/{chunk-6DTALXOA.js → chunk-E5HAY3DQ.js} +1 -1
  138. package/dist/{chunk-D5CY4E7I.js → chunk-EHHMNKOA.js} +1 -1
  139. package/dist/{chunk-54USDN5Q.js → chunk-EJ4522YM.js} +1 -1
  140. package/dist/{chunk-URNVIZXU.js → chunk-EKVTRI3N.js} +52 -6
  141. package/dist/{chunk-KID6T2A4.js → chunk-EMZHB767.js} +44 -35
  142. package/dist/{chunk-KBI4FVBZ.js → chunk-EPDNWY5P.js} +111 -22
  143. package/dist/{chunk-AQO3BT3I.js → chunk-EPNWEOOE.js} +13 -13
  144. package/dist/{chunk-5C6ZIJ7J.js → chunk-ESTR7QUP.js} +1 -1
  145. package/dist/{chunk-Q2VQNAPU.js → chunk-EVPBYX2Z.js} +2 -2
  146. package/dist/{chunk-CVYDJ2TW.js → chunk-EWCEUO65.js} +1 -1
  147. package/dist/{chunk-RNSAVQEU.js → chunk-EWZYFG5Q.js} +8 -3
  148. package/dist/{chunk-R7F6HKTH.js → chunk-EXDW3E2R.js} +3 -1
  149. package/dist/{chunk-TY4JLBFO.js → chunk-F2S3M5MB.js} +3 -3
  150. package/dist/{chunk-BT2PUK3S.js → chunk-FXPAUDCZ.js} +1 -1
  151. package/dist/{chunk-3C7NE4JE.js → chunk-G2EAYH4U.js} +1 -1
  152. package/dist/{chunk-WDA2KAV7.js → chunk-GSJLB6JI.js} +1 -1
  153. package/dist/{chunk-M67ARGXY.js → chunk-H37FSTF2.js} +1 -1
  154. package/dist/{chunk-ZR4CQBAP.js → chunk-H74466TM.js} +1 -1
  155. package/dist/{chunk-3BAGFZFN.js → chunk-HHGOCBVQ.js} +9 -9
  156. package/dist/{working-memory-BACVOAIV.js → chunk-HSFBNCNC.js} +3 -3
  157. package/dist/{chunk-PF7EQSE5.js → chunk-IIZITKBV.js} +6 -6
  158. package/dist/{chunk-2FA4PYYX.js → chunk-IM2I7OOS.js} +1 -1
  159. package/dist/{chunk-DSKEYEL2.js → chunk-IXEFO6HD.js} +3 -3
  160. package/dist/{chunk-TN2E6ZCM.js → chunk-J4U5Z7VX.js} +214 -58
  161. package/dist/{chunk-PU7UYQRC.js → chunk-J5EYOBFF.js} +1 -1
  162. package/dist/{chunk-RWKX2TK5.js → chunk-J6I7X272.js} +1 -1
  163. package/dist/{chunk-BDLRTLXY.js → chunk-JITNFE4W.js} +6 -2
  164. package/dist/{chunk-PUMH7JSB.js → chunk-JNVQGLPX.js} +3 -3
  165. package/dist/{chunk-VAOUB2TK.js → chunk-JTB5V6BV.js} +1 -1
  166. package/dist/{chunk-QYRI25IP.js → chunk-JUOZLXLH.js} +2 -2
  167. package/dist/{chunk-RYRNY6IF.js → chunk-JYFYIGYK.js} +2 -2
  168. package/dist/{chunk-2PRAXZWM.js → chunk-KOYGRP2L.js} +10 -5
  169. package/dist/{chunk-4AFD4R35.js → chunk-KRUYQFSH.js} +5 -5
  170. package/dist/chunk-L25OAFOU.js +93 -0
  171. package/dist/{chunk-FUQ2OSDV.js → chunk-L2VZBPJJ.js} +25 -8
  172. package/dist/{chunk-LCDW2HUL.js → chunk-LHOKWZKL.js} +1 -1
  173. package/dist/{chunk-BN26LSME.js → chunk-LKYGSJ2E.js} +13 -2
  174. package/dist/chunk-LNOS24GV.js +214 -0
  175. package/dist/{chunk-O32HTNRX.js → chunk-LZKYVMBY.js} +13 -4
  176. package/dist/{chunk-35FD67ZB.js → chunk-M7EES2KM.js} +3 -3
  177. package/dist/chunk-MBBFL2SP.js +45 -0
  178. package/dist/{chunk-4R2WD6T3.js → chunk-MPQXQ5IE.js} +2 -2
  179. package/dist/{chunk-QQMYD4M7.js → chunk-MXEIZUXG.js} +1 -1
  180. package/dist/{chunk-6QJXFLNN.js → chunk-MZJBUAC3.js} +5 -5
  181. package/dist/chunk-NEHOQ3P2.js +19 -0
  182. package/dist/{chunk-BRZR76TS.js → chunk-NJFJWQNN.js} +49 -14
  183. package/dist/{chunk-Q7J2SQJ6.js → chunk-NO3M2AFM.js} +100 -32
  184. package/dist/{gotrue-session-2IT7MDGW.js → chunk-NUTZJZKW.js} +7 -8
  185. package/dist/{chunk-6UZDGEGQ.js → chunk-NV6A5HDN.js} +71 -1
  186. package/dist/{chunk-ZI3HA3XW.js → chunk-O2MEBF23.js} +3 -3
  187. package/dist/{chunk-V6YLIDLK.js → chunk-ODBRU6BF.js} +577 -230
  188. package/dist/{chunk-6N3HUOV3.js → chunk-OFGFRT5J.js} +2 -2
  189. package/dist/{chunk-HKQ7UTSE.js → chunk-OPNROU5B.js} +1 -1
  190. package/dist/{chunk-SQM4ZTL2.js → chunk-ORNCMRIX.js} +60 -33
  191. package/dist/{chunk-RUUX2ZST.js → chunk-P47MPWNU.js} +1 -1
  192. package/dist/{org-key-admin-4W6LZ5KZ.js → chunk-P5XTVIEC.js} +62 -17
  193. package/dist/{chunk-OZNCAMS3.js → chunk-POPRJKAY.js} +122 -20
  194. package/dist/{chunk-SRFRUN66.js → chunk-PPGPCWRC.js} +5 -5
  195. package/dist/{chunk-B6PFGUYJ.js → chunk-PVEM537A.js} +46 -5
  196. package/dist/{chunk-QJBX3MBV.js → chunk-Q7BU5Q6I.js} +3 -3
  197. package/dist/{chunk-SXKIGBNF.js → chunk-QFIVK3G5.js} +1 -1
  198. package/dist/{chunk-BRIZ6OZ3.js → chunk-QMSEL5YS.js} +81 -23
  199. package/dist/{chunk-BJEQBKGH.js → chunk-QYJANQWC.js} +8 -8
  200. package/dist/{chunk-4U56FZ7B.js → chunk-R7RM766N.js} +3 -3
  201. package/dist/{chunk-QI25QRUP.js → chunk-RD4E5G3B.js} +2 -2
  202. package/dist/{chunk-QP53XLC5.js → chunk-RHX6MNWY.js} +1 -1
  203. package/dist/chunk-RIECV7SR.js +234 -0
  204. package/dist/{chunk-3YBJ36UX.js → chunk-RLDMUSRM.js} +2 -2
  205. package/dist/{chunk-CXQ724WH.js → chunk-RVWALVDT.js} +2 -2
  206. package/dist/{chunk-VWTUKKUF.js → chunk-RZIKSE6N.js} +10 -4
  207. package/dist/chunk-SBAU735L.js +328 -0
  208. package/dist/{chunk-XRCX6R5S.js → chunk-SBJV2PCU.js} +1 -1
  209. package/dist/chunk-SO7KTH3Q.js +27 -0
  210. package/dist/{chunk-327Q53AS.js → chunk-SP37DONS.js} +17 -6
  211. package/dist/{chunk-KA4WVDLX.js → chunk-T6S45OLV.js} +4 -4
  212. package/dist/chunk-TCTED52S.js +120 -0
  213. package/dist/{chunk-HLQ4MS42.js → chunk-TO6TB75E.js} +4 -4
  214. package/dist/{chunk-465RCKVI.js → chunk-U373MEF5.js} +6 -6
  215. package/dist/{chunk-AL4HXKDJ.js → chunk-VGE6IHZB.js} +10 -10
  216. package/dist/{chunk-WBG7XYVR.js → chunk-VHJVD3MZ.js} +8 -0
  217. package/dist/{chunk-ZZEKOI2M.js → chunk-VMJ2UH4S.js} +1 -1
  218. package/dist/{chunk-3ICF4IAZ.js → chunk-VS4LPG66.js} +1 -1
  219. package/dist/{chunk-E3YI6FVJ.js → chunk-VV5EZZNT.js} +1 -1
  220. package/dist/{chunk-OPU3NYOO.js → chunk-W2IRQJ6R.js} +12 -0
  221. package/dist/{chunk-PV5BIXT6.js → chunk-WR75WUEP.js} +1 -1
  222. package/dist/{chunk-XUB4GFPN.js → chunk-WYLFDSIE.js} +144 -99
  223. package/dist/{chunk-4L33OFU4.js → chunk-X2P43TB2.js} +6 -3
  224. package/dist/chunk-X2X3IGZU.js +215 -0
  225. package/dist/{chunk-KIKB6FY3.js → chunk-X6A2VWVJ.js} +2 -2
  226. package/dist/chunk-XMVSLLED.js +38 -0
  227. package/dist/{chunk-KDMVVHZH.js → chunk-XTWHNLGD.js} +95 -23
  228. package/dist/{chunk-QY3TM2T3.js → chunk-YMJOLEPE.js} +4 -4
  229. package/dist/{chunk-RFKRCVGW.js → chunk-YYA5LXCT.js} +1 -1
  230. package/dist/{chunk-HSKABJE6.js → chunk-ZDUO6QBH.js} +5 -10
  231. package/dist/{chunk-PDJWHL63.js → chunk-ZEKCHV43.js} +4 -4
  232. package/dist/{chunk-23KWUA7Z.js → chunk-ZNEBZCZR.js} +53 -2
  233. package/dist/chunk-ZNUJFX7L.js +135 -0
  234. package/dist/{chunk-TP7RXGMP.js → chunk-ZRKEUCUZ.js} +315 -67
  235. package/dist/{chunk-6Y3KUO7Q.js → chunk-ZSZPMUNX.js} +244 -21
  236. package/dist/{chunk-4P2BFNSB.js → chunk-ZVTKWWLV.js} +2 -2
  237. package/dist/{chunk-RCOIKTHM.js → chunk-ZZXMB5F2.js} +1 -1
  238. package/dist/{cleanup-graph-garbage-6F22Y5SD.js → cleanup-graph-garbage-CAA625RJ.js} +10 -9
  239. package/dist/cloud-sync-command-DBB7WRHL.js +96 -0
  240. package/dist/cloud-sync-db-init-V7IB23W5.js +46 -0
  241. package/dist/{cloud-sync-shutdown-FWERMZB5.js → cloud-sync-shutdown-2D3B73CJ.js} +10 -4
  242. package/dist/{cloud-sync-state-DJRZORFI.js → cloud-sync-state-5DFKLWNB.js} +1 -1
  243. package/dist/{co-activation-UEEDCM5U.js → co-activation-JVN7EBS6.js} +4 -4
  244. package/dist/{co-occurrence-W6OPFRSY.js → co-occurrence-526PTFGT.js} +8 -7
  245. package/dist/{code-context-index-DWR6HS35.js → code-context-index-XOP6TLPZ.js} +5 -4
  246. package/dist/{conversation-entity-extractor-LWUIH3UB.js → conversation-entity-extractor-JO56DB2E.js} +9 -4
  247. package/dist/{conversation-wiki-populator-5EGRW2UZ.js → conversation-wiki-populator-EBUZXYEU.js} +1 -1
  248. package/dist/{crdt-sync-NHJX3ZJD.js → crdt-sync-LIJLZKFE.js} +2 -2
  249. package/dist/{crm-webhook-OY467ATC.js → crm-webhook-55F7Y6D2.js} +2 -2
  250. package/dist/{cto-delegation-gate-XH4YSQVC.js → cto-delegation-gate-QIYV4KFO.js} +41 -24
  251. package/dist/daemon-auth-SSPY5ODS.js +46 -0
  252. package/dist/{daemon-orchestration-7425ZVNH.js → daemon-orchestration-Q3GJA2JJ.js} +46 -30
  253. package/dist/{daemon-ready-VGPSRI3Z.js → daemon-ready-MKBH4QWD.js} +1 -1
  254. package/dist/{daemon-restart-report-HZU5LWXN.js → daemon-restart-report-EIVD6WDJ.js} +1 -1
  255. package/dist/db-integrity-events-5DTASXJF.js +22 -0
  256. package/dist/{db-restore-events-TFCZ4CPM.js → db-restore-events-2FI4ZMKA.js} +1 -1
  257. package/dist/{doc-graph-extractor-RR2VSO4W.js → doc-graph-extractor-P46R4QME.js} +13 -8
  258. package/dist/{documents-TRZ4R2ZW.js → documents-7KFEREWO.js} +12 -11
  259. package/dist/{dreaming-MAMZAPYW.js → dreaming-JMUXBAPU.js} +29 -27
  260. package/dist/employee-boot-brief-HN63D2BY.js +71 -0
  261. package/dist/{exe-drift-VBWB5E6S.js → exe-drift-7J7GX7DP.js} +5 -5
  262. package/dist/{exe-export-XJJTVVT7.js → exe-export-KJ7B2OBU.js} +14 -13
  263. package/dist/{exe-import-G6O7JFNZ.js → exe-import-HPZDWKGE.js} +14 -13
  264. package/dist/{exe-key-A2WJLLTM.js → exe-key-2PREP464.js} +117 -105
  265. package/dist/{exe-org-HXF4XBB3.js → exe-org-VEIISATF.js} +88 -26
  266. package/dist/{exe-snapshot-TL3TLKLR.js → exe-snapshot-JXQFLUKA.js} +33 -40
  267. package/dist/{fast-db-init-SO2XUQ7T.js → fast-db-init-SCXJXOYX.js} +1 -1
  268. package/dist/{founder-context-IOCADC66.js → founder-context-U2SZPLYV.js} +4 -3
  269. package/dist/{founder-monitor-envelope-SMILO4BT.js → founder-monitor-envelope-5KTKNL5G.js} +4 -4
  270. package/dist/fts-integrity-LKAZEZJE.js +96 -0
  271. package/dist/gateway/index.js +19 -19
  272. package/dist/{git-staleness-I6WT7EXV.js → git-staleness-S4NDR7NX.js} +4 -4
  273. package/dist/{global-procedures-3RMO6C3C.js → global-procedures-ZOFTOXIR.js} +6 -6
  274. package/dist/gotrue-session-ODZ2ERJX.js +25 -0
  275. package/dist/{graph-auto-extract-NKTYPD2I.js → graph-auto-extract-CLVTSUSB.js} +8 -7
  276. package/dist/{graph-rag-EOGPZOYI.js → graph-rag-LSJLWGXV.js} +4 -3
  277. package/dist/{hook-integrity-RTBF2BL2.js → hook-integrity-SGTKBL6W.js} +1 -1
  278. package/dist/{hook-prefetch-V5KCGW2N.js → hook-prefetch-BDSLB6F4.js} +26 -24
  279. package/dist/hooks/bug-report-worker.js +30 -28
  280. package/dist/hooks/codex-stop-task-finalizer.js +32 -30
  281. package/dist/hooks/commit-complete.js +29 -27
  282. package/dist/hooks/error-recall.js +11 -10
  283. package/dist/hooks/exe-heartbeat-hook.js +7 -6
  284. package/dist/hooks/ingest-worker.js +10 -9
  285. package/dist/hooks/ingest.js +18 -16
  286. package/dist/hooks/instructions-loaded.js +12 -8
  287. package/dist/hooks/manifest.json +20 -20
  288. package/dist/hooks/notification.js +8 -7
  289. package/dist/hooks/post-compact.js +7 -6
  290. package/dist/hooks/post-tool-combined.js +33 -14
  291. package/dist/hooks/pre-compact.js +31 -28
  292. package/dist/hooks/pre-tool-use.js +61 -34
  293. package/dist/hooks/prompt-submit.js +45 -42
  294. package/dist/hooks/session-end.js +40 -35
  295. package/dist/hooks/session-start.js +29 -71
  296. package/dist/hooks/stop.js +42 -36
  297. package/dist/hooks/subagent-stop.js +49 -33
  298. package/dist/hooks/summary-worker.js +35 -33
  299. package/dist/index.js +40 -38
  300. package/dist/{installer-G67M3DME.js → installer-EA7T3PBD.js} +7 -7
  301. package/dist/{installer-L3ZKMOPU.js → installer-KCEM4RCG.js} +7 -7
  302. package/dist/{installer-QBWZSYDG.js → installer-RC2JCKSE.js} +9 -7
  303. package/dist/{intercom-queue-R5LSXD3V.js → intercom-queue-6DZPHHQ2.js} +25 -23
  304. package/dist/{key-backup-status-3RC6XTUX.js → key-backup-status-6L2ARCXW.js} +1 -1
  305. package/dist/lib/agent-config.js +2 -2
  306. package/dist/lib/cloud-sync.js +25 -19
  307. package/dist/lib/config.js +1 -1
  308. package/dist/lib/consolidation.js +9 -9
  309. package/dist/lib/crypto.js +3 -1
  310. package/dist/lib/database.js +18 -4
  311. package/dist/lib/db-backup.js +4 -2
  312. package/dist/lib/db-daemon-client.js +5 -4
  313. package/dist/lib/db-write-worker.js +3 -3
  314. package/dist/lib/db.js +18 -4
  315. package/dist/lib/device-registry.js +2 -2
  316. package/dist/lib/embed-worker.js +1 -1
  317. package/dist/lib/embedder.js +5 -4
  318. package/dist/lib/employee-templates.js +7 -7
  319. package/dist/lib/employees.js +4 -4
  320. package/dist/lib/exe-daemon-client.js +4 -3
  321. package/dist/lib/exe-daemon.js +473 -206
  322. package/dist/lib/hybrid-search.js +8 -8
  323. package/dist/lib/identity-templates.js +1 -1
  324. package/dist/lib/identity.js +4 -4
  325. package/dist/lib/license.js +6 -4
  326. package/dist/lib/messaging.js +26 -24
  327. package/dist/lib/post-tool-memory.js +2 -2
  328. package/dist/lib/registry-proxy.js +3 -1
  329. package/dist/lib/reminders.js +5 -5
  330. package/dist/lib/schedules.js +8 -8
  331. package/dist/lib/session-registry.js +6 -6
  332. package/dist/lib/skill-learning.js +14 -12
  333. package/dist/lib/stack-invariants.js +21 -0
  334. package/dist/lib/status-brief.js +3 -3
  335. package/dist/lib/store.js +7 -7
  336. package/dist/lib/task-router.js +5 -5
  337. package/dist/lib/tasks.js +27 -25
  338. package/dist/lib/tmux-routing.js +25 -23
  339. package/dist/lib/token-spend.js +5 -5
  340. package/dist/{license-gate-6R3F3NQR.js → license-gate-HMSMUIAU.js} +5 -4
  341. package/dist/{license-sync-VBJCXBAP.js → license-sync-FBCMPC4J.js} +4 -3
  342. package/dist/{mailbox-inbox-CP2VRBXZ.js → mailbox-inbox-4MEUOL3J.js} +2 -2
  343. package/dist/{mailbox-outbox-HXI7DYNC.js → mailbox-outbox-J6WIF3UY.js} +1 -1
  344. package/dist/mcp/register-tools.js +114 -102
  345. package/dist/mcp/server.js +120 -108
  346. package/dist/mcp/tools/complete-reminder.js +6 -6
  347. package/dist/mcp/tools/create-reminder.js +6 -6
  348. package/dist/mcp/tools/create-task.js +30 -27
  349. package/dist/mcp/tools/deactivate-behavior.js +15 -13
  350. package/dist/mcp/tools/list-reminders.js +6 -6
  351. package/dist/mcp/tools/list-tasks.js +30 -27
  352. package/dist/mcp/tools/send-message.js +29 -26
  353. package/dist/mcp/tools/update-task.js +28 -26
  354. package/dist/{mcp-http-config-BWX7AMAZ.js → mcp-http-config-YKJN52HG.js} +5 -5
  355. package/dist/{mcp-session-identity-GOBGICLI.js → mcp-session-identity-QLUVYWPR.js} +25 -23
  356. package/dist/{memory-cards-4EXX3BQS.js → memory-cards-K2OSX4AW.js} +7 -7
  357. package/dist/{memory-graph-extractor-OYVZIJ3R.js → memory-graph-extractor-YLTSKLSY.js} +9 -8
  358. package/dist/{memory-poisoning-defense-AHOD2EBX.js → memory-poisoning-defense-IPO4ADRL.js} +4 -4
  359. package/dist/{memory-pressure-watchdog-COJC3OYS.js → memory-pressure-watchdog-ZI3TH2OF.js} +26 -24
  360. package/dist/{memory-queue-4VN3DTOE.js → memory-queue-QJNWKWKG.js} +2 -2
  361. package/dist/{memory-queue-client-OI4LDT4W.js → memory-queue-client-HIHCA3W4.js} +6 -5
  362. package/dist/{memory-reflection-V2SNAUIB.js → memory-reflection-ZKVRU3NT.js} +4 -4
  363. package/dist/{monitor-grants-W3ISOLMC.js → monitor-grants-BW2WL4IU.js} +3 -3
  364. package/dist/{notification-ledger-J6Q2GAYU.js → notification-ledger-4REL6M3T.js} +27 -25
  365. package/dist/{notifications-56TP36IK.js → notifications-UXJUAP3C.js} +25 -23
  366. package/dist/{orchestration-events-CKQ756NA.js → orchestration-events-GMRGGBMN.js} +5 -5
  367. package/dist/{orchestration-phase-ZJ4XVNFO.js → orchestration-phase-UDS24CVQ.js} +2 -2
  368. package/dist/{orchestrator-ERSQ3KNT.js → orchestrator-5TV75HDJ.js} +27 -25
  369. package/dist/{org-brief-session-FSZ3QJDN.js → org-brief-session-7AZZEO3F.js} +13 -13
  370. package/dist/{org-consent-policy-BN7MKCZ2.js → org-consent-policy-XT6UZENB.js} +2 -2
  371. package/dist/{org-join-HTQ57JYH.js → org-join-BMBQDMVF.js} +10 -7
  372. package/dist/org-key-admin-SCCVVE2H.js +38 -0
  373. package/dist/{org-key-pin-OTOKAELD.js → org-key-pin-5WUSPGVN.js} +3 -3
  374. package/dist/{org-membership-SML6GCHP.js → org-membership-ESALTLLN.js} +4 -4
  375. package/dist/{org-partner-config-XQGOB7EJ.js → org-partner-config-YKHPDPZR.js} +6 -6
  376. package/dist/{org-partner-runtime-RNNUL5IN.js → org-partner-runtime-WLZAZJIY.js} +14 -14
  377. package/dist/{org-row-visibility-JZFJ3YRJ.js → org-row-visibility-A5XM4MVO.js} +1 -1
  378. package/dist/{pipeline-router-IUENNTAY.js → pipeline-router-UEVXDUHR.js} +5 -5
  379. package/dist/{plan-limits-ZUII366B.js → plan-limits-QEP32X4W.js} +8 -7
  380. package/dist/{platform-procedures-2VQGBJU7.js → platform-procedures-44NDFTB6.js} +1 -1
  381. package/dist/{postinstall-failure-A7WTAXD6.js → postinstall-failure-B2JHNFPM.js} +2 -2
  382. package/dist/{project-boot-3DFY5F35.js → project-boot-LEZSK3W7.js} +9 -1
  383. package/dist/project-name-ILJTETUE.js +14 -0
  384. package/dist/{projection-worker-MBWUPB5O.js → projection-worker-GMRC6SBH.js} +4 -4
  385. package/dist/{prospective-memory-GHE5ORN4.js → prospective-memory-QLHSAPSG.js} +4 -4
  386. package/dist/{push-notifications-PTQ5DHCD.js → push-notifications-GKRPXOGI.js} +26 -24
  387. package/dist/reproject-worktree-memories-IXYGHTGA.js +224 -0
  388. package/dist/{reranker-NUIGAPA3.js → reranker-GB4VEMUV.js} +2 -2
  389. package/dist/{retrieval-health-XHCERQPG.js → retrieval-health-QIBPBGN6.js} +5 -1
  390. package/dist/{review-polling-CYBCBHZA.js → review-polling-MU47NQ2N.js} +26 -24
  391. package/dist/runtime/index.js +37 -35
  392. package/dist/seat-benefit-VDQJYJOB.js +17 -0
  393. package/dist/{session-events-X33W4JNA.js → session-events-YMQ3VGBJ.js} +26 -24
  394. package/dist/{session-kill-telemetry-I2F5PUIW.js → session-kill-telemetry-VGPKE57M.js} +26 -24
  395. package/dist/{session-mcp-config-YKNXG5DW.js → session-mcp-config-F7KATWOF.js} +10 -1
  396. package/dist/{session-scope-WXP4F7OF.js → session-scope-P46KAHXQ.js} +26 -24
  397. package/dist/{setup-wizard-2CTVITIX.js → setup-wizard-BRNHSJ4U.js} +4 -3
  398. package/dist/{shard-manager-YEOHEOHL.js → shard-manager-UROHZE46.js} +2 -2
  399. package/dist/{skill-refinement-VDR5OT4V.js → skill-refinement-NLYDSFEH.js} +5 -5
  400. package/dist/{stack-promote-XVC7FRJW.js → stack-promote-NHFTNIM2.js} +7 -5
  401. package/dist/{stack-release-6PWAACRM.js → stack-release-AQVKZUS7.js} +7 -5
  402. package/dist/{stack-update-24VRBQ3U.js → stack-update-LLX5KT7G.js} +38 -6
  403. package/dist/{steward-gate-2ZNYDVFO.js → steward-gate-JKHYFAHA.js} +5 -5
  404. package/dist/subagent-brief-injection-VDVOHDOU.js +97 -0
  405. package/dist/subagent-spawn-ledger-USYZXU3M.js +22 -0
  406. package/dist/{support-inbox-ZHKLDETT.js → support-inbox-S6YO5YZU.js} +5 -4
  407. package/dist/{support-outbox-DSWU3IXH.js → support-outbox-HFJWG52M.js} +1 -1
  408. package/dist/{task-enforcement-GQDUTIFY.js → task-enforcement-LXW45H5Z.js} +26 -24
  409. package/dist/{task-request-inbox-BFXNUU44.js → task-request-inbox-UCSKDC3R.js} +2 -2
  410. package/dist/{task-scope-ERHPHLUM.js → task-scope-GDLFE26E.js} +25 -23
  411. package/dist/{tasks-crud-NVNF3NRA.js → tasks-crud-W7VGWRUV.js} +25 -23
  412. package/dist/{tasks-notify-DXLU5VCT.js → tasks-notify-CT7NWCS4.js} +26 -24
  413. package/dist/{tasks-review-BST6N34P.js → tasks-review-WCSKOUFW.js} +25 -23
  414. package/dist/{telemetry-upload-NY3EKOGY.js → telemetry-upload-FK5WKXVB.js} +10 -9
  415. package/dist/{token-budget-CLB3GUIX.js → token-budget-GBNGSIU3.js} +5 -5
  416. package/dist/{tool-capability-index-WZWVBI2R.js → tool-capability-index-BT2GGLLI.js} +1 -1
  417. package/dist/{tool-telemetry-PALAZUXT.js → tool-telemetry-VKMRS6MP.js} +1 -1
  418. package/dist/tui/App.js +39 -37
  419. package/dist/{tui-data-CNFSRZP7.js → tui-data-FTPV2XBQ.js} +25 -23
  420. package/dist/{update-support-ZVB3CNGY.js → update-support-J6UWPUOC.js} +18 -15
  421. package/dist/{work-state-heartbeat-O7UMB6SE.js → work-state-heartbeat-4LDF6DSM.js} +1 -1
  422. package/dist/{worker-gate-6DQQWDNB.js → worker-gate-35VZINZG.js} +2 -2
  423. package/dist/{workflow-engine-5ZULFH7O.js → workflow-engine-4DA6RSHI.js} +2 -2
  424. package/dist/working-memory-DPFRSKH3.js +18 -0
  425. package/dist/{worktree-ZWA35PKW.js → worktree-H3YFJ3NU.js} +6 -6
  426. package/dist/{worktree-sweep-5LCVT46V.js → worktree-sweep-4EDLLQ2E.js} +9 -7
  427. package/package.json +11 -5
  428. package/release-notes.json +86 -34
  429. package/src/commands/exe/call.md +6 -4
  430. package/src/commands/exe-reference.md +11 -7
  431. package/src/commands/exe.md +16 -20
  432. package/dist/chunk-HISFWAAK.js +0 -90
  433. package/dist/daemon-auth-OCHW2U6B.js +0 -20
  434. package/dist/project-name-EWG5KAZP.js +0 -10
  435. package/dist/session-summary-5EYI4XW4.js +0 -128
  436. /package/dist/{chunk-YAZ5ECEG.js → chunk-3NBWARKC.js} +0 -0
  437. /package/dist/{chunk-6C4UAKMS.js → chunk-6CAX7YH4.js} +0 -0
  438. /package/dist/{chunk-FIPUVCKC.js → chunk-7DMCAGSC.js} +0 -0
  439. /package/dist/{chunk-X7LY3IOB.js → chunk-JRPYLUXH.js} +0 -0
  440. /package/dist/{chunk-L4LPJMPV.js → chunk-QYECRPGD.js} +0 -0
  441. /package/dist/{chunk-UELGUVNL.js → chunk-UY6HRDB2.js} +0 -0
  442. /package/dist/{chunk-YWN3XMPU.js → chunk-YSDIJ3VI.js} +0 -0
  443. /package/dist/{core-memory-BZFPVS43.js → core-memory-CMPPHJN5.js} +0 -0
  444. /package/dist/{entity-boost-VXXED26D.js → entity-boost-CDFAXVYL.js} +0 -0
  445. /package/dist/{memory-isolation-3PIOWLYP.js → memory-isolation-ZB5S5HCO.js} +0 -0
  446. /package/dist/{message-queue-client-5XW7ZOK2.js → message-queue-client-PL2POQOV.js} +0 -0
  447. /package/dist/{oauth-server-KW5M437C.js → oauth-server-NP7KGF7N.js} +0 -0
  448. /package/dist/{webhook-pipe-QJRFZVKA.js → webhook-pipe-UQSEHFPO.js} +0 -0
  449. /package/dist/{wiki-acl-RXFV5OEU.js → wiki-acl-5DKOWOIH.js} +0 -0
  450. /package/dist/{wiki-client-57QWZIHQ.js → wiki-client-AXNM5FWY.js} +0 -0
@@ -51,7 +51,7 @@ SMTP_PASS=
51
51
  # --- Auth gateway (exe-auth — shared SSO sign-in UI at auth.<domain>) ---
52
52
  # Bug 58840873: pin the auth image so compose never falls back to its stale
53
53
  # hardcoded default tag. Keep in sync with the manifest AUTH_IMAGE_TAG.
54
- AUTH_IMAGE_TAG=update.askexe.com/askexe/exe-auth:v0.2.0
54
+ AUTH_IMAGE_TAG=update.askexe.com/askexe/exe-auth:v0.5.0
55
55
  # Bug 379e5c13: server_name / CORS from the CONFIGURED apex domain, not by naive
56
56
  # last-2-label slicing (which breaks company.co.uk → co.uk).
57
57
  AUTH_SERVER_NAME=auth.CHANGEME_DOMAIN
@@ -61,15 +61,59 @@ AUTH_DEFAULT_PRODUCT=AUTH
61
61
  # the sign-up UI/route instead of showing a form that always fails.
62
62
  AUTH_DISABLE_SIGNUP=true
63
63
 
64
+ # --- Centralized role administration (permissions) ---
65
+ # The apps refuse to let an unmanaged first login mint its own admin
66
+ # (exe-crm#102 / exe-wiki#45). Both flags below are therefore left at their
67
+ # SECURE default of true, stated explicitly rather than inherited silently.
68
+ #
69
+ # Because of that, a fresh CRM gets its first admin from EXE_CRM_ADMIN_EMAIL
70
+ # in the CRM section below — NOT from whoever signs in first. That user is
71
+ # seeded at boot, so their sign-in is an existing-user login and is allowed;
72
+ # every OTHER unmanaged first login is still refused. If you leave
73
+ # EXE_CRM_ADMIN_EMAIL empty, the seed silently does not happen and the CRM
74
+ # cannot be bootstrapped at all. The wiki does not need this: its first admin
75
+ # comes from POST /system/enable-multi-user, authorised by WIKI_AUTH_TOKEN.
76
+ CRM_REQUIRE_MANAGED_PERMS=true
77
+ WIKI_REQUIRE_MANAGED_PERMS=true
78
+ #
79
+ # MANAGED MODE — roles set ONLY in the Exe dashboard. Empty means unmanaged:
80
+ # no token can carry permissions for an org that was never named. To adopt it:
81
+ # EXE_ORG_ID=<org id from the Exe dashboard>
82
+ # EXE_ORG_WORKSPACE_ID=<the org's canonical ACTIVE CRM workspace>
83
+ # EXE_ORG_WORKSPACE_ID must ALREADY EXIST and have a legitimate admin, because
84
+ # managed users join THAT workspace instead of creating their own. That is why
85
+ # managed mode cannot be the shipped default: on a fresh stack the workspace
86
+ # does not exist yet, so its id is unknowable at install time. Bootstrap the
87
+ # stack first, then set these and redeploy. The wiki needs only EXE_ORG_ID.
88
+ EXE_ORG_ID=
89
+ EXE_ORG_WORKSPACE_ID=
90
+
64
91
  # --- CRM ---
65
92
  CRM_IMAGE_TAG=update.askexe.com/askexe/exe-crm:v0.9.51
66
93
  CRM_SERVER_URL=https://crm.CHANGEME_DOMAIN
67
94
  CRM_APP_SECRET=CHANGEME_CRM_APP_SECRET
95
+ # The CRM's first admin — seeded at boot by WorkspaceBootstrapService, which is
96
+ # what makes that person an EXISTING user whose sign-in is allowed while every
97
+ # other unmanaged first login is refused (e51ca54c §10.6b). Must be a real
98
+ # mailbox that also exists in GoTrue (CUSTOMER-GUIDE Step 6), so it cannot be
99
+ # generated. Leave it empty and the seed silently throws: the CRM comes up
100
+ # healthy with no workspace and no user, and nobody can ever sign in.
101
+ # SET_MANUALLY
102
+ EXE_CRM_ADMIN_EMAIL=CHANGEME_ADMIN_EMAIL
68
103
  CRM_HOST_PORT=3000
69
104
 
70
105
  # --- Wiki ---
71
106
  WIKI_IMAGE_TAG=update.askexe.com/askexe/exe-wiki:v0.9.26
72
- WIKI_DB_SCHEMA=wiki
107
+ # Bug 2a8d3003 — DO NOT change this without migrating the data first.
108
+ # The wiki's users / workspaces / workspace_documents live in `public`. Point it at a
109
+ # different schema and Prisma will NOT error: it finds no tables, assumes a fresh
110
+ # database, creates empty ones, and the wiki comes up with zero documents and the
111
+ # admin locked out. It looks exactly like catastrophic data loss while every row is
112
+ # still sitting safely in `public`. That misread has a very expensive next step —
113
+ # restoring a backup over good data. This value used to default to `wiki`, and the
114
+ # only thing holding production together was a host-local compose override.
115
+ # exe-wiki-schema-guard blocks the boot if you get this wrong.
116
+ WIKI_DB_SCHEMA=public
73
117
  WIKI_VECTOR_DB=exeOsMcp
74
118
  WIKI_AUTH_TOKEN=CHANGEME_WIKI_AUTH_TOKEN
75
119
  WIKI_JWT_SECRET=CHANGEME_WIKI_JWT_SECRET
@@ -80,26 +80,73 @@ SMTP_PASS=
80
80
  # Bug 58840873: pin the auth image so compose never falls back to its stale
81
81
  # hardcoded default tag. Keep in sync with the manifest AUTH_IMAGE_TAG. exe-auth
82
82
  # ships only from the private update.askexe.com registry (no GHCR mirror).
83
- AUTH_IMAGE_TAG=update.askexe.com/askexe/exe-auth:v0.2.0
83
+ AUTH_IMAGE_TAG=update.askexe.com/askexe/exe-auth:v0.5.0
84
84
  # Bug 379e5c13: server_name / CORS derived from the CONFIGURED apex domain,
85
85
  # not by naive last-2-label slicing (which breaks company.co.uk → co.uk).
86
86
  AUTH_SERVER_NAME=auth.CHANGEME_DOMAIN
87
87
  AUTH_CORS_ORIGIN=https://CHANGEME_DOMAIN
88
88
  AUTH_DEFAULT_PRODUCT=AUTH
89
+ # Brand word after the gold "EXE" in the login logo (exe-auth v0.5.0+). One
90
+ # identity for the whole front door — ?product= never rebrands the page. Leave
91
+ # unset for the stack default "EXE INTELLIGENCE"; white-label deploys set their
92
+ # own word here.
93
+ #AUTH_BRAND_NAME=INTELLIGENCE
89
94
  # Bug 5960ebb1: invite-only — mirror GOTRUE_DISABLE_SIGNUP so the auth SPA hides
90
95
  # the sign-up UI/route instead of showing a form that always fails.
91
96
  AUTH_DISABLE_SIGNUP=true
92
97
 
98
+ # --- Centralized role administration (permissions) ---
99
+ # The apps refuse to let an unmanaged first login mint its own admin
100
+ # (exe-crm#102 / exe-wiki#45). Both flags below are therefore left at their
101
+ # SECURE default of true, stated explicitly rather than inherited silently.
102
+ #
103
+ # Because of that, a fresh CRM gets its first admin from EXE_CRM_ADMIN_EMAIL
104
+ # in the CRM section below — NOT from whoever signs in first. That user is
105
+ # seeded at boot, so their sign-in is an existing-user login and is allowed;
106
+ # every OTHER unmanaged first login is still refused. If you leave
107
+ # EXE_CRM_ADMIN_EMAIL empty, the seed silently does not happen and the CRM
108
+ # cannot be bootstrapped at all. The wiki does not need this: its first admin
109
+ # comes from POST /system/enable-multi-user, authorised by WIKI_AUTH_TOKEN.
110
+ CRM_REQUIRE_MANAGED_PERMS=true
111
+ WIKI_REQUIRE_MANAGED_PERMS=true
112
+ #
113
+ # MANAGED MODE — roles set ONLY in the Exe dashboard. Empty means unmanaged:
114
+ # no token can carry permissions for an org that was never named. To adopt it:
115
+ # EXE_ORG_ID=<org id from the Exe dashboard>
116
+ # EXE_ORG_WORKSPACE_ID=<the org's canonical ACTIVE CRM workspace>
117
+ # EXE_ORG_WORKSPACE_ID must ALREADY EXIST and have a legitimate admin, because
118
+ # managed users join THAT workspace instead of creating their own. That is why
119
+ # managed mode cannot be the shipped default: on a fresh stack the workspace
120
+ # does not exist yet, so its id is unknowable at install time. Bootstrap the
121
+ # stack first, then set these and redeploy. The wiki needs only EXE_ORG_ID.
122
+ EXE_ORG_ID=
123
+ EXE_ORG_WORKSPACE_ID=
124
+
93
125
  # --- CRM ---
94
126
  CRM_IMAGE_TAG=ghcr.io/askexe/exe-crm:v0.9.51
95
127
  CRM_SERVER_URL=https://crm.CHANGEME_DOMAIN
96
128
  CRM_APP_SECRET=CHANGEME_CRM_APP_SECRET
97
129
  EXE_CRM_ADMIN_TOKEN=CHANGEME_EXE_CRM_ADMIN_TOKEN
130
+ # The CRM's first admin — seeded at boot by WorkspaceBootstrapService, which
131
+ # is what makes that person an EXISTING user whose sign-in is allowed while
132
+ # every other unmanaged first login is refused (e51ca54c §10.6b). Must be a
133
+ # real mailbox that also exists in GoTrue, so it cannot be generated.
134
+ # SET_MANUALLY
135
+ EXE_CRM_ADMIN_EMAIL=CHANGEME_ADMIN_EMAIL
98
136
  CRM_HOST_PORT=3000
99
137
 
100
138
  # --- Wiki ---
101
139
  WIKI_IMAGE_TAG=ghcr.io/askexe/exe-wiki:v0.9.26
102
- WIKI_DB_SCHEMA=wiki
140
+ # Bug 2a8d3003 — DO NOT change this without migrating the data first.
141
+ # The wiki's users / workspaces / workspace_documents live in `public`. Point it at a
142
+ # different schema and Prisma will NOT error: it finds no tables, assumes a fresh
143
+ # database, creates empty ones, and the wiki comes up with zero documents and the
144
+ # admin locked out. It looks exactly like catastrophic data loss while every row is
145
+ # still sitting safely in `public`. That misread has a very expensive next step —
146
+ # restoring a backup over good data. This value used to default to `wiki`, and the
147
+ # only thing holding production together was a host-local compose override.
148
+ # exe-wiki-schema-guard blocks the boot if you get this wrong.
149
+ WIKI_DB_SCHEMA=public
103
150
  WIKI_VECTOR_DB=exeOsMcp
104
151
  WIKI_AUTH_TOKEN=CHANGEME_WIKI_AUTH_TOKEN
105
152
  EXE_WIKI_ADMIN_TOKEN=CHANGEME_EXE_WIKI_ADMIN_TOKEN
@@ -8,16 +8,18 @@
8
8
  # wiki.<domain> → http://exe-sso-edge:80 (unified SSO redirect)
9
9
  # erp.<domain> → http://exe-sso-edge:80 (unified SSO redirect)
10
10
  # gateway.<domain> → http://exe-gateway:3100
11
- # dashboard.askexe.com → http://exe-dashboard:80
11
+ # dashboard.<domain> → http://exe-sso-edge:80 (unified SSO redirect — NOT
12
+ # exe-dashboard:80, which would skip the login gate)
12
13
  # monitor.<domain> → http://exe-monitor-hub:8090
13
14
  # auth.<domain> → http://exe-sso-edge:80 (auth gateway + real health aliases)
14
15
  # api.<domain> → http://exe-os:8765
15
16
  # No config.yml or credentials file needed — the token embeds everything.
16
17
  #
17
- # IMPORTANT: crm/wiki/erp MUST route through exe-sso-edge (NOT straight to the
18
- # app container). The edge is what 302s unauthenticated visitors to
19
- # auth.<domain>; routing direct to the app skips the redirect (this was the
20
- # bug). exe-sso-edge picks the app by Host header, so all three share one route.
18
+ # IMPORTANT: crm/wiki/erp/dashboard MUST route through exe-sso-edge (NOT
19
+ # straight to the app container). The edge is what 302s unauthenticated
20
+ # visitors to auth.<domain>; routing direct to the app skips the redirect
21
+ # (this was the bug). exe-sso-edge picks the app by Host header, so all four
22
+ # share one route.
21
23
  #
22
24
  # ADVANCED (config file mode): only needed if you want to manage routes locally
23
25
  # instead of via the Cloudflare dashboard. Leave CLOUDFLARE_TUNNEL_TOKEN empty
@@ -44,9 +46,14 @@ ingress:
44
46
  # Gateway (WhatsApp, webhooks, pairing)
45
47
  - hostname: gateway.CHANGEME_DOMAIN
46
48
  service: http://exe-gateway:3100
47
- # AskExe customer dashboard (nginx in exe-dashboard listens on container port 80).
48
- - hostname: dashboard.askexe.com
49
- service: http://exe-dashboard:80
49
+ # Dashboard through the unified SSO edge, same as crm/wiki/erp. The edge
50
+ # gates on the canonical exe_access_token cookie and 302s unauthenticated
51
+ # visitors to auth.<domain>/?product=Dashboard&redirect=<dashboard-url><uri>.
52
+ # Routing straight to exe-dashboard:80 skips that gate (same class of bug as
53
+ # 96f8b2b2 for crm/wiki/erp) and leaves an ungated hostname serving 200 to
54
+ # anonymous requests.
55
+ - hostname: dashboard.CHANGEME_DOMAIN
56
+ service: http://exe-sso-edge:80
50
57
  # Monitor — /_/ admin panel blocked; only the API and UI are exposed publicly.
51
58
  # The PocketBase superadmin UI must be accessed via SSH tunnel:
52
59
  # ssh -L 8090:127.0.0.1:8090 <vps> then open http://localhost:8090/_/
@@ -254,7 +254,7 @@ services:
254
254
  options: { max-size: "10m", max-file: "3" }
255
255
 
256
256
  exe-monitor-hub:
257
- image: ${MONITOR_HUB_IMAGE_TAG:-ghcr.io/askexe/exe-monitor-hub:v0.9.5}
257
+ image: ${MONITOR_HUB_IMAGE_TAG:-ghcr.io/askexe/exe-monitor-hub:v0.9.5@sha256:1af31893cc8b1e51892be7d7705fa6c6b3108af8366b1fa8da2879d8ae21ec88}
258
258
  container_name: exe-monitor-hub
259
259
  restart: unless-stopped
260
260
  # bug d3d49101 / 1fdb8be9: PocketBase's data dir for this hub is a RELATIVE
@@ -332,7 +332,7 @@ services:
332
332
  # ------------------------------------------------------------------
333
333
 
334
334
  exe-crm:
335
- image: ${CRM_IMAGE_TAG:-ghcr.io/askexe/exe-crm:v0.9.51}
335
+ image: ${CRM_IMAGE_TAG:-ghcr.io/askexe/exe-crm:v0.9.51@sha256:73864ce14aaac06b73d67100bbf88e669fd13c7da39edd9644107037211559b7}
336
336
  container_name: exe-crm
337
337
  restart: unless-stopped
338
338
  # bug ad4471ad: NO compose `command` override. The image entrypoint
@@ -368,7 +368,52 @@ services:
368
368
  MONITOR_API_KEY: ${MONITOR_API_KEY:-${EXE_MONITOR_KEY:-}}
369
369
  APP_SECRET: ${CRM_APP_SECRET:?CRM_APP_SECRET is required}
370
370
  EXE_CRM_ADMIN_TOKEN: ${EXE_CRM_ADMIN_TOKEN:-}
371
+ # ── Centralized role administration (e51ca54c §10.6b) ──────────────────
372
+ # READ BEFORE CHANGING. These four values decide whether a fresh install
373
+ # can seat an administrator at all, and whether a stranger can seat
374
+ # himself. Both failure modes are one empty variable away.
375
+ #
376
+ # HOW A FIRST ADMIN IS MEANT TO APPEAR. On an empty database
377
+ # WorkspaceBootstrapService (workspace-bootstrap.service.ts, OnModuleInit)
378
+ # seeds one workspace (activationStatus=PENDING_CREATION) plus the user
379
+ # named by EXE_CRM_ADMIN_EMAIL. When that person later signs in through
380
+ # GoTrue they are an EXISTING user, so the first-login path is never
381
+ # entered; the pending workspace is activated and setupDefaultRoles grants
382
+ # them Admin. The first admin is therefore chosen by the OPERATOR, in this
383
+ # file, before anyone logs in.
384
+ #
385
+ # WHY EXE_CRM_ADMIN_EMAIL IS NOT OPTIONAL IN PRACTICE. bootstrapIfEmpty()
386
+ # THROWS when it is empty, and onModuleInit swallows that error as
387
+ # "non-fatal" — the CRM boots healthy with no workspace and no user. Leave
388
+ # it unset and the seed silently does not happen; the next sign-in is then
389
+ # a FIRST login, which exe-crm#102 refuses (403), and the
390
+ # POST /api/auth/admin-token break-glass also fails ("No workspace found"),
391
+ # because it activates an existing workspace and never creates one. That is
392
+ # a fresh install nobody can ever get into. It is left `:-` rather than
393
+ # `:?` ONLY so that already-provisioned customer stacks keep parsing this
394
+ # file; a NEW install that omits it is broken, which is why generate-env.ts
395
+ # now emits it and .env.customer.example marks it SET_MANUALLY.
371
396
  EXE_CRM_ADMIN_EMAIL: ${EXE_CRM_ADMIN_EMAIL:-}
397
+ # The guard itself stays at its SECURE default, stated rather than
398
+ # inherited. exe-crm#102 makes managed-permissions required by default so
399
+ # an unmanaged FIRST login can no longer create a workspace and take its
400
+ # Admin role. Because the bootstrap above seats the intended owner as an
401
+ # existing user, keeping this ON costs a correct install nothing — and it
402
+ # still refuses every OTHER unmanaged first login. Set it to false only for
403
+ # a deliberate no-control-plane bootstrap where you accept that the first
404
+ # unmanaged sign-in becomes a workspace Admin; the app logs that choice as
405
+ # a SECURITY DOWNGRADE on every boot.
406
+ CRM_REQUIRE_MANAGED_PERMS: ${CRM_REQUIRE_MANAGED_PERMS:-true}
407
+ # Managed mode (roles set ONLY in the Exe dashboard). Empty here means
408
+ # unmanaged: no token can carry permissions for an org that was never
409
+ # named, so every login resolves { managed: false }. To adopt it, set both
410
+ # — EXE_ORG_WORKSPACE_ID must name an ALREADY-EXISTING active workspace
411
+ # with a legitimate admin, because managed users join THAT workspace
412
+ # instead of minting their own. That ordering is why managed mode cannot be
413
+ # the shipped default: on a fresh stack the workspace does not exist yet,
414
+ # so its id is unknowable at install time. Bootstrap first, then adopt.
415
+ EXE_ORG_ID: ${EXE_ORG_ID:-}
416
+ EXE_ORG_WORKSPACE_ID: ${EXE_ORG_WORKSPACE_ID:-}
372
417
  GOTRUE_URL: http://gotrue:9999
373
418
  # Bug 51086951: the EXPECTED JWT issuer. GOTRUE_URL above is the INTERNAL
374
419
  # address used to call GoTrue, but browser tokens are signed with the PUBLIC
@@ -376,6 +421,18 @@ services:
376
421
  # SAME public value here so issuer validation accepts real browser sessions
377
422
  # instead of rejecting them as iss-mismatch. Same default as the gotrue pin.
378
423
  GOTRUE_JWT_ISSUER: ${GOTRUE_JWT_ISSUER:-${GOTRUE_EXTERNAL_URL:?GOTRUE_EXTERNAL_URL is required — set to https://auth.<your-domain> in .env}}
424
+ # Bug 550d6ab7: CRM verifies GoTrue tokens JWKS-first with an HS256
425
+ # shared-secret fallback (exe-crm access-token.service.ts verifyGoTrueToken
426
+ # -> getGoTrueSymmetricVerificationKey, landed in exe-crm 80c5cd95). Our
427
+ # GoTrue is configured with a SYMMETRIC GOTRUE_JWT_SECRET and no
428
+ # GOTRUE_JWT_KEYS, so it publishes an empty JWKS ({"keys":[]}) — and it
429
+ # MUST stay empty, because publishing an `oct` key would publish the
430
+ # signing secret itself. The HS256 fallback is therefore the ONLY viable
431
+ # verification path, and it returns null when this variable is unset. That
432
+ # is the same defect class as bug 0c0d5187 on exe-wiki (see the wiki
433
+ # service block below): the fix landed in the app, the secret was never
434
+ # handed to the container, so SSO stayed dead. Fail loud.
435
+ GOTRUE_JWT_SECRET: ${GOTRUE_JWT_SECRET:?GOTRUE_JWT_SECRET is required}
379
436
  PG_DATABASE_URL: postgres://${POSTGRES_USER:-exe}:${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required}@exe-db:5432/${POSTGRES_DB:-exedb}?sslmode=disable
380
437
  REDIS_URL: redis://:${REDIS_PASSWORD:?REDIS_PASSWORD is required}@redis:6379
381
438
  CLICKHOUSE_URL: http://${CLICKHOUSE_USER:-exe}:${CLICKHOUSE_PASSWORD:?CLICKHOUSE_PASSWORD is required}@clickhouse:8123/${CLICKHOUSE_DB:-default}
@@ -401,7 +458,7 @@ services:
401
458
  options: { max-size: "10m", max-file: "3" }
402
459
 
403
460
  exe-crm-worker:
404
- image: ${CRM_IMAGE_TAG:-ghcr.io/askexe/exe-crm:v0.9.51}
461
+ image: ${CRM_IMAGE_TAG:-ghcr.io/askexe/exe-crm:v0.9.51@sha256:73864ce14aaac06b73d67100bbf88e669fd13c7da39edd9644107037211559b7}
405
462
  container_name: exe-crm-worker
406
463
  restart: unless-stopped
407
464
  command: ["yarn", "worker:prod"]
@@ -460,17 +517,63 @@ services:
460
517
  driver: json-file
461
518
  options: { max-size: "10m", max-file: "3" }
462
519
 
520
+ # Bug 2a8d3003: one-shot pre-flight that refuses to let exe-wiki start against a
521
+ # schema where its data isn't. Reuses the already-present Postgres image purely for
522
+ # its psql client — no extra pull. See wiki-schema-guard.sh for the full rationale;
523
+ # the short version is that Prisma silently bootstraps an empty schema rather than
524
+ # erroring, so "wiki looks wiped" and "wiki is misconfigured" are indistinguishable
525
+ # at a glance, and the wrong diagnosis (restore from backup) destroys real data.
526
+ exe-wiki-schema-guard:
527
+ image: ${EXE_DB_IMAGE:-pgvector/pgvector:0.8.0-pg16@sha256:a132765ec351c65111b5b675928a3a0515a466a40f97277329db8b8209ad8bc9}
528
+ container_name: exe-wiki-schema-guard
529
+ restart: "no"
530
+ depends_on:
531
+ exe-db:
532
+ condition: service_healthy
533
+ environment:
534
+ # MUST stay byte-identical to exe-wiki's DATABASE_URL below — the guard's whole
535
+ # job is to assert on the schema the wiki itself will resolve.
536
+ DATABASE_URL: postgres://${POSTGRES_USER:-exe}:${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required}@exe-db:5432/${POSTGRES_DB:-exedb}?schema=${WIKI_DB_SCHEMA:-public}&sslmode=disable
537
+ WIKI_SCHEMA_GUARD: ${WIKI_SCHEMA_GUARD:-enforce}
538
+ volumes:
539
+ - ./wiki-schema-guard.sh:/usr/local/bin/wiki-schema-guard.sh:ro
540
+ # Fail OPEN when the script itself is missing, fail CLOSED on what it detects.
541
+ # On a stack dir that predates this change Docker materialises the bind source as
542
+ # an empty directory, and a guard that can't be found must not be the reason the
543
+ # wiki stays down — an absent guard is no worse than the status quo, whereas a
544
+ # guard that wedges the wiki is a new outage. stack-update installs the real
545
+ # script next to docker-compose.yml on every run.
546
+ entrypoint:
547
+ - bash
548
+ - -c
549
+ - |
550
+ if [ -f /usr/local/bin/wiki-schema-guard.sh ]; then
551
+ exec bash /usr/local/bin/wiki-schema-guard.sh
552
+ fi
553
+ echo "[wiki-schema-guard] script not installed next to docker-compose.yml — skipping."
554
+ echo "[wiki-schema-guard] run 'exe-os stack update' to install it; until then the"
555
+ echo "[wiki-schema-guard] wiki can boot against an empty schema undetected (bug 2a8d3003)."
556
+ networks:
557
+ - backend
558
+ logging:
559
+ driver: json-file
560
+ options: { max-size: "10m", max-file: "3" }
561
+
463
562
  exe-wiki:
464
- image: ${WIKI_IMAGE_TAG:-ghcr.io/askexe/exe-wiki:v0.9.26}
563
+ image: ${WIKI_IMAGE_TAG:-ghcr.io/askexe/exe-wiki:v0.9.26@sha256:c0d61d183b274a67977fdfb1b40d5e4736559a9d90507843183e5642da2a959d}
465
564
  container_name: exe-wiki
466
565
  restart: unless-stopped
467
566
  # Wiki uses Prisma — runs migrate on boot via built-in entrypoint.
468
- # If tables don't exist, Prisma creates them automatically.
567
+ # If tables don't exist, Prisma creates them automatically. That convenience is
568
+ # exactly why exe-wiki-schema-guard has to clear the boot first (bug 2a8d3003):
569
+ # pointed at the wrong schema, Prisma builds empty tables instead of complaining.
469
570
  depends_on:
470
571
  exe-db:
471
572
  condition: service_healthy
472
573
  gotrue:
473
574
  condition: service_healthy
575
+ exe-wiki-schema-guard:
576
+ condition: service_completed_successfully
474
577
  # SECURITY (bug 67d62490): no blanket `env_file: .env`. Each service
475
578
  # receives ONLY the secrets it owns via the explicit `environment:` block
476
579
  # below (values interpolated from .env at compose-parse time, never the
@@ -486,10 +589,55 @@ services:
486
589
  ERROR_REPORTING_ENABLED: "true"
487
590
  MONITOR_ERROR_URL: http://exe-monitor-hub:8090/api/exe-monitor/errors
488
591
  MONITOR_API_KEY: ${MONITOR_API_KEY:-${EXE_MONITOR_KEY:-}}
489
- DATABASE_URL: postgres://${POSTGRES_USER:-exe}:${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required}@exe-db:5432/${POSTGRES_DB:-exedb}?schema=${WIKI_DB_SCHEMA:-wiki}&sslmode=disable
592
+ # Bug 2a8d3003: the wiki's live data lives in the `public` schema and ALWAYS has.
593
+ # This default used to be `wiki`, and the ONLY thing keeping production pointed at
594
+ # its real content was a hand-written docker-compose.override.yml on the host that
595
+ # dropped the schema param. Drop that override — or honour WIKI_DB_SCHEMA=wiki —
596
+ # and the wiki boots against an empty `wiki` schema that Prisma will happily
597
+ # bootstrap: zero users, zero documents, admin locked out, and the real rows
598
+ # sitting untouched in `public` looking exactly like data loss. `public` is now the
599
+ # explicit, authoritative default so base and override agree.
600
+ #
601
+ # Why keep `?schema=` rather than omitting it: an omitted param makes resolution
602
+ # implicit (it falls back to the role's search_path, which ALTER ROLE can silently
603
+ # change). Naming the schema pins it deterministically and keeps the knob available
604
+ # for operators who genuinely want isolation — see WIKI_DB_SCHEMA in .env.example
605
+ # for the migration that requires. exe-wiki-schema-guard below enforces the choice.
606
+ DATABASE_URL: postgres://${POSTGRES_USER:-exe}:${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required}@exe-db:5432/${POSTGRES_DB:-exedb}?schema=${WIKI_DB_SCHEMA:-public}&sslmode=disable
490
607
  AUTH_TOKEN: ${WIKI_AUTH_TOKEN:?WIKI_AUTH_TOKEN is required}
491
608
  EXE_WIKI_ADMIN_TOKEN: ${EXE_WIKI_ADMIN_TOKEN:-}
609
+ # ── Centralized role administration (e51ca54c §10.6b) ──────────────────
610
+ # The wiki keeps the SECURE default, stated rather than inherited.
611
+ #
612
+ # exe-wiki#45 makes WIKI_REQUIRE_MANAGED_PERMS default ON: an unmanaged
613
+ # identity (which is every identity while EXE_ORG_ID is empty) provisions
614
+ # as a non-privileged `default` — it can sign in, gets no admin and no
615
+ # workspace, and sees "contact your administrator to request access to a
616
+ # team". Unlike the CRM this does NOT strand a fresh install, because the
617
+ # wiki's first admin does not come from an SSO first login at all: a fresh
618
+ # wiki boots in single-user mode (prisma seed sets multi_user_mode=false)
619
+ # and POST /system/enable-multi-user — authorised by the admin-token bypass
620
+ # in validatedRequest.js, which accepts EXE_WIKI_ADMIN_TOKEN *or* the
621
+ # always-required AUTH_TOKEN above — creates the first `role: admin` user
622
+ # directly, never touching resolveGoTrueUser. So the guard costs a correct
623
+ # install nothing here and is left ON.
624
+ #
625
+ # Managed mode: set EXE_ORG_ID (shared with exe-crm) and roles follow the
626
+ # dashboard's caps. The wiki needs no workspace-id pin because it never
627
+ # creates a tenant, so it can be flipped as soon as an org id exists.
628
+ # Setting this to false restores first-user-admin and is logged as a
629
+ # SECURITY DOWNGRADE on every boot.
630
+ WIKI_REQUIRE_MANAGED_PERMS: ${WIKI_REQUIRE_MANAGED_PERMS:-true}
631
+ EXE_ORG_ID: ${EXE_ORG_ID:-}
492
632
  GOTRUE_URL: http://gotrue:9999
633
+ # Bug 0c0d5187: the wiki verifies GoTrue HS256 tokens with GOTRUE_JWT_SECRET
634
+ # (server/utils/gotrue/index.js getGoTrueJwtSecret reads ONLY
635
+ # GOTRUE_JWT_SECRET || EXE_GOTRUE_JWT_SECRET) — NOT the wiki-local JWT_SECRET
636
+ # below. Without it, GoTrue's JWKS is empty so the asymmetric path is dead too,
637
+ # and every SSO exchange AND the wiki's own password form 401 with
638
+ # "Invalid or expired SSO token." Fail loud rather than ship a wiki nobody can
639
+ # log in to.
640
+ GOTRUE_JWT_SECRET: ${GOTRUE_JWT_SECRET:?GOTRUE_JWT_SECRET is required}
493
641
  # Bug 98a528a7 (class): the PUBLIC auth domain the wiki login link must point at.
494
642
  # Same lever as ERP — without it the wiki frontend can default to auth.askexe.com
495
643
  # instead of the customer's auth.<domain>. The wiki image must build its SSO
@@ -552,7 +700,7 @@ services:
552
700
  # to pin the split server image; falls back to the combined EXE_OS_IMAGE_TAG
553
701
  # for backward compatibility with existing manifests.
554
702
  exe-os:
555
- image: ${EXE_OS_SERVER_IMAGE_TAG:-${EXE_OS_IMAGE_TAG:-${EXED_IMAGE_TAG:-ghcr.io/askexe/exe-os:v0.9.311}}}
703
+ image: ${EXE_OS_SERVER_IMAGE_TAG:-${EXE_OS_IMAGE_TAG:-${EXED_IMAGE_TAG:-ghcr.io/askexe/exe-os:v0.9.311@sha256:011730042f55277724dbe83fde1a6416db2f3e8bf62d3254738d68d8de33e1f9}}}
556
704
  container_name: exe-os
557
705
  restart: unless-stopped
558
706
  # bug debf4a39 / 93ddfa27: the daemon connects to exe-db at boot
@@ -617,7 +765,22 @@ services:
617
765
  # the GGUF model files at /home/exed/.exe-os/models/.
618
766
  # ------------------------------------------------------------------
619
767
  exe-os-worker:
620
- image: ${EXE_OS_WORKER_IMAGE_TAG:-ghcr.io/askexe/exe-os-worker:v0.9.311}
768
+ # NO DEFAULT ON PURPOSE. This slot used to default to
769
+ # ghcr.io/askexe/exe-os-worker:v0.9.311 — an image that has NEVER been
770
+ # published (GHCR returns "manifest unknown" for every tag, and the package
771
+ # itself 404s). Nobody noticed because embeddings are off by default and this
772
+ # service is profile-gated, so the tag was only ever a promise.
773
+ #
774
+ # A default that cannot be pulled is worse than no default: it turns "enable
775
+ # embeddings" into an unpullable-image failure, and it cannot be digest-pinned,
776
+ # which is what the strict-digest gate requires. The slot is left EMPTY instead —
777
+ # `docker compose` refuses to start a service with no image, so activating the
778
+ # embeddings profile without setting this fails loudly and immediately. When a
779
+ # worker image is published, set EXE_OS_WORKER_IMAGE_TAG to a digest-pinned ref
780
+ # (ghcr.io/askexe/exe-os-worker:<tag>@sha256:<digest>) — strict-digest enforces
781
+ # the pin from there. Deliberately NOT `:?`: that would make the variable
782
+ # mandatory for every deploy, including the default embeddings-off one.
783
+ image: ${EXE_OS_WORKER_IMAGE_TAG:-}
621
784
  container_name: exe-os-worker
622
785
  profiles: ["embeddings"]
623
786
  restart: unless-stopped
@@ -656,7 +819,7 @@ services:
656
819
  options: { max-size: "10m", max-file: "3" }
657
820
 
658
821
  exe-gateway:
659
- image: ${GATEWAY_IMAGE_TAG:-ghcr.io/askexe/exe-gateway:v0.9.24}
822
+ image: ${GATEWAY_IMAGE_TAG:-ghcr.io/askexe/exe-gateway:v0.9.24@sha256:9cda7c00d886b4ab16a3b88aa953aa45c991f2977084ea8532f55fcaf75e8f37}
660
823
  container_name: exe-gateway
661
824
  restart: unless-stopped
662
825
  # Gateway needs 45s to flush outbound queues + disconnect Baileys adapters.
@@ -804,7 +967,7 @@ services:
804
967
  # include "monitor-agent". See deploy/monitor/HOST-ACCESS-CONSENT.md.
805
968
  # ------------------------------------------------------------------
806
969
  exe-monitor-agent:
807
- image: ${MONITOR_AGENT_IMAGE_TAG:-ghcr.io/askexe/exe-monitor-agent:v0.9.5}
970
+ image: ${MONITOR_AGENT_IMAGE_TAG:-ghcr.io/askexe/exe-monitor-agent:v0.9.5@sha256:cada7dace00ccb629aa10976e80e20d7d25685042519bd2aec58ab4bf35ff0cd}
808
971
  container_name: exe-monitor-agent
809
972
  # Host-access consent gate: only starts when the operator has
810
973
  # acknowledged the host-access scope (COMPOSE_PROFILES=monitor-agent).
@@ -853,11 +1016,32 @@ services:
853
1016
  networks:
854
1017
  - backend
855
1018
  healthcheck:
856
- # bug 9ce2a68b: the released agent image binary is /agent (scratch image,
857
- # see exe-monitor internal/dockerfile_agent), NOT /app/beszel-agent. The
858
- # binary-native `/agent health` checks the freshness of the agent's health
859
- # file, which only stays fresh while it is actively reporting to the hub.
860
- test: ["CMD", "/agent", "health"]
1019
+ # COUPLED TO THE PINNED AGENT IMAGE GENERATION change both together.
1020
+ #
1021
+ # Two agent image generations exist with DIFFERENT binary paths:
1022
+ # v0.9.4 and earlier -> /app/beszel-agent (has a shell)
1023
+ # v2026.08.09-*-stamped -> /agent (scratch, NO shell)
1024
+ #
1025
+ # bug 9ce2a68b moved this to `/agent health` for the stamped generation.
1026
+ # The stack manifest pins the v0.9.4 generation (the stamped monitor
1027
+ # rollout in stack 0.9.34 was attempted 3x on exe-db-jkt and rolled back
1028
+ # every time), so the check must address /app/beszel-agent again.
1029
+ #
1030
+ # A generation-agnostic check is NOT possible: the stamped image is
1031
+ # scratch, so there is no shell for a `a || b` fallback and no
1032
+ # wget/curl for a port probe. Docker healthchecks execute inside the
1033
+ # container, so the check can only use binaries the image ships.
1034
+ #
1035
+ # If this mismatches the pinned image the container is permanently
1036
+ # unhealthy from the first recreate. Observed live on exe-db-jkt
1037
+ # 2026-08-10 08:13-08:40, every 30s:
1038
+ # Health check ... error: OCI runtime exec failed: exec: "/agent":
1039
+ # stat /agent: no such file or directory
1040
+ #
1041
+ # The binary-native `health` subcommand checks the freshness of the
1042
+ # agent's health file, which only stays fresh while it is actively
1043
+ # reporting to the hub.
1044
+ test: ["CMD", "/app/beszel-agent", "health"]
861
1045
  interval: 30s
862
1046
  timeout: 5s
863
1047
  start_period: 30s
@@ -884,7 +1068,7 @@ services:
884
1068
  # Without a token the binary exits 0 (no crash-loop). (bug 3416cead)
885
1069
  # ------------------------------------------------------------------
886
1070
  exe-update:
887
- image: ${EXE_OS_IMAGE_TAG:-${EXED_IMAGE_TAG:-ghcr.io/askexe/exe-os:v0.9.311}}
1071
+ image: ${EXE_OS_IMAGE_TAG:-${EXED_IMAGE_TAG:-ghcr.io/askexe/exe-os:v0.9.311@sha256:011730042f55277724dbe83fde1a6416db2f3e8bf62d3254738d68d8de33e1f9}}
888
1072
  container_name: exe-update
889
1073
  profiles: ["registry-proxy", "askexe-control-plane"]
890
1074
  restart: unless-stopped
@@ -983,9 +1167,16 @@ services:
983
1167
  # Probe an app subdomain so the gate's server block (not the default 404)
984
1168
  # answers. Any 2xx/3xx from the edge proves it is serving.
985
1169
  # bug f1e79d3b: nginx:alpine ships BusyBox wget, which does NOT support
986
- # --max-redirect — that flag made the probe error out. Drop it and follow
987
- # the unauthenticated 302 to a 2xx; either way wget exits 0 when served.
988
- test: ["CMD", "wget", "-qO-", "--header=Host: crm.${DOMAIN}", "http://127.0.0.1:80/"]
1170
+ # --max-redirect — that flag made the probe error out.
1171
+ # Follow-up: probing "/" on a gated vhost cannot work either. Anonymous "/"
1172
+ # is exactly what the gate 302s to auth.<domain>, and BusyBox wget FOLLOWS
1173
+ # redirects with no way to cap them, so the probe chases the login funnel
1174
+ # and dies with "wget: too many redirections" — the edge is serving
1175
+ # correctly but reports unhealthy (seen live on askexe.com once crm was
1176
+ # routed through the edge). Probe the crm vhost's EXEMPT /healthz instead:
1177
+ # it never redirects and it proves vhost selection + gate exemption + the
1178
+ # CRM upstream in one request.
1179
+ test: ["CMD", "wget", "-qO-", "--header=Host: crm.${DOMAIN}", "http://127.0.0.1:80/healthz"]
989
1180
  interval: 30s
990
1181
  timeout: 5s
991
1182
  retries: 3
@@ -1086,7 +1277,7 @@ services:
1086
1277
  # 4 services: gunicorn API, websocket, RQ worker, scheduler.
1087
1278
 
1088
1279
  exe-erp:
1089
- image: ${ERP_IMAGE_TAG:-ghcr.io/askexe/exe-erp:v0.2.2}
1280
+ image: ${ERP_IMAGE_TAG:-ghcr.io/askexe/exe-erp:v0.2.2@sha256:00fecc790ff848bfd4b7d87f42f859edfeb6090a7ca26bf2359dc6cb96767afb}
1090
1281
  container_name: exe-erp
1091
1282
  restart: unless-stopped
1092
1283
  entrypoint: ["/usr/local/bin/entrypoint.sh"]
@@ -1227,7 +1418,7 @@ services:
1227
1418
  options: { max-size: "10m", max-file: "3" }
1228
1419
 
1229
1420
  exe-erp-websocket:
1230
- image: ${ERP_IMAGE_TAG:-ghcr.io/askexe/exe-erp:v0.2.2}
1421
+ image: ${ERP_IMAGE_TAG:-ghcr.io/askexe/exe-erp:v0.2.2@sha256:00fecc790ff848bfd4b7d87f42f859edfeb6090a7ca26bf2359dc6cb96767afb}
1231
1422
  container_name: exe-erp-websocket
1232
1423
  restart: unless-stopped
1233
1424
  entrypoint: []
@@ -1289,7 +1480,7 @@ services:
1289
1480
  options: { max-size: "10m", max-file: "3" }
1290
1481
 
1291
1482
  exe-erp-queue:
1292
- image: ${ERP_IMAGE_TAG:-ghcr.io/askexe/exe-erp:v0.2.2}
1483
+ image: ${ERP_IMAGE_TAG:-ghcr.io/askexe/exe-erp:v0.2.2@sha256:00fecc790ff848bfd4b7d87f42f859edfeb6090a7ca26bf2359dc6cb96767afb}
1293
1484
  container_name: exe-erp-queue
1294
1485
  restart: unless-stopped
1295
1486
  entrypoint: []
@@ -1320,11 +1511,16 @@ services:
1320
1511
  networks:
1321
1512
  - backend
1322
1513
  healthcheck:
1323
- # Verify the bench worker process is alive and Redis queue is reachable.
1324
- # bug c8fb434f: pgrep is NOT in the Frappe image (exit 127) → permanent
1325
- # false-unhealthy. ps + grep are present; the [r] bracket avoids matching
1326
- # the grep process itself.
1327
- test: ["CMD-SHELL", "ps -ef | grep -q '[r]q worker'"]
1514
+ # Liveness = the RQ worker process (PID 1) is still running.
1515
+ # bug ed19883e: the slim Frappe base has NEITHER pgrep NOR ps/procps
1516
+ # (exit 127 "not found") the process-table probes were permanently
1517
+ # false-unhealthy 180s stack-update gate → rollback. Also `bench
1518
+ # worker` runs the RQ worker in-process (frappe.utils.background_jobs
1519
+ # .start_worker → Worker.work()); PID 1 argv stays "bench worker", so a
1520
+ # `rq worker` grep would miss it even if ps existed. Use python (the
1521
+ # image base is python:3.14-slim) to read PID 1's argv directly — this
1522
+ # matches the exe-erp repo's own docker-compose.yml probe.
1523
+ test: ["CMD-SHELL", "python -c \"import sys; cmd=open('/proc/1/cmdline','rb').read().replace(b'\\0', b' '); sys.exit(0 if b'bench worker' in cmd else 1)\""]
1328
1524
  interval: 30s
1329
1525
  timeout: 5s
1330
1526
  start_period: 30s
@@ -1338,7 +1534,7 @@ services:
1338
1534
  options: { max-size: "10m", max-file: "3" }
1339
1535
 
1340
1536
  exe-erp-scheduler:
1341
- image: ${ERP_IMAGE_TAG:-ghcr.io/askexe/exe-erp:v0.2.2}
1537
+ image: ${ERP_IMAGE_TAG:-ghcr.io/askexe/exe-erp:v0.2.2@sha256:00fecc790ff848bfd4b7d87f42f859edfeb6090a7ca26bf2359dc6cb96767afb}
1342
1538
  container_name: exe-erp-scheduler
1343
1539
  restart: unless-stopped
1344
1540
  entrypoint: []
@@ -1369,11 +1565,13 @@ services:
1369
1565
  networks:
1370
1566
  - backend
1371
1567
  healthcheck:
1372
- # Verify the bench schedule process is alive.
1373
- # bug c8fb434f: pgrep is NOT in the Frappe image (exit 127) → permanent
1374
- # false-unhealthy. ps + grep are present; the [b] bracket avoids matching
1375
- # the grep process itself.
1376
- test: ["CMD-SHELL", "ps -ef | grep -q '[b]ench schedule'"]
1568
+ # Liveness = the scheduler process (PID 1) is still running.
1569
+ # bug ed19883e: the slim Frappe base has NEITHER pgrep NOR ps/procps
1570
+ # (exit 127 "not found") the process-table probe was permanently
1571
+ # false-unhealthy 180s stack-update gate → rollback. Use python (the
1572
+ # image base is python:3.14-slim) to read PID 1's argv directly — this
1573
+ # matches the exe-erp repo's own docker-compose.yml probe.
1574
+ test: ["CMD-SHELL", "python -c \"import sys; cmd=open('/proc/1/cmdline','rb').read().replace(b'\\0', b' '); sys.exit(0 if b'bench schedule' in cmd else 1)\""]
1377
1575
  interval: 30s
1378
1576
  timeout: 5s
1379
1577
  start_period: 30s
@@ -1466,7 +1664,7 @@ services:
1466
1664
  # a fresh/migrated install boots an old image while the manifest reads "latest".
1467
1665
  # generate-env.ts now always writes AUTH_IMAGE_TAG, and this default mirrors the
1468
1666
  # pin as defense-in-depth — keep BOTH in lockstep on every auth release.
1469
- image: ${AUTH_IMAGE_TAG:-update.askexe.com/askexe/exe-auth:v0.2.0}
1667
+ image: ${AUTH_IMAGE_TAG:-update.askexe.com/askexe/exe-auth:v0.5.0@sha256:ecc57f92b1f2c2d9df4fafe648622fe1fdd87c27012048d950e3f6b82086ccf9}
1470
1668
  container_name: exe-auth
1471
1669
  restart: unless-stopped
1472
1670
  environment:
@@ -1483,6 +1681,14 @@ services:
1483
1681
  AUTH_CORS_ORIGIN: ${AUTH_CORS_ORIGIN:-https://${DOMAIN}}
1484
1682
  AUTH_DEFAULT_REDIRECT: ${AUTH_DEFAULT_REDIRECT:-}
1485
1683
  AUTH_DEFAULT_PRODUCT: ${AUTH_DEFAULT_PRODUCT:-AUTH}
1684
+ # Brand word rendered after the gold "EXE" in the login logo (exe-auth
1685
+ # v0.5.0+). The auth gateway is ONE front door for the whole stack, so it
1686
+ # carries one identity regardless of which product bounced the visitor
1687
+ # here — ?product= no longer rebrands the page. Unset => the image
1688
+ # defaults to INTELLIGENCE ("EXE INTELLIGENCE"); white-label deploys set
1689
+ # AUTH_BRAND_NAME in .env. Passthrough is required or the .env value can
1690
+ # never reach the container.
1691
+ AUTH_BRAND_NAME: ${AUTH_BRAND_NAME:-}
1486
1692
  # Bug 5960ebb1: invite-only posture. Mirror GOTRUE_DISABLE_SIGNUP (which
1487
1693
  # blocks signup server-side) so the auth SPA hides its sign-up UI/route
1488
1694
  # instead of presenting a form that can only ever fail. Defaults true.