@wenathlan/saddle 2.0.167 → 2.0.170

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.
package/Dockerfile CHANGED
@@ -1,4 +1,4 @@
1
- # saddle 2.0.167 — THE ENGINE CONTAINER FILE
1
+ # saddle 2.0.170 — THE ENGINE CONTAINER FILE
2
2
  #
3
3
  # THE FAMILY CONTAINER DOCTRINE, THE SADDLE SHAPE: one versioned container
4
4
  # per engine, built like the site homes (argan, cadria) build theirs — a
@@ -158,7 +158,7 @@ ARG NODE_BASE_DIGEST=5adad64f9a1fca2d51972caa65983634c8a774eb300d72b20f7f2a0f603
158
158
  # ===== stage: solbuild — the locked npm tree and the console spa =====
159
159
  FROM --platform=$BUILDPLATFORM ${NODE_BASE_TAG}@sha256:${NODE_BASE_DIGEST} AS solbuild
160
160
 
161
- ARG SADDLE_VERSION=2.0.167
161
+ ARG SADDLE_VERSION=2.0.170
162
162
 
163
163
  WORKDIR /app
164
164
 
@@ -172,6 +172,12 @@ COPY saddle/ .
172
172
  # so the vault sources ride the context beside the saddle and land at
173
173
  # /vault where the import resolves (the family context fix).
174
174
  COPY vault/ /vault
175
+ # the routerbase sibling of the family law: saddle/routerbase.ts
176
+ # re-exports the family routerBase from ../devthink/routerbase.ts, so
177
+ # the devthink sources land at /devthink where the sol build resolves
178
+ # the re-export (sources only - the same never-installed law the vault
179
+ # sibling above rides, the family context fix the lanes carry).
180
+ COPY devthink/ /devthink
175
181
  RUN npm run web:build:pages \
176
182
  # the theme build emits the hashed SPA bundles straight at the app
177
183
  # root (the family static-first vite contract): stage them into a
@@ -186,7 +192,7 @@ RUN npm run web:build:pages \
186
192
  # ===== stage: base — the hardened runtime floor every profile shares =====
187
193
  FROM ${NODE_BASE_TAG}@sha256:${NODE_BASE_DIGEST} AS base
188
194
 
189
- ARG SADDLE_VERSION=2.0.167
195
+ ARG SADDLE_VERSION=2.0.170
190
196
  # the revision stamp the devthink container carries (DEVTHINK_REVISION
191
197
  # there): the publish legs pass the release sha at build time and the
192
198
  # revision label below declares the default-unknown local shape.
@@ -255,6 +261,20 @@ RUN case "${TARGETARCH:-}" in \
255
261
  esac
256
262
 
257
263
  COPY --from=solbuild /app/package.json /app/package-lock.json ./
264
+ # the both-blocks grammar heal (the saddle container law): the family
265
+ # manifest carries the same seats inside dependencies and
266
+ # devDependencies, and every npm tested (11.19.1 and 12.0.2) answers
267
+ # that duplicate seat by planting dev:true over the whole lock tree -
268
+ # so a plain `npm ci --omit=dev` installs nothing and the runtime stage
269
+ # dies at the cleanup walk with no node_modules to sweep. The derived
270
+ # prod-only manifest (the dependencies block alone, byte-identical
271
+ # specs) restores the honest production install: the lock the
272
+ # foundry/lockprod.ts heal stamped walks beside it and the --omit=dev
273
+ # lane lands the full production closure (the 1372-package install the
274
+ # local verification measured). Nothing is removed - the dev seats stay
275
+ # in the lock for the full-install lanes, only the runtime's own view
276
+ # of the manifest narrows.
277
+ RUN node -e "const fs=require('node:fs');const m=JSON.parse(fs.readFileSync('package.json','utf8'));delete m.devDependencies;fs.writeFileSync('package.json',JSON.stringify(m,null,2)+'\n')"
258
278
  RUN apt-get update \
259
279
  && apt-get upgrade -y \
