@elaraai/e3-rack 0.0.0-stage → 1.0.94

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 (307) hide show
  1. package/LICENSE.md +54 -0
  2. package/README.md +409 -2
  3. package/dist/src/client/client.spec.d.ts +6 -0
  4. package/dist/src/client/client.spec.d.ts.map +1 -0
  5. package/dist/src/client/client.spec.js +150 -0
  6. package/dist/src/client/client.spec.js.map +1 -0
  7. package/dist/src/client/connect.d.ts +52 -0
  8. package/dist/src/client/connect.d.ts.map +1 -0
  9. package/dist/src/client/connect.js +112 -0
  10. package/dist/src/client/connect.js.map +1 -0
  11. package/dist/src/client/hub-client.d.ts +57 -0
  12. package/dist/src/client/hub-client.d.ts.map +1 -0
  13. package/dist/src/client/hub-client.js +87 -0
  14. package/dist/src/client/hub-client.js.map +1 -0
  15. package/dist/src/client/local-http.d.ts +78 -0
  16. package/dist/src/client/local-http.d.ts.map +1 -0
  17. package/dist/src/client/local-http.js +149 -0
  18. package/dist/src/client/local-http.js.map +1 -0
  19. package/dist/src/client/session-attempts.d.ts +20 -0
  20. package/dist/src/client/session-attempts.d.ts.map +1 -0
  21. package/dist/src/client/session-attempts.js +88 -0
  22. package/dist/src/client/session-attempts.js.map +1 -0
  23. package/dist/src/client/session-data-server.d.ts +37 -0
  24. package/dist/src/client/session-data-server.d.ts.map +1 -0
  25. package/dist/src/client/session-data-server.js +280 -0
  26. package/dist/src/client/session-data-server.js.map +1 -0
  27. package/dist/src/client/session-data-server.spec.d.ts +6 -0
  28. package/dist/src/client/session-data-server.spec.d.ts.map +1 -0
  29. package/dist/src/client/session-data-server.spec.js +192 -0
  30. package/dist/src/client/session-data-server.spec.js.map +1 -0
  31. package/dist/src/client/session.d.ts +119 -0
  32. package/dist/src/client/session.d.ts.map +1 -0
  33. package/dist/src/client/session.js +290 -0
  34. package/dist/src/client/session.js.map +1 -0
  35. package/dist/src/hub/config.d.ts +74 -0
  36. package/dist/src/hub/config.d.ts.map +1 -0
  37. package/dist/src/hub/config.js +159 -0
  38. package/dist/src/hub/config.js.map +1 -0
  39. package/dist/src/hub/fair-lease-store.d.ts +36 -0
  40. package/dist/src/hub/fair-lease-store.d.ts.map +1 -0
  41. package/dist/src/hub/fair-lease-store.js +73 -0
  42. package/dist/src/hub/fair-lease-store.js.map +1 -0
  43. package/dist/src/hub/hub.d.ts +91 -0
  44. package/dist/src/hub/hub.d.ts.map +1 -0
  45. package/dist/src/hub/hub.js +641 -0
  46. package/dist/src/hub/hub.js.map +1 -0
  47. package/dist/src/hub/hub.spec.d.ts +6 -0
  48. package/dist/src/hub/hub.spec.d.ts.map +1 -0
  49. package/dist/src/hub/hub.spec.js +363 -0
  50. package/dist/src/hub/hub.spec.js.map +1 -0
  51. package/dist/src/hub/infrastructure.spec.d.ts +6 -0
  52. package/dist/src/hub/infrastructure.spec.d.ts.map +1 -0
  53. package/dist/src/hub/infrastructure.spec.js +211 -0
  54. package/dist/src/hub/infrastructure.spec.js.map +1 -0
  55. package/dist/src/hub/lock.d.ts +62 -0
  56. package/dist/src/hub/lock.d.ts.map +1 -0
  57. package/dist/src/hub/lock.js +165 -0
  58. package/dist/src/hub/lock.js.map +1 -0
  59. package/dist/src/hub/main.d.ts +16 -0
  60. package/dist/src/hub/main.d.ts.map +1 -0
  61. package/dist/src/hub/main.js +56 -0
  62. package/dist/src/hub/main.js.map +1 -0
  63. package/dist/src/hub/session-bridge.d.ts +116 -0
  64. package/dist/src/hub/session-bridge.d.ts.map +1 -0
  65. package/dist/src/hub/session-bridge.js +336 -0
  66. package/dist/src/hub/session-bridge.js.map +1 -0
  67. package/dist/src/identity/file-identity-store.d.ts +18 -0
  68. package/dist/src/identity/file-identity-store.d.ts.map +1 -0
  69. package/dist/src/identity/file-identity-store.js +34 -0
  70. package/dist/src/identity/file-identity-store.js.map +1 -0
  71. package/dist/src/identity/file-identity-store.spec.d.ts +6 -0
  72. package/dist/src/identity/file-identity-store.spec.d.ts.map +1 -0
  73. package/dist/src/identity/file-identity-store.spec.js +59 -0
  74. package/dist/src/identity/file-identity-store.spec.js.map +1 -0
  75. package/dist/src/identity/in-memory-identity-store.d.ts +48 -0
  76. package/dist/src/identity/in-memory-identity-store.d.ts.map +1 -0
  77. package/dist/src/identity/in-memory-identity-store.js +102 -0
  78. package/dist/src/identity/in-memory-identity-store.js.map +1 -0
  79. package/dist/src/identity/machine-identity.d.ts +134 -0
  80. package/dist/src/identity/machine-identity.d.ts.map +1 -0
  81. package/dist/src/identity/machine-identity.js +65 -0
  82. package/dist/src/identity/machine-identity.js.map +1 -0
  83. package/dist/src/index.d.ts +34 -0
  84. package/dist/src/index.d.ts.map +1 -0
  85. package/dist/src/index.js +34 -0
  86. package/dist/src/index.js.map +1 -0
  87. package/dist/src/lease/closure.d.ts +96 -0
  88. package/dist/src/lease/closure.d.ts.map +1 -0
  89. package/dist/src/lease/closure.js +192 -0
  90. package/dist/src/lease/closure.js.map +1 -0
  91. package/dist/src/lease/closure.spec.d.ts +6 -0
  92. package/dist/src/lease/closure.spec.d.ts.map +1 -0
  93. package/dist/src/lease/closure.spec.js +35 -0
  94. package/dist/src/lease/closure.spec.js.map +1 -0
  95. package/dist/src/lease/coordinator.d.ts +34 -0
  96. package/dist/src/lease/coordinator.d.ts.map +1 -0
  97. package/dist/src/lease/coordinator.js +42 -0
  98. package/dist/src/lease/coordinator.js.map +1 -0
  99. package/dist/src/lease/in-memory-lease-store.d.ts +58 -0
  100. package/dist/src/lease/in-memory-lease-store.d.ts.map +1 -0
  101. package/dist/src/lease/in-memory-lease-store.js +132 -0
  102. package/dist/src/lease/in-memory-lease-store.js.map +1 -0
  103. package/dist/src/lease/lease-store.d.ts +169 -0
  104. package/dist/src/lease/lease-store.d.ts.map +1 -0
  105. package/dist/src/lease/lease-store.js +94 -0
  106. package/dist/src/lease/lease-store.js.map +1 -0
  107. package/dist/src/lease/lease-store.spec.d.ts +6 -0
  108. package/dist/src/lease/lease-store.spec.d.ts.map +1 -0
  109. package/dist/src/lease/lease-store.spec.js +8 -0
  110. package/dist/src/lease/lease-store.spec.js.map +1 -0
  111. package/dist/src/lease/rack-dispatch.d.ts +105 -0
  112. package/dist/src/lease/rack-dispatch.d.ts.map +1 -0
  113. package/dist/src/lease/rack-dispatch.js +6 -0
  114. package/dist/src/lease/rack-dispatch.js.map +1 -0
  115. package/dist/src/mutation-queue.d.ts +13 -0
  116. package/dist/src/mutation-queue.d.ts.map +1 -0
  117. package/dist/src/mutation-queue.js +17 -0
  118. package/dist/src/mutation-queue.js.map +1 -0
  119. package/dist/src/paths.d.ts +56 -0
  120. package/dist/src/paths.d.ts.map +1 -0
  121. package/dist/src/paths.js +82 -0
  122. package/dist/src/paths.js.map +1 -0
  123. package/dist/src/protocol/control.d.ts +295 -0
  124. package/dist/src/protocol/control.d.ts.map +1 -0
  125. package/dist/src/protocol/control.js +74 -0
  126. package/dist/src/protocol/control.js.map +1 -0
  127. package/dist/src/protocol/index.d.ts +9 -0
  128. package/dist/src/protocol/index.d.ts.map +1 -0
  129. package/dist/src/protocol/index.js +9 -0
  130. package/dist/src/protocol/index.js.map +1 -0
  131. package/dist/src/protocol/rack-wire.d.ts +357 -0
  132. package/dist/src/protocol/rack-wire.d.ts.map +1 -0
  133. package/dist/src/protocol/rack-wire.js +262 -0
  134. package/dist/src/protocol/rack-wire.js.map +1 -0
  135. package/dist/src/protocol/rack-wire.spec.d.ts +6 -0
  136. package/dist/src/protocol/rack-wire.spec.d.ts.map +1 -0
  137. package/dist/src/protocol/rack-wire.spec.js +14 -0
  138. package/dist/src/protocol/rack-wire.spec.js.map +1 -0
  139. package/dist/src/protocol/registration.d.ts +70 -0
  140. package/dist/src/protocol/registration.d.ts.map +1 -0
  141. package/dist/src/protocol/registration.js +67 -0
  142. package/dist/src/protocol/registration.js.map +1 -0
  143. package/dist/src/protocol/session-data.d.ts +69 -0
  144. package/dist/src/protocol/session-data.d.ts.map +1 -0
  145. package/dist/src/protocol/session-data.js +26 -0
  146. package/dist/src/protocol/session-data.js.map +1 -0
  147. package/dist/src/protocol/task-envelope.d.ts +198 -0
  148. package/dist/src/protocol/task-envelope.d.ts.map +1 -0
  149. package/dist/src/protocol/task-envelope.js +93 -0
  150. package/dist/src/protocol/task-envelope.js.map +1 -0
  151. package/dist/src/protocol/wire-golden.d.ts +8 -0
  152. package/dist/src/protocol/wire-golden.d.ts.map +1 -0
  153. package/dist/src/protocol/wire-golden.js +34 -0
  154. package/dist/src/protocol/wire-golden.js.map +1 -0
  155. package/dist/src/protocol/wire-samples.d.ts +11 -0
  156. package/dist/src/protocol/wire-samples.d.ts.map +1 -0
  157. package/dist/src/protocol/wire-samples.js +52 -0
  158. package/dist/src/protocol/wire-samples.js.map +1 -0
  159. package/dist/src/registry/file-rack-registry.d.ts +68 -0
  160. package/dist/src/registry/file-rack-registry.d.ts.map +1 -0
  161. package/dist/src/registry/file-rack-registry.js +138 -0
  162. package/dist/src/registry/file-rack-registry.js.map +1 -0
  163. package/dist/src/registry/file-rack-registry.spec.d.ts +6 -0
  164. package/dist/src/registry/file-rack-registry.spec.d.ts.map +1 -0
  165. package/dist/src/registry/file-rack-registry.spec.js +50 -0
  166. package/dist/src/registry/file-rack-registry.spec.js.map +1 -0
  167. package/dist/src/registry/rack-registry.d.ts +64 -0
  168. package/dist/src/registry/rack-registry.d.ts.map +1 -0
  169. package/dist/src/registry/rack-registry.js +65 -0
  170. package/dist/src/registry/rack-registry.js.map +1 -0
  171. package/dist/src/registry/rack-registry.spec.d.ts +6 -0
  172. package/dist/src/registry/rack-registry.spec.d.ts.map +1 -0
  173. package/dist/src/registry/rack-registry.spec.js +8 -0
  174. package/dist/src/registry/rack-registry.spec.js.map +1 -0
  175. package/dist/src/routing/eligibility.d.ts +38 -0
  176. package/dist/src/routing/eligibility.d.ts.map +1 -0
  177. package/dist/src/routing/eligibility.js +78 -0
  178. package/dist/src/routing/eligibility.js.map +1 -0
  179. package/dist/src/routing/host-coupling.d.ts +26 -0
  180. package/dist/src/routing/host-coupling.d.ts.map +1 -0
  181. package/dist/src/routing/host-coupling.js +62 -0
  182. package/dist/src/routing/host-coupling.js.map +1 -0
  183. package/dist/src/routing/policy.d.ts +188 -0
  184. package/dist/src/routing/policy.d.ts.map +1 -0
  185. package/dist/src/routing/policy.js +212 -0
  186. package/dist/src/routing/policy.js.map +1 -0
  187. package/dist/src/routing/routing.spec.d.ts +6 -0
  188. package/dist/src/routing/routing.spec.d.ts.map +1 -0
  189. package/dist/src/routing/routing.spec.js +100 -0
  190. package/dist/src/routing/routing.spec.js.map +1 -0
  191. package/dist/src/routing/tiers.d.ts +22 -0
  192. package/dist/src/routing/tiers.d.ts.map +1 -0
  193. package/dist/src/routing/tiers.js +26 -0
  194. package/dist/src/routing/tiers.js.map +1 -0
  195. package/dist/src/runner/api-server.d.ts +45 -0
  196. package/dist/src/runner/api-server.d.ts.map +1 -0
  197. package/dist/src/runner/api-server.js +21 -0
  198. package/dist/src/runner/api-server.js.map +1 -0
  199. package/dist/src/runner/capacity.d.ts +13 -0
  200. package/dist/src/runner/capacity.d.ts.map +1 -0
  201. package/dist/src/runner/capacity.js +18 -0
  202. package/dist/src/runner/capacity.js.map +1 -0
  203. package/dist/src/runner/rack-body.d.ts +43 -0
  204. package/dist/src/runner/rack-body.d.ts.map +1 -0
  205. package/dist/src/runner/rack-body.js +208 -0
  206. package/dist/src/runner/rack-body.js.map +1 -0
  207. package/dist/src/runner/rack-runner.d.ts +62 -0
  208. package/dist/src/runner/rack-runner.d.ts.map +1 -0
  209. package/dist/src/runner/rack-runner.js +75 -0
  210. package/dist/src/runner/rack-runner.js.map +1 -0
  211. package/dist/src/runner/rack-runner.spec.d.ts +6 -0
  212. package/dist/src/runner/rack-runner.spec.d.ts.map +1 -0
  213. package/dist/src/runner/rack-runner.spec.js +302 -0
  214. package/dist/src/runner/rack-runner.spec.js.map +1 -0
  215. package/dist/src/runner/split.spec.d.ts +6 -0
  216. package/dist/src/runner/split.spec.d.ts.map +1 -0
  217. package/dist/src/runner/split.spec.js +188 -0
  218. package/dist/src/runner/split.spec.js.map +1 -0
  219. package/dist/src/runner-event.d.ts +261 -0
  220. package/dist/src/runner-event.d.ts.map +1 -0
  221. package/dist/src/runner-event.js +332 -0
  222. package/dist/src/runner-event.js.map +1 -0
  223. package/dist/src/server/beast2.d.ts +19 -0
  224. package/dist/src/server/beast2.d.ts.map +1 -0
  225. package/dist/src/server/beast2.js +18 -0
  226. package/dist/src/server/beast2.js.map +1 -0
  227. package/dist/src/server/body-limit.d.ts +16 -0
  228. package/dist/src/server/body-limit.d.ts.map +1 -0
  229. package/dist/src/server/body-limit.js +53 -0
  230. package/dist/src/server/body-limit.js.map +1 -0
  231. package/dist/src/server/body-limit.spec.d.ts +2 -0
  232. package/dist/src/server/body-limit.spec.d.ts.map +1 -0
  233. package/dist/src/server/body-limit.spec.js +32 -0
  234. package/dist/src/server/body-limit.spec.js.map +1 -0
  235. package/dist/src/server/bounded.d.ts +16 -0
  236. package/dist/src/server/bounded.d.ts.map +1 -0
  237. package/dist/src/server/bounded.js +39 -0
  238. package/dist/src/server/bounded.js.map +1 -0
  239. package/dist/src/server/dispatch.d.ts +137 -0
  240. package/dist/src/server/dispatch.d.ts.map +1 -0
  241. package/dist/src/server/dispatch.js +267 -0
  242. package/dist/src/server/dispatch.js.map +1 -0
  243. package/dist/src/server/env-publication.d.ts +45 -0
  244. package/dist/src/server/env-publication.d.ts.map +1 -0
  245. package/dist/src/server/env-publication.js +10 -0
  246. package/dist/src/server/env-publication.js.map +1 -0
  247. package/dist/src/server/index.d.ts +16 -0
  248. package/dist/src/server/index.d.ts.map +1 -0
  249. package/dist/src/server/index.js +17 -0
  250. package/dist/src/server/index.js.map +1 -0
  251. package/dist/src/server/protocol.spec.d.ts +6 -0
  252. package/dist/src/server/protocol.spec.d.ts.map +1 -0
  253. package/dist/src/server/protocol.spec.js +78 -0
  254. package/dist/src/server/protocol.spec.js.map +1 -0
  255. package/dist/src/server/rack-routes.d.ts +175 -0
  256. package/dist/src/server/rack-routes.d.ts.map +1 -0
  257. package/dist/src/server/rack-routes.js +755 -0
  258. package/dist/src/server/rack-routes.js.map +1 -0
  259. package/dist/src/server/rack-routes.spec.d.ts +6 -0
  260. package/dist/src/server/rack-routes.spec.d.ts.map +1 -0
  261. package/dist/src/server/rack-routes.spec.js +477 -0
  262. package/dist/src/server/rack-routes.spec.js.map +1 -0
  263. package/dist/src/server/storage-bridge.d.ts +140 -0
  264. package/dist/src/server/storage-bridge.d.ts.map +1 -0
  265. package/dist/src/server/storage-bridge.js +6 -0
  266. package/dist/src/server/storage-bridge.js.map +1 -0
  267. package/dist/src/state-file.d.ts +10 -0
  268. package/dist/src/state-file.d.ts.map +1 -0
  269. package/dist/src/state-file.js +38 -0
  270. package/dist/src/state-file.js.map +1 -0
  271. package/dist/src/testing/in-memory.d.ts +116 -0
  272. package/dist/src/testing/in-memory.d.ts.map +1 -0
  273. package/dist/src/testing/in-memory.js +193 -0
  274. package/dist/src/testing/in-memory.js.map +1 -0
  275. package/dist/src/testing/index.d.ts +14 -0
  276. package/dist/src/testing/index.d.ts.map +1 -0
  277. package/dist/src/testing/index.js +14 -0
  278. package/dist/src/testing/index.js.map +1 -0
  279. package/dist/src/testing/lease-store-contract.d.ts +14 -0
  280. package/dist/src/testing/lease-store-contract.d.ts.map +1 -0
  281. package/dist/src/testing/lease-store-contract.js +78 -0
  282. package/dist/src/testing/lease-store-contract.js.map +1 -0
  283. package/dist/src/testing/protocol-suite.d.ts +38 -0
  284. package/dist/src/testing/protocol-suite.d.ts.map +1 -0
  285. package/dist/src/testing/protocol-suite.js +207 -0
  286. package/dist/src/testing/protocol-suite.js.map +1 -0
  287. package/dist/src/testing/rack-registry-contract.d.ts +15 -0
  288. package/dist/src/testing/rack-registry-contract.d.ts.map +1 -0
  289. package/dist/src/testing/rack-registry-contract.js +106 -0
  290. package/dist/src/testing/rack-registry-contract.js.map +1 -0
  291. package/dist/src/testing/test-rack-agent.d.ts +121 -0
  292. package/dist/src/testing/test-rack-agent.d.ts.map +1 -0
  293. package/dist/src/testing/test-rack-agent.js +316 -0
  294. package/dist/src/testing/test-rack-agent.js.map +1 -0
  295. package/dist/src/tls/tls.d.ts +67 -0
  296. package/dist/src/tls/tls.d.ts.map +1 -0
  297. package/dist/src/tls/tls.js +200 -0
  298. package/dist/src/tls/tls.js.map +1 -0
  299. package/dist/src/tls/tls.spec.d.ts +6 -0
  300. package/dist/src/tls/tls.spec.d.ts.map +1 -0
  301. package/dist/src/tls/tls.spec.js +101 -0
  302. package/dist/src/tls/tls.spec.js.map +1 -0
  303. package/dist/src/version.d.ts +9 -0
  304. package/dist/src/version.d.ts.map +1 -0
  305. package/dist/src/version.js +10 -0
  306. package/dist/src/version.js.map +1 -0
  307. package/package.json +72 -3
