@elaraai/e3-rack 0.0.0-bootstrap.0 → 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 (306) hide show
  1. package/README.md +407 -2
  2. package/dist/src/client/client.spec.d.ts +6 -0
  3. package/dist/src/client/client.spec.d.ts.map +1 -0
  4. package/dist/src/client/client.spec.js +150 -0
  5. package/dist/src/client/client.spec.js.map +1 -0
  6. package/dist/src/client/connect.d.ts +52 -0
  7. package/dist/src/client/connect.d.ts.map +1 -0
  8. package/dist/src/client/connect.js +112 -0
  9. package/dist/src/client/connect.js.map +1 -0
  10. package/dist/src/client/hub-client.d.ts +57 -0
  11. package/dist/src/client/hub-client.d.ts.map +1 -0
  12. package/dist/src/client/hub-client.js +87 -0
  13. package/dist/src/client/hub-client.js.map +1 -0
  14. package/dist/src/client/local-http.d.ts +78 -0
  15. package/dist/src/client/local-http.d.ts.map +1 -0
  16. package/dist/src/client/local-http.js +149 -0
  17. package/dist/src/client/local-http.js.map +1 -0
  18. package/dist/src/client/session-attempts.d.ts +20 -0
  19. package/dist/src/client/session-attempts.d.ts.map +1 -0
  20. package/dist/src/client/session-attempts.js +88 -0
  21. package/dist/src/client/session-attempts.js.map +1 -0
  22. package/dist/src/client/session-data-server.d.ts +37 -0
  23. package/dist/src/client/session-data-server.d.ts.map +1 -0
  24. package/dist/src/client/session-data-server.js +280 -0
  25. package/dist/src/client/session-data-server.js.map +1 -0
  26. package/dist/src/client/session-data-server.spec.d.ts +6 -0
  27. package/dist/src/client/session-data-server.spec.d.ts.map +1 -0
  28. package/dist/src/client/session-data-server.spec.js +192 -0
  29. package/dist/src/client/session-data-server.spec.js.map +1 -0
  30. package/dist/src/client/session.d.ts +119 -0
  31. package/dist/src/client/session.d.ts.map +1 -0
  32. package/dist/src/client/session.js +290 -0
  33. package/dist/src/client/session.js.map +1 -0
  34. package/dist/src/hub/config.d.ts +74 -0
  35. package/dist/src/hub/config.d.ts.map +1 -0
  36. package/dist/src/hub/config.js +159 -0
  37. package/dist/src/hub/config.js.map +1 -0
  38. package/dist/src/hub/fair-lease-store.d.ts +36 -0
  39. package/dist/src/hub/fair-lease-store.d.ts.map +1 -0
  40. package/dist/src/hub/fair-lease-store.js +73 -0
  41. package/dist/src/hub/fair-lease-store.js.map +1 -0
  42. package/dist/src/hub/hub.d.ts +91 -0
  43. package/dist/src/hub/hub.d.ts.map +1 -0
  44. package/dist/src/hub/hub.js +641 -0
  45. package/dist/src/hub/hub.js.map +1 -0
  46. package/dist/src/hub/hub.spec.d.ts +6 -0
  47. package/dist/src/hub/hub.spec.d.ts.map +1 -0
  48. package/dist/src/hub/hub.spec.js +363 -0
  49. package/dist/src/hub/hub.spec.js.map +1 -0
  50. package/dist/src/hub/infrastructure.spec.d.ts +6 -0
  51. package/dist/src/hub/infrastructure.spec.d.ts.map +1 -0
  52. package/dist/src/hub/infrastructure.spec.js +211 -0
  53. package/dist/src/hub/infrastructure.spec.js.map +1 -0
  54. package/dist/src/hub/lock.d.ts +62 -0
  55. package/dist/src/hub/lock.d.ts.map +1 -0
  56. package/dist/src/hub/lock.js +165 -0
  57. package/dist/src/hub/lock.js.map +1 -0
  58. package/dist/src/hub/main.d.ts +16 -0
  59. package/dist/src/hub/main.d.ts.map +1 -0
  60. package/dist/src/hub/main.js +56 -0
  61. package/dist/src/hub/main.js.map +1 -0
  62. package/dist/src/hub/session-bridge.d.ts +116 -0
  63. package/dist/src/hub/session-bridge.d.ts.map +1 -0
  64. package/dist/src/hub/session-bridge.js +336 -0
  65. package/dist/src/hub/session-bridge.js.map +1 -0
  66. package/dist/src/identity/file-identity-store.d.ts +18 -0
  67. package/dist/src/identity/file-identity-store.d.ts.map +1 -0
  68. package/dist/src/identity/file-identity-store.js +34 -0
  69. package/dist/src/identity/file-identity-store.js.map +1 -0
  70. package/dist/src/identity/file-identity-store.spec.d.ts +6 -0
  71. package/dist/src/identity/file-identity-store.spec.d.ts.map +1 -0
  72. package/dist/src/identity/file-identity-store.spec.js +59 -0
  73. package/dist/src/identity/file-identity-store.spec.js.map +1 -0
  74. package/dist/src/identity/in-memory-identity-store.d.ts +48 -0
  75. package/dist/src/identity/in-memory-identity-store.d.ts.map +1 -0
  76. package/dist/src/identity/in-memory-identity-store.js +102 -0
  77. package/dist/src/identity/in-memory-identity-store.js.map +1 -0
  78. package/dist/src/identity/machine-identity.d.ts +134 -0
  79. package/dist/src/identity/machine-identity.d.ts.map +1 -0
  80. package/dist/src/identity/machine-identity.js +65 -0
  81. package/dist/src/identity/machine-identity.js.map +1 -0
  82. package/dist/src/index.d.ts +34 -0
  83. package/dist/src/index.d.ts.map +1 -0
  84. package/dist/src/index.js +34 -0
  85. package/dist/src/index.js.map +1 -0
  86. package/dist/src/lease/closure.d.ts +96 -0
  87. package/dist/src/lease/closure.d.ts.map +1 -0
  88. package/dist/src/lease/closure.js +192 -0
  89. package/dist/src/lease/closure.js.map +1 -0
  90. package/dist/src/lease/closure.spec.d.ts +6 -0
  91. package/dist/src/lease/closure.spec.d.ts.map +1 -0
  92. package/dist/src/lease/closure.spec.js +35 -0
  93. package/dist/src/lease/closure.spec.js.map +1 -0
  94. package/dist/src/lease/coordinator.d.ts +34 -0
  95. package/dist/src/lease/coordinator.d.ts.map +1 -0
  96. package/dist/src/lease/coordinator.js +42 -0
  97. package/dist/src/lease/coordinator.js.map +1 -0
  98. package/dist/src/lease/in-memory-lease-store.d.ts +58 -0
  99. package/dist/src/lease/in-memory-lease-store.d.ts.map +1 -0
  100. package/dist/src/lease/in-memory-lease-store.js +132 -0
  101. package/dist/src/lease/in-memory-lease-store.js.map +1 -0
  102. package/dist/src/lease/lease-store.d.ts +169 -0
  103. package/dist/src/lease/lease-store.d.ts.map +1 -0
  104. package/dist/src/lease/lease-store.js +94 -0
  105. package/dist/src/lease/lease-store.js.map +1 -0
  106. package/dist/src/lease/lease-store.spec.d.ts +6 -0
  107. package/dist/src/lease/lease-store.spec.d.ts.map +1 -0
  108. package/dist/src/lease/lease-store.spec.js +8 -0
  109. package/dist/src/lease/lease-store.spec.js.map +1 -0
  110. package/dist/src/lease/rack-dispatch.d.ts +105 -0
  111. package/dist/src/lease/rack-dispatch.d.ts.map +1 -0
  112. package/dist/src/lease/rack-dispatch.js +6 -0
  113. package/dist/src/lease/rack-dispatch.js.map +1 -0
  114. package/dist/src/mutation-queue.d.ts +13 -0
  115. package/dist/src/mutation-queue.d.ts.map +1 -0
  116. package/dist/src/mutation-queue.js +17 -0
  117. package/dist/src/mutation-queue.js.map +1 -0
  118. package/dist/src/paths.d.ts +56 -0
  119. package/dist/src/paths.d.ts.map +1 -0
  120. package/dist/src/paths.js +82 -0
  121. package/dist/src/paths.js.map +1 -0
  122. package/dist/src/protocol/control.d.ts +295 -0
  123. package/dist/src/protocol/control.d.ts.map +1 -0
  124. package/dist/src/protocol/control.js +74 -0
  125. package/dist/src/protocol/control.js.map +1 -0
  126. package/dist/src/protocol/index.d.ts +9 -0
  127. package/dist/src/protocol/index.d.ts.map +1 -0
  128. package/dist/src/protocol/index.js +9 -0
  129. package/dist/src/protocol/index.js.map +1 -0
  130. package/dist/src/protocol/rack-wire.d.ts +357 -0
  131. package/dist/src/protocol/rack-wire.d.ts.map +1 -0
  132. package/dist/src/protocol/rack-wire.js +262 -0
  133. package/dist/src/protocol/rack-wire.js.map +1 -0
  134. package/dist/src/protocol/rack-wire.spec.d.ts +6 -0
  135. package/dist/src/protocol/rack-wire.spec.d.ts.map +1 -0
  136. package/dist/src/protocol/rack-wire.spec.js +14 -0
  137. package/dist/src/protocol/rack-wire.spec.js.map +1 -0
  138. package/dist/src/protocol/registration.d.ts +70 -0
  139. package/dist/src/protocol/registration.d.ts.map +1 -0
  140. package/dist/src/protocol/registration.js +67 -0
  141. package/dist/src/protocol/registration.js.map +1 -0
  142. package/dist/src/protocol/session-data.d.ts +69 -0
  143. package/dist/src/protocol/session-data.d.ts.map +1 -0
  144. package/dist/src/protocol/session-data.js +26 -0
  145. package/dist/src/protocol/session-data.js.map +1 -0
  146. package/dist/src/protocol/task-envelope.d.ts +198 -0
  147. package/dist/src/protocol/task-envelope.d.ts.map +1 -0
  148. package/dist/src/protocol/task-envelope.js +93 -0
  149. package/dist/src/protocol/task-envelope.js.map +1 -0
  150. package/dist/src/protocol/wire-golden.d.ts +8 -0
  151. package/dist/src/protocol/wire-golden.d.ts.map +1 -0
  152. package/dist/src/protocol/wire-golden.js +34 -0
  153. package/dist/src/protocol/wire-golden.js.map +1 -0
  154. package/dist/src/protocol/wire-samples.d.ts +11 -0
  155. package/dist/src/protocol/wire-samples.d.ts.map +1 -0
  156. package/dist/src/protocol/wire-samples.js +52 -0
  157. package/dist/src/protocol/wire-samples.js.map +1 -0
  158. package/dist/src/registry/file-rack-registry.d.ts +68 -0
  159. package/dist/src/registry/file-rack-registry.d.ts.map +1 -0
  160. package/dist/src/registry/file-rack-registry.js +138 -0
  161. package/dist/src/registry/file-rack-registry.js.map +1 -0
  162. package/dist/src/registry/file-rack-registry.spec.d.ts +6 -0
  163. package/dist/src/registry/file-rack-registry.spec.d.ts.map +1 -0
  164. package/dist/src/registry/file-rack-registry.spec.js +50 -0
  165. package/dist/src/registry/file-rack-registry.spec.js.map +1 -0
  166. package/dist/src/registry/rack-registry.d.ts +64 -0
  167. package/dist/src/registry/rack-registry.d.ts.map +1 -0
  168. package/dist/src/registry/rack-registry.js +65 -0
  169. package/dist/src/registry/rack-registry.js.map +1 -0
  170. package/dist/src/registry/rack-registry.spec.d.ts +6 -0
  171. package/dist/src/registry/rack-registry.spec.d.ts.map +1 -0
  172. package/dist/src/registry/rack-registry.spec.js +8 -0
  173. package/dist/src/registry/rack-registry.spec.js.map +1 -0
  174. package/dist/src/routing/eligibility.d.ts +38 -0
  175. package/dist/src/routing/eligibility.d.ts.map +1 -0
  176. package/dist/src/routing/eligibility.js +78 -0
  177. package/dist/src/routing/eligibility.js.map +1 -0
  178. package/dist/src/routing/host-coupling.d.ts +26 -0
  179. package/dist/src/routing/host-coupling.d.ts.map +1 -0
  180. package/dist/src/routing/host-coupling.js +62 -0
  181. package/dist/src/routing/host-coupling.js.map +1 -0
  182. package/dist/src/routing/policy.d.ts +188 -0
  183. package/dist/src/routing/policy.d.ts.map +1 -0
  184. package/dist/src/routing/policy.js +212 -0
  185. package/dist/src/routing/policy.js.map +1 -0
  186. package/dist/src/routing/routing.spec.d.ts +6 -0
  187. package/dist/src/routing/routing.spec.d.ts.map +1 -0
  188. package/dist/src/routing/routing.spec.js +100 -0
  189. package/dist/src/routing/routing.spec.js.map +1 -0
  190. package/dist/src/routing/tiers.d.ts +22 -0
  191. package/dist/src/routing/tiers.d.ts.map +1 -0
  192. package/dist/src/routing/tiers.js +26 -0
  193. package/dist/src/routing/tiers.js.map +1 -0
  194. package/dist/src/runner/api-server.d.ts +45 -0
  195. package/dist/src/runner/api-server.d.ts.map +1 -0
  196. package/dist/src/runner/api-server.js +21 -0
  197. package/dist/src/runner/api-server.js.map +1 -0
  198. package/dist/src/runner/capacity.d.ts +13 -0
  199. package/dist/src/runner/capacity.d.ts.map +1 -0
  200. package/dist/src/runner/capacity.js +18 -0
  201. package/dist/src/runner/capacity.js.map +1 -0
  202. package/dist/src/runner/rack-body.d.ts +43 -0
  203. package/dist/src/runner/rack-body.d.ts.map +1 -0
  204. package/dist/src/runner/rack-body.js +208 -0
  205. package/dist/src/runner/rack-body.js.map +1 -0
  206. package/dist/src/runner/rack-runner.d.ts +62 -0
  207. package/dist/src/runner/rack-runner.d.ts.map +1 -0
  208. package/dist/src/runner/rack-runner.js +75 -0
  209. package/dist/src/runner/rack-runner.js.map +1 -0
  210. package/dist/src/runner/rack-runner.spec.d.ts +6 -0
  211. package/dist/src/runner/rack-runner.spec.d.ts.map +1 -0
  212. package/dist/src/runner/rack-runner.spec.js +302 -0
  213. package/dist/src/runner/rack-runner.spec.js.map +1 -0
  214. package/dist/src/runner/split.spec.d.ts +6 -0
  215. package/dist/src/runner/split.spec.d.ts.map +1 -0
  216. package/dist/src/runner/split.spec.js +188 -0
  217. package/dist/src/runner/split.spec.js.map +1 -0
  218. package/dist/src/runner-event.d.ts +261 -0
  219. package/dist/src/runner-event.d.ts.map +1 -0
  220. package/dist/src/runner-event.js +332 -0
  221. package/dist/src/runner-event.js.map +1 -0
  222. package/dist/src/server/beast2.d.ts +19 -0
  223. package/dist/src/server/beast2.d.ts.map +1 -0
  224. package/dist/src/server/beast2.js +18 -0
  225. package/dist/src/server/beast2.js.map +1 -0
  226. package/dist/src/server/body-limit.d.ts +16 -0
  227. package/dist/src/server/body-limit.d.ts.map +1 -0
  228. package/dist/src/server/body-limit.js +53 -0
  229. package/dist/src/server/body-limit.js.map +1 -0
  230. package/dist/src/server/body-limit.spec.d.ts +2 -0
  231. package/dist/src/server/body-limit.spec.d.ts.map +1 -0
  232. package/dist/src/server/body-limit.spec.js +32 -0
  233. package/dist/src/server/body-limit.spec.js.map +1 -0
  234. package/dist/src/server/bounded.d.ts +16 -0
  235. package/dist/src/server/bounded.d.ts.map +1 -0
  236. package/dist/src/server/bounded.js +39 -0
  237. package/dist/src/server/bounded.js.map +1 -0
  238. package/dist/src/server/dispatch.d.ts +137 -0
  239. package/dist/src/server/dispatch.d.ts.map +1 -0
  240. package/dist/src/server/dispatch.js +267 -0
  241. package/dist/src/server/dispatch.js.map +1 -0
  242. package/dist/src/server/env-publication.d.ts +45 -0
  243. package/dist/src/server/env-publication.d.ts.map +1 -0
  244. package/dist/src/server/env-publication.js +10 -0
  245. package/dist/src/server/env-publication.js.map +1 -0
  246. package/dist/src/server/index.d.ts +16 -0
  247. package/dist/src/server/index.d.ts.map +1 -0
  248. package/dist/src/server/index.js +17 -0
  249. package/dist/src/server/index.js.map +1 -0
  250. package/dist/src/server/protocol.spec.d.ts +6 -0
  251. package/dist/src/server/protocol.spec.d.ts.map +1 -0
  252. package/dist/src/server/protocol.spec.js +78 -0
  253. package/dist/src/server/protocol.spec.js.map +1 -0
  254. package/dist/src/server/rack-routes.d.ts +175 -0
  255. package/dist/src/server/rack-routes.d.ts.map +1 -0
  256. package/dist/src/server/rack-routes.js +755 -0
  257. package/dist/src/server/rack-routes.js.map +1 -0
  258. package/dist/src/server/rack-routes.spec.d.ts +6 -0
  259. package/dist/src/server/rack-routes.spec.d.ts.map +1 -0
  260. package/dist/src/server/rack-routes.spec.js +477 -0
  261. package/dist/src/server/rack-routes.spec.js.map +1 -0
  262. package/dist/src/server/storage-bridge.d.ts +140 -0
  263. package/dist/src/server/storage-bridge.d.ts.map +1 -0
  264. package/dist/src/server/storage-bridge.js +6 -0
  265. package/dist/src/server/storage-bridge.js.map +1 -0
  266. package/dist/src/state-file.d.ts +10 -0
  267. package/dist/src/state-file.d.ts.map +1 -0
  268. package/dist/src/state-file.js +38 -0
  269. package/dist/src/state-file.js.map +1 -0
  270. package/dist/src/testing/in-memory.d.ts +116 -0
  271. package/dist/src/testing/in-memory.d.ts.map +1 -0
  272. package/dist/src/testing/in-memory.js +193 -0
  273. package/dist/src/testing/in-memory.js.map +1 -0
  274. package/dist/src/testing/index.d.ts +14 -0
  275. package/dist/src/testing/index.d.ts.map +1 -0
  276. package/dist/src/testing/index.js +14 -0
  277. package/dist/src/testing/index.js.map +1 -0
  278. package/dist/src/testing/lease-store-contract.d.ts +14 -0
  279. package/dist/src/testing/lease-store-contract.d.ts.map +1 -0
  280. package/dist/src/testing/lease-store-contract.js +78 -0
  281. package/dist/src/testing/lease-store-contract.js.map +1 -0
  282. package/dist/src/testing/protocol-suite.d.ts +38 -0
  283. package/dist/src/testing/protocol-suite.d.ts.map +1 -0
  284. package/dist/src/testing/protocol-suite.js +207 -0
  285. package/dist/src/testing/protocol-suite.js.map +1 -0
  286. package/dist/src/testing/rack-registry-contract.d.ts +15 -0
  287. package/dist/src/testing/rack-registry-contract.d.ts.map +1 -0
  288. package/dist/src/testing/rack-registry-contract.js +106 -0
  289. package/dist/src/testing/rack-registry-contract.js.map +1 -0
  290. package/dist/src/testing/test-rack-agent.d.ts +121 -0
  291. package/dist/src/testing/test-rack-agent.d.ts.map +1 -0
  292. package/dist/src/testing/test-rack-agent.js +316 -0
  293. package/dist/src/testing/test-rack-agent.js.map +1 -0
  294. package/dist/src/tls/tls.d.ts +67 -0
  295. package/dist/src/tls/tls.d.ts.map +1 -0
  296. package/dist/src/tls/tls.js +200 -0
  297. package/dist/src/tls/tls.js.map +1 -0
  298. package/dist/src/tls/tls.spec.d.ts +6 -0
  299. package/dist/src/tls/tls.spec.d.ts.map +1 -0
  300. package/dist/src/tls/tls.spec.js +101 -0
  301. package/dist/src/tls/tls.spec.js.map +1 -0
  302. package/dist/src/version.d.ts +9 -0
  303. package/dist/src/version.d.ts.map +1 -0
  304. package/dist/src/version.js +10 -0
  305. package/dist/src/version.js.map +1 -0
  306. package/package.json +67 -7
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Copyright (c) 2025 Elara AI Pty Ltd
3
+ * Licensed under BSL 1.1. See LICENSE for details.
4
+ */
5
+ import { AsyncLocalStorage } from 'node:async_hooks';
6
+ import { Hono } from 'hono';
7
+ import type { ExecutionOwner } from '@elaraai/e3-types';
8
+ import { type LeaseCoordinator } from '../lease/coordinator.js';
9
+ import type { RackLeaseStore } from '../lease/lease-store.js';
10
+ import type { RackAttemptStore } from '../server/rack-routes.js';
11
+ import type { RackPutTarget, RackStorageBridge } from '../server/storage-bridge.js';
12
+ /** Identifies the session that currently owns repository I/O for a lease. */
13
+ export interface SessionEndpoint {
14
+ /** Private socket; never sent to a rack. */
15
+ dataSocket: string;
16
+ /** Private session credential; never sent to a rack. */
17
+ dataToken: string;
18
+ }
19
+ interface LeaseContext {
20
+ repo: string;
21
+ leaseId: string;
22
+ }
23
+ /**
24
+ * Shares one lease fence and its context across the routes and dispatcher.
25
+ * Storage bridge signatures remain compatible with cloud backends; only the
26
+ * local proxy needs the calling lease to scope a URL to its current attempt.
27
+ */
28
+ export declare class SessionLeaseCoordinator implements LeaseCoordinator {
29
+ private readonly serial;
30
+ private readonly context;
31
+ run<T>(repo: string, leaseId: string, operation: () => Promise<T>): Promise<T>;
32
+ /** Returns the calling transition's identity, refusing unfenced access. */
33
+ current(repo: string): LeaseContext;
34
+ }
35
+ /** Configures the hub's opaque pass-through to repository-owning sessions. */
36
+ export interface SessionBridgeOptions {
37
+ /** Conditional lease state. */
38
+ leases: RackLeaseStore;
39
+ /** Shared route/dispatch fence. */
40
+ coordinator: SessionLeaseCoordinator;
41
+ /** Selects a live session of the lease's exact repository and e3 version. */
42
+ session: (repo: string, leaseId: string) => SessionEndpoint;
43
+ /** Returns every live subscriber for log tees; only session() persists. */
44
+ subscribers?: (repo: string, leaseId: string) => SessionEndpoint[];
45
+ /** Resolves the hub's process identity for execution liveness. */
46
+ owner: ExecutionOwner;
47
+ /**
48
+ * The origin racks reach the hub at, when configured: a relay's, a tunnel's
49
+ * or a TLS terminator's. Every data URL is built on it; without it, on the
50
+ * origin the rack's own request named.
51
+ */
52
+ advertiseOrigin: () => string | undefined;
53
+ /** Controls capability expiration in tests. */
54
+ now?: () => number;
55
+ }
56
+ /**
57
+ * Proxies objects and attempt records without opening a repository in the hub.
58
+ * Capability URLs bind to one lease attempt, expire in fifteen minutes and
59
+ * are aborted on reclaim or termination. PUT acknowledgments are repeatable
60
+ * until cleanup so a lost HTTP response cannot force another body write.
61
+ * @example
62
+ * const bridge = new SessionProxyStorageBridge(options);
63
+ * app.route('/', bridge.dataRoutes());
64
+ */
65
+ export declare class SessionProxyStorageBridge implements RackStorageBridge {
66
+ private readonly options;
67
+ /** Carries the rack request's own origin, for data URLs when no origin is advertised. */
68
+ readonly origin: AsyncLocalStorage<string>;
69
+ /** Forwards execution records and buffered logs to the repository session. */
70
+ readonly attempts: RackAttemptStore;
71
+ private readonly capabilities;
72
+ private readonly logs;
73
+ private readonly now;
74
+ /** @param options - Stores, session selection, ownership and URL origin */
75
+ constructor(options: SessionBridgeOptions);
76
+ private post;
77
+ private flush;
78
+ private mint;
79
+ private url;
80
+ readDescriptor(repo: string, hash: string): Promise<{
81
+ kind: "url";
82
+ url: string;
83
+ data?: undefined;
84
+ } | {
85
+ kind: "inline";
86
+ data: Buffer<ArrayBuffer>;
87
+ url?: undefined;
88
+ }>;
89
+ writeTarget(repo: string, leaseId: string, hash: string, size: number): Promise<RackPutTarget | null>;
90
+ commitWrite(repo: string, leaseId: string, rackId: string, hash: string, size: number, version: string): Promise<boolean>;
91
+ stagedTarget(repo: string, _workspace: string, hash: string, size: bigint): Promise<{
92
+ url: string;
93
+ uploadId: string;
94
+ }>;
95
+ commitStaged(repo: string, hash: string, uploadId: string): Promise<boolean>;
96
+ writeInline(repo: string, data: Uint8Array): Promise<string>;
97
+ readEnvManifest(): Promise<null>;
98
+ presignEnvLayer(): Promise<string>;
99
+ /** Aborts old-attempt streams immediately, before another session takes ownership. */
100
+ revoke(repo: string, leaseId: string): void;
101
+ /** Stops streams to a departing session while retaining acknowledged receipts. */
102
+ interrupt(repo: string, leaseId: string): void;
103
+ discardWrites(repo: string, leaseId: string, _hashes: readonly string[]): Promise<number>;
104
+ private prune;
105
+ private valid;
106
+ /**
107
+ * Streams data URLs with backpressure and no rack bearer header. A transfer
108
+ * may take the rest of its capability's fifteen minutes, so a large object
109
+ * over a slow link is not cut short of the time its URL grants.
110
+ */
111
+ dataRoutes(): Hono;
112
+ /** Aborts outstanding transfers and clears unreferenced log timers on shutdown. */
113
+ close(): void;
114
+ }
115
+ export {};
116
+ //# sourceMappingURL=session-bridge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-bridge.d.ts","sourceRoot":"","sources":["../../../src/hub/session-bridge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,336 @@
1
+ /**
2
+ * Copyright (c) 2025 Elara AI Pty Ltd
3
+ * Licensed under BSL 1.1. See LICENSE for details.
4
+ */
5
+ import { AsyncLocalStorage } from 'node:async_hooks';
6
+ import { randomUUID } from 'node:crypto';
7
+ import { Readable } from 'node:stream';
8
+ import { Hono } from 'hono';
9
+ import { ArrayType, BooleanType, StringType, variant } from '@elaraai/east';
10
+ import { InMemoryLeaseCoordinator } from '../lease/coordinator.js';
11
+ import { RackHubError, socketRequest, socketStream } from '../client/local-http.js';
12
+ import { LeaseDataType, LeaseOutcomeType, LeaseRunningType, LeaseStoppedType, SessionLogsType, SessionUploadType, TouchObjectsType, VerifyOutputType } from '../protocol/session-data.js';
13
+ /**
14
+ * Shares one lease fence and its context across the routes and dispatcher.
15
+ * Storage bridge signatures remain compatible with cloud backends; only the
16
+ * local proxy needs the calling lease to scope a URL to its current attempt.
17
+ */
18
+ export class SessionLeaseCoordinator {
19
+ serial = new InMemoryLeaseCoordinator();
20
+ context = new AsyncLocalStorage();
21
+ run(repo, leaseId, operation) {
22
+ return this.serial.run(repo, leaseId, () => this.context.run({ repo, leaseId }, operation));
23
+ }
24
+ /** Returns the calling transition's identity, refusing unfenced access. */
25
+ current(repo) {
26
+ const context = this.context.getStore();
27
+ if (context === undefined || context.repo !== repo)
28
+ throw new Error('Rack storage requires a lease context');
29
+ return context;
30
+ }
31
+ }
32
+ /**
33
+ * Proxies objects and attempt records without opening a repository in the hub.
34
+ * Capability URLs bind to one lease attempt, expire in fifteen minutes and
35
+ * are aborted on reclaim or termination. PUT acknowledgments are repeatable
36
+ * until cleanup so a lost HTTP response cannot force another body write.
37
+ * @example
38
+ * const bridge = new SessionProxyStorageBridge(options);
39
+ * app.route('/', bridge.dataRoutes());
40
+ */
41
+ export class SessionProxyStorageBridge {
42
+ options;
43
+ /** Carries the rack request's own origin, for data URLs when no origin is advertised. */
44
+ origin = new AsyncLocalStorage();
45
+ /** Forwards execution records and buffered logs to the repository session. */
46
+ attempts;
47
+ capabilities = new Map();
48
+ logs = new Map();
49
+ now;
50
+ /** @param options - Stores, session selection, ownership and URL origin */
51
+ constructor(options) {
52
+ this.options = options;
53
+ this.now = options.now ?? Date.now;
54
+ this.attempts = {
55
+ closure: (lease) => this.post(lease, '/v1/attempts/closure', LeaseDataType, { leaseJson: JSON.stringify(lease) }, ArrayType(StringType)),
56
+ running: async (lease) => { await this.post(lease, '/v1/attempts/running', LeaseRunningType, { leaseJson: JSON.stringify(lease), owner: options.owner }, BooleanType); },
57
+ stopped: async (lease, how, cause) => {
58
+ await this.flush(lease.repo, lease.attempt?.executionId);
59
+ await this.post(lease, '/v1/attempts/stopped', LeaseStoppedType, { leaseJson: JSON.stringify(lease), cancelled: how === 'cancelled', cause }, BooleanType);
60
+ },
61
+ outcome: async (lease, result) => {
62
+ await this.flush(lease.repo, lease.attempt?.executionId);
63
+ await this.post(lease, '/v1/attempts/outcome', LeaseOutcomeType, { leaseJson: JSON.stringify(lease), resultJson: JSON.stringify(result) }, BooleanType);
64
+ },
65
+ verifyOutput: (lease, hash) => this.post(lease, '/v1/verify', VerifyOutputType, { hash }, BooleanType),
66
+ logs: {
67
+ append: async (repo, taskHash, inputsHash, executionId, stream, data) => {
68
+ const key = JSON.stringify([repo, executionId]);
69
+ let buffer = this.logs.get(key);
70
+ if (buffer === undefined) {
71
+ buffer = { ...options.coordinator.current(repo), taskHash, inputsHash, executionId, chunks: [], bytes: 0 };
72
+ this.logs.set(key, buffer);
73
+ }
74
+ buffer.chunks.push({ stream: variant(stream, null), data });
75
+ buffer.bytes += Buffer.byteLength(data);
76
+ if (buffer.bytes >= 32768)
77
+ await this.flush(repo, executionId);
78
+ else if (buffer.timer === undefined) {
79
+ buffer.timer = setTimeout(() => {
80
+ buffer.timer = undefined;
81
+ void this.flush(repo, executionId).catch((err) => console.warn('rack log flush:', err));
82
+ }, 250);
83
+ buffer.timer.unref();
84
+ }
85
+ },
86
+ flush: (repo, _task, _inputs, id) => this.flush(repo, id),
87
+ },
88
+ };
89
+ }
90
+ post(context, path, requestType, body, responseType) {
91
+ const session = this.options.session(context.repo, context.leaseId);
92
+ return socketRequest(session.dataSocket, 'POST', path, { bearer: session.dataToken, requestType, body, responseType });
93
+ }
94
+ async flush(repo, executionId) {
95
+ if (executionId === undefined)
96
+ return;
97
+ const key = JSON.stringify([repo, executionId]);
98
+ const buffer = this.logs.get(key);
99
+ if (buffer === undefined)
100
+ return;
101
+ if (buffer.timer !== undefined) {
102
+ clearTimeout(buffer.timer);
103
+ buffer.timer = undefined;
104
+ }
105
+ if (buffer.pending !== undefined)
106
+ await buffer.pending;
107
+ if (buffer.chunks.length === 0)
108
+ return;
109
+ const chunks = buffer.chunks.splice(0);
110
+ buffer.bytes = 0;
111
+ const body = {
112
+ taskHash: buffer.taskHash, inputsHash: buffer.inputsHash, executionId, chunks,
113
+ };
114
+ const owner = this.options.session(buffer.repo, buffer.leaseId);
115
+ const pending = socketRequest(owner.dataSocket, 'POST', '/v1/logs', {
116
+ bearer: owner.dataToken, requestType: SessionLogsType, body, responseType: BooleanType,
117
+ }).then(async () => {
118
+ // A subscriber's display is best effort; it cannot cause persisted logs
119
+ // to be retried, or prevent the owner from recording the outcome.
120
+ await Promise.allSettled((this.options.subscribers?.(buffer.repo, buffer.leaseId) ?? [])
121
+ .filter((session) => session.dataSocket !== owner.dataSocket)
122
+ .map((session) => socketRequest(session.dataSocket, 'POST', '/v1/logs/tee', {
123
+ bearer: session.dataToken, requestType: SessionLogsType, body, responseType: BooleanType, timeoutMs: 1000,
124
+ })));
125
+ });
126
+ // A batch whose flush fails is not kept: the rack is told its lines were
127
+ // not stored and posts them again, so keeping them too would write them
128
+ // twice once the session answers.
129
+ buffer.pending = pending;
130
+ try {
131
+ await pending;
132
+ }
133
+ finally {
134
+ if (buffer.pending === pending)
135
+ buffer.pending = undefined;
136
+ if (buffer.chunks.length === 0 && buffer.pending === undefined)
137
+ this.logs.delete(key);
138
+ }
139
+ }
140
+ async mint(context, hash, upload) {
141
+ this.prune();
142
+ const lease = await this.options.leases.get(context.repo, context.leaseId);
143
+ if (lease?.status !== 'claimed')
144
+ throw new RackHubError('lease_ended', 'Rack lease is no longer claimed');
145
+ for (const cap of this.capabilities.values()) {
146
+ if (cap.repo === context.repo && cap.leaseId === context.leaseId && cap.hash === hash &&
147
+ !cap.revoked && cap.executionId === lease.attempt?.executionId && (cap.upload === undefined) === (upload === undefined)) {
148
+ if (upload !== undefined && Number(cap.upload.size) !== Number(upload.size))
149
+ throw new Error('Upload size changed');
150
+ if (cap.expires <= this.now()) {
151
+ cap.expires = this.now() + 15 * 60_000;
152
+ cap.abort = new AbortController();
153
+ }
154
+ return cap;
155
+ }
156
+ }
157
+ if (this.capabilities.size >= 65536)
158
+ throw new RackHubError('capacity', 'Rack data capability limit reached');
159
+ const cap = { ...context, id: randomUUID(), hash, executionId: lease.attempt?.executionId,
160
+ expires: this.now() + 15 * 60_000, revoked: false, abort: new AbortController(), ...(upload === undefined ? {} : { upload }) };
161
+ this.capabilities.set(cap.id, cap);
162
+ return cap;
163
+ }
164
+ url(cap) {
165
+ // Behind a relay or a TLS terminator the request's origin is the hub's
166
+ // own side of it, which the rack cannot reach (or reaches in clear).
167
+ const origin = this.options.advertiseOrigin() ?? this.origin.getStore();
168
+ if (origin === undefined)
169
+ throw new Error('Rack listener has no reachable origin');
170
+ return `${origin}/api/rack/data/${cap.id}`;
171
+ }
172
+ async readDescriptor(repo, hash) {
173
+ const context = this.options.coordinator.current(repo);
174
+ const session = this.options.session(repo, context.leaseId);
175
+ const head = await socketStream(session.dataSocket, 'HEAD', `/v1/objects/${hash}`, { bearer: session.dataToken });
176
+ head.resume();
177
+ if (head.statusCode !== 200)
178
+ throw new Error(`Rack input ${hash} is unavailable`);
179
+ const size = Number(head.headers['content-length']);
180
+ if (!Number.isSafeInteger(size) || size < 0)
181
+ throw new Error('Invalid input object length');
182
+ if (size > 4096)
183
+ return { kind: 'url', url: this.url(await this.mint(context, hash)) };
184
+ const response = await socketStream(session.dataSocket, 'GET', `/v1/objects/${hash}`, { bearer: session.dataToken });
185
+ const chunks = [];
186
+ let length = 0;
187
+ for await (const chunk of response) {
188
+ length += chunk.length;
189
+ if (length > 4096) {
190
+ response.destroy();
191
+ throw new Error('Inline input exceeds 4096 bytes');
192
+ }
193
+ chunks.push(chunk);
194
+ }
195
+ if (response.statusCode !== 200 || length !== size)
196
+ throw new Error('Input object read failed');
197
+ return { kind: 'inline', data: Buffer.concat(chunks) };
198
+ }
199
+ async writeTarget(repo, leaseId, hash, size) {
200
+ const context = { repo, leaseId };
201
+ if ((await this.post(context, '/v1/touch', TouchObjectsType, [hash], ArrayType(BooleanType)))[0])
202
+ return null;
203
+ const cap = await this.mint(context, hash, { leaseId, hash, size: BigInt(size), receipt: randomUUID() });
204
+ return { url: this.url(cap), headers: {} };
205
+ }
206
+ async commitWrite(repo, leaseId, rackId, hash, size, version) {
207
+ const lease = await this.options.leases.get(repo, leaseId);
208
+ if (lease?.status !== 'claimed' || lease.claimedBy !== rackId)
209
+ return false;
210
+ const cap = [...this.capabilities.values()].find((c) => c.repo === repo && c.leaseId === leaseId &&
211
+ c.hash === hash && c.executionId === lease.attempt?.executionId && c.upload?.receipt === version && Number(c.upload.size) === size);
212
+ if (cap?.upload === undefined || cap.revoked)
213
+ return false;
214
+ return this.post(cap, `/v1/uploads/${cap.id}/commit`, SessionUploadType, cap.upload, BooleanType);
215
+ }
216
+ async stagedTarget(repo, _workspace, hash, size) {
217
+ const context = this.options.coordinator.current(repo);
218
+ const cap = await this.mint(context, hash, { leaseId: context.leaseId, hash, size, receipt: randomUUID() });
219
+ return { url: this.url(cap), uploadId: cap.id };
220
+ }
221
+ async commitStaged(repo, hash, uploadId) {
222
+ const context = this.options.coordinator.current(repo);
223
+ const cap = this.capabilities.get(uploadId);
224
+ if (cap?.upload === undefined || cap.repo !== repo || cap.hash !== hash || cap.leaseId !== context.leaseId || !(await this.valid(cap)))
225
+ return false;
226
+ return this.post(cap, `/v1/uploads/${cap.id}/commit`, SessionUploadType, cap.upload, BooleanType);
227
+ }
228
+ async writeInline(repo, data) {
229
+ if (data.length > 4096)
230
+ throw new Error('Inline rack output exceeds 4096 bytes');
231
+ const context = this.options.coordinator.current(repo);
232
+ const session = this.options.session(repo, context.leaseId);
233
+ return socketRequest(session.dataSocket, 'POST', '/v1/objects', { bearer: session.dataToken, rawBody: data, responseType: StringType });
234
+ }
235
+ readEnvManifest() { return Promise.resolve(null); }
236
+ presignEnvLayer() { return Promise.reject(new Error('Environments are not supported by the local rack hub')); }
237
+ /** Aborts old-attempt streams immediately, before another session takes ownership. */
238
+ revoke(repo, leaseId) {
239
+ for (const cap of this.capabilities.values())
240
+ if (cap.repo === repo && cap.leaseId === leaseId) {
241
+ cap.revoked = true;
242
+ cap.abort.abort();
243
+ }
244
+ }
245
+ /** Stops streams to a departing session while retaining acknowledged receipts. */
246
+ interrupt(repo, leaseId) {
247
+ for (const cap of this.capabilities.values())
248
+ if (cap.repo === repo && cap.leaseId === leaseId) {
249
+ cap.abort.abort();
250
+ cap.abort = new AbortController();
251
+ }
252
+ }
253
+ async discardWrites(repo, leaseId, _hashes) {
254
+ this.revoke(repo, leaseId);
255
+ const caps = [...this.capabilities.values()].filter((cap) => cap.repo === repo && cap.leaseId === leaseId);
256
+ for (const cap of caps)
257
+ this.capabilities.delete(cap.id);
258
+ const results = await Promise.allSettled(caps.map(async (cap) => {
259
+ if (cap.upload !== undefined) {
260
+ const session = this.options.session(repo, leaseId);
261
+ await socketRequest(session.dataSocket, 'DELETE', `/v1/uploads/${cap.id}`, { bearer: session.dataToken, responseType: BooleanType });
262
+ }
263
+ }));
264
+ const failure = results.find((result) => result.status === 'rejected');
265
+ if (failure?.status === 'rejected')
266
+ throw failure.reason;
267
+ return caps.filter((cap) => cap.upload !== undefined).length;
268
+ }
269
+ prune() {
270
+ for (const cap of this.capabilities.values())
271
+ if (cap.expires <= this.now()) {
272
+ cap.abort.abort();
273
+ // Keep upload receipts until lease cleanup so acknowledged uploads can
274
+ // still be committed after their URL expires; deny any further PUT.
275
+ if (cap.upload === undefined)
276
+ this.capabilities.delete(cap.id);
277
+ }
278
+ }
279
+ async valid(cap) {
280
+ if (cap.revoked || cap.abort.signal.aborted || cap.expires <= this.now())
281
+ return false;
282
+ const lease = await this.options.leases.get(cap.repo, cap.leaseId);
283
+ return lease?.status === 'claimed' && lease.attempt?.executionId === cap.executionId;
284
+ }
285
+ /**
286
+ * Streams data URLs with backpressure and no rack bearer header. A transfer
287
+ * may take the rest of its capability's fifteen minutes, so a large object
288
+ * over a slow link is not cut short of the time its URL grants.
289
+ */
290
+ dataRoutes() {
291
+ const app = new Hono();
292
+ app.on(['GET', 'PUT'], '/api/rack/data/:cap', async (c) => {
293
+ if (c.req.header('authorization') !== undefined)
294
+ return c.text('Capability URLs do not accept Authorization', 400);
295
+ const cap = this.capabilities.get(c.req.param('cap'));
296
+ if (cap === undefined || !await this.valid(cap) || (cap.upload === undefined ? 'GET' : 'PUT') !== c.req.method)
297
+ return c.notFound();
298
+ let session;
299
+ try {
300
+ session = this.options.session(cap.repo, cap.leaseId);
301
+ }
302
+ catch {
303
+ return c.text('No live repository session', 410);
304
+ }
305
+ const remaining = Math.max(1, cap.expires - this.now());
306
+ const signal = AbortSignal.any([cap.abort.signal, c.req.raw.signal, AbortSignal.timeout(remaining)]);
307
+ const upload = cap.upload;
308
+ const path = upload === undefined ? `/v1/objects/${cap.hash}` : `/v1/uploads/${cap.id}?${new URLSearchParams({
309
+ lease: cap.leaseId, hash: cap.hash, size: `${upload.size}`, receipt: upload.receipt,
310
+ })}`;
311
+ const response = await socketStream(session.dataSocket, c.req.method, path, {
312
+ bearer: session.dataToken, signal, timeoutMs: remaining,
313
+ ...(upload === undefined ? {} : { rawBody: c.req.raw.body === null ? new Uint8Array() : Readable.fromWeb(c.req.raw.body) }),
314
+ });
315
+ const headers = new Headers({ 'content-type': 'application/octet-stream' });
316
+ for (const name of ['content-length', 'x-amz-version-id']) {
317
+ const value = response.headers[name];
318
+ if (typeof value === 'string')
319
+ headers.set(name, value);
320
+ }
321
+ return new Response(Readable.toWeb(response), { status: response.statusCode ?? 502, headers });
322
+ });
323
+ return app;
324
+ }
325
+ /** Aborts outstanding transfers and clears unreferenced log timers on shutdown. */
326
+ close() {
327
+ for (const cap of this.capabilities.values())
328
+ cap.abort.abort();
329
+ for (const buffer of this.logs.values())
330
+ if (buffer.timer !== undefined)
331
+ clearTimeout(buffer.timer);
332
+ this.capabilities.clear();
333
+ this.logs.clear();
334
+ }
335
+ }
336
+ //# sourceMappingURL=session-bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-bridge.js","sourceRoot":"","sources":["../../../src/hub/session-bridge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Copyright (c) 2025 Elara AI Pty Ltd
3
+ * Licensed under BSL 1.1. See LICENSE for details.
4
+ */
5
+ import { InMemoryMachineIdentityStore, type IdentityFile } from './in-memory-identity-store.js';
6
+ /**
7
+ * Persists machine credentials atomically, with one writer under the hub lock.
8
+ * Failed writes leave memory unchanged; corrupt files fail closed.
9
+ * @example
10
+ * const identities = new FileMachineIdentityStore(rackHome());
11
+ */
12
+ export declare class FileMachineIdentityStore extends InMemoryMachineIdentityStore {
13
+ private readonly file;
14
+ /** @param home - The private rack home owned by this hub */
15
+ constructor(home: string);
16
+ protected persist(state: IdentityFile): Promise<void>;
17
+ }
18
+ //# sourceMappingURL=file-identity-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-identity-store.d.ts","sourceRoot":"","sources":["../../../src/identity/file-identity-store.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,EAAoB,4BAA4B,EAAE,KAAK,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAElH;;;;;GAKG;AACH,qBAAa,wBAAyB,SAAQ,4BAA4B;IACxE,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAE9B,4DAA4D;gBAChD,IAAI,EAAE,MAAM;cAWL,OAAO,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;CAG/D"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Copyright (c) 2025 Elara AI Pty Ltd
3
+ * Licensed under BSL 1.1. See LICENSE for details.
4
+ */
5
+ import { join } from 'node:path';
6
+ import { decodeBeast2For, encodeBeast2For } from '@elaraai/east';
7
+ import { ensureRackHome, IDENTITY_FILE } from '../paths.js';
8
+ import { readStateFile, writeStateFile } from '../state-file.js';
9
+ import { IdentityFileType, InMemoryMachineIdentityStore } from './in-memory-identity-store.js';
10
+ /**
11
+ * Persists machine credentials atomically, with one writer under the hub lock.
12
+ * Failed writes leave memory unchanged; corrupt files fail closed.
13
+ * @example
14
+ * const identities = new FileMachineIdentityStore(rackHome());
15
+ */
16
+ export class FileMachineIdentityStore extends InMemoryMachineIdentityStore {
17
+ file;
18
+ /** @param home - The private rack home owned by this hub */
19
+ constructor(home) {
20
+ super();
21
+ this.file = join(ensureRackHome(home), IDENTITY_FILE);
22
+ this.ready = readStateFile(this.file).then((bytes) => {
23
+ if (bytes !== null)
24
+ this.state = decodeBeast2For(IdentityFileType)(bytes);
25
+ });
26
+ // Loading starts at construction; the first operation still receives
27
+ // its failure, even if the caller has not made one yet.
28
+ void this.ready.catch(() => undefined);
29
+ }
30
+ persist(state) {
31
+ return writeStateFile(this.file, encodeBeast2For(IdentityFileType)(state));
32
+ }
33
+ }
34
+ //# sourceMappingURL=file-identity-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-identity-store.js","sourceRoot":"","sources":["../../../src/identity/file-identity-store.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,4BAA4B,EAAqB,MAAM,+BAA+B,CAAC;AAElH;;;;;GAKG;AACH,MAAM,OAAO,wBAAyB,SAAQ,4BAA4B;IACvD,IAAI,CAAS;IAE9B,4DAA4D;IAC5D,YAAY,IAAY;QACtB,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,aAAa,CAAC,CAAC;QACtD,IAAI,CAAC,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE;YACnD,IAAI,KAAK,KAAK,IAAI;gBAAE,IAAI,CAAC,KAAK,GAAG,eAAe,CAAC,gBAAgB,CAAC,CAAC,KAAK,CAAC,CAAC;QAC5E,CAAC,CAAC,CAAC;QACH,qEAAqE;QACrE,wDAAwD;QACxD,KAAK,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IACzC,CAAC;IAEkB,OAAO,CAAC,KAAmB;QAC5C,OAAO,cAAc,CAAC,IAAI,CAAC,IAAI,EAAE,eAAe,CAAC,gBAAgB,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IAC7E,CAAC;CACF"}
@@ -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=file-identity-store.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-identity-store.spec.d.ts","sourceRoot":"","sources":["../../../src/identity/file-identity-store.spec.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Copyright (c) 2025 Elara AI Pty Ltd
3
+ * Licensed under BSL 1.1. See LICENSE for details.
4
+ */
5
+ import { it } from 'node:test';
6
+ import assert from 'node:assert/strict';
7
+ import { mkdtemp, rm, stat, writeFile } from 'node:fs/promises';
8
+ import { tmpdir } from 'node:os';
9
+ import { join } from 'node:path';
10
+ import { decodeBeast2For } from '@elaraai/east';
11
+ import { FileMachineIdentityStore } from './file-identity-store.js';
12
+ import { IdentityFileType } from './in-memory-identity-store.js';
13
+ import { generateEnrollmentToken } from './machine-identity.js';
14
+ import { readStateFile } from '../state-file.js';
15
+ it('consumes an enrollment once across racing callers and reload, with private atomic state', async (t) => {
16
+ const home = await mkdtemp(join(tmpdir(), 'e3r-'));
17
+ t.after(() => rm(home, { recursive: true, force: true }));
18
+ const store = new FileMachineIdentityStore(home);
19
+ const { tokenHash } = generateEnrollmentToken();
20
+ await store.putEnrollmentToken(tokenHash, new Date(Date.now() + 60_000));
21
+ const results = await Promise.all(Array.from({ length: 12 }, () => store.consumeEnrollmentToken(tokenHash, new Date())));
22
+ assert.equal(results.filter(Boolean).length, 1);
23
+ assert.equal(await new FileMachineIdentityStore(home).consumeEnrollmentToken(tokenHash, new Date()), false);
24
+ const file = join(home, 'identity.beast2');
25
+ await writeFile(`${file}.crash.partial`, 'incomplete');
26
+ await store.putRackToken('one', 'rack', new Date(), 'current');
27
+ assert.equal((await new FileMachineIdentityStore(home).getRackToken('one'))?.rackId, 'rack');
28
+ if (process.platform !== 'win32') {
29
+ assert.equal((await stat(home)).mode & 0o777, 0o700);
30
+ assert.equal((await stat(file)).mode & 0o777, 0o600);
31
+ }
32
+ });
33
+ it('rejects and prunes expired enrollment tokens and retains rotation grace across reload', async (t) => {
34
+ const home = await mkdtemp(join(tmpdir(), 'e3r-'));
35
+ t.after(() => rm(home, { recursive: true, force: true }));
36
+ const store = new FileMachineIdentityStore(home);
37
+ await store.putEnrollmentToken('expired', new Date(0));
38
+ assert.equal(await store.consumeEnrollmentToken('expired', new Date()), false);
39
+ const bytes = await readStateFile(join(home, 'identity.beast2'));
40
+ assert.ok(bytes);
41
+ assert.equal(decodeBeast2For(IdentityFileType)(bytes).enrollmentTokens.size, 0);
42
+ await store.putRackToken('old', 'rack', new Date(), 'current');
43
+ await store.putRackToken('next', 'rack', new Date(), 'pending');
44
+ const cutoff = new Date(Date.now() + 60_000);
45
+ await store.promoteRackToken('rack', 'next', cutoff);
46
+ const reload = new FileMachineIdentityStore(home);
47
+ assert.equal((await reload.getRackToken('next'))?.status, 'current');
48
+ assert.deepEqual((await reload.getRackToken('old'))?.graceUntil, cutoff);
49
+ await reload.putRackToken('third', 'rack', new Date(), 'pending');
50
+ await reload.promoteRackToken('rack', 'third', new Date(cutoff.getTime() + 60_000));
51
+ assert.deepEqual((await reload.getRackToken('old'))?.graceUntil, cutoff, 'existing grace is never extended');
52
+ });
53
+ it('fails closed on a corrupt identity file', async (t) => {
54
+ const home = await mkdtemp(join(tmpdir(), 'e3r-'));
55
+ t.after(() => rm(home, { recursive: true, force: true }));
56
+ await writeFile(join(home, 'identity.beast2'), 'broken');
57
+ await assert.rejects(new FileMachineIdentityStore(home).getRackToken('anything'));
58
+ });
59
+ //# sourceMappingURL=file-identity-store.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-identity-store.spec.js","sourceRoot":"","sources":["../../../src/identity/file-identity-store.spec.ts"],"names":[],"mappings":"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"}