260
280
  && rm -rf /var/lib/apt/lists/* \
@@ -489,7 +509,7 @@ LABEL org.opencontainers.image.title="saddle (the android os-distribution lane)"
489
509
  # docker build --target osv -t saddle:2.0.165-v .
490
510
  # docker build --target oswindows -t saddle:2.0.165-windows .
491
511
  FROM scratch AS osgrapheneos
492
- ARG SADDLE_VERSION=2.0.167
512
+ ARG SADDLE_VERSION=2.0.170
493
513
  ARG SADDLE_OS_PROFILE="grapheneos"
494
514
  ENV SADDLE_OS_PROFILE=grapheneos
495
515
  LABEL org.opencontainers.image.title="saddle (the grapheneos os-distribution catalog lane)" \
@@ -500,7 +520,7 @@ LABEL org.opencontainers.image.title="saddle (the grapheneos os-distribution cat
500
520
  COPY osprofiles/grapheneos.json /saddle/osprofile.json
501
521
 
502
522
  FROM scratch AS osmaruos
503
- ARG SADDLE_VERSION=2.0.167
523
+ ARG SADDLE_VERSION=2.0.170
504
524
  ARG SADDLE_OS_PROFILE="maruos"
505
525
  ENV SADDLE_OS_PROFILE=maruos
506
526
  LABEL org.opencontainers.image.title="saddle (the maru os os-distribution catalog lane)" \
@@ -511,7 +531,7 @@ LABEL org.opencontainers.image.title="saddle (the maru os os-distribution catalo
511
531
  COPY osprofiles/maruos.json /saddle/osprofile.json
512
532
 
513
533
  FROM scratch AS osv
514
- ARG SADDLE_VERSION=2.0.167
534
+ ARG SADDLE_VERSION=2.0.170
515
535
  ARG SADDLE_OS_PROFILE="v"
516
536
  ENV SADDLE_OS_PROFILE=v
517
537
  LABEL org.opencontainers.image.title="saddle (the v os-distribution catalog lane)" \
@@ -522,7 +542,7 @@ LABEL org.opencontainers.image.title="saddle (the v os-distribution catalog lane
522
542
  COPY osprofiles/v.json /saddle/osprofile.json
523
543
 
524
544
  FROM scratch AS oswindows
525
- ARG SADDLE_VERSION=2.0.167
545
+ ARG SADDLE_VERSION=2.0.170
526
546
  ARG SADDLE_OS_PROFILE="windows"
527
547
  ENV SADDLE_OS_PROFILE=windows
528
548
  LABEL org.opencontainers.image.title="saddle (the windows os-distribution catalog lane)" \
@@ -533,7 +553,7 @@ LABEL org.opencontainers.image.title="saddle (the windows os-distribution catalo
533
553
  COPY osprofiles/windows.json /saddle/osprofile.json
534
554
 
535
555
  FROM scratch AS ospostmarketos
536
- ARG SADDLE_VERSION=2.0.167
556
+ ARG SADDLE_VERSION=2.0.170
537
557
  ARG SADDLE_OS_PROFILE="postmarketos"
538
558
  ENV SADDLE_OS_PROFILE=postmarketos
539
559
  LABEL org.opencontainers.image.title="saddle (the postmarketos os-distribution catalog lane)" \
@@ -544,7 +564,7 @@ LABEL org.opencontainers.image.title="saddle (the postmarketos os-distribution c
544
564
  COPY osprofiles/postmarketos.json /saddle/osprofile.json
545
565
 
546
566
  FROM scratch AS oslineageos
547
- ARG SADDLE_VERSION=2.0.167
567
+ ARG SADDLE_VERSION=2.0.170
548
568
  ARG SADDLE_OS_PROFILE="lineageos"
549
569
  ENV SADDLE_OS_PROFILE=lineageos
550
570
  LABEL org.opencontainers.image.title="saddle (the lineageos os-distribution catalog lane)" \
@@ -555,7 +575,7 @@ LABEL org.opencontainers.image.title="saddle (the lineageos os-distribution cata
555
575
  COPY osprofiles/lineageos.json /saddle/osprofile.json
556
576
 
557
577
  FROM scratch AS oscalyxos
558
- ARG SADDLE_VERSION=2.0.167
578
+ ARG SADDLE_VERSION=2.0.170
559
579
  ARG SADDLE_OS_PROFILE="calyxos"
560
580
  ENV SADDLE_OS_PROFILE=calyxos
561
581
  LABEL org.opencontainers.image.title="saddle (the calyxos os-distribution catalog lane)" \
@@ -566,7 +586,7 @@ LABEL org.opencontainers.image.title="saddle (the calyxos os-distribution catalo
566
586
  COPY osprofiles/calyxos.json /saddle/osprofile.json
567
587
 
568
588
  FROM scratch AS osarchlinux
569
- ARG SADDLE_VERSION=2.0.167
589
+ ARG SADDLE_VERSION=2.0.170
570
590
  ARG SADDLE_OS_PROFILE="archlinux"
571
591
  ENV SADDLE_OS_PROFILE=archlinux
572
592
  LABEL org.opencontainers.image.title="saddle (the archlinux os-distribution catalog lane)" \
@@ -577,7 +597,7 @@ LABEL org.opencontainers.image.title="saddle (the archlinux os-distribution cata
577
597
  COPY osprofiles/archlinux.json /saddle/osprofile.json
578
598
 
579
599
  FROM scratch AS osalpine
580
- ARG SADDLE_VERSION=2.0.167
600
+ ARG SADDLE_VERSION=2.0.170
581
601
  ARG SADDLE_OS_PROFILE="alpine"
582
602
  ENV SADDLE_OS_PROFILE=alpine
583
603
  LABEL org.opencontainers.image.title="saddle (the alpine os-distribution catalog lane)" \
@@ -588,7 +608,7 @@ LABEL org.opencontainers.image.title="saddle (the alpine os-distribution catalog
588
608
  COPY osprofiles/alpine.json /saddle/osprofile.json
589
609
 
590
610
  FROM scratch AS osdebian
591
- ARG SADDLE_VERSION=2.0.167
611
+ ARG SADDLE_VERSION=2.0.170
592
612
  ARG SADDLE_OS_PROFILE="debian"
593
613
  ENV SADDLE_OS_PROFILE=debian
594
614
  LABEL org.opencontainers.image.title="saddle (the debian os-distribution catalog lane)" \
@@ -599,7 +619,7 @@ LABEL org.opencontainers.image.title="saddle (the debian os-distribution catalog
599
619
  COPY osprofiles/debian.json /saddle/osprofile.json
600
620
 
601
621
  FROM scratch AS osfedora
602
- ARG SADDLE_VERSION=2.0.167
622
+ ARG SADDLE_VERSION=2.0.170
603
623
  ARG SADDLE_OS_PROFILE="fedora"
604
624
  ENV SADDLE_OS_PROFILE=fedora
605
625
  LABEL org.opencontainers.image.title="saddle (the fedora os-distribution catalog lane)" \
@@ -610,7 +630,7 @@ LABEL org.opencontainers.image.title="saddle (the fedora os-distribution catalog
610
630
  COPY osprofiles/fedora.json /saddle/osprofile.json
611
631
 
612
632
  FROM scratch AS osnixos
613
- ARG SADDLE_VERSION=2.0.167
633
+ ARG SADDLE_VERSION=2.0.170
614
634
  ARG SADDLE_OS_PROFILE="nixos"
615
635
  ENV SADDLE_OS_PROFILE=nixos
616
636
  LABEL org.opencontainers.image.title="saddle (the nixos os-distribution catalog lane)" \
@@ -621,7 +641,7 @@ LABEL org.opencontainers.image.title="saddle (the nixos os-distribution catalog
621
641
  COPY osprofiles/nixos.json /saddle/osprofile.json
622
642
 
623
643
  FROM scratch AS osvoid
624
- ARG SADDLE_VERSION=2.0.167
644
+ ARG SADDLE_VERSION=2.0.170
625
645
  ARG SADDLE_OS_PROFILE="void"
626
646
  ENV SADDLE_OS_PROFILE=void
627
647
  LABEL org.opencontainers.image.title="saddle (the void os-distribution catalog lane)" \
package/README.md CHANGED
@@ -31,7 +31,7 @@ domain modules land beside the saddle domains:
31
31
  | `media.ts`, `render.ts`, `quantum.ts`, `tiers.ts` | media pipeline, GPU registry, quantum simulation, tier policy |
32
32
  | `security.ts`, `alternatives.ts` | hardening gates and the alternative-stack catalog |
33
33
  | `index.html`, `App.tsx`, `sol/` | the single SPA shell and the Sol theme (one tsx app with one stylesheet per theme) |
34
- | `server.ts`, `sandbox.ts` | the zero-dependency self-hosted API of the 2.0.167 grand-merge envelope (`node server.ts`; the api answers its version at `/api/v1/health`) and the browser-pure engine port |
34
+ | `server.ts`, `sandbox.ts` | the zero-dependency self-hosted API of the 2.0.170 grand-merge envelope (`node server.ts`; the api answers its version at `/api/v1/health`) and the browser-pure engine port |
35
35
  | `vercel.json`, `netlify.toml` | the platform deploy adapters beside the surface they publish (the caddy ghost stays out of the tree) |
36
36
  | `specs/*` package exports | processors, gpus, cores, boards and the qemu/mttg/passage/docker envelopes |
37
37
 
package/docker.config CHANGED
@@ -2,7 +2,7 @@
2
2
  "meta": {
3
3
  "name": "docker.config",
4
4
  "title": "saddle - Docker Engine and Containerization Configuration",
5
- "version": "2.0.167",
5
+ "version": "2.0.170",
6
6
  "date": "2026-11-06",
7
7
  "language": "en",
8
8
  "sources": [
@@ -121,9 +121,9 @@ The remaining categories of the old file were already domain-owned: kernel featu
121
121
 
122
122
  ## 3b. Test patterns
123
123
 
124
- Tests live in the `test/` directory (one of the five allowed directories) and run with the Node built-in runner: `node --test 'test/*.test.ts'` (153 tests across eleven files - engine, features, media, render, simulation, specs, virtualcpu, virtualgpu, virtualmemory, web, workflow - all passing on Node 24 and 26), the same runner the ci.yml smoke gate executes on Node 26.7.0. Patterns:
124
+ Tests live in the `test/` directory (one of the five allowed directories) and run on vitest: `vitest run` reads `test/*.test.ts` (153 tests across eleven files - engine, features, media, render, simulation, specs, virtualcpu, virtualgpu, virtualmemory, web, workflow), the same runner the ci.yml family suite executes through the app's `npm test`. Patterns:
125
125
 
126
- - `node:test` (`describe`/`it` or top-level `test`) plus `node:assert/strict`; no Jest, no third-party harness, no install step (the engine has zero runtime dependencies).
126
+ - vitest (`describe`/`it`) plus `node:assert/strict`; no Jest, no node:test (vitest is the only CI runner), no install step beyond the dev tooling (the engine has zero runtime dependencies).
127
127
  - One test file per area under test (planner and lint rules, cpuinfo/meminfo generators, MIG density validator, QEMU argv builder); assertions target public exports, never module internals.
128
128
  - Doc-tests remain `@example` blocks in JSDoc (the planner asserts in `index.ts`), and every test stays deterministic: ports use the 30000-59999 random band or explicit ephemeral allocation, no network, no timing-dependent assertions.
129
129
 
@@ -266,7 +266,7 @@ Supporting subsystems: warm pool (prewarm/acquire/release/refill/drain, MAP_PRIV
266
266
  | TypeScript | 7.0.2 (Go-native port); `strict`, `verbatimModuleSyntax`, `isolatedModules`, `erasableSyntaxOnly`, `exactOptionalPropertyTypes`, `noUncheckedIndexedAccess`; ES2025 target with `lib ES2025+ESNext.Disposable` |
267
267
  | Erasable modern syntax | `using`/`await using` (modellease, MetricsStore, sandboxhandle), `satisfies` (`engineversions as const satisfies Record<string,string>`), const type parameters (`validateSpec<const T>`), `#private` fields and accessors, `import.meta.filename` for CLI detection |
268
268
  | ESM | `"type": "module"`; relative imports carry the `.ts` extension; no CommonJS `require` |
269
- | Tests | `node:test` + `node:assert/strict` executed with `node --test 'test/*.test.ts'`; the dedicated `test/` directory holds eleven test files (148 cases: 86 planner/generator originals, 24 workflow/simulation gates, 18 feature-audit builders, 4 media pipeline, 1 snapshot regression, 9 web edition), ci.yml runs them on Node 26.7.0; no Jest; doc-tests as `@example` blocks (the planner asserts live in `index.ts` planner docs) |
269
+ | Tests | vitest + `node:assert/strict` executed with `vitest run` over `test/*.test.ts`; the dedicated `test/` directory holds eleven test files (148 cases: 86 planner/generator originals, 24 workflow/simulation gates, 18 feature-audit builders, 4 media pipeline, 1 snapshot regression, 9 web edition), ci.yml runs them through the app's `npm test`; no Jest; no node:test; doc-tests as `@example` blocks (the planner asserts live in `index.ts` planner docs) |
270
270
  | C++26 | `import std;` permitted, `std::expected` as the error channel, `std::mdspan` with the p2662r3 fallback guard, `std::print`/`std::format`, `enum class`, `[[nodiscard]]` on validate/argv/topology, RAII for every fd/socket (no exceptions in happy paths) |
271
271
  | C | C11 with `_GNU_SOURCE`, `dlsym(RTLD_NEXT)` resolution, xorshift32 deterministic drift, constructor priority 101 |
272
272
  | Python | 3.14 floor; stdlib only (socket, json, logging); try/except logging on every operation |
@@ -318,7 +318,7 @@ The old pins survive only inside `legacyCatalog` provenance blocks in `virtualha
318
318
 
319
319
  ## 15. CI graph (summary)
320
320
 
321
- Five workflows gate the tree: ci.yml (lint, typecheck matrix Node 24/26, the node:test suite in `test/`, build, flat-structure md5 guard, JSON schema validation of the ten data files), publishghcr.yml (the combined container pipeline: per-arch validation legs on push - clinfo/vulkaninfo/glxinfo probes inside the image - and the four-arch GHCR publication with provenance+SBOM on release), release.yml (GHCR + npm provenance + level-9 zip with SHA-256), pages.yml (the merged web pipeline: module syntax gate, boot smoke on port 37810, html lint, the webapi auth/mesh suite with a throwaway `E2UGH_DB`, the zip -9 static bundle and the GitHub Pages deployment of the web tree) and security.yml (CodeQL C/C++/TS, dependency review, Dockle/Trivy SARIF, gitleaks, Scorecard, Biome security group, license allowlist). Full descriptions and pins are in `readme.md`; the workflows themselves are the executable contract.
321
+ Five workflows gate the tree: ci.yml (lint, typecheck matrix Node 24/26, the vitest suite in `test/`, build, flat-structure md5 guard, JSON schema validation of the ten data files), publishghcr.yml (the combined container pipeline: per-arch validation legs on push - clinfo/vulkaninfo/glxinfo probes inside the image - and the four-arch GHCR publication with provenance+SBOM on release), release.yml (GHCR + npm provenance + level-9 zip with SHA-256), pages.yml (the merged web pipeline: module syntax gate, boot smoke on port 37810, html lint, the webapi auth/mesh suite with a throwaway `E2UGH_DB`, the zip -9 static bundle and the GitHub Pages deployment of the web tree) and security.yml (CodeQL C/C++/TS, dependency review, Dockle/Trivy SARIF, gitleaks, Scorecard, Biome security group, license allowlist). Full descriptions and pins are in `readme.md`; the workflows themselves are the executable contract.
322
322
 
323
323
  ## 16. Compliance checklist
324
324
 
@@ -220,7 +220,7 @@ The CI pipeline enforces the flat contract: one concern per file with 20-25 rela
220
220
  | 42 | `docs/viability.md` | the proof, research methodology, 145-entry bibliography and roadmap appendices A-C |
221
221
  | 43 | `docs/alternatives.md` | ten alternative sandbox families compared against the engine |
222
222
  | 43b | `docs/optimization.md` | the build-pipeline optimization roadmap (cross-compilation, prebuilt QEMU/Mesa layers, runner strategy) for the slow multi-arch grid |
223
- | 44 | `test/` | node:test suite executed by ci.yml with `node --test` (153 cases, including CI-gate replay and end-to-end simulation suites) |
223
+ | 44 | `test/` | vitest suite executed by ci.yml (`npm test`, `vitest run` over `test/*.test.ts`; 153 cases, including CI-gate replay and end-to-end simulation suites) |
224
224
  | 45 | `web/` | the web node (v7): `index.html` terminal sandbox, `login.html`/`register.html`/`dashboard.html` auth pages, `sandbox.js` browser-pure engine port (20 commands), `localauth.js` static-edge browser accounts (login/register/dashboard keep working on github pages with no api; accounts are mirrored to IndexedDB so partial clears never lose them, the dashboard carries backup/restore keyfile buttons, and the CODEOWNERS admins iakadion + akadion are seeded automatically with the shared bootstrap password - surviving any storage reset), `server.js` self-hosted node:http API (`/api/v1` sandboxes and exec), `db.js` sqlite persistence layer, `auth.js` scrypt password hashing with cookie sessions, `mesh.js` HMAC-signed node-to-node calls, `package.json`/`Dockerfile` (image `ghcr.io/wenathlan/e2ugh`; node pinned by the root `.nvmrc`), `schema.prisma`/`init.sql` database definition kept flat per the two-level contract, `mime.types`, `vercel.json`/`netlify.toml` static deploy without functions, `caddyfile` self-host proxy for devthink.pro, `readme.md` web docs |
225
225
  | 46 | `LICENSE` | full MIT text (Copyright (c) 2026 wenathlan) |
226
226
 
@@ -327,7 +327,7 @@ docker run --rm -p 37810:37810 -e E2UGH_DB=/data/e2ugh.db -v e2ughdata:/data \
327
327
 
328
328
  Five workflows gate the repository, all pinned to action releases verified on 2026-08-22:
329
329
 
330
- - **ci.yml** (every push and pull request) - six gates: Biome 2.5.11 lint and format; `tsc --noEmit` with TypeScript 7.0.2 on a Node 24/26 matrix; the Node built-in test runner over the dedicated `test/` suite (153 node:test cases); a build gate; the flat-structure contract (flat tree, no deep nesting, no duplicated content via md5); strict JSON schema validation of every tracked data file.
330
+ - **ci.yml** (every push and pull request) - six gates: Biome 2.5.11 lint and format; `tsc --noEmit` with TypeScript 7.0.2 on a Node 24/26 matrix; the vitest runner over the dedicated `test/` suite (153 test cases); a build gate; the flat-structure contract (flat tree, no deep nesting, no duplicated content via md5); strict JSON schema validation of every tracked data file.
331
331
  - **hardware variants** (publishghcr.yml matrix) - one dockerfile, three curated profiles baked at build time so operators pull exactly the flavor they need: `ghcr.io/wenathlan/e2ugh:latest` (max: EPYC 9965 192c/384t, 1 TiB plan, 8x B200), `:balanced` (Threadripper PRO 9995WX 96c/192t, 512 GiB, 2x B200) and `:lite` (Ryzen 9 9950X3D 16c/32t, 128 GiB, 1x RTX 5090); every profile tag also carries semver and sha suffixes, and the runtime overlay (`VHE_TOTALRAM_GB`, `VHE_CPUS`, `VHE_GPU_PROFILE` environment variables) still lets any variant report any catalog identity up to 18 TiB at run time - the baked profile is the default identity, never a ceiling.
332
332
  - **publishghcr.yml** (the combined container pipeline; main, tags and releases) - per-architecture validation legs (`linux/amd64` native; `linux/arm64`, `linux/ppc64le` and `linux/s390x` under QEMU user emulation) that validate the live stack inside the built image: `clinfo` must report Rusticl OpenCL 3.1 on llvmpipe, `vulkaninfo --summary` must find lavapipe Vulkan 1.4, and Xvfb + `glxinfo` must report llvmpipe OpenGL 4.6, executed with `--memory-swap=-1 --shm-size=2g`. On releases, production builds push per-arch images by digest (the max profile ships the four-arch registry surface: amd64, arm64, ppc64le, s390x) with provenance `mode=max` and SBOM attestations before `docker buildx imagetools` assembles the per-profile manifest indexes.
333
333
  - **release.yml** (version tags and releases) - three publications: the multi-arch GHCR image, the npm package with `--provenance --access public` (zero runtime dependencies), and a maximum-compression (level 9) source zip with SHA-256 checksums.
@@ -384,7 +384,7 @@ The test contract is the Node built-in runner: no Jest, no third-party harness,
384
384
  # from a checkout (Node 24+)
385
385
  npm run typecheck # tsc --noEmit, the TypeScript 7.0.2 gate
386
386
  npm run lint # Biome 2.5.11
387
- node --test 'test/*.test.ts' # the node:test suite (153 tests) - the glob form ci.yml uses
387
+ npm test # the vitest suite (153 tests) - vitest run over test/*.test.ts, the form ci.yml uses
388
388
 
389
389
  # from the published image (GHCR, linux/amd64 and linux/arm64)
390
390
  docker run --rm --memory-swap=-1 --shm-size=2g \
@@ -424,7 +424,7 @@ The `test/` suite (Node 26.7.0 in CI, Node 24+ locally, `node:assert/strict`, 15
424
424
  | SADDLE v5 / v6 (Meta TS) | 2026-08-22 wave | `src_core_orchestrator.ts` (2080 lines) and `coreorchestrator.ts` (1283 lines): RBAC, NUMA scheduler, balloon, SR-IOV, passage protocols, quotas, preemption, leader election, OTel exporter; plus `pipelinememorypassage.ts` host-aware memory planning | Unique subsystems absorbed into `orchestrator.ts` (5238 lines) and `virtualmemory.ts`; duplicated buses/state machines/metrics collapsed into one of each |
425
425
  | Virtual Hardware Engine v5 (e2ugh) | 2026-08-22, English | The 44-file public repo generation: 9 TS modules, 11 JSONs, 4 workflows, 10 English docs with ~120 cited sources | Baseline of the v2 code tree; modules renamed and extended (hardware -> virtualcpu, memory -> virtualmemory) |
426
426
  | e2ugh v2 | 2026-08-22+ | The merge: 57 CPUs, 19 GPUs + APUs, one event bus (51 typed topics), one 15-state lifecycle, one metrics store | Baseline of the v3 redistribution |
427
- | e2ugh v3-v9 (this repo) | 2026-08-23+ | The rebrand and redistribution: github.com/wenathlan/e2ugh, package 1.2.1, `future.ts` decomposed into `scheduler.ts` and `compute.ts` (checkpoint plans into `virtualization.ts`, post-quantum into `security.ts`), a `test/` node:test suite, and the corrected version ledger (kernel 7.1.9/6.18.45, edk2-stable202605, wgpu 30.x, Spin 3.6.0, containerd 2.3.4, WireGuard via kernel mainline) | Current |
427
+ | e2ugh v3-v9 (this repo) | 2026-08-23+ | The rebrand and redistribution: github.com/wenathlan/e2ugh, package 1.2.1, `future.ts` decomposed into `scheduler.ts` and `compute.ts` (checkpoint plans into `virtualization.ts`, post-quantum into `security.ts`), a `test/` vitest suite, and the corrected version ledger (kernel 7.1.9/6.18.45, edk2-stable202605, wgpu 30.x, Spin 3.6.0, containerd 2.3.4, WireGuard via kernel mainline) | Current |
428
428
 
429
429
  Full per-generation detail, including the verification reports for each wave, is preserved in `docs/viability.md`.
430
430
 
package/modalities.ts CHANGED
@@ -2492,7 +2492,7 @@ export const MODALITYENGINE: readonly ModalitySpec[] = [
2492
2492
  slug: "runnerdeno",
2493
2493
  lane: "runner",
2494
2494
  isolation: "process",
2495
- state: "planned",
2495
+ state: "live",
2496
2496
  drivers: ["deno"],
2497
2497
  hardening: ["permission-map"],
2498
2498
  boot: "hot",
@@ -2508,10 +2508,18 @@ export const MODALITYENGINE: readonly ModalitySpec[] = [
2508
2508
  num: 19,
2509
2509
  lane: "runner",
2510
2510
  binary: "deno",
2511
- // the payload rides the staged program file — the read permission maps
2512
- // the sandbox alone, the stdin dash never rides (no pipe is written).
2511
+ // the real deno runner: the payload rides the staged program file —
2512
+ // the read permission maps the sandbox alone, the module cache rides
2513
+ // the sandbox tree (DENO_DIR never touches the host home — the same
2514
+ // seat the edge lane carries), the stdin dash never rides (no pipe
2515
+ // is written). the runtime detection rides the spawner's own absence
2516
+ // grammar — the same probehostbinary seat the edge lane table
2517
+ // carries — and a host without the deno binary answers the honest
2518
+ // coded absence, never a faked run (the seat never fakes a runtime).
2513
2519
  argv: ({ file }) => ["deno", "run", "--allow-read=/sandbox", file],
2520
+ env: { DENO_DIR: "/sandbox/denodir" },
2514
2521
  hardening: ["permission-map"],
2522
+ probe: "the host must carry the deno runtime (the runner-deno seat the owner's law 268 charges)",
2515
2523
  }),
2516
2524
  },
2517
2525
  {
@@ -4191,7 +4199,7 @@ export const MODALITYENGINE: readonly ModalitySpec[] = [
4191
4199
  slug: "storagesqlite",
4192
4200
  lane: "sandbox",
4193
4201
  isolation: "process",
4194
- state: "planned",
4202
+ state: "live",
4195
4203
  drivers: ["sqlite"],
4196
4204
  hardening: ["file-quota"],
4197
4205
  boot: "hot",
@@ -4207,27 +4215,60 @@ export const MODALITYENGINE: readonly ModalitySpec[] = [
4207
4215
  num: 72,
4208
4216
  lane: "sandbox",
4209
4217
  operate: async (request) => {
4218
+ // the real vault roundtrip: the node:sqlite runtime the engine host
4219
+ // carries — without it the seat answers the honest coded absence, and
4220
+ // with it one full roundtrip (create, insert, select, drop) on a
4221
+ // private in-memory table, the readback compared against the payload
4222
+ // character for character (the seat never fakes a database and never
4223
+ // opens a file — the store rides :memory: alone, the caller values
4224
+ // ride parameterized statements, only the seat's own generated table
4225
+ // name rides the statement text).
4226
+ let sqlite: typeof import("node:sqlite");
4210
4227
  try {
4211
- const sqlite = (await import("node:sqlite")) as typeof import("node:sqlite");
4212
- const database = new sqlite.DatabaseSync(":memory:");
4213
- try {
4214
- database.exec("CREATE TABLE sandbox_artifacts (id INTEGER PRIMARY KEY, payload TEXT)");
4215
- const statement = database.prepare("INSERT INTO sandbox_artifacts (payload) VALUES (?)");
4216
- statement.run(request.code.slice(0, 1024));
4217
- const rows = database.prepare("SELECT id, payload FROM sandbox_artifacts").all();
4218
- return {
4219
- stdout: `sqlite vault: ${rows.length} row(s) answered by the in-memory store (node:sqlite, :memory:)`,
4220
- artifact: { rows: rows.length, engine: "node:sqlite" },
4221
- };
4222
- } finally {
4223
- database.close();
4224
- }
4228
+ sqlite = (await import("node:sqlite")) as typeof import("node:sqlite");
4225
4229
  } catch (error) {
4226
4230
  throw Object.assign(
4227
4231
  new Error(`the sqlite runtime is absent from this host: ${error instanceof Error ? error.message : String(error)}`),
4228
4232
  { code: "HOST_RUNTIME_ABSENT" },
4229
4233
  );
4230
4234
  }
4235
+ const table = `saddle_sqlite_${Date.now().toString(36)}${Math.floor(Math.random() * 1679616).toString(36)}`;
4236
+ const payload = request.code;
4237
+ let readback = "";
4238
+ const database = new sqlite.DatabaseSync(":memory:");
4239
+ try {
4240
+ database.exec(`CREATE TABLE ${table} (k TEXT PRIMARY KEY, v TEXT)`);
4241
+ database.prepare(`INSERT INTO ${table} (k, v) VALUES (?, ?)`).run("saddle-payload", payload);
4242
+ readback = String(
4243
+ (database.prepare(`SELECT v FROM ${table} WHERE k = ?`).get("saddle-payload") as { v?: unknown } | undefined)?.v ?? "",
4244
+ );
4245
+ } catch (error) {
4246
+ throw Object.assign(
4247
+ new Error(`the sqlite vault refused the roundtrip: ${error instanceof Error ? error.message : String(error)}`),
4248
+ { code: "SQLITE_ROUNDTRIP_FAILED" },
4249
+ );
4250
+ } finally {
4251
+ try {
4252
+ database.exec(`DROP TABLE IF EXISTS ${table}`);
4253
+ } catch {
4254
+ /* the roundtrip already answered; the drop is best effort */
4255
+ }
4256
+ try {
4257
+ database.close();
4258
+ } catch {
4259
+ /* the close is best effort */
4260
+ }
4261
+ }
4262
+ if (readback !== payload) {
4263
+ throw Object.assign(
4264
+ new Error(`the sqlite roundtrip answered ${readback.length} characters for ${payload.length} written`),
4265
+ { code: "SQLITE_ROUNDTRIP_FAILED" },
4266
+ );
4267
+ }
4268
+ return {
4269
+ stdout: `sqlite vault: ${payload.length} characters roundtripped through the node:sqlite in-memory store`,
4270
+ artifact: { rows: 1, engine: "node:sqlite", roundtrip: true, store: ":memory:" },
4271
+ };
4231
4272
  },