package/LICENSE.md ADDED
@@ -0,0 +1,54 @@
1
+ # Business Source License 1.1
2
+
3
+ Copyright (c) 2025 Elara AI Pty Ltd
4
+
5
+ ## License
6
+
7
+ **Licensor:** Elara AI Pty Ltd
8
+
9
+ **Licensed Work:** @elaraai/e3-rack
10
+
11
+ **Change Date:** Four years from the date of each release
12
+
13
+ **Change License:** AGPL-3.0
14
+
15
+ ## Terms
16
+
17
+ The Licensed Work is provided under the terms of the Business Source License 1.1 as detailed below.
18
+
19
+ ### Grant of Rights
20
+
21
+ The Licensor grants you the right to copy, modify, create derivative works, redistribute, and make non-production use of the Licensed Work.
22
+
23
+ ### Production Use Limitation
24
+
25
+ **"Production Use"** means any use by or on behalf of a for-profit entity, other than for evaluation, testing, or development purposes.
26
+
27
+ Production Use requires a separate commercial license from the Licensor.
28
+
29
+ ### Change Date
30
+
31
+ On the Change Date (four years after each release), the Licensed Work will be made available under the Change License (AGPL-3.0).
32
+
33
+ ### No Warranty
34
+
35
+ THE LICENSED WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND. THE LICENSOR DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
36
+
37
+ ### Package-Specific Licenses
38
+
39
+ Individual packages within this repository may have different licensing terms. See the LICENSE.md file in each package directory for details.
40
+
41
+ ## Commercial Licensing
42
+
43
+ To obtain a commercial license for Production Use, contact:
44
+
45
+ **Email:** support@elara.ai
46
+ **Website:** https://elaraai.com
47
+
48
+ ## Governing Law
49
+
50
+ This license is governed by the laws of New South Wales, Australia.
51
+
52
+ ---
53
+
54
+ *Elara AI Pty Ltd*
package/README.md CHANGED
@@ -1,3 +1,410 @@
1
- # Temporary Holding Version
1
+ # @elaraai/e3-rack
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Delegate eligible task bodies from a local e3 repository to a rack agent. The
4
+ local process still plans the graph, checks caches and owns the repository.
5
+ One hub per operating-system user coordinates the rack fleet across projects.
6
+
7
+ ## Connect a rack
8
+
9
+ A rack reaches this machine's hub over HTTPS. The hub serves its rack listener
10
+ with its own certificate, made the first time the listener serves TLS, and every
11
+ rack pins that certificate by its SHA-256 fingerprint. No certificate authority
12
+ or DNS name is involved, so the rack can reach the hub through any relay, and a
13
+ relay or tunnel in between forwards bytes it cannot read.
14
+
15
+ The usual path is an SSH reverse tunnel. The hub listens on the loopback only;
16
+ this machine keeps a tunnel open to a relay that the rack can reach, for
17
+ example `ssh -N -R 7401:127.0.0.1:7331 relay`, and nothing connects into this
18
+ machine directly:
19
+
20
+ ```sh
21
+ e3 rack enroll --listen 127.0.0.1:7331 --allow 127.0.0.1 \
22
+ --advertise-url https://relay.example:7401 --label workshop
23
+ ```
24
+
25
+ On a private network the rack can connect to the hub directly instead:
26
+
27
+ ```sh
28
+ e3 rack enroll --listen 10.0.0.5:7331 --label workshop
29
+ ```
30
+
31
+ This starts the hub and prints the **source origin**, the hub's **certificate
32
+ SHA-256**, the matching e3-rack release and a short-lived single-use enrollment
33
+ token. The source origin is how the rack reaches this machine: the
34
+ `--advertise-url`, or the listener's own address when none is set. It is not a
35
+ repository URL or the rack server's address; `localhost` on the rack would point
36
+ back at the worker itself. Keep the hub and any tunnel available while work runs.
37
+
38
+ `--advertise-url` names the origin racks reach the hub at: a relay's, a
39
+ tunnel's or a TLS terminator's. The hub builds every rack and data URL on it.
40
+ Without it, data URLs use the origin each rack request named, which is right
41
+ only when the rack connects to the listener directly. A listener on every
42
+ address (`0.0.0.0` or `::`) has no address to hand out, so enrollment needs
43
+ `--advertise-url` there.
44
+
45
+ The certificate is kept in the hub's private state (`hub-tls.beast2`, beside
46
+ its other state) until it is removed. With the hub stopped, removing that file
47
+ makes a new certificate on the next start, which every worker must then pin again.
48
+
49
+ Plain HTTP (`--no-tls`) is for a TLS terminator in front of the hub, as in
50
+ `e3 rack config --no-tls --listen 127.0.0.1:7331 --allow 127.0.0.1 --advertise-url https://terminator.example`.
51
+ The hub refuses a plain-HTTP listener on a wildcard or public address, or
52
+ without `--allow`: anything on the path of plain HTTP reads every rack token,
53
+ data URL, input, output and log. A VPN whose hub decrypts and re-encrypts
54
+ traffic is not encrypted end to end. `--allow <ip,...>` admits only the named
55
+ addresses (through a local tunnel or terminator, `127.0.0.1`); it does not
56
+ encrypt anything.
57
+
58
+ Use the matching, verified companion release from e3-cloud **inside an already
59
+ prepared worker VM**, at the release `e3 rack enroll` printed. Replace the public
60
+ allocation path below with the one the host provisioner supplies; do not write
61
+ allocation JSON yourself:
62
+
63
+ ```sh
64
+ sudo /opt/e3-rack/releases/<release>/e3-rack configure \
65
+ --allocation '/var/lib/e3-rack/<agent-uuid>/allocation.json'
66
+ sudo /opt/e3-rack/releases/<release>/e3-rack enroll \
67
+ --allocation '/var/lib/e3-rack/<agent-uuid>/allocation.json'
68
+ ```
69
+
70
+ `configure` asks for the source origin and, for an HTTPS origin, the
71
+ certificate SHA-256 the hub printed (none for a publicly trusted certificate,
72
+ such as a cloud deployment's), then the worker label (`workshop` here), runtime
73
+ settings and optional private-registry access. `enroll` asks for the one-time
74
+ token. Both use hidden terminal prompts. The worker stores origins, pins,
75
+ identities, settings and registry credentials together in encrypted storage; no
76
+ token or source address is included in the printed shell commands. The
77
+ enrollment token is intentionally displayed by this local minting command: do
78
+ not copy its output into logs or a plaintext file.
79
+
80
+ A plain-HTTP source origin needs `--allow-vpn-http` on both worker commands, and
81
+ the worker's host must authorize that exact destination. Use it only on a path
82
+ encrypted end to end, such as an SSH tunnel.
83
+
84
+ The local command waits for a **new registration**, including one with no boot,
85
+ tiers or capacity yet; `--no-wait` returns after printing the handoff. A label
86
+ filters the observed registrations, so compare the result with the worker's
87
+ enrollment result. Enrollment does not prove service activation or readiness.
88
+ `e3 rack list` shows `enrolled-not-started` before a first heartbeat.
89
+
90
+ Once the worker has been activated and explicitly resumed, its heartbeat
91
+ publishes concurrent capacity as free plus occupied slots. Busy workers remain
92
+ eligible; fenced, draining or unhealthy workers publish zero capacity and show
93
+ `unavailable`. Existing work can still finish during drain. Only heartbeats
94
+ refresh readiness: lease polling cannot keep an old capacity report alive.
95
+ After 45 seconds without a heartbeat the worker stops counting as available.
96
+ Source boot approval, when required, remains a separate gate from worker resume:
97
+ `e3 rack config --require-approval` quarantines a rack after each reboot until
98
+ `e3 rack approve <rack>` admits the boot it reports; enrollment admits the first.
99
+
100
+ Independent agents belong to separate worker VMs, each with its own identity,
101
+ encrypted configuration and host-enforced resource allocation. Whole GPUs are
102
+ assigned through the host lifecycle, independently of this enrollment command.
103
+ One worker can connect to up to 32 mutually trusted e3 sources. To add another,
104
+ first drain the worker and stop its services and socket activation. Inside it:
105
+
106
+ ```sh
107
+ sudo /opt/e3-rack/releases/<release>/e3-rack configure \
108
+ --allocation '/var/lib/e3-rack/<agent-uuid>/allocation.json' --add-source
109
+ sudo /opt/e3-rack/releases/<release>/e3-rack enroll \
110
+ --allocation '/var/lib/e3-rack/<agent-uuid>/allocation.json' --source '<new-source-uuid>'
111
+ ```
112
+
113
+ `configure --add-source` asks you to confirm that these sources may share the
114
+ agent's trust boundary, then privately asks for the new origin. It prints the
115
+ source UUID for `enroll`. Obtain that source's token with `e3 rack enroll` on
116
+ the additional local e3 machine, or from the chosen cloud deployment. Omit the
117
+ HTTP opt-in for HTTPS. The host must approve the additional endpoint. Existing
118
+ identities and registry access are preserved, every source must be enrolled
119
+ before startup, and the next boot remains fenced.
120
+
121
+ Connections have separate clients, credentials, caches and completion journals.
122
+ One worker-wide task/GPU limit bounds actual execution across them; per-source
123
+ capacity reports must not be added together as physical reservations. All
124
+ connections share the worker's lifecycle. They also share its process and
125
+ credential trust boundary: a worker is the trust boundary. Put sources on one
126
+ worker only when they trust each other, such as one team's developer hubs.
127
+ Each client environment needs a worker of its own, and a cloud deployment and
128
+ developer hubs belong on separate workers. Independent agents have no shared
129
+ e3 task scheduler.
130
+
131
+ The hub keeps no task data: leases and data URLs live in its memory, and every
132
+ object passes through it from the repository's session. What a worker keeps is
133
+ the worker's own policy. The hub checks each object a rack reads against its
134
+ lease's closure only, so an agent that fetches every object again for each task
135
+ and keeps nothing once the task ends works with it unchanged.
136
+
137
+ The companion now provides `e3-rack source list --allocation <path>` inside the
138
+ worker. It lists source UUIDs and enrollment state; `--show-origins` displays
139
+ origins only on a terminal. To disconnect or re-enroll, drain and stop the worker,
140
+ revoke the selected rack at its source, then use `source remove` or `source reset`
141
+ with `--allocation <path> --source <UUID> --acknowledge-revocation`. Confirm the
142
+ UUID at the hidden prompt. Reset prints a **new** UUID. Use it with `enroll
143
+ --source`, supplying a fresh token obtained from the e3 source. Reset preserves
144
+ the origin and keeps old caches/pending results out of the new identity.
145
+ The flag acknowledges prior revocation; it does
146
+ not revoke remotely. Other sources and retained data are preserved. Reconcile
147
+ host endpoint policy before activation; the next boot remains fenced.
148
+
149
+ Private registry access has its own worker-local commands:
150
+
151
+ ```sh
152
+ sudo /opt/e3-rack/releases/<release>/e3-rack registry status \
153
+ --allocation '/var/lib/e3-rack/<agent-uuid>/allocation.json'
154
+ # After a verified drain and stopping the worker services/sockets:
155
+ sudo /opt/e3-rack/releases/<release>/e3-rack registry configure \
156
+ --allocation '/var/lib/e3-rack/<agent-uuid>/allocation.json'
157
+ ```
158
+
159
+ Status can run while active and shows only whether credentials are stored; it
160
+ does not test GHCR access. Configure privately prompts for username/read-only
161
+ token without re-entering worker settings. Use `registry clear --allocation
162
+ <path>` to remove stored access, confirming the installation UUID privately.
163
+ Changes require the same release, the writer lock and no unresolved enrollment
164
+ or service transition. They preserve current source identities/rotations, settings,
165
+ journals and cached images/task data. Credentials stay in the encrypted bundle;
166
+ no Docker login file is created. A later service activation receives the change,
167
+ with the next boot still fenced. Neither replacing nor clearing revokes a token
168
+ remotely. After interruption, repeat configure with the intended values or clear
169
+ to ensure absence; repeating the current values or an absent clear makes no write.
170
+
171
+ The companion also provides worker-local `reconcile-services`: after a completed
172
+ drain and stopped services, it applies the host's next allocation generation and
173
+ resource limits. The previous service-plan hash and exact drain/operation/boot
174
+ binding protect the transition. Interrupted updates keep activation guarded and
175
+ retry with the same operation; ciphertext and journals remain unchanged. This
176
+ supports prepared GPU/VFIO assignments at the same executable release; the host
177
+ still performs and verifies the physical move, and network quarantine remains.
178
+
179
+ For a compatible upgrade or downgrade, the target verified executable's
180
+ `switch-release` uses another completed drain, the previous plan hash and a new
181
+ allocation generation, with unchanged resource/device budgets. Both binaries
182
+ must report the same managed-state contract. It changes the runtime version in
183
+ the **current** encrypted configuration, preserving all identities, registry
184
+ access and journals. Interrupted commits stay guarded and retry with the same
185
+ operation; downgrades never restore saved credentials or state. Keep both
186
+ four-file releases available through receipt recovery. Services stay stopped
187
+ and quarantined until separately activated and resumed.
188
+
189
+ If the selected release never resumed, its exact recorded predecessor's
190
+ `recover-release` uses the completed release receipt, current plan hash and a
191
+ new operation at another generation increase. It requires either the original
192
+ drained journal or a valid fenced journal with no operation at the failed
193
+ generation, and verifies both releases against the recorded fingerprints. Any
194
+ resume attempt, including one from a prior boot, prevents this recovery. Current
195
+ credentials and journals are preserved; services remain stopped and quarantined.
196
+ A release that resumed needs a fresh drain and normal `switch-release`; general
197
+ journal repair and recovery after an undrainable resumed release remain open.
198
+
199
+ The named host manager, source aliases, incompatible-state migrations,
200
+ service/network activation and hardware acceptance remain
201
+ [cloud #253](https://github.com/elaraai/e3-cloud/issues/253) work. Enrollment alone
202
+ is not an end-to-end deployment. See the companion's
203
+ [worker configuration and service contract](https://github.com/elaraai/e3-cloud/blob/72c5c629e1708421171d3c86b8d7cb470f46802e/packages/e3-rack-agent/credentials/README.md).
204
+
205
+ ## Select work
206
+
207
+ ```sh
208
+ e3 rack policy . set 'train_*' rack --size large
209
+ e3 dataflow run . dev --rack
210
+ e3 watch src/main.ts . dev --start --rack
211
+ ```
212
+
213
+ Policies live in `<repo>/rack/policy.beast2`. Rules match the entire task name
214
+ with `*` and `?`; the first matching rule wins. By default, unmatched tasks
215
+ stay local. Rules and placement do not change task or cache hashes.
216
+
217
+ ```sh
218
+ e3 rack policy . show
219
+ e3 rack policy . enable # opt in without a flag on each run
220
+ e3 rack policy . mode auto # route eligible unmatched tasks too
221
+ e3 rack policy . set 'ingest_*' local
222
+ e3 rack policy . defaults --no-spill --claim-timeout 30
223
+ e3 dataflow run . dev --no-rack # override the saved opt-in
224
+ e3 dataflow run . dev --rack-only # wait when compatible racks are busy
225
+ e3 rack policy . unset 'train_*'
226
+ e3 rack policy . reset
227
+ ```
228
+
229
+ Defaults are disabled, opt-in selection, `large`, a 1440-minute timeout,
230
+ capacity spill enabled, a 30-second claim recheck and no local retry of task
231
+ failures. `--rack-only` disables capacity spill; infrastructure failures can
232
+ still fall back locally. It does not force ineligible tasks onto a rack.
233
+ Rack flags apply to local dataflows and watch with `--start`, and are refused
234
+ for remote repository URLs. Calls, mutations and intake remain local.
235
+
236
+ Only stock runner configurations can delegate. Custom commands, environments,
237
+ platform extensions and unscannable programs stay local. Programs that access
238
+ host files, environment variables or network resources also stay local by
239
+ default: their dependencies may exist only on this machine. A task rule's
240
+ `--allow-host-access` deliberately overrides that guard, for a rack that
241
+ provides those resources itself. The companion agent's task VMs have no network
242
+ device, host files or injected environment, so such a task fails there.
243
+
244
+ ## Execution and recovery
245
+
246
+ Current sources include optional first-claim queue timings in grant response
247
+ headers. The companion agent records admission, input/image preparation, VM and
248
+ guest startup milestones on its own monotonic clock. Its
249
+ [latency benchmark and p50/p95 report](https://github.com/elaraai/e3-cloud/blob/998d879ac6c199e7f300bf9b9531293b4e8f5dc0/scripts/rack/LATENCY.md)
250
+ separate cached CPU, GPU and first-use observations, including failures and
251
+ missing data. Guest execution entry precedes runtime imports and CUDA startup;
252
+ source queue time is separate and cannot be added as exact end-to-end latency.
253
+ This adds no persisted or BEAST2 wire field and requires no new credentials.
254
+
255
+ After a cache miss, the local planner offers an eligible task or split-task
256
+ unit to the hub. A compatible healthy agent claims it, downloads the task and
257
+ its complete input object graph, executes in a VM, and streams logs. It uploads
258
+ each output object by hash and size, then commits its upload receipt. The local
259
+ session verifies the complete output graph before recording success. Planning,
260
+ splitting and merging decisions remain local; map and merge bodies can both
261
+ run remotely, including when the map outputs were already cached.
262
+
263
+ Local work and fallback share the run's existing `-j` and `--memory` budget.
264
+ Remote bodies consume rack slots without reserving a local runner slot. Local
265
+ orchestrator width includes compatible rack capacity.
266
+
267
+ Concurrent sessions of the same canonical repository and e3 release can share
268
+ one lease. Cancelling one subscriber leaves the others running; cancelling
269
+ the last stops the lease. An interrupted remote attempt and its local retry
270
+ have distinct execution records. The task's timeout and the run's own
271
+ cancellation are not retried locally; an attempt the rack stopped on its own
272
+ is. Task failures are returned unless the policy explicitly enables
273
+ `--retry-task-failures-locally`; infrastructure failures fall back locally.
274
+
275
+ Ctrl-C in `e3 dataflow run` requests cancellation of that run, including its
276
+ delegated work. A current companion agent keeps an outbound cancellation
277
+ request open while work is active. The source checks the watched leases every
278
+ 250 ms and answers as soon as one stops; the agent aborts that task and destroys
279
+ its VM before waiting for log delivery. Other tasks and sources keep running.
280
+ The normal health heartbeat remains 15 seconds. Against an older source without
281
+ this endpoint, the new agent uses a one-second heartbeat while busy.
282
+
283
+ The companion also cancels input/layer downloads, output uploads, storage requests
284
+ and upload retry waits. It stops parallel transfers and waits for their cleanup
285
+ before deleting staging or releasing the invocation slot. A finished guest's VM
286
+ is destroyed before waiting for output delivery. Each object/layer HTTP attempt
287
+ has a 120-second deadline, including its body; cancellation interrupts it immediately.
288
+ Cancellation also reaches first-use image builds, environment composition,
289
+ scratch-disk preparation and VM startup. A shared image build continues for its
290
+ remaining leases; the last cancelled waiter stops it and waits for cleanup.
291
+ Cancelled boots retain GPU ownership until process exit is confirmed, and
292
+ uncertain teardown requires recovery. Filesystem operations and final publication
293
+ already in progress still finish safely before cleanup; a process stuck in kernel I/O can
294
+ delay that. See the [companion cancellation boundaries](https://github.com/elaraai/e3-cloud/blob/72c5c629e1708421171d3c86b8d7cb470f46802e/packages/e3-rack-agent/README.md#cancelling-delegated-work).
295
+
296
+ This targets cancellation delivery within about a second on a healthy connection;
297
+ network failures and VM/GPU teardown can take longer. The CLI's cancellation
298
+ message is not confirmation that the remote VM has exited. Killing the CLI
299
+ without allowing its signal handler to run relies on lost-session cleanup.
300
+
301
+ ```sh
302
+ e3 rack status # does not start an absent hub
303
+ e3 rack list
304
+ e3 task logs . dev.train_model --follow
305
+ e3 rack remove workshop # revoke this rack's credential
306
+ e3 rack config --require-approval # quarantine each reboot until approved
307
+ e3 rack approve workshop # admit the boot this rack last reported
308
+ e3 rack stop # stop the shared hub
309
+ ```
310
+
311
+ Hub state lives under `~/.e3/rack` (override with `E3_RACK_HOME`). The rack
312
+ listener is off until configured. A running hub and client must have compatible
313
+ control schemas (currently protocol 2 exactly); incompatible clients receive
314
+ an explicit diagnostic. Agents must report a bundled e3 version at least as
315
+ new as the project's. Stop and restart the hub after an incompatible upgrade.
316
+
317
+ A hub that `e3 dataflow run` starts exits after 15 idle minutes, while a run
318
+ waits up to 10 seconds for an enrolled rack to report healthy and fixes its
319
+ parallelism as it starts; with no rack enrolled, or a policy that selects no
320
+ task for a rack, it starts at once. An agent reconnecting after the hub exits
321
+ backs off for up to 30 seconds. For a hub racks can always reach, run
322
+ `e3 rack hub` as a service, or set `e3 rack config --idle-exit 0`.
323
+
324
+ A rack reads and writes each object through a data URL that lasts 15 minutes,
325
+ and a transfer may take all of it; an object is at most 5 GiB. Over a slow
326
+ link, a large object can outlast its URL, and the work then falls back locally.
327
+
328
+ ## Local API server
329
+
330
+ Install matching `@elaraai/e3-api-server` and `@elaraai/e3-rack`, then opt in:
331
+
332
+ ```sh
333
+ e3-api-server --repos ./repos --rack -j 4 --memory 8G
334
+ ```
335
+
336
+ Each API dataflow run gets its own rack session using the server's existing
337
+ shared budget. Repository rules still select tasks; explicit server opt-in
338
+ overrides the policy's `enabled` default. A programmatic server can use:
339
+
340
+ ```ts
341
+ import { createServer } from '@elaraai/e3-api-server';
342
+ import { createDataflowRunnerFactory } from '@elaraai/e3-rack';
343
+
344
+ const server = await createServer({
345
+ reposDir: './repos',
346
+ dataflowRunner: createDataflowRunnerFactory(),
347
+ });
348
+ await server.start();
349
+ ```
350
+
351
+ The server can run without the optional rack package. Its portable handlers
352
+ accept a runner factory; they do not import Node-only hub code.
353
+
354
+ ## Compatibility and verification boundaries
355
+
356
+ If `rack status` reports a listener error, confirm the configured address is
357
+ present on this machine and the port is free; a listener the hub refuses to
358
+ serve says why. Prefer a stable source origin, such as a relay's address. A
359
+ changed source origin needs a deliberate managed source transition;
360
+ re-running worker `configure` preserves an enrolled source's origin and identity.
361
+ If a rack is incompatible, its executable's bundled e3 must be upgraded along
362
+ with its runtime images. Managed `configure` deliberately refuses a release
363
+ change: use the companion's guarded `switch-release` workflow above. Releases
364
+ without its managed-state compatibility contract are refused. Do not switch its executable symlink or erase its service plan and
365
+ journals to bypass this guard. If a task stays local, inspect `rack policy show`,
366
+ the task's runner/platforms and host-access dependencies; `--rack-only` cannot
367
+ override eligibility.
368
+
369
+ Public package entries are `@elaraai/e3-rack` (local hub/client/routing/runner
370
+ APIs), `@elaraai/e3-rack/protocol` (wire types),
371
+ `@elaraai/e3-rack/server` (shared control-plane mechanisms without local hub startup),
372
+ `@elaraai/e3-rack/testing` (test agent and contracts), and
373
+ `@elaraai/e3-rack/hub-main` (hub process entry point).
374
+
375
+ Hosts adopt shared routes, dispatch, identity and store interfaces through the
376
+ server entry; this avoids importing the local hub's filesystem/runner adapters.
377
+ They implement `RackRegistry.compareAndSetRack`:
378
+ replace exactly the observed registration, atomically, and refuse a missing or
379
+ changed record. `updateRack` retries a patch against the latest value. This
380
+ preserves revocation, boot approval and drain state across concurrent requests;
381
+ a process-local coordinator alone cannot protect a distributed control plane.
382
+ Run `rackRegistryContract` from the testing entry against each backend. The file
383
+ registry retains its single-writer hub lock and heartbeat coalescing. This changes
384
+ no persisted or wire schema.
385
+
386
+ The shared routes retain a fixed 250 ms claim cadence by default. Cloud hosts
387
+ can set `pollMaxMs` to back off an idle poll (×1.5 per empty check), and
388
+ `policyRecheckMs` to reduce idle policy reads. For example, the cloud adapter
389
+ uses a one-second maximum and five-second idle policy refresh. Every actual
390
+ claim rereads registration and policy, including drain, revocation and boot
391
+ approval; cached idle permission cannot authorize work. Cancellation watches
392
+ keep their separate 250 ms cadence. Object reads, target signing and environment
393
+ layer signing run at most eight operations concurrently. Output declarations
394
+ are reserved first, and every started operation settles before the lease fence
395
+ is released, including after a failure. Commits stay sequential for distributed
396
+ backends whose transactions condition on the same lease row.
397
+
398
+ This implementation speaks e3-cloud's v2 task/unit leases with per-object
399
+ uploads. The protocol golden fixtures come from e3-cloud's rack protocol, and
400
+ the cancellation-watch fixtures match the companion agent
401
+ ([e3-cloud #254](https://github.com/elaraai/e3-cloud/pull/254)). The test rack
402
+ agent executes real e3 runners and exercises the transfer path, over TLS with a
403
+ pinned certificate; those tests do not establish KVM or hardware acceptance for
404
+ the deployed agent. A companion agent must bundle e3 at least as new as this
405
+ release, and pin the hub's certificate; an older bundle falls back locally.
406
+
407
+ See [the epic reconciliation](../../design/e3-rack-reconciliation.md) for the
408
+ changes required by current cloud and workspace APIs, wire migration rules,
409
+ and the remaining companion work. Custom environments, remote function calls,
410
+ automatic reconnect after hub restart and embedded UI integration are deferred.
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Copyright (c) 2025 Elara AI Pty Ltd
3
+ * Licensed under BSL 1.1. See LICENSE for details.
4
+ */
5
+ export {};
6
+ //# sourceMappingURL=client.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.spec.d.ts","sourceRoot":"","sources":["../../../src/client/client.spec.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Copyright (c) 2025 Elara AI Pty Ltd
3
+ * Licensed under BSL 1.1. See LICENSE for details.
4
+ */
5
+ import { afterEach, it } from 'node:test';
6
+ import assert from 'node:assert/strict';
7
+ import { mkdtemp, readFile, readdir, rm } from 'node:fs/promises';
8
+ import { tmpdir } from 'node:os';
9
+ import { join } from 'node:path';
10
+ import { spawn } from 'node:child_process';
11
+ import { setTimeout as delay } from 'node:timers/promises';
12
+ import { Hono } from 'hono';
13
+ import { some, variant } from '@elaraai/east';
14
+ import { LocalStorage, repoInit, uuidv7 } from '@elaraai/e3-core';
15
+ import { sendSuccess } from '../server/beast2.js';
16
+ import { connectHub, RackHubOutdatedError, RackHubUnavailableError } from './connect.js';
17
+ import { HubClient } from './hub-client.js';
18
+ import { RackSession } from './session.js';
19
+ import { closeSocketServer, serveOnSocket } from './local-http.js';
20
+ import { EmptyType, HubHelloType } from '../protocol/control.js';
21
+ import { HUB_PROTOCOL } from '../version.js';
22
+ import { hubSocketPath } from '../paths.js';
23
+ import { readHubLock } from '../hub/lock.js';
24
+ import { TestRackAgent } from '../testing/test-rack-agent.js';
25
+ const cleanup = [];
26
+ afterEach(async () => { for (const close of cleanup.splice(0).reverse())
27
+ await close(); });
28
+ async function home() {
29
+ const path = await mkdtemp(join(tmpdir(), 'e3c-'));
30
+ cleanup.push(() => rm(path, { recursive: true, force: true }));
31
+ cleanup.push(async () => {
32
+ await new HubClient(path).stop().catch(() => { });
33
+ for (let i = 0; i < 100 && await readHubLock(path) !== null; i++)
34
+ await delay(20);
35
+ const lock = await readHubLock(path);
36
+ if (lock !== null)
37
+ try {
38
+ process.kill(Number(lock.pid), 'SIGKILL');
39
+ }
40
+ catch { /* already gone */ }
41
+ });
42
+ return path;
43
+ }
44
+ async function fixture() {
45
+ const path = await home();
46
+ const client = await connectHub({ home: path });
47
+ await client.setConfig({ ...await client.getConfig(), listen: some({ host: '127.0.0.1', port: 0n }) });
48
+ const mint = await client.enroll();
49
+ const agent = await TestRackAgent.enroll(mint.apiUrl, mint.enrollmentToken, { certificateSha256: mint.certificateSha256.type === 'some' ? mint.certificateSha256.value : undefined });
50
+ cleanup.push(() => agent.close());
51
+ const repo = join(path, 'repo');
52
+ assert(repoInit(repo).success);
53
+ const storage = new LocalStorage();
54
+ const session = await RackSession.open(client, { storage, repoPath: repo, workspace: 'dev', label: 'client test' });
55
+ cleanup.push(() => session.close());
56
+ const request = { tier: 'node', computeSize: 'large', eventJson: JSON.stringify({ version: 2, workspace: 'dev', taskName: 'task', closure: { hashes: [] },
57
+ runnerEvent: { mode: 'task', taskHash: 'a'.repeat(64), repo: 'replaced', inputHashes: [], launchId: uuidv7(), timeoutMs: 30000 } }) };
58
+ return { path, client, agent, repo, storage, session, request };
59
+ }
60
+ it('cold-starts the published hub entry, logs startup and shares it across three processes', async () => {
61
+ const path = await home();
62
+ const module = new URL('./connect.js', import.meta.url).href;
63
+ const script = `import { connectHub } from ${JSON.stringify(module)};
64
+ const hub = await connectHub({ home: process.argv[1] });
65
+ console.log(Number((await hub.hello()).pid));`;
66
+ const run = () => new Promise((resolve, reject) => {
67
+ const child = spawn(process.execPath, ['--input-type=module', '-e', script, path], { stdio: ['ignore', 'pipe', 'pipe'], windowsHide: true });
68
+ let out = '';
69
+ let error = '';
70
+ child.stdout.on('data', (data) => { out += data.toString(); });
71
+ child.stderr.on('data', (data) => { error += data.toString(); });
72
+ child.once('error', reject);
73
+ child.once('exit', (code) => { if (code !== 0)
74
+ reject(new Error(error));
75
+ else
76
+ resolve(Number(out.trim())); });
77
+ });
78
+ const pids = await Promise.all([run(), run(), run()]);
79
+ assert.equal(new Set(pids).size, 1);
80
+ const client = await connectHub({ home: path, spawn: false });
81
+ assert.equal(Number((await client.status()).hello.pid), pids[0]);
82
+ assert.match(await readFile(join(path, 'hub.log'), 'utf8'), /rack hub started/);
83
+ assert.equal(Number((await readHubLock(path)).pid), pids[0]);
84
+ });
85
+ it('drains an older protocol, refuses a newer one and leaves its sessions alone', async () => {
86
+ const path = await home();
87
+ let protocol = BigInt(HUB_PROTOCOL) - 1n;
88
+ let drains = 0;
89
+ const app = new Hono();
90
+ app.post('/v1/hello', () => sendSuccess(HubHelloType, { version: '1.0.0', protocol, pid: BigInt(process.pid), draining: false }));
91
+ app.post('/v1/drain', () => { drains++; return sendSuccess(EmptyType, {}); });
92
+ const server = await serveOnSocket(app, hubSocketPath(path));
93
+ cleanup.push(() => closeSocketServer(server, hubSocketPath(path)));
94
+ await assert.rejects(connectHub({ home: path }), (err) => err instanceof RackHubOutdatedError && /older.*exit once idle/.test(err.message));
95
+ assert.equal(drains, 1);
96
+ protocol = BigInt(HUB_PROTOCOL) + 1n;
97
+ await assert.rejects(connectHub({ home: path }), (err) => err instanceof RackHubOutdatedError && /newer/.test(err.message));
98
+ assert.equal(drains, 1);
99
+ });
100
+ it('delivers claim, log tee and terminal outcome in order, then removes its session data socket', async () => {
101
+ const f = await fixture();
102
+ const handle = await f.session.createLease(f.request);
103
+ const grant = await f.agent.claim();
104
+ assert(grant);
105
+ const claimed = await handle.next();
106
+ assert.equal(claimed.kind, 'claimed');
107
+ assert(handle.attempt);
108
+ const logs = [];
109
+ f.session.onLog(handle.attempt.executionId, (_stream, data) => { logs.push(data); });
110
+ await f.agent.logs(grant, [{ stream: variant('stdout', null), data: 'first\n' }, { stream: variant('stdout', null), data: 'second\n' }]);
111
+ assert.deepEqual(logs, ['first\n', 'second\n']);
112
+ await f.agent.complete(grant, { taskName: 'task', status: 'failed', exitCode: 7, error: 'expected failure' });
113
+ const completed = await handle.next();
114
+ assert.equal(completed.kind, 'completed');
115
+ if (completed.kind === 'completed')
116
+ assert.equal(completed.result.exitCode, 7);
117
+ await f.session.close();
118
+ await f.session.close();
119
+ assert.equal((await f.client.status()).sessions.length, 0);
120
+ assert.equal((await readdir(f.path)).filter((name) => name.startsWith('s-') && name.endsWith('.sock')).length, 0);
121
+ });
122
+ it('unblocks every lease promptly after the real hub is killed and refuses reconnecting the old session', async () => {
123
+ const f = await fixture();
124
+ const handle = await f.session.createLease(f.request);
125
+ const hello = await f.client.hello();
126
+ const waiting = handle.next();
127
+ const started = Date.now();
128
+ process.kill(Number(hello.pid), 'SIGKILL');
129
+ const update = await waiting;
130
+ assert.equal(update.kind, 'lost');
131
+ assert(Date.now() - started < 3000);
132
+ assert.equal(f.session.hubLost, true);
133
+ await assert.rejects(f.session.createLease(f.request), RackHubUnavailableError);
134
+ const replacement = await connectHub({ home: f.path });
135
+ assert.notEqual(Number((await replacement.hello()).pid), Number(hello.pid));
136
+ assert.equal((await replacement.status()).sessions.length, 0);
137
+ });
138
+ it('keeps another handle subscribed when identical work is requested twice in one session', async () => {
139
+ const f = await fixture();
140
+ const [first, second] = await Promise.all([f.session.createLease(f.request), f.session.createLease(f.request)]);
141
+ assert.equal(first.leaseId, second.leaseId);
142
+ await first.cancel();
143
+ assert.equal((await first.next()).kind, 'cancelled');
144
+ const grant = await f.agent.claim();
145
+ assert(grant);
146
+ assert.equal((await second.next()).kind, 'claimed');
147
+ await f.agent.complete(grant, { taskName: 'task', status: 'failed' });
148
+ assert.equal((await second.next()).kind, 'completed');
149
+ });
150
+ //# sourceMappingURL=client.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.spec.js","sourceRoot":"","sources":["../../../src/client/client.spec.ts"],"names":[],"mappings":"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"}