@wenathlan/saddle 2.0.150 → 2.0.152

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 (296) hide show
  1. package/Dockerfile +43 -23
  2. package/README.md +4 -4
  3. package/biome.jsonc +134 -0
  4. package/dist/App.d.ts +3 -0
  5. package/dist/App.js +50 -0
  6. package/dist/Sol/Sol.d.ts +3 -0
  7. package/dist/Sol/Sol.js +46 -0
  8. package/dist/Sol/agentbrowser/agentbrowser.d.ts +9 -0
  9. package/dist/Sol/agentbrowser/agentbrowser.js +121 -0
  10. package/dist/Sol/architecture/architecture.d.ts +9 -0
  11. package/dist/Sol/architecture/architecture.js +93 -0
  12. package/dist/Sol/compute/compute.d.ts +9 -0
  13. package/dist/Sol/compute/compute.js +22 -0
  14. package/dist/Sol/console/console.d.ts +15 -0
  15. package/dist/Sol/console/console.js +292 -0
  16. package/dist/Sol/dashboard/dashboard.d.ts +10 -0
  17. package/dist/Sol/dashboard/dashboard.js +181 -0
  18. package/dist/Sol/docs/docs.d.ts +9 -0
  19. package/dist/Sol/docs/docs.js +28 -0
  20. package/dist/Sol/home/home.d.ts +35 -0
  21. package/dist/Sol/home/home.js +239 -0
  22. package/dist/Sol/integrations/integrations.d.ts +9 -0
  23. package/dist/Sol/integrations/integrations.js +83 -0
  24. package/dist/Sol/intro/intro.d.ts +7 -0
  25. package/dist/Sol/intro/intro.js +102 -0
  26. package/dist/Sol/login/login.d.ts +16 -0
  27. package/dist/Sol/login/login.js +49 -0
  28. package/dist/Sol/notfound/notfound.d.ts +9 -0
  29. package/dist/Sol/notfound/notfound.js +15 -0
  30. package/dist/Sol/onboarding/onboarding.d.ts +25 -0
  31. package/dist/Sol/onboarding/onboarding.js +60 -0
  32. package/dist/Sol/playground/playground.d.ts +9 -0
  33. package/dist/Sol/playground/playground.js +127 -0
  34. package/dist/Sol/register/register.d.ts +16 -0
  35. package/dist/Sol/register/register.js +193 -0
  36. package/dist/Sol/shell/Shell.d.ts +124 -0
  37. package/dist/Sol/shell/Shell.js +335 -0
  38. package/dist/Sol/wingets/ComputeBootladder.d.ts +6 -0
  39. package/dist/Sol/wingets/ComputeBootladder.js +71 -0
  40. package/dist/Sol/wingets/ComputeModalitycatalog.d.ts +7 -0
  41. package/dist/Sol/wingets/ComputeModalitycatalog.js +71 -0
  42. package/dist/Sol/wingets/ConsoleApibadge.d.ts +7 -0
  43. package/dist/Sol/wingets/ConsoleApibadge.js +5 -0
  44. package/dist/Sol/wingets/ConsoleEdgelaw.d.ts +6 -0
  45. package/dist/Sol/wingets/ConsoleEdgelaw.js +36 -0
  46. package/dist/Sol/wingets/ConsoleEventstimeline.d.ts +19 -0
  47. package/dist/Sol/wingets/ConsoleEventstimeline.js +12 -0
  48. package/dist/Sol/wingets/ConsoleSnapshotcontrols.d.ts +8 -0
  49. package/dist/Sol/wingets/ConsoleSnapshotcontrols.js +8 -0
  50. package/dist/Sol/wingets/ConsoleSpecpanel.d.ts +21 -0
  51. package/dist/Sol/wingets/ConsoleSpecpanel.js +75 -0
  52. package/dist/Sol/wingets/ConsoleTerminal.d.ts +22 -0
  53. package/dist/Sol/wingets/ConsoleTerminal.js +77 -0
  54. package/dist/Sol/wingets/DashboardAccountpanel.d.ts +18 -0
  55. package/dist/Sol/wingets/DashboardAccountpanel.js +65 -0
  56. package/dist/Sol/wingets/DashboardAdminpanel.d.ts +6 -0
  57. package/dist/Sol/wingets/DashboardAdminpanel.js +90 -0
  58. package/dist/Sol/wingets/DashboardAuditlog.d.ts +8 -0
  59. package/dist/Sol/wingets/DashboardAuditlog.js +50 -0
  60. package/dist/Sol/wingets/DashboardCreatesandboxform.d.ts +10 -0
  61. package/dist/Sol/wingets/DashboardCreatesandboxform.js +71 -0
  62. package/dist/Sol/wingets/DashboardEventstimeline.d.ts +8 -0
  63. package/dist/Sol/wingets/DashboardEventstimeline.js +83 -0
  64. package/dist/Sol/wingets/DashboardGlobalsandboxestable.d.ts +8 -0
  65. package/dist/Sol/wingets/DashboardGlobalsandboxestable.js +57 -0
  66. package/dist/Sol/wingets/DashboardMeshnodestable.d.ts +8 -0
  67. package/dist/Sol/wingets/DashboardMeshnodestable.js +103 -0
  68. package/dist/Sol/wingets/DashboardSandboxlist.d.ts +10 -0
  69. package/dist/Sol/wingets/DashboardSandboxlist.js +163 -0
  70. package/dist/Sol/wingets/DashboardUserstable.d.ts +8 -0
  71. package/dist/Sol/wingets/DashboardUserstable.js +78 -0
  72. package/dist/Sol/wingets/HomeEntrygate.d.ts +23 -0
  73. package/dist/Sol/wingets/HomeEntrygate.js +73 -0
  74. package/dist/Sol/wingets/HomeMetricstrip.d.ts +9 -0
  75. package/dist/Sol/wingets/HomeMetricstrip.js +4 -0
  76. package/dist/Sol/wingets/HomeRuntimediagram.d.ts +1 -0
  77. package/dist/Sol/wingets/HomeRuntimediagram.js +22 -0
  78. package/dist/Sol/wingets/LoginForm.d.ts +19 -0
  79. package/dist/Sol/wingets/LoginForm.js +141 -0
  80. package/dist/Sol/wingets/RegisterPasswordconfirm.d.ts +13 -0
  81. package/dist/Sol/wingets/RegisterPasswordconfirm.js +16 -0
  82. package/dist/Sol/wingets/RegisterPasswordmeter.d.ts +19 -0
  83. package/dist/Sol/wingets/RegisterPasswordmeter.js +65 -0
  84. package/dist/Sol/wingets/RegisterUsernamerules.d.ts +11 -0
  85. package/dist/Sol/wingets/RegisterUsernamerules.js +16 -0
  86. package/dist/Sol/wingets/index.d.ts +39 -0
  87. package/dist/Sol/wingets/index.js +39 -0
  88. package/dist/alternatives.d.ts +508 -0
  89. package/dist/alternatives.js +950 -0
  90. package/dist/api.d.ts +154 -0
  91. package/dist/api.js +174 -0
  92. package/dist/auth.d.ts +268 -0
  93. package/dist/auth.js +476 -0
  94. package/dist/automation.d.ts +425 -0
  95. package/dist/automation.js +566 -0
  96. package/dist/browser.d.ts +561 -0
  97. package/dist/browser.js +1058 -0
  98. package/dist/capacitor.config.d.ts +45 -0
  99. package/dist/capacitor.config.js +48 -0
  100. package/dist/catalog.d.ts +43 -0
  101. package/dist/catalog.js +44 -0
  102. package/dist/cli.d.ts +16 -0
  103. package/dist/cli.js +168 -0
  104. package/dist/communication.d.ts +181 -0
  105. package/dist/communication.js +376 -0
  106. package/dist/compute.d.ts +1160 -0
  107. package/dist/compute.js +2272 -0
  108. package/dist/dashboardlib.d.ts +58 -0
  109. package/dist/dashboardlib.js +161 -0
  110. package/dist/db.d.ts +569 -0
  111. package/dist/db.js +1080 -0
  112. package/dist/distribution.d.ts +528 -0
  113. package/dist/distribution.js +810 -0
  114. package/dist/drizzle.config.d.ts +20 -0
  115. package/dist/drizzle.config.js +18 -0
  116. package/dist/edgelandes.d.ts +70 -0
  117. package/dist/edgelandes.host.d.ts +26 -0
  118. package/dist/edgelandes.host.js +1089 -0
  119. package/dist/edgelandes.inproc.d.ts +27 -0
  120. package/dist/edgelandes.inproc.js +952 -0
  121. package/dist/edgelandes.js +1133 -0
  122. package/dist/edgelandes.ninety.d.ts +31 -0
  123. package/dist/edgelandes.ninety.js +174 -0
  124. package/dist/edgelandes.virtual.d.ts +25 -0
  125. package/dist/edgelandes.virtual.js +629 -0
  126. package/dist/edgeplane.d.ts +114 -0
  127. package/dist/edgeplane.js +305 -0
  128. package/dist/examplelocaljob.d.ts +1 -0
  129. package/{docs/example-localjob.ts → dist/examplelocaljob.js} +0 -1
  130. package/dist/execution.d.ts +529 -0
  131. package/dist/execution.js +760 -0
  132. package/dist/family.d.ts +16 -0
  133. package/dist/family.js +42 -0
  134. package/dist/familyurl.d.ts +21 -0
  135. package/dist/familyurl.js +24 -0
  136. package/dist/format.d.ts +18 -0
  137. package/dist/format.js +59 -0
  138. package/dist/foundation.d.ts +172 -0
  139. package/dist/foundation.js +312 -0
  140. package/dist/index.d.ts +735 -0
  141. package/dist/index.js +1020 -0
  142. package/dist/integration.d.ts +235 -0
  143. package/dist/integration.js +228 -0
  144. package/dist/intelligence.d.ts +93 -0
  145. package/dist/intelligence.js +144 -0
  146. package/dist/isolation.d.ts +128 -0
  147. package/dist/isolation.js +95 -0
  148. package/dist/labels.d.ts +665 -0
  149. package/dist/labels.js +751 -0
  150. package/dist/localauth.d.ts +127 -0
  151. package/dist/localauth.js +413 -0
  152. package/dist/media.d.ts +343 -0
  153. package/dist/media.js +985 -0
  154. package/dist/mesh.d.ts +177 -0
  155. package/dist/mesh.js +468 -0
  156. package/dist/modalityregistry.d.ts +109 -0
  157. package/dist/modalityregistry.js +1386 -0
  158. package/dist/modes.d.ts +358 -0
  159. package/dist/modes.js +133 -0
  160. package/dist/onboardingstate.d.ts +79 -0
  161. package/dist/onboardingstate.js +282 -0
  162. package/dist/orchestrator.d.ts +1338 -0
  163. package/dist/orchestrator.js +4310 -0
  164. package/dist/packager/targetcli.js +4 -0
  165. package/dist/paths.d.ts +2 -0
  166. package/dist/paths.js +14 -0
  167. package/dist/performance.d.ts +415 -0
  168. package/dist/performance.js +826 -0
  169. package/dist/quantum.d.ts +809 -0
  170. package/dist/quantum.js +2072 -0
  171. package/dist/render.d.ts +380 -0
  172. package/dist/render.js +1072 -0
  173. package/dist/runall.d.ts +2 -0
  174. package/dist/runall.js +39 -0
  175. package/dist/runtimeprobe.d.ts +4 -0
  176. package/dist/runtimeprobe.js +8 -0
  177. package/dist/sandbox.d.ts +355 -0
  178. package/dist/sandbox.js +1721 -0
  179. package/dist/sandboxenvprofile.d.ts +29 -0
  180. package/dist/sandboxenvprofile.js +89 -0
  181. package/dist/sandboxprofile.d.ts +137 -0
  182. package/dist/sandboxprofile.js +241 -0
  183. package/dist/sandboxqueue.d.ts +108 -0
  184. package/dist/sandboxqueue.js +201 -0
  185. package/dist/sandboxquota.d.ts +121 -0
  186. package/dist/sandboxquota.js +236 -0
  187. package/dist/scheduler.d.ts +595 -0
  188. package/dist/scheduler.js +1258 -0
  189. package/dist/scriptlanes.d.ts +182 -0
  190. package/dist/scriptlanes.js +660 -0
  191. package/dist/scripts.buildengine.d.ts +1 -0
  192. package/dist/scripts.buildengine.js +39 -0
  193. package/dist/scripts.securitygate.d.ts +1 -0
  194. package/dist/scripts.securitygate.js +68 -0
  195. package/dist/scripts.validateflatnative.d.ts +1 -0
  196. package/dist/scripts.validateflatnative.js +72 -0
  197. package/dist/security.d.ts +500 -0
  198. package/dist/security.js +1169 -0
  199. package/dist/seedcatalog.d.ts +13 -0
  200. package/dist/seedcatalog.js +46 -0
  201. package/dist/server.d.ts +20 -0
  202. package/dist/server.js +53 -0
  203. package/dist/speccatalog.d.ts +31 -0
  204. package/dist/speccatalog.js +163 -0
  205. package/dist/tiers.d.ts +1001 -0
  206. package/dist/tiers.js +2169 -0
  207. package/dist/utils.d.ts +2 -0
  208. package/dist/utils.js +5 -0
  209. package/dist/vaulthandoff.d.ts +141 -0
  210. package/dist/vaulthandoff.js +444 -0
  211. package/dist/virtual.d.ts +640 -0
  212. package/dist/virtual.js +1284 -0
  213. package/dist/virtualcpu.d.ts +315 -0
  214. package/dist/virtualcpu.js +1180 -0
  215. package/dist/virtualgpu.d.ts +344 -0
  216. package/dist/virtualgpu.js +1000 -0
  217. package/dist/virtualization.d.ts +606 -0
  218. package/dist/virtualization.js +1186 -0
  219. package/dist/virtualmemory.d.ts +817 -0
  220. package/dist/virtualmemory.js +1795 -0
  221. package/dist/vite.config.d.ts +3 -0
  222. package/dist/vite.config.js +43 -0
  223. package/dist/vitest.config.d.ts +2 -0
  224. package/dist/vitest.config.js +25 -0
  225. package/dist/webserver.d.ts +166 -0
  226. package/dist/webserver.js +2226 -0
  227. package/dist/workloadlaw.d.ts +133 -0
  228. package/dist/workloadlaw.js +193 -0
  229. package/docker.config +13 -6
  230. package/docs/artifactavailability.md +2 -2
  231. package/docs/{containerplatforms-2.1.0.md → containerplatforms.2.1.0.md} +1 -1
  232. package/docs/{documentmap-1.8.12.md → documentmap.1.8.12.md} +1 -1
  233. package/docs/ninety.modalities.md +108 -0
  234. package/docs/planning.ninety.md +391 -0
  235. package/docs/{releasenotes-1.8.13.md → releasenotes.1.8.13.md} +2 -2
  236. package/docs/{releasenotes-1.8.14.md → releasenotes.1.8.14.md} +1 -1
  237. package/docs/{repo-synthesis-1.8.16.md → repo.synthesis.1.8.16.md} +1 -1
  238. package/docs/{todo-1.8.15.md → todo.1.8.15.md} +1 -1
  239. package/docs/{todo-1.8.16.md → todo.1.8.16.md} +5 -5
  240. package/docs/todo.md +2 -2
  241. package/docs/workloadlaw.md +104 -0
  242. package/index.ts +4 -3
  243. package/manifest.json +1 -1
  244. package/modalityregistry.ts +1493 -0
  245. package/orchestrator.ts +1 -1
  246. package/package.json +76 -37
  247. package/docs/CONVERSA.txt +0 -1523
  248. package/docs/architecture-1.8.19.png +0 -0
  249. package/docs/assets/architecture.svg +0 -45
  250. package/docs/assets/saddle-browser-trace.webp +0 -0
  251. package/docs/assets/saddle-hero-bridge.webp +0 -0
  252. package/docs/assets/saddle-mark.webp +0 -0
  253. package/docs/assets/saddle-runtime-map.webp +0 -0
  254. package/docs/assets/saddlemark.svg +0 -13
  255. package/docs/example-publicapi.ts +0 -7
  256. package/docs/saddle.archive.1.8.17.tar.gz.gpg +0 -0
  257. /package/docs/{architecture-1.8.19.md → architecture.1.8.19.md} +0 -0
  258. /package/docs/{architecture-1.8.19.mmd → architecture.1.8.19.mmd} +0 -0
  259. /package/docs/{artifactresearch-1.8.9.md → artifactresearch.1.8.9.md} +0 -0
  260. /package/docs/{batches-1816-1820.md → batches.1816.to.1820.md} +0 -0
  261. /package/docs/{brancharchive-2026-08-18.md → brancharchive.2026.08.18.md} +0 -0
  262. /package/docs/{containerplatforms-1.8.17.md → containerplatforms.1.8.17.md} +0 -0
  263. /package/docs/{e2ugh-engine.md → e2ugh.engine.md} +0 -0
  264. /package/docs/{flatbuildresearch-1.8.11.md → flatbuildresearch.1.8.11.md} +0 -0
  265. /package/docs/{licenseaudit-1.8.12.md → licenseaudit.1.8.12.md} +0 -0
  266. /package/docs/{mobileconversionresearch-1.8.10.md → mobileconversionresearch.1.8.10.md} +0 -0
  267. /package/docs/{native-wrappers.md → native.wrappers.md} +0 -0
  268. /package/docs/{nativeidentityresearch-1.8.11.md → nativeidentityresearch.1.8.11.md} +0 -0
  269. /package/docs/{releasenotes-1.8.12.md → releasenotes.1.8.12.md} +0 -0
  270. /package/docs/{releasenotes-1.8.15.md → releasenotes.1.8.15.md} +0 -0
  271. /package/docs/{releasenotes-1.8.16.md → releasenotes.1.8.16.md} +0 -0
  272. /package/docs/{releasenotes-1.8.17.md → releasenotes.1.8.17.md} +0 -0
  273. /package/docs/{releasenotes-1.8.18.md → releasenotes.1.8.18.md} +0 -0
  274. /package/docs/{releasenotes-1.8.19.md → releasenotes.1.8.19.md} +0 -0
  275. /package/docs/{reorganization-1.8.8.md → reorganization.1.8.8.md} +0 -0
  276. /package/docs/{reorganization-1.8.9.md → reorganization.1.8.9.md} +0 -0
  277. /package/docs/{repo-analysis-1.8.21.json → repo.analysis.1.8.21.json} +0 -0
  278. /package/docs/{repo-candidates-1.8.21.jsonl → repo.candidates.1.8.21.jsonl} +0 -0
  279. /package/docs/{repo-research-1.8.16.md → repo.research.1.8.16.md} +0 -0
  280. /package/docs/{repo-research-1.8.21.md → repo.research.1.8.21.md} +0 -0
  281. /package/docs/{repo-selected-1.8.21.json → repo.selected.1.8.21.json} +0 -0
  282. /package/docs/{repo-synthesis-1.8.21.md → repo.synthesis.1.8.21.md} +0 -0
  283. /package/docs/{research-1.8.18-isolation.md → research.1.8.18.isolation.md} +0 -0
  284. /package/docs/{research-1.8.19-virtual-browser.md → research.1.8.19.virtual.browser.md} +0 -0
  285. /package/docs/{scanningresearch-1.8.11.md → scanningresearch.1.8.11.md} +0 -0
  286. /package/docs/{securityaudit-1.8.7.md → securityaudit.1.8.7.md} +0 -0
  287. /package/docs/{securityreferences-1.8.11.md → securityreferences.1.8.11.md} +0 -0
  288. /package/docs/{securityresearch-1.8.11.md → securityresearch.1.8.11.md} +0 -0
  289. /package/docs/{signingresearch-1.8.12.md → signingresearch.1.8.12.md} +0 -0
  290. /package/docs/{tauri-glib-risk-1.8.11.md → tauri.glib.risk.1.8.11.md} +0 -0
  291. /package/docs/{todo-1.8.18.md → todo.1.8.18.md} +0 -0
  292. /package/docs/{todo-1.8.19.md → todo.1.8.19.md} +0 -0
  293. /package/docs/{toolchainresearch-1.8.9.md → toolchainresearch.1.8.9.md} +0 -0
  294. /package/docs/{web-duplicatecleanup.md → web.duplicatecleanup.md} +0 -0
  295. /package/docs/{web-ideas.md → web.ideas.md} +0 -0
  296. /package/docs/{workflowimprovements-2026-08-19.md → workflowimprovements.2026.08.19.md} +0 -0
package/dist/api.d.ts ADDED
@@ -0,0 +1,154 @@
1
+ /**
2
+ * shared api client for the React interface: one typed module replaces
3
+ * the three copies of apibase()/iscrossorigin()/apifetch() that lived
4
+ * inside login.js, register.js and dashboard.js. the api base resolves
5
+ * from the ?api= query string (persisted for convenience), the
6
+ * window.SADDLE_API global, the "saddle_api" localstorage key or
7
+ * same-origin; cross-origin targets switch to credentials: "include"
8
+ * so the saddlesession cookie still flows. json in, json out, family
9
+ * error handling.
10
+ */
11
+ /** standardized error payload answered by every /api/v1 endpoint. */
12
+ export type ApiErrorBody = {
13
+ error: {
14
+ code: string;
15
+ message: string;
16
+ };
17
+ };
18
+ /** health endpoint payload (/api/v1/health). */
19
+ export type HealthStatus = {
20
+ ok: boolean;
21
+ version: string;
22
+ uptime: number;
23
+ };
24
+ /** sandbox creation request carried by POST /api/v1/sandboxes. */
25
+ export type SandboxSpecPayload = {
26
+ model: string;
27
+ vcpus: number;
28
+ ramgb: number;
29
+ gpu: string;
30
+ mig: string;
31
+ quotamb?: number;
32
+ };
33
+ /** sandbox creation response. */
34
+ export type SandboxCreated = {
35
+ id: string;
36
+ state: string;
37
+ };
38
+ /** exec response shared by the api and the local engine dispatcher. */
39
+ export type ExecResult = {
40
+ output: string;
41
+ exitCode: number;
42
+ };
43
+ /** session payload answered by the auth endpoints (/api/v1/auth/*). */
44
+ export type ApiSession = {
45
+ user: string;
46
+ role: "admin" | "user";
47
+ issuedat: string;
48
+ expiresat: string;
49
+ };
50
+ declare global {
51
+ interface Window {
52
+ /** optional api base override for static-edge deployments. */
53
+ SADDLE_API?: string;
54
+ }
55
+ }
56
+ /**
57
+ * resolves the api base: ?api= wins (and persists), then the window
58
+ * global, then localstorage, then same origin ("").
59
+ *
60
+ * @returns the trimmed api base ("" means same-origin).
61
+ */
62
+ export declare function apibase(): string;
63
+ /**
64
+ * checks whether the api base points at a different origin.
65
+ *
66
+ * @param base the resolved api base ("" means same-origin).
67
+ * @returns true when the target is cross-origin.
68
+ */
69
+ export declare function iscrossorigin(base: string): boolean;
70
+ /**
71
+ * fetch wrapper: json in, json out, credentials follow the origin.
72
+ * sets the content-type automatically whenever a body is present and
73
+ * switches to credentials: "include" for cross-origin bases so the
74
+ * session cookie still reaches the api.
75
+ *
76
+ * @param path the api path (must start with "/").
77
+ * @param init the fetch init; body must already be a json string.
78
+ * @returns the raw response for ok/status handling by the caller.
79
+ */
80
+ export declare function apifetch(path: string, init?: RequestInit): Promise<Response>;
81
+ /**
82
+ * probes the api health endpoint with a short timeout.
83
+ *
84
+ * @param base the api base to probe ("" means same-origin).
85
+ * @param timeoutms abort budget in milliseconds.
86
+ * @returns the health payload when the api answers ok, null otherwise.
87
+ */
88
+ export declare function fetchhealth(base: string, timeoutms?: number): Promise<HealthStatus | null>;
89
+ /**
90
+ * reads the {error:{code,message}} payload the family endpoints answer
91
+ * with; returns null when the body is not a recognizable error object.
92
+ *
93
+ * @param response a non-ok api response.
94
+ * @returns the error message when present, null otherwise.
95
+ */
96
+ export declare function apierrormessage(response: Response): Promise<string | null>;
97
+ /**
98
+ * human label for the resolved api base, used by the auth pages status
99
+ * line ("api: same origin" or the cross-origin target with credentials).
100
+ *
101
+ * @returns the status line text.
102
+ */
103
+ export declare function apistatustext(): string;
104
+ /** one modality record of the compute plan (the modalities endpoint). */
105
+ export type ModalityRecord = {
106
+ id: string;
107
+ label: string;
108
+ family: string;
109
+ isolation: string;
110
+ state: "live" | "host" | "planned";
111
+ drivers: string[];
112
+ hardening: string[];
113
+ boot: "hot" | "warm" | "cold";
114
+ bootms: number;
115
+ gpu: boolean;
116
+ network: boolean;
117
+ mime: string;
118
+ };
119
+ /** the modality plan payload answered by the modalities endpoint. */
120
+ export type ModalityPlan = {
121
+ plancount: number;
122
+ live: number;
123
+ host: number;
124
+ planned: number;
125
+ records: ModalityRecord[];
126
+ };
127
+ /** one edge lane of the sandbox modality surface (the edge lanes endpoint). */
128
+ export type EdgeLane = {
129
+ modality: string;
130
+ kind: "live" | "host";
131
+ runtime: string;
132
+ probed: boolean;
133
+ present: boolean;
134
+ };
135
+ /**
136
+ * fetches the compute modality plan with a short timeout: the counts and
137
+ * the records the compute face renders. the surface never invents rows —
138
+ * when the engine does not answer the caller renders the honest line.
139
+ *
140
+ * @param base the api base to query ("" means same-origin).
141
+ * @param timeoutms abort budget in milliseconds.
142
+ * @returns the plan payload when the api answers ok, null otherwise.
143
+ */
144
+ export declare function fetchmodalities(base?: string, timeoutms?: number): Promise<ModalityPlan | null>;
145
+ /**
146
+ * fetches the edge lane table with a short timeout: one row per live or
147
+ * host modality with its runtime and probe outcome; null when the engine
148
+ * does not answer.
149
+ *
150
+ * @param base the api base to query ("" means same-origin).
151
+ * @param timeoutms abort budget in milliseconds.
152
+ * @returns the lane list when the api answers ok, null otherwise.
153
+ */
154
+ export declare function fetchedgelanes(base?: string, timeoutms?: number): Promise<EdgeLane[]>;
package/dist/api.js ADDED
@@ -0,0 +1,174 @@
1
+ /**
2
+ * shared api client for the React interface: one typed module replaces
3
+ * the three copies of apibase()/iscrossorigin()/apifetch() that lived
4
+ * inside login.js, register.js and dashboard.js. the api base resolves
5
+ * from the ?api= query string (persisted for convenience), the
6
+ * window.SADDLE_API global, the "saddle_api" localstorage key or
7
+ * same-origin; cross-origin targets switch to credentials: "include"
8
+ * so the saddlesession cookie still flows. json in, json out, family
9
+ * error handling.
10
+ */
11
+ /** localstorage key carrying the persisted api base override. */
12
+ const apistoragekey = "saddle_api";
13
+ /**
14
+ * resolves the api base: ?api= wins (and persists), then the window
15
+ * global, then localstorage, then same origin ("").
16
+ *
17
+ * @returns the trimmed api base ("" means same-origin).
18
+ */
19
+ export function apibase() {
20
+ try {
21
+ const fromquery = new URLSearchParams(window.location.search).get("api");
22
+ if (fromquery !== null && fromquery.trim() !== "") {
23
+ const trimmed = fromquery.trim().replace(/\/+$/, "");
24
+ window.localStorage.setItem(apistoragekey, trimmed);
25
+ return trimmed;
26
+ }
27
+ const preset = window.SADDLE_API || window.localStorage.getItem(apistoragekey) || "";
28
+ return String(preset).trim().replace(/\/+$/, "");
29
+ }
30
+ catch {
31
+ return "";
32
+ }
33
+ }
34
+ /**
35
+ * checks whether the api base points at a different origin.
36
+ *
37
+ * @param base the resolved api base ("" means same-origin).
38
+ * @returns true when the target is cross-origin.
39
+ */
40
+ export function iscrossorigin(base) {
41
+ if (base === "")
42
+ return false;
43
+ try {
44
+ return new URL(base, window.location.href).origin !== window.location.origin;
45
+ }
46
+ catch {
47
+ return false;
48
+ }
49
+ }
50
+ /**
51
+ * fetch wrapper: json in, json out, credentials follow the origin.
52
+ * sets the content-type automatically whenever a body is present and
53
+ * switches to credentials: "include" for cross-origin bases so the
54
+ * session cookie still reaches the api.
55
+ *
56
+ * @param path the api path (must start with "/").
57
+ * @param init the fetch init; body must already be a json string.
58
+ * @returns the raw response for ok/status handling by the caller.
59
+ */
60
+ export async function apifetch(path, init = {}) {
61
+ const base = apibase();
62
+ const headers = new Headers(init.headers);
63
+ if (init.body !== undefined && !headers.has("content-type")) {
64
+ headers.set("content-type", "application/json");
65
+ }
66
+ const credentials = iscrossorigin(base) ? "include" : "same-origin";
67
+ return fetch(`${base}${path}`, { ...init, headers, credentials });
68
+ }
69
+ /**
70
+ * probes the api health endpoint with a short timeout.
71
+ *
72
+ * @param base the api base to probe ("" means same-origin).
73
+ * @param timeoutms abort budget in milliseconds.
74
+ * @returns the health payload when the api answers ok, null otherwise.
75
+ */
76
+ export async function fetchhealth(base, timeoutms = 1500) {
77
+ try {
78
+ const url = `${base ? base.replace(/\/+$/, "") : ""}/api/v1/health`;
79
+ const controller = new AbortController();
80
+ const timer = setTimeout(() => controller.abort(), timeoutms);
81
+ const response = await fetch(url, { signal: controller.signal });
82
+ clearTimeout(timer);
83
+ if (!response.ok)
84
+ return null;
85
+ const body = (await response.json());
86
+ return body && body.ok === true ? body : null;
87
+ }
88
+ catch {
89
+ return null;
90
+ }
91
+ }
92
+ /**
93
+ * reads the {error:{code,message}} payload the family endpoints answer
94
+ * with; returns null when the body is not a recognizable error object.
95
+ *
96
+ * @param response a non-ok api response.
97
+ * @returns the error message when present, null otherwise.
98
+ */
99
+ export async function apierrormessage(response) {
100
+ try {
101
+ const payload = (await response.json());
102
+ if (payload?.error && typeof payload.error.message === "string") {
103
+ return payload.error.message;
104
+ }
105
+ }
106
+ catch {
107
+ /* keep null: the generic message stays */
108
+ }
109
+ return null;
110
+ }
111
+ /**
112
+ * human label for the resolved api base, used by the auth pages status
113
+ * line ("api: same origin" or the cross-origin target with credentials).
114
+ *
115
+ * @returns the status line text.
116
+ */
117
+ export function apistatustext() {
118
+ const base = apibase();
119
+ return base === "" ? "api: same origin" : `api: ${base} (cross-origin, credentials: include)`;
120
+ }
121
+ /**
122
+ * fetches the compute modality plan with a short timeout: the counts and
123
+ * the records the compute face renders. the surface never invents rows —
124
+ * when the engine does not answer the caller renders the honest line.
125
+ *
126
+ * @param base the api base to query ("" means same-origin).
127
+ * @param timeoutms abort budget in milliseconds.
128
+ * @returns the plan payload when the api answers ok, null otherwise.
129
+ */
130
+ export async function fetchmodalities(base = apibase(), timeoutms = 3000) {
131
+ try {
132
+ const url = `${base ? base.replace(/\/+$/, "") : ""}/api/v1/modalities`;
133
+ const controller = new AbortController();
134
+ const timer = setTimeout(() => controller.abort(), timeoutms);
135
+ const response = await fetch(url, { signal: controller.signal });
136
+ clearTimeout(timer);
137
+ if (!response.ok)
138
+ return null;
139
+ const body = (await response.json());
140
+ if (body === null || !Array.isArray(body.records))
141
+ return null;
142
+ return body;
143
+ }
144
+ catch {
145
+ return null;
146
+ }
147
+ }
148
+ /**
149
+ * fetches the edge lane table with a short timeout: one row per live or
150
+ * host modality with its runtime and probe outcome; null when the engine
151
+ * does not answer.
152
+ *
153
+ * @param base the api base to query ("" means same-origin).
154
+ * @param timeoutms abort budget in milliseconds.
155
+ * @returns the lane list when the api answers ok, null otherwise.
156
+ */
157
+ export async function fetchedgelanes(base = apibase(), timeoutms = 3000) {
158
+ try {
159
+ const url = `${base ? base.replace(/\/+$/, "") : ""}/api/v1/edge/lanes`;
160
+ const controller = new AbortController();
161
+ const timer = setTimeout(() => controller.abort(), timeoutms);
162
+ const response = await fetch(url, { signal: controller.signal });
163
+ clearTimeout(timer);
164
+ if (!response.ok)
165
+ return null;
166
+ const body = (await response.json());
167
+ if (body === null || !Array.isArray(body.lanes))
168
+ return null;
169
+ return body.lanes;
170
+ }
171
+ catch {
172
+ return null;
173
+ }
174
+ }
package/dist/auth.d.ts ADDED
@@ -0,0 +1,268 @@
1
+ /**
2
+ * auth.ts — security layer for the saddle web node (v7-BACK).
3
+ *
4
+ * everything that touches credentials and sessions lives here: scrypt
5
+ * password hashing (n=16384, r=8, p=1) with timing-safe verification,
6
+ * opaque base64url session tokens stored only as sha256 hashes, the
7
+ * saddlesession cookie builder, the in-memory ip rate limiter, and the
8
+ * requireauth/requireadmin request guards. the bootstrap rule is part
9
+ * of the contract: the first user registered on a node with zero users
10
+ * is granted role admin (see createsession caller in server.ts), so a
11
+ * fresh main or standalone node always has one operator.
12
+ *
13
+ * contexts (9): passwordhashing, sessiontokens, sessionstore, cookies,
14
+ * ratelimiter, requestguards, usernamepolicy, timingequalizer,
15
+ * bootstrap.
16
+ *
17
+ * rules: lowercase identifiers, english jsdoc in third person, no emoji,
18
+ * try/catch on every fallible path, node:* modules only, zero
19
+ * dependencies, no hardcoded localhost anywhere.
20
+ */
21
+ import type { IncomingMessage } from 'node:http';
22
+ /** the request context of one session mint plus an optional ttl override. */
23
+ export type SessionContext = {
24
+ ip?: string;
25
+ useragent?: string;
26
+ ttlms?: number;
27
+ };
28
+ /** the minted session values handed to the register/login routes. */
29
+ export type SessionMaterial = {
30
+ token: string;
31
+ tokenhash: string;
32
+ expiresat: string;
33
+ };
34
+ /** a session minted by the main node and relayed to the clone. */
35
+ export type ForwardedSession = {
36
+ token: string;
37
+ userid: string;
38
+ expiresat: string;
39
+ ip?: string;
40
+ useragent?: string;
41
+ };
42
+ /** the authenticated user projection resolved by requireauth. */
43
+ export type SessionUser = {
44
+ id: string;
45
+ username: string;
46
+ role: string;
47
+ expiresat: string;
48
+ };
49
+ /** the success arm of a policy validation. */
50
+ export type ValidationOk = {
51
+ ok: true;
52
+ };
53
+ /** the failure arm of a policy validation. */
54
+ export type ValidationError = {
55
+ ok: false;
56
+ code: string;
57
+ message: string;
58
+ };
59
+ /** the outcome of a username or password policy check. */
60
+ export type ValidationOutcome = ValidationOk | ValidationError;
61
+ /** one in-memory rate limit bucket. */
62
+ export type RateBucket = {
63
+ count: number;
64
+ resetat: number;
65
+ };
66
+ /** the verdict of the in-memory rate limiter. */
67
+ export type RateVerdict = {
68
+ allowed: boolean;
69
+ retryafter: number;
70
+ };
71
+ /** one pinned rate limit window. */
72
+ export type RateWindow = {
73
+ limit: number;
74
+ windowms: number;
75
+ };
76
+ /** the augmented request shape requireauth injects the resolved user and
77
+ * the raw token on; the augmentation below carries the two optional
78
+ * properties onto every node:http incoming message in the project. */
79
+ export type AuthedRequest = IncomingMessage & {
80
+ user?: SessionUser;
81
+ sessiontoken?: string;
82
+ };
83
+ declare module 'node:http' {
84
+ interface IncomingMessage {
85
+ user?: SessionUser;
86
+ sessiontoken?: string;
87
+ }
88
+ }
89
+ /**
90
+ * hashes one password with scrypt and a fresh 32-byte random salt.
91
+ *
92
+ * @param password the plaintext password (8-128 chars,
93
+ * enforced by the route layer).
94
+ * @returns the hex digest and the hex salt, both ready for the users row.
95
+ */
96
+ export declare function hashpassword(password: string): {
97
+ passwordhash: string;
98
+ salt: string;
99
+ };
100
+ /**
101
+ * verifies one password against a stored hash and salt using
102
+ * timingSafeEqual; any input or encoding failure resolves to false so
103
+ * callers never leak the failure reason.
104
+ *
105
+ * @param password the plaintext candidate.
106
+ * @param passwordhash the stored hex digest.
107
+ * @param salt the stored hex salt.
108
+ * @returns true when the password matches.
109
+ */
110
+ export declare function verifypassword(password: string, passwordhash: string, salt: string): boolean;
111
+ /**
112
+ * burns one scrypt round against the dummy pair so a login for an
113
+ * unknown username costs the same time as a login for a real one.
114
+ *
115
+ * @returns always false.
116
+ */
117
+ declare function burndummy(): boolean;
118
+ /**
119
+ * mints one opaque session token: 32 random bytes in base64url.
120
+ *
121
+ * @returns the bearer/cookie token.
122
+ */
123
+ export declare function sessiontoken(): string;
124
+ /**
125
+ * derives the sha256 hex digest stored in the sessions table; the raw
126
+ * token never touches the database.
127
+ *
128
+ * @param token the raw session token.
129
+ * @returns the hex token hash.
130
+ */
131
+ export declare function tokenhash(token: string): string;
132
+ /**
133
+ * creates one session row for a user and returns the token material.
134
+ *
135
+ * @param userid the user id.
136
+ * @param context the request context and an optional ttl override.
137
+ * @returns the minted session values.
138
+ */
139
+ export declare function createsession(userid: string, context?: SessionContext): SessionMaterial;
140
+ /**
141
+ * caches one session minted elsewhere (the mesh main node) into the
142
+ * local sessions table with the remote userid.
143
+ *
144
+ * @param fields the forwarded session.
145
+ * @returns void.
146
+ */
147
+ export declare function cachesession(fields: ForwardedSession): void;
148
+ /**
149
+ * resolves the user behind one raw token: hash lookup, expiry check and
150
+ * user projection in one call.
151
+ *
152
+ * @param token the raw session token.
153
+ * @returns the user or null when the session is
154
+ * missing, expired or orphaned.
155
+ */
156
+ export declare function getsessionuser(token: string): SessionUser | null;
157
+ /**
158
+ * destroys one session by raw token.
159
+ *
160
+ * @param token the raw session token.
161
+ * @returns true when a session row was removed.
162
+ */
163
+ export declare function destroysession(token: string): boolean;
164
+ /** the session cookie name shared by every node in the mesh. */
165
+ export declare const cookiename = "saddlesession";
166
+ /**
167
+ * builds the set-cookie value for one session token: HttpOnly,
168
+ * SameSite=Strict, Path=/, Max-Age=86400 and Secure whenever the
169
+ * request arrived through https (directly or via x-forwarded-proto).
170
+ *
171
+ * @param token the raw session token.
172
+ * @param req the incoming request, used only to detect https.
173
+ * @returns the set-cookie header value.
174
+ */
175
+ export declare function cookiefor(token: string, req?: IncomingMessage): string;
176
+ /**
177
+ * builds the expiring set-cookie value used by logout.
178
+ *
179
+ * @returns the set-cookie header value clearing the cookie.
180
+ */
181
+ export declare function cookieclear(): string;
182
+ /**
183
+ * extracts the session token from the saddlesession cookie or the
184
+ * Authorization: Bearer header of one request.
185
+ *
186
+ * @param req the incoming request.
187
+ * @returns the raw token or null.
188
+ */
189
+ export declare function extracttoken(req: IncomingMessage): string | null;
190
+ /**
191
+ * the auth guard: reads the cookie or bearer token, resolves the
192
+ * session and injects req.user = {id, username, role, expiresat}.
193
+ *
194
+ * @param req the incoming request.
195
+ * @returns the injected user or null when
196
+ * unauthenticated.
197
+ */
198
+ export declare function requireauth(req: IncomingMessage): SessionUser | null;
199
+ /**
200
+ * the admin guard: requireauth plus the admin role check.
201
+ *
202
+ * @param req the incoming request.
203
+ * @returns the admin user or null.
204
+ */
205
+ export declare function requireadmin(req: IncomingMessage): SessionUser | null;
206
+ /**
207
+ * validates one username candidate against the v7 policy; the candidate
208
+ * is coerced defensively so route layers can pass raw json fields.
209
+ *
210
+ * @param username the candidate.
211
+ * @returns the validation outcome.
212
+ */
213
+ export declare function validateusername(username: unknown): ValidationOutcome;
214
+ /**
215
+ * validates one password candidate against the v7 policy (8-128 chars);
216
+ * the candidate is coerced defensively so route layers can pass raw json
217
+ * fields.
218
+ *
219
+ * @param password the candidate.
220
+ * @returns the validation outcome.
221
+ */
222
+ export declare function validatepassword(password: unknown): ValidationOutcome;
223
+ /**
224
+ * the in-memory rate limiter with lazy sweeping: every hit refreshes
225
+ * its own window and, once the map grows past the sweep limit, expired
226
+ * buckets from other keys are dropped.
227
+ *
228
+ * @param bucketkey the bucket key (e.g. `login:10.0.0.1`).
229
+ * @param limit the allowed hits per window.
230
+ * @param windowms the window length in milliseconds.
231
+ * @returns the verdict plus the retry-after seconds for 429 responses.
232
+ */
233
+ export declare function ratelimit(bucketkey: string, limit: number, windowms: number): RateVerdict;
234
+ /** the two pinned buckets from the v7 contract. */
235
+ export declare const ratelimits: {
236
+ register: RateWindow;
237
+ login: RateWindow;
238
+ };
239
+ /**
240
+ * the ONLY admin usernames: the CODEOWNERS allowlist (iakadion,
241
+ * inathlan, aasblor and nasblor). the web admin surface mirrors the
242
+ * repository CODEOWNERS file - the two files are the same contract.
243
+ */
244
+ export declare const adminusernames: string[];
245
+ /**
246
+ * resolves the role for a new registration: only the CODEOWNERS
247
+ * usernames are admins; every other account (including the very first
248
+ * registration) is a plain user. the seeded admins below guarantee the
249
+ * allowlist accounts exist before anyone registers.
250
+ *
251
+ * @param username the requested username.
252
+ * @returns the role for the new row.
253
+ */
254
+ export declare function bootstraprole(username?: string): 'admin' | 'user';
255
+ /**
256
+ * creates the CODEOWNERS admin accounts with the resolved bootstrap
257
+ * password (the env pin, or the per-boot mint) when the database does not
258
+ * carry them yet. idempotent: an admin the operator re-registered with a
259
+ * rotated password is never overwritten. runs on every node boot so a
260
+ * wiped database never locks the admin surface out. when the password was
261
+ * minted for this boot, the one-time secret is written to the operator
262
+ * console exactly once - only for the rows that actually landed.
263
+ *
264
+ * @returns the admin usernames that were created.
265
+ */
266
+ export declare function seedadmins(): string[];
267
+ /** burn dummy re-exported for tests that assert timing equality. */
268
+ export { burndummy };