4232
4273
  }),
4233
4274
  },
@@ -4427,7 +4468,7 @@ export const MODALITYENGINE: readonly ModalitySpec[] = [
4427
4468
  slug: "networkdns",
4428
4469
  lane: "sandbox",
4429
4470
  isolation: "process",
4430
- state: "planned",
4471
+ state: "live",
4431
4472
  drivers: ["dns"],
4432
4473
  hardening: ["resolver-allowlist"],
4433
4474
  boot: "hot",
@@ -4443,11 +4484,47 @@ export const MODALITYENGINE: readonly ModalitySpec[] = [
4443
4484
  num: 77,
4444
4485
  lane: "sandbox",
4445
4486
  operate: async (request) => {
4487
+ // the real resolution roundtrip: the node:dns resolver the engine
4488
+ // host carries — one lookup of the pinned well-known name
4489
+ // ("localhost", the name every host resolver answers; the caller
4490
+ // may name another, the roundtrip rides the same resolver call)
4491
+ // with the answered records verified as the honest result (the
4492
+ // non-empty answer, the address, the address family). the seat
4493
+ // never fakes a response and never opens a socket beyond the
4494
+ // resolver call — a host without a working resolver path answers
4495
+ // the honest coded refusal, never a canned answer (the wire
4496
+ // question the builddnsquery builder frames rides the artifact
4497
+ // beside the records the resolver actually answered — the same
4498
+ // grammar the sqlite vault roundtrip carries).
4499
+ const dns = await import("node:dns");
4446
4500
  const inputs = (request.inputs ?? {}) as { domain?: string; qtype?: string };
4447
- const packet = builddnsquery(inputs.domain ?? "devthink.dev", inputs.qtype ?? "a");
4501
+ const domain = inputs.domain ?? "localhost";
4502
+ const qtype = (inputs.qtype ?? "a").toLowerCase();
4503
+ const packet = builddnsquery(domain, qtype);
4504
+ const family = qtype === "a" ? 4 : qtype === "aaaa" ? 6 : 0;
4505
+ let records: import("node:dns").LookupAddress[];
4506
+ try {
4507
+ records = await dns.promises.lookup(domain, { all: true, family });
4508
+ } catch (error) {
4509
+ throw Object.assign(
4510
+ new Error(
4511
+ `the host resolver refused the ${qtype} roundtrip for ${domain}: ${error instanceof Error ? error.message : String(error)}`,
4512
+ ),
4513
+ { code: "DNS_RESOLUTION_REFUSED" },
4514
+ );
4515
+ }
4516
+ if (
4517
+ records.length === 0 ||
4518
+ records.some((record) => record.address.length === 0 || (record.family !== 4 && record.family !== 6))
4519
+ ) {
4520
+ throw Object.assign(
4521
+ new Error(`the dns roundtrip answered ${records.length} records for ${domain} — the honest result never rides a malformed answer`),
4522
+ { code: "DNS_ROUNDTRIP_FAILED" },
4523
+ );
4524
+ }
4448
4525
  return {
4449
- stdout: `dns: the ${inputs.qtype ?? "a"} query for ${inputs.domain ?? "devthink.dev"} answered as a ${packet.length / 2}-byte wire packet`,
4450
- artifact: { packet, domain: inputs.domain ?? "devthink.dev", qtype: inputs.qtype ?? "a" },
4526
+ stdout: `dns resolver: ${domain} resolved ${records.length} records through the node:dns host resolver (the ${qtype} question, the honest roundtrip)`,
4527
+ artifact: { packet, domain, qtype, records, resolved: true, resolver: "node:dns" },
4451
4528
  };
4452
4529
  },
4453
4530
  }),