@wenathlan/saddle 2.1.6 → 2.1.8

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 (300) hide show
  1. package/Dockerfile +170 -66
  2. package/LICENSE +0 -0
  3. package/README.md +73 -58
  4. package/alternatives.ts +0 -0
  5. package/biome.jsonc +134 -0
  6. package/boards.json +2 -2
  7. package/browser.ts +482 -0
  8. package/cli.ts +274 -0
  9. package/compute.ts +4 -4
  10. package/cores.json +2 -2
  11. package/distribution.ts +731 -0
  12. package/docker.config +37 -9
  13. package/docs/.gitkeep +0 -0
  14. package/docs/actionrecorder.md +0 -0
  15. package/docs/actionsincident.md +0 -0
  16. package/docs/alternatives.md +0 -0
  17. package/docs/architecture.md +0 -0
  18. package/docs/{architecture-1.8.19.md → architecture1819.md} +0 -0
  19. package/docs/{architecture-1.8.19.mmd → architecture1819.mmd} +0 -0
  20. package/docs/artifactavailability.md +2 -2
  21. package/docs/{artifactresearch-1.8.9.md → artifactresearch189.md} +0 -0
  22. package/docs/{batches-1816-1820.md → batches1816to1820.md} +0 -0
  23. package/docs/{brancharchive-2026-08-18.md → brancharchive20260818.md} +0 -0
  24. package/docs/branchaudit.md +0 -0
  25. package/docs/browser.md +18 -1
  26. package/docs/capabilityreport.md +0 -0
  27. package/docs/comparativeaudit.md +0 -0
  28. package/docs/consolidation.md +0 -0
  29. package/docs/{containerplatforms-1.8.17.md → containerplatforms1817.md} +0 -0
  30. package/docs/{containerplatforms-2.1.0.md → containerplatforms210.md} +1 -1
  31. package/docs/{documentmap-1.8.12.md → documentmap1812.md} +1 -1
  32. package/docs/{e2ugh-engine.md → e2ughengine.md} +0 -0
  33. package/docs/ecosystemplan.md +0 -0
  34. package/docs/enginearchitecture.md +0 -0
  35. package/docs/extension.md +13 -2
  36. package/docs/featureaudit.md +0 -0
  37. package/docs/{flatbuildresearch-1.8.11.md → flatbuildresearch1811.md} +0 -0
  38. package/docs/gapmatrix.md +0 -0
  39. package/docs/hardware.md +0 -0
  40. package/docs/libraryapi.md +1 -1
  41. package/docs/{licenseaudit-1.8.12.md → licenseaudit1812.md} +0 -0
  42. package/docs/logs/examplesession.json +54 -0
  43. package/docs/memoryengine.md +0 -0
  44. package/docs/{mobileconversionresearch-1.8.10.md → mobileconversionresearch1810.md} +0 -0
  45. package/docs/modes.md +0 -0
  46. package/docs/{nativeidentityresearch-1.8.11.md → nativeidentityresearch1811.md} +0 -0
  47. package/docs/{native-wrappers.md → nativewrappers.md} +0 -0
  48. package/docs/ninetymodalities.md +191 -0
  49. package/docs/optimization.md +0 -0
  50. package/docs/packageaudit185.md +0 -0
  51. package/docs/performance.md +0 -0
  52. package/docs/{planning.1.8.18.md → planning1818.md} +0 -0
  53. package/docs/{planning.1.8.19.md → planning1819.md} +0 -0
  54. package/docs/planningengine.md +186 -0
  55. package/docs/planningninety.md +391 -0
  56. package/docs/platformpipelineaudit.md +0 -0
  57. package/docs/platformpipelines.md +0 -0
  58. package/docs/productindex.md +0 -0
  59. package/docs/registryresearch.md +0 -0
  60. package/docs/release.md +0 -0
  61. package/docs/release17notes.md +0 -0
  62. package/docs/release181notes.md +0 -0
  63. package/docs/release182notes.md +0 -0
  64. package/docs/release184notes.md +0 -0
  65. package/docs/release185notes.md +0 -0
  66. package/docs/release18notes.md +0 -0
  67. package/docs/releaseassets.md +0 -0
  68. package/docs/{releasenotes-1.8.12.md → releasenotes1812.md} +0 -0
  69. package/docs/{releasenotes-1.8.13.md → releasenotes1813.md} +2 -2
  70. package/docs/{releasenotes-1.8.14.md → releasenotes1814.md} +1 -1
  71. package/docs/{releasenotes-1.8.15.md → releasenotes1815.md} +0 -0
  72. package/docs/{releasenotes-1.8.16.md → releasenotes1816.md} +0 -0
  73. package/docs/{releasenotes-1.8.17.md → releasenotes1817.md} +0 -0
  74. package/docs/{releasenotes-1.8.18.md → releasenotes1818.md} +0 -0
  75. package/docs/{releasenotes-1.8.19.md → releasenotes1819.md} +0 -0
  76. package/docs/{reorganization-1.8.8.md → reorganization188.md} +0 -0
  77. package/docs/{reorganization-1.8.9.md → reorganization189.md} +0 -0
  78. package/docs/{repo-analysis-1.8.21.json → repoanalysis1821.json} +0 -0
  79. package/docs/{repo-candidates-1.8.21.jsonl → repocandidates1821.jsonl} +0 -0
  80. package/docs/{repo-research-1.8.16.md → reporesearch1816.md} +0 -0
  81. package/docs/{repo-research-1.8.21.md → reporesearch1821.md} +0 -0
  82. package/docs/{repo-selected-1.8.21.json → reposelected1821.json} +0 -0
  83. package/docs/{repo-synthesis-1.8.16.md → reposynthesis1816.md} +1 -1
  84. package/docs/{repo-synthesis-1.8.21.md → reposynthesis1821.md} +0 -0
  85. package/docs/{research-1.8.18-isolation.md → research1818isolation.md} +0 -0
  86. package/docs/{research-1.8.19-virtual-browser.md → research1819virtualbrowser.md} +0 -0
  87. package/docs/roadmapp2p3.md +0 -0
  88. package/docs/s3compatible.md +0 -0
  89. package/docs/{scanningresearch-1.8.11.md → scanningresearch1811.md} +0 -0
  90. package/docs/security.md +0 -0
  91. package/docs/{securityaudit-1.8.7.md → securityaudit187.md} +0 -0
  92. package/docs/{securityreferences-1.8.11.md → securityreferences1811.md} +0 -0
  93. package/docs/{securityresearch-1.8.11.md → securityresearch1811.md} +0 -0
  94. package/docs/{signingresearch-1.8.12.md → signingresearch1812.md} +0 -0
  95. package/docs/signingwithoutcertificates.md +0 -0
  96. package/docs/signpathapplication.md +0 -0
  97. package/docs/sitemap.md +0 -0
  98. package/docs/{tauri-glib-risk-1.8.11.md → tauriglibrisk1811.md} +0 -0
  99. package/docs/todo.md +2 -2
  100. package/docs/{todo-1.8.15.md → todo1815.md} +1 -1
  101. package/docs/{todo-1.8.16.md → todo1816.md} +5 -5
  102. package/docs/{todo-1.8.18.md → todo1818.md} +0 -0
  103. package/docs/{todo-1.8.19.md → todo1819.md} +0 -0
  104. package/docs/{toolchainresearch-1.8.9.md → toolchainresearch189.md} +0 -0
  105. package/docs/toolchains.md +0 -0
  106. package/docs/usage.md +0 -0
  107. package/docs/viability.md +0 -0
  108. package/docs/virtualization.md +0 -0
  109. package/docs/{web-duplicatecleanup.md → webduplicatecleanup.md} +0 -0
  110. package/docs/{web-ideas.md → webideas.md} +0 -0
  111. package/docs/{workflowimprovements-2026-08-19.md → workflowimprovements20260819.md} +0 -0
  112. package/docs/workflowinputs.md +0 -0
  113. package/docs/workflowoperations.md +0 -0
  114. package/docs/workloadlaw.md +104 -0
  115. package/execution.ts +677 -0
  116. package/foundation.ts +314 -0
  117. package/gpumonitor.cpp +0 -0
  118. package/gpus.json +1 -1
  119. package/index.ts +38 -29
  120. package/isolation.ts +84 -0
  121. package/modalityregistry.ts +1493 -0
  122. package/{dist/modes.js → modes.ts} +79 -62
  123. package/mttg.config +1 -1
  124. package/orchestrator.ts +54 -17
  125. package/package.json +245 -146
  126. package/passage.config +0 -0
  127. package/performance.ts +0 -0
  128. package/processors.json +1 -1
  129. package/qemu.config +2 -2
  130. package/qemubridge.py +0 -0
  131. package/quantum.ts +0 -0
  132. package/render.ts +0 -0
  133. package/sandbox.ts +2035 -0
  134. package/scheduler.ts +1 -1
  135. package/security.ts +0 -0
  136. package/server.ts +52 -0
  137. package/tiers.ts +9 -3
  138. package/tsconfig.json +22 -13
  139. package/virtual.ts +1105 -0
  140. package/virtualcpu.ts +1 -0
  141. package/virtualgpu.ts +2 -1
  142. package/virtualhardware.c +0 -0
  143. package/virtualhardware.json +2 -2
  144. package/virtualization.ts +1 -1
  145. package/virtualizationcore.cpp +0 -0
  146. package/virtualmemory.ts +0 -0
  147. package/vm.config.json +2 -2
  148. package/workloadlaw.ts +293 -0
  149. package/biome.json +0 -151
  150. package/dist/acquisition.d.ts +0 -359
  151. package/dist/acquisition.d.ts.map +0 -1
  152. package/dist/acquisition.js +0 -629
  153. package/dist/acquisition.js.map +0 -1
  154. package/dist/alternatives.d.ts +0 -509
  155. package/dist/alternatives.d.ts.map +0 -1
  156. package/dist/alternatives.js +0 -951
  157. package/dist/alternatives.js.map +0 -1
  158. package/dist/automation.d.ts +0 -426
  159. package/dist/automation.d.ts.map +0 -1
  160. package/dist/automation.js +0 -561
  161. package/dist/automation.js.map +0 -1
  162. package/dist/browser.d.ts +0 -562
  163. package/dist/browser.d.ts.map +0 -1
  164. package/dist/browser.js +0 -1057
  165. package/dist/browser.js.map +0 -1
  166. package/dist/cli.d.ts +0 -17
  167. package/dist/cli.d.ts.map +0 -1
  168. package/dist/cli.js +0 -169
  169. package/dist/cli.js.map +0 -1
  170. package/dist/communication.d.ts +0 -192
  171. package/dist/communication.d.ts.map +0 -1
  172. package/dist/communication.js +0 -418
  173. package/dist/communication.js.map +0 -1
  174. package/dist/compute.d.ts +0 -1161
  175. package/dist/compute.d.ts.map +0 -1
  176. package/dist/compute.js +0 -2273
  177. package/dist/compute.js.map +0 -1
  178. package/dist/distribution.d.ts +0 -529
  179. package/dist/distribution.d.ts.map +0 -1
  180. package/dist/distribution.js +0 -811
  181. package/dist/distribution.js.map +0 -1
  182. package/dist/docs/example-localjob.d.ts +0 -2
  183. package/dist/docs/example-localjob.d.ts.map +0 -1
  184. package/dist/docs/example-localjob.js +0 -15
  185. package/dist/docs/example-localjob.js.map +0 -1
  186. package/dist/docs/example-publicapi.d.ts +0 -2
  187. package/dist/docs/example-publicapi.d.ts.map +0 -1
  188. package/dist/docs/example-publicapi.js +0 -7
  189. package/dist/docs/example-publicapi.js.map +0 -1
  190. package/dist/execution.d.ts +0 -530
  191. package/dist/execution.d.ts.map +0 -1
  192. package/dist/execution.js +0 -761
  193. package/dist/execution.js.map +0 -1
  194. package/dist/format.d.ts +0 -19
  195. package/dist/format.d.ts.map +0 -1
  196. package/dist/format.js +0 -60
  197. package/dist/format.js.map +0 -1
  198. package/dist/foundation.d.ts +0 -176
  199. package/dist/foundation.d.ts.map +0 -1
  200. package/dist/foundation.js +0 -309
  201. package/dist/foundation.js.map +0 -1
  202. package/dist/index.d.ts +0 -735
  203. package/dist/index.d.ts.map +0 -1
  204. package/dist/index.js +0 -1020
  205. package/dist/index.js.map +0 -1
  206. package/dist/integration.d.ts +0 -236
  207. package/dist/integration.d.ts.map +0 -1
  208. package/dist/integration.js +0 -236
  209. package/dist/integration.js.map +0 -1
  210. package/dist/intelligence.d.ts +0 -94
  211. package/dist/intelligence.d.ts.map +0 -1
  212. package/dist/intelligence.js +0 -145
  213. package/dist/intelligence.js.map +0 -1
  214. package/dist/isolation.d.ts +0 -129
  215. package/dist/isolation.d.ts.map +0 -1
  216. package/dist/isolation.js +0 -96
  217. package/dist/isolation.js.map +0 -1
  218. package/dist/media.d.ts +0 -344
  219. package/dist/media.d.ts.map +0 -1
  220. package/dist/media.js +0 -986
  221. package/dist/media.js.map +0 -1
  222. package/dist/modes.d.ts +0 -359
  223. package/dist/modes.d.ts.map +0 -1
  224. package/dist/modes.js.map +0 -1
  225. package/dist/operations.d.ts +0 -67
  226. package/dist/operations.d.ts.map +0 -1
  227. package/dist/operations.js +0 -94
  228. package/dist/operations.js.map +0 -1
  229. package/dist/orchestrator.d.ts +0 -1339
  230. package/dist/orchestrator.d.ts.map +0 -1
  231. package/dist/orchestrator.js +0 -4308
  232. package/dist/orchestrator.js.map +0 -1
  233. package/dist/packager/targetcli.js +0 -4
  234. package/dist/performance.d.ts +0 -416
  235. package/dist/performance.d.ts.map +0 -1
  236. package/dist/performance.js +0 -827
  237. package/dist/performance.js.map +0 -1
  238. package/dist/quantum.d.ts +0 -810
  239. package/dist/quantum.d.ts.map +0 -1
  240. package/dist/quantum.js +0 -2073
  241. package/dist/quantum.js.map +0 -1
  242. package/dist/render.d.ts +0 -381
  243. package/dist/render.d.ts.map +0 -1
  244. package/dist/render.js +0 -1073
  245. package/dist/render.js.map +0 -1
  246. package/dist/scheduler.d.ts +0 -596
  247. package/dist/scheduler.d.ts.map +0 -1
  248. package/dist/scheduler.js +0 -1259
  249. package/dist/scheduler.js.map +0 -1
  250. package/dist/security.d.ts +0 -501
  251. package/dist/security.d.ts.map +0 -1
  252. package/dist/security.js +0 -1170
  253. package/dist/security.js.map +0 -1
  254. package/dist/server.d.ts +0 -21
  255. package/dist/server.d.ts.map +0 -1
  256. package/dist/server.js +0 -52
  257. package/dist/server.js.map +0 -1
  258. package/dist/tiers.d.ts +0 -1002
  259. package/dist/tiers.d.ts.map +0 -1
  260. package/dist/tiers.js +0 -2165
  261. package/dist/tiers.js.map +0 -1
  262. package/dist/virtual.d.ts +0 -641
  263. package/dist/virtual.d.ts.map +0 -1
  264. package/dist/virtual.js +0 -1285
  265. package/dist/virtual.js.map +0 -1
  266. package/dist/virtualcpu.d.ts +0 -316
  267. package/dist/virtualcpu.d.ts.map +0 -1
  268. package/dist/virtualcpu.js +0 -1180
  269. package/dist/virtualcpu.js.map +0 -1
  270. package/dist/virtualgpu.d.ts +0 -345
  271. package/dist/virtualgpu.d.ts.map +0 -1
  272. package/dist/virtualgpu.js +0 -1000
  273. package/dist/virtualgpu.js.map +0 -1
  274. package/dist/virtualization.d.ts +0 -607
  275. package/dist/virtualization.d.ts.map +0 -1
  276. package/dist/virtualization.js +0 -1187
  277. package/dist/virtualization.js.map +0 -1
  278. package/dist/virtualmemory.d.ts +0 -818
  279. package/dist/virtualmemory.d.ts.map +0 -1
  280. package/dist/virtualmemory.js +0 -1796
  281. package/dist/virtualmemory.js.map +0 -1
  282. package/dist/vitest.config.d.ts +0 -3
  283. package/dist/vitest.config.d.ts.map +0 -1
  284. package/dist/vitest.config.js +0 -26
  285. package/dist/vitest.config.js.map +0 -1
  286. package/dist/webscrape.d.ts +0 -720
  287. package/dist/webscrape.d.ts.map +0 -1
  288. package/dist/webscrape.js +0 -2697
  289. package/dist/webscrape.js.map +0 -1
  290. package/docs/CONVERSA.txt +0 -1523
  291. package/docs/architecture-1.8.19.png +0 -0
  292. package/docs/assets/architecture.svg +0 -45
  293. package/docs/assets/saddlemark.svg +0 -13
  294. package/docs/example-localjob.ts +0 -15
  295. package/docs/example-publicapi.ts +0 -7
  296. package/docs/saddle.archive.1.8.17.tar.gz.gpg +0 -0
  297. package/media.ts +0 -1337
  298. package/web/manifest.json +0 -30
  299. package/web/readme.md +0 -383
  300. package/web/tsconfig.json +0 -25
package/web/manifest.json DELETED
@@ -1,30 +0,0 @@
1
- {
2
- "manifest_version": 3,
3
- "name": "Saddle browser bridge",
4
- "version": "2.1.6",
5
- "description": "User initiated page snapshots through the Saddle browser contract.",
6
- "minimum_chrome_version": "110",
7
- "permissions": [
8
- "activeTab",
9
- "scripting",
10
- "storage"
11
- ],
12
- "icons": {
13
- "32": "icon32.png",
14
- "64": "icon64.png",
15
- "128": "icon128.png"
16
- },
17
- "background": {
18
- "service_worker": "worker.js",
19
- "type": "module"
20
- },
21
- "action": {
22
- "default_title": "Saddle",
23
- "default_popup": "popup.html",
24
- "default_icon": {
25
- "32": "icon32.png",
26
- "64": "icon64.png",
27
- "128": "icon128.png"
28
- }
29
- }
30
- }
package/web/readme.md DELETED
@@ -1,383 +0,0 @@
1
- # saddle web — the sandbox in your browser
2
-
3
- the web edition of the saddle virtual hardware engine: one page that boots a
4
- virtual container — cpu, ram, gpu, mesa software graphics — entirely in the
5
- browser, plus one self-hosted node api exposing the exact same sandbox over
6
- http for automation. no framework, no build step, no serverless functions.
7
-
8
- version 2.1.6 — reported by `/api/v1/health` and kept in lockstep with
9
- `package.json` and the `meta.version` envelope of the ten data documents
10
- (v6-SYNC worklog task).
11
-
12
- ## run it
13
-
14
- ```bash
15
- # the whole thing: built spa + self-hosted api (zero dependencies)
16
- node web/server.ts
17
- # it prints the endpoint once, e.g. listening on 0.0.0.0:48213
18
- # override with --port 8080 or PORT=8080 / SADDLE_HOST
19
- # (first run: `npm run web:build` produces web/dist/public; the server
20
- # answers a honest 503 until the build exists)
21
-
22
- # the browser console also runs 100% client-side on any static host:
23
- # the browser port (sandbox.ts) needs no api; the api badge stays in
24
- # "engine: local" mode when /api/v1 is absent.
25
- ```
26
-
27
- inside the console: pick the cpu model (epyc 9965 192c, ryzen 9950x3d,
28
- threadripper pro 9995wx, threadripper 7980x, core ultra 9 285k, epyc 9955,
29
- xeon 6980p, m3 ultra), the vcpu count (1-192), ram (1-1024 gb), the gpu
30
- (rtx 5090, rtx pro 6000, b200, h100, a100, rx 9070 xt, mi350x) and the mig
31
- profile, press **start sandbox** and type in the terminal:
32
-
33
- ```
34
- help lscpu cat /proc/cpuinfo
35
- cat /proc/meminfo free -h nvidia-smi
36
- nvidia-smi -L clinfo vulkaninfo --summary
37
- glxinfo uname -a ls /etc/virtual
38
- env docker --version qemu-system-x86_64 --version
39
- uptime whoami neofetch
40
- clear echo <text>
41
- ```
42
-
43
- arrow keys browse history, tab completes commands, the side panel streams
44
- the bus events (vm:created, vm:started, snapshot:created, exec:completed,
45
- vm:stopped, vm:deleted).
46
-
47
- ## files
48
-
49
- | file | role |
50
- |---------------|------|
51
- | `App.tsx` | the single React entry (the 2.1.2 one-entry doctrine: the app owns its bootstrap — the createRoot self-mount and the index.css import live inside; index.html loads /App.tsx directly, no main.tsx wrapper) |
52
- | `index.html` | the vite spa shell (`<div id="root">` + the /App.tsx module script) |
53
- | page folders `Home/` `Architecture/` `AgentBrowser/` `Compute/` `Integrations/` `Playground/` `Console/` `Dashboard/` `Login/` `Register/` `Docs/` `NotFound/` | one folder per route carrying its own `<Page>.tsx` and components (the duck.ai conversion doctrine): the Console folder carries the absorbed e2ugh console surface (spec panel, boot terminal with history and tab-complete, events timeline, snapshot controls, api badge), the Login/Register/Dashboard folders carry the absorbed account pages (safenext redirect, strength meter, admin tables, mesh ping with client probe, keyfile backup/restore, local sandbox shelf) |
54
- | loose shell files (`PageShell.tsx`, `SiteHeader.tsx`, `SaddleMark.tsx`, `SectionRail.tsx`, `RuntimeDiagram.tsx`, `ErrorBoundary.tsx`, `ThemeContext.tsx`, `button.tsx`, `card.tsx`, `tooltip.tsx`, `sonner.tsx`, `utils.ts`, `paths.ts`) | the shared interface modules at the web root |
55
- | `api.ts` | the consolidated api-base helpers (the three duplicated copies of the absorbed pages joined into one module) |
56
- | `localauth.ts` | static-edge account fallback (the typed module conversion of the window-global original): when no api answers (github pages / netlify / vercel clones), login/register/dashboard switch to browser-local accounts (pbkdf2-sha256 via webcrypto, never synced). persistence: accounts are mirrored to IndexedDB and repaired in both directions on load (a partial storage clear never loses them), the CODEOWNERS admins (iakadion, inathlan, aasblor and nasblor, shared bootstrap password `cdw782FG7pjxQVw`, kept in lockstep with the server seed in `auth.ts`) are re-seeded whenever missing, and the dashboard ships explicit backup/restore keyfile buttons for a full wipe; only the CODEOWNERS accounts are admins |
57
- | `sandbox.ts` | browser-pure port of the engine generators (zero imports, runs in node too), the typed surface absorbed in the module itself (the 2.1.4 typescript conversion) |
58
- | `server.ts` | self-hosted node:http api: built spa serving (web/dist/public) + `/api/v1` |
59
- | `db.ts` | node:sqlite data layer: users, sessions, nodes, sandboxes, sandboxfiles, events, audit |
60
- | `auth.ts` | security layer: scrypt hashing, saddlesession cookies, rate limiter, request guards, CODEOWNERS admin seed |
61
- | `mesh.ts` | signed node-to-node mesh: hmac requests, aes-gcm payloads, clone heartbeat |
62
- | `schema.prisma` / `init.sql` / `drizzle.config.ts` | the three schema mirrors of the db.ts migrations (prisma, raw sql, drizzle kit) |
63
- | `mime.types` | the extension to content-type table parsed by server.ts at boot |
64
- | `manifest.json` / `popup.html` / `popup.css` / `icon.svg` / `icon32.png` / `icon64.png` / `icon128.png` | the browser-extension surface regenerated by buildextension.yml on the runners |
65
- | `package.json` | the web manifest (`@wenathlan/saddle-web`, private, never published): the deploy root owns its package and its dependency set mirrors the repository-root package.json one-to-one (the same set, kept in lockstep) so the vite build runs standalone; the published npm package is the central `@wenathlan/saddle` without web |
66
- | `Dockerfile` (repo root) | container image for ghcr.io/wenathlan/saddle (the main image; web/ ships inside) |
67
- | `vercel.json` | static hosting config for the React SPA at the web root (the deploy root, `outputDirectory: dist/public`, SPA rewrite), **no functions** |
68
- | `netlify.toml` | static hosting config for the React SPA at the web root (`publish = "dist/public"`, the build command runs from the repository root, SPA rewrite 200), **no functions** |
69
- | `caddyfile` | self-host reverse proxy for the node api (devthink.pro + www) |
70
- | `readme.md` | this document |
71
-
72
- ## architecture
73
-
74
- - **browser sandbox = engine port, pure.** `sandbox.ts` rewrites the
75
- engine generators for the web platform: `cpudata` (the
76
- `best_virtual_processors` bank plus the showcase additions), `gpudata`
77
- (the verified `gpu_bank`), `cpuinfo`/`lscpu` (byte-realistic procfs with
78
- the zen 5 avx-512 flag set), `meminfo`/`freeh` (the 47-field payload with
79
- the memoized snapshot so `cat /proc/meminfo` and `free -h` agree),
80
- `nvidiaSmiTable` (the exact 89-character adapter table, driver 575.57.08,
81
- cuda 12.9), `clinfoSummary` (rusticl opencl 3.1), `vulkanSummary`
82
- (lavapipe vulkan 1.4), `glxinfoSummary` (llvmpipe gl 4.6), `mesaenv`
83
- (`lp_native_vector_width=512` and friends), `bootSequence` (firecracker
84
- 125 ms dmesg) and `dispatch` (the command parser).
85
- - **self-hosted api = same contracts.** `server.ts` imports that very same
86
- `sandbox.ts` module (it is plain esm, no dom, no node builtin) so the
87
- browser terminal and the http api can never drift apart.
88
-
89
- ### api surface (`/api/v1`)
90
-
91
- | method | route | payload |
92
- |--------|-------|---------|
93
- | get | `/health` | `{ok, version, uptime}` |
94
- | get | `/specs/cpus` `/specs/gpus` `/specs/memory` | the repository json catalogs (processors.json, gpus.json, cores.json), cached in a map |
95
- | post | `/sandboxes` | `{model, vcpus, ramgb, gpu, mig}` -> `201` sandbox record (uuid, state `created -> running` after the 125 ms ramp) |
96
- | get | `/sandboxes/:id` | status + specs + remaining ttl |
97
- | post | `/sandboxes/:id/exec` | `{command}` -> `{output, exitCode, durationMs}` through the shared dispatcher |
98
- | delete | `/sandboxes/:id` | destroys the sandbox |
99
-
100
- sandboxes live in an in-memory map with a 15 minute ttl and a 60 s sweeper.
101
- errors are standardized `{error:{code,message}}`; cors is open
102
- (`access-control-allow-origin: *` plus the options preflight handler);
103
- every response carries `x-content-type-options: nosniff`; the listen port
104
- defaults once at boot to a random 30000-59999 value and is overridable by
105
- `--port`, `PORT` or `SADDLE_PORT`; no address is hardcoded to localhost.
106
-
107
- ## mesh architecture
108
-
109
- the web tier ships as one directory that deploys in three shapes:
110
-
111
- - **main node** (devthink.pro): the full container — static pages plus the
112
- authoritative api (auth, users, admin, mesh registry, audit) and the
113
- sqlite database on `/data`.
114
- - **clone** (vercel, netlify, any static mirror): the same bytes served as
115
- pure static files. the pages detect the missing `/api/v1` and fall back
116
- to the local browser engine; for auth-backed features the visitor points
117
- the page at the main node with `?api=https://devthink.pro` (persisted in
118
- `localStorage` as `saddle_api`; cross-origin requests switch to
119
- `credentials: "include"` so the `saddlesession` cookie flows).
120
- - **standalone**: the built spa opened from any static host or disk (the
121
- /console route) — the terminal runs 100% client-side, no account, no api.
122
-
123
- auth flow (identical on main and clones): the `/login` and `/register`
124
- routes post to `/api/v1/auth/{login,register}`; the backend validates,
125
- hashes with scrypt, creates the session server-side and answers with the
126
- httponly `saddlesession` cookie; the `/dashboard` route calls
127
- `/api/v1/auth/me`, renders the user view, and unlocks the admin view when
128
- the role is `admin` (overview cards, mesh node table with ping through
129
- `/api/v1/mesh/*`, users, global sandboxes, audit log). logout posts
130
- `/api/v1/auth/logout` and the cookie is revoked server-side.
131
-
132
- ```
133
- vercel clone netlify clone
134
- (static bytes) (static bytes)
135
- \ / ?api=https://devthink.pro
136
- \ local engine / credentials: include
137
- +------- + ------+
138
- |
139
- v https (caddy, auto tls, gzip)
140
- www.devthink.pro ---> devthink.pro
141
- | reverse_proxy /api/* -> 127.0.0.1:39721
142
- v
143
- [ ghcr.io/wenathlan/saddle (the main image; web/ ships inside) ]
144
- node 26 slim - server.ts
145
- /api/v1/auth - /api/v1/admin - /api/v1/sandboxes
146
- mesh: HMAC-signed gossip + AES-GCM payloads
147
- sqlite /data/saddle.db (0600)
148
- |
149
- +-- future mesh peers (other nodes,
150
- same image, same contract)
151
- ```
152
-
153
- ## security
154
-
155
- - **passwords**: scrypt (server-side, per-user salt); the wire carries
156
- username + password only over https; hashes and salts never appear in
157
- any api response — the admin users table renders account fields only.
158
- - **sessions**: opaque `saddlesession` httponly cookie; the session store
159
- keeps only hashes of the token, never the token itself. no password or
160
- token is ever written to `localStorage` — the only client-side key is
161
- the optional cross-origin api base (`saddle_api`), which is not a
162
- credential.
163
- - **rate limiting**: login, register and auth-protected routes are rate
164
- limited per ip + username on the api; the login page answers failures
165
- with one generic message (no user enumeration).
166
- - **headers**: `x-content-type-options: nosniff`, `x-frame-options: deny`,
167
- `referrer-policy: strict-origin-when-cross-origin` and a restrictive
168
- `permissions-policy` — identical on server.ts, caddy and netlify.
169
- - **mesh**: node-to-node traffic is hmac-signed (replay-windowed
170
- timestamps) with aes-gcm encrypted payloads; mesh keys live only in the
171
- main-node environment, never in the browser.
172
- - **database**: sqlite at `$SADDLE_DB` (container default
173
- `/data/saddle.db`), volume-mounted, file mode 0600, never baked into the
174
- image.
175
-
176
- ## deploy matrix
177
-
178
- | target | method | what runs |
179
- |--------|--------|-----------|
180
- | vercel (clone) | `web/vercel.json` (the deploy root), SPA output from `dist/public` | bytes only: local engine, `?api=` pointing at the main node for auth |
181
- | netlify (clone) | `web/netlify.toml`, `publish = "dist/public"` (the file lives at the web root), node 26.7.0 | bytes only: same behavior as vercel |
182
- | self-host (main, devthink.pro) | `docker build .` (the repo-root Dockerfile) -> `ghcr.io/wenathlan/saddle` (the main image; web/ ships inside), caddyfile in front | static pages + full `/api/v1` (auth, admin, mesh) + sqlite on `/data` |
183
-
184
- ## why the static + node split
185
-
186
- static hosts cannot run docker or sqlite and the project forbids
187
- serverless functions, so the split is the only shape that satisfies every
188
- rule at once: the sandbox compute is the visitor's own browser (the
189
- `sandbox.ts` engine port runs client-side on every clone), and the
190
- authoritative api is one plain node container an operator runs anywhere —
191
- devthink.pro today, any other node tomorrow, joined to the mesh. the same
192
- directory serves both worlds without a build step, a framework or a
193
- single dependency.
194
-
195
- ## deploy options
196
-
197
- - **vercel static** — the web-root `vercel.json` (the deploy root) sets
198
- `outputDirectory: web/dist/public` with an SPA rewrite and cache
199
- headers. static edge only: the page runs the local engine in the
200
- browser; there are no functions.
201
- - **netlify static** — the web-root `netlify.toml` (the deploy root) sets
202
- `publish = "web/dist/public"` on node 26.7.0 with an SPA rewrite and
203
- security headers. same rule: static edge only, no functions.
204
- - **self-host (devthink.pro pattern)** — run `node web/server.ts` (or the
205
- `ghcr.io/wenathlan/saddle (the main image; web/ ships inside)` container behind `web/caddyfile`):
206
- caddy serves the static files on devthink.pro (+ www redirect, gzip,
207
- security headers, auto tls) and reverse-proxies `/api/*` to the node
208
- process on the documented port. this is the only mode where the
209
- api-backed badge lights up and the dashboard unlocks the admin view.
210
-
211
- ## relation to @wenathlan/saddle
212
-
213
- saddle is the "vm published as a package" surface family
214
- (package/extension/workflow/native/web) that the sedal runtime consumes.
215
- this web edition is the **web surface of that family for saddle**: the
216
- browser page is the zero-install demo of the virtual container, and the
217
- node api is the self-hostable control plane with the same lifecycle and
218
- exec contracts saddle exposes natively. because the api is plain node:http
219
- with zero dependencies, it embeds inside any saddle native host without
220
- adding a single package to the tree.
221
-
222
- ## anti-serverless policy
223
-
224
- no vercel functions, no netlify functions, no paid frameworks, no docker
225
- cloud wrappers. the static hosts above serve bytes only; the compute
226
- surface (the sandbox dispatcher) is either the visitor's own browser or a
227
- node process the operator runs — nothing in between.
228
-
229
- ## worklog
230
-
231
- ### task v7-FRONT — auth pages, dashboard, deploy matrix (2026-08-23)
232
-
233
- frontend-web pass over `web/` while v7-BACK lands the api in parallel.
234
- created `login.html` (username + password, generic inline errors,
235
- sanitized `?next=` redirect, configurable api base via `?api=` /
236
- `window.SADDLE_API` / `localStorage`, `credentials: "include"` on
237
- cross-origin clones), `register.html` (3-32 `[a-z0-9.-]` username with
238
- auto-lowercase, min-8 password with a length+class strength meter,
239
- confirmation match, rule checklist, no e-mail per project policy) and
240
- `dashboard.html` (tabbed user/admin panel: my sandboxes with create/open
241
- through `/api/v1/sandboxes`, bus event timeline polling
242
- `/api/v1/events?since=`, account info from `/api/v1/auth/me`, admin
243
- overview cards, mesh node table with `/api/v1/mesh` ping, users table
244
- without hash/salt columns, global sandboxes, audit log, username filter,
245
- skeleton loaders, empty states, 401 gate to the login link). added
246
- `package.json` (deploy manifest only, never published; zero dependencies, engines
247
- `>=26.7.0`, honored by the root `.nvmrc`), `web/Dockerfile` (node:26-slim image for
248
- ghcr.io/wenathlan/saddle (the main image; web/ ships inside), `/data` volume for `$SADDLE_DB`, unprivileged
249
- user, fetch-based healthcheck on 127.0.0.1 justified in-file); updated
250
- `netlify.toml` (publish `.` from inside `web/`, node 26.7.0, security
251
- headers, still zero functions), `caddyfile` (devthink.pro + www redirect,
252
- gzip, security headers, commented tls), `index.html` (sign-in link in the
253
- header only) and this readme (mesh architecture diagram, security,
254
- deploy matrix, static+node split rationale). contexts (22): loginform,
255
- registerform, strengthmeter, apibase, credentials, safenext, tabs,
256
- userpanel, sandboxlist, sandboxcreate, eventspoll, adminoverview, meshping,
257
- nodestable, userstable, auditlog, emptystates, skeletons, fatalgate,
258
- dockerhealthcheck, deploymatrix, worklog.
259
-
260
- validated live against the real v7 `server.js` (register -> login ->
261
- me -> sandbox create/exec -> events poll -> admin overview/users/nodes/
262
- sandboxes/audit -> 401 gates -> logout, all green) plus static checks
263
- (tag balance, `node --check` on every inline script, id cross-reference,
264
- json/toml contracts). contract notes for v7-BACK: `/auth/me` currently
265
- returns `{id, username, role, expiresat}` — the dashboard already picks
266
- `createdAt`/`lastLoginAt` defensively and lights them up when present;
267
- `GET /api/v1/sandboxes` (my list) is not live yet — the panel shows a
268
- rollout note on 404 and creation still works; `/api/v1/mesh/ping` does
269
- not exist yet — the ping button tries it first (mesh-signed) and falls
270
- back to a browser-side `/api/v1/health` latency probe.
271
-
272
- ### task v16-CHECK — repo-wide consistency audit (2026-08-23)
273
-
274
- auditoria total de metadados, versões, sincronização e testes pelo repositório
275
- inteiro (82 arquivos reais no `find`). resultado: **112 checks OK / 19 DIVERGE**
276
- (detalhado no relatório da task; nenhum arquivo corrigido — auditoria somente).
277
- testes: `node --test tests/*.test.ts` = **153/153 pass** (o readme principal
278
- ainda alega 148 em três lugares). principais divergências para corrigir:
279
-
280
- 1. `cores.json:4` metadata.version **6.0.0** (esperado 1.2.12; único dos dez
281
- data documents fora de lockstep, e usa envelope `metadata` em vez de `meta`)
282
- 2. `Dockerfile:589` label `org.opencontainers.image.version="6.0.0"` e título
283
- "e2ugh v6" (esperado 1.2.12)
284
- 3. `docker-compose.yml` image `e2ugh/engine:6.0.0` (linhas 100/273/320),
285
- `com.e2ugh.version: "6.0.0"` (:172) e cinco títulos "e2ugh v6 (...)"
286
- 4. `docker.config:621/633` labels OCI saddle **6.0.0** dentro do doc v9
287
- 5. `engineversions` (index.ts) não tem as chaves **npm 12.0.2** e
288
- **python 3.14.7** que o readme documenta no ledger
289
- 6. readme principal alega **148 testes** (linhas 278, 343 e linha 46 da
290
- tabela) — real: 153
291
- 7. `docs/architecture.md` inventário desatualizado: compute.ts 2992→3141,
292
- sandbox.js 1433→1786, server.js 640→1942, index.html 646→343,
293
- web readme 120→311, tests 3.674→4.311, total "62.200 linhas / 65
294
- arquivos"→71.399/82
295
- 8. readme principal, tabela de arquivos: faltam as linhas de `media.ts` e
296
- `.github/workflows/pages.yml` (the merged web pipeline); a linha 47 cita `web/Dockerfile` que
297
- **não existe em disco**
298
- 9. `web/Dockerfile` também é listado na tabela files deste readme (e no
299
- architecture.md) — arquivo ausente; a tabela não cobre db.js/auth.js/
300
- index.js/login.js/register.js/dashboard.js/drizzle.config.ts/init.sql/
301
- schema.prisma/mime.types
302
- 10. `/api/v1/mesh/ping` consumido pelo dashboard.js (com fallback) e citado
303
- aqui, mas não implementado no server.js
304
- 11. `GET /api/v1/sandboxes` (shelf) e `POST /api/v1/sandboxes/:id/resume`
305
- implementados mas ausentes da api table e do banner do server.js; o
306
- worklog v7-FRONT acima ainda diz "not live yet" (stale)
307
- 12. pages.yml: o pipeline web mesclado (validação, testes, bundle estático e Pages) documenta
308
- cinco actions docker/* — o job não existe (jobs reais: validate, test,
309
- staticbundle, release); readme principal :282 repete a alegação
310
- 13. comando `streaming` implementado no dispatcher mas ausente do array
311
- `commands`, do help e das manualpages (`man streaming` falha)
312
- 14. `ci.yml:28` trigger `branches: ain]` corrompido (deveria ser main) — o
313
- yaml valida como escalar, então o push CI nunca dispara no branch certo
314
- 15. package.json `exports` expõe cinco subpaths `./web/*` mas `files` não
315
- embarca web/ — exports quebrados no tarball npm publicado
316
-
317
- sincronizado e confirmado OK: as versões 1.2.12 nos demais dez configs +
318
- package.json + server.js + sandbox.js + ambos readmes; as 17 chaves de
319
- `engineversions` batem com o ledger do readme e com os ARGs do Dockerfile;
320
- os 8 cpus e 7 gpus de cpudata/gpudata batem com processors.json/gpus.json
321
- (cores/threads/vram/pci id); os 22 pins de actions do package.json batem
322
- com os `uses:` reais dos cinco workflows; todos os alvos de exports/files
323
- existem no disco; todos os COPYs do Dockerfile apontam para arquivos
324
- existentes; bibliografia = 145 entradas reais; SADDLE_MAX_SANDBOXES
325
- documentado (server.js:516 + fim deste readme).
326
-
327
- ## api surface additions
328
-
329
- - `GET /api/v1/sandboxes` - the user shelf: every sandbox row of the account (live and expired) with files/bytes/resumable
330
- - `POST /api/v1/sandboxes/:id/resume` - take the container back: rebuilds the engine from the stored spec, rebinds the persistent workspace, renews the ttl
331
- - `streaming` terminal command + `planmodelstreaming` (npm api) - the streaming memory plan: any workload size inside a small hot window
332
- - sandbox creation is uncapped by default (`SADDLE_MAX_SANDBOXES=0`)
333
-
334
- ## bottleneck analysis: where each layer really lives
335
-
336
- The project rule is absolute: no physical hardware is touched, probed or
337
- required anywhere. every layer below is software; the table states, per
338
- layer, where the bytes actually live and what the real ceiling is - so the
339
- "sensation" of an auto-contained vm with unlimited resources maps to
340
- engineering truth, not marketing.
341
-
342
- | layer | where data lives | reported identity | real ceiling | verdict |
343
- |---|---|---|---|---|
344
- | browser terminal (clones) | tab memory (ram of the visitor device, sandboxed by the browser itself) | EPYC 9965, 1 TiB, B200 | tab memory budget of the browser (~2-4 GiB typical); zero disk writes | no physical server touched; ceiling is the visitor browser, not any host |
345
- | sandbox id workspace | `sandboxfiles` table inside the container sqlite (`/data/saddle.db`), quota 16 MiB per id | `df -h` shows the quota as a filesystem | the container volume size; survives process restart, dies with `docker rm` unless the volume persists | auto-contained: the id, the files and the quota travel with the container |
346
- | container memory plan | `/etc/virtual/meminfo` snapshot (1 TiB) + LD_PRELOAD overlay | `free -h` reports 1 TiB | host ram + swap with `--memory-swap=-1`; the kernel OOM killer is the only real wall | unlimited is virtual identity; physical OOM never happens in the browser path because nothing heavy runs there |
347
- | vCPU / threads | cpuinfo spoof (192c/384t) + llvmpipe `LP_NUM_THREADS=0` | `lscpu` reports 192c/384t | host cores; llvmpipe caps at 32 threads (MR 31551) | virtual topology is free; compute-bound work is bounded by host cores |
348
- | vGPU | software adapters (llvmpipe/lavapipe/rusticl + smi tables) | `nvidia-smi` lists 8x B200 | cpu rasterization throughput; no cuda kernels run | identity is unlimited; throughput is cpu-bound by design |
349
- | mesh/auth state | sqlite of the main container (devthink.pro) | - | single main node db; clones forward | the main db is the management plane, sandboxes stay in their own container |
350
-
351
- Three honest bottlenecks worth knowing: (1) the browser tab memory bounds
352
- the clone terminal experience - by design, since the whole point is running
353
- the container in the visitor browser without any server round trip;
354
- (2) llvmpipe caps its own thread pool at 32 regardless of the spoofed
355
- topology, so rendering throughput follows that cap; (3) the main db grows
356
- with users and sandbox rows - the quota (16 MiB per sandbox id) and the
357
- sweep jobs bound it, and the db file is the single artifact to back up.
358
- None of these touch physical hardware of any visitor: the heaviest object
359
- that ever reaches a clone visitor is static javascript.
360
-
361
- ## the unlimited path: how "any size" actually runs
362
-
363
- The project rejects hardware limits by design; the two techniques that
364
- make unbounded workloads real in software, both implemented:
365
-
366
- 1. **streaming memory** (`compute.ts planmodelstreaming`, terminal command
367
- `streaming`): any workload decomposes into layers that load on demand
368
- (mmap + page cache) and evict after the last consumer, so a 1.5 tb
369
- model runs on a 4 gb window - proven: 3072 layers, 384 batches, peak
370
- 4.0 GiB resident. The workload size is unbounded; only the window is
371
- fixed. This is the exact technique production inference engines use
372
- for models larger than ram.
373
- 2. **federation of free nodes** (`compute.ts planfederatedquota`, the mesh
374
- in `mesh.js`): every clone/main node contributes its window to the
375
- pool; quotas sum across nodes, so capacity grows with each free host
376
- that joins (github runners, free vps tiers, contributor machines) -
377
- no hardware purchase anywhere.
378
-
379
- Combined: the identity layer reports unlimited (18 tb, 384 threads), the
380
- streaming layer makes any workload size executable inside a small window,
381
- and federation multiplies the window across free hosts. That is the
382
- no-limits architecture; the sandbox shelf itself is uncapped by default
383
- (SADDLE_MAX_SANDBOXES=0).
package/web/tsconfig.json DELETED
@@ -1,25 +0,0 @@
1
- {
2
- "include": ["**/*.ts", "**/*.tsx"],
3
- "exclude": ["node_modules", "build", "dist", "**/*.test.ts"],
4
- "compilerOptions": {
5
- "incremental": true,
6
- "tsBuildInfoFile": "./node_modules/typescript/tsbuildinfo",
7
- "noEmit": true,
8
- "target": "ESNext",
9
- "module": "ESNext",
10
- "strict": true,
11
- "lib": ["esnext", "dom", "dom.iterable"],
12
- "jsx": "preserve",
13
- "esModuleInterop": true,
14
- "skipLibCheck": true,
15
- "allowImportingTsExtensions": true,
16
- "moduleResolution": "bundler",
17
- "forceConsistentCasingInFileNames": true,
18
- "resolveJsonModule": true,
19
- "types": ["node", "vite/client"],
20
- "paths": {
21
- "@/*": ["./*"],
22
- "@saddle/isolation": ["../isolation.ts"]
23
- }
24
- }
25
- }