@wenathlan/saddle 2.0.160 → 2.0.162

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.160 — THE ENGINE CONTAINER FILE
1
+ # saddle 2.0.162 — 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
@@ -16,14 +16,18 @@
16
16
  # THE THREE VARIANT PROFILES (the saddle-exclusive law: the only app of
17
17
  # the family that ships three) — select with the target flag:
18
18
  #
19
- # docker build --target max -t saddle:2.0.160 .
20
- # docker build --target balanced -t saddle:2.0.160-balanced .
21
- # docker build --target lite -t saddle:2.0.160-lite .
19
+ # docker build --target max -t saddle:2.0.162 .
20
+ # docker build --target balanced -t saddle:2.0.162-balanced .
21
+ # docker build --target lite -t saddle:2.0.162-lite .
22
22
  #
23
23
  # max — the full profile catalog: the epyc 9965 class, 192 vcpu,
24
24
  # 384 threads, 1024 gib ram plan, the b200 gpu plan x8.
25
25
  # balanced — the threadripper pro 9995wx class, 96 vcpu, 192 threads,
26
- # 512 gib ram plan, the b200 gpu plan x2.
26
+ # 512 gib ram plan, the b200 gpu plan x2. THE DEFAULT
27
+ # TARGET: the plain `docker build -f saddle/Dockerfile .`
28
+ # with no --target answers the balanced stage (the last
29
+ # stage of the file) — the SADDLE_PROFILE lane's documented
30
+ # default — and the publish legs always name their target.
27
31
  # lite — the ryzen 9950x3d class, 16 vcpu, 32 threads, 128 gib
28
32
  # ram plan, the rtx 5090 gpu plan x1.
29
33
  #
@@ -31,7 +35,31 @@
31
35
  # assert), the catalog constants stay the publish matrix's single source,
32
36
  # and every profile ships the same engine graph — the same ninety
33
37
  # modalities, the same console, the same health surface; only the declared
34
- # hardware plan differs.
38
+ # hardware plan differs. Each variant stage re-declares the six plan
39
+ # build-args with its own honest defaults and re-stamps the plan ENV, so a
40
+ # local `--target` leg answers its real hardware plan with no args passed
41
+ # (the publish legs pass the matrix constants per leg and override the
42
+ # defaults — one lane, two honest entries).
43
+ #
44
+ # THE ELEVEN-DEVICE BANK the profiles declare against (the exotic law's
45
+ # hardware vocabulary): the quickCatalog of saddle/virtualhardware.json
46
+ # carries exactly eleven virtual devices — seven cpu classes (epyc 9965,
47
+ # epyc 9755, threadripper 7995wx, ryzen 9950x3d, ryzen 9950x3d2, ryzen
48
+ # 9800x3d, xeon 6980p) and four gpu classes (rtx 5090, rtx pro 6000,
49
+ # b200, h100) — and the three profiles stamp their plan (the cpu class,
50
+ # the vcpu/thread counts, the ram plan, the gpu class and count) from
51
+ # that bank; every device stays virtual (the engine presents it, the
52
+ # visitor machine never lends its own).
53
+ #
54
+ # THE GUEST OS LANE the sandbox modalities answer (the owner's nickname
55
+ # law, saddle/docs/ninetymodalities.md): the spec vocabulary boots linux
56
+ # plus the nickname builds — marsh (marshmallow) resolves android,
57
+ # grapenios and grafene os resolve grapheneos, omashi resolves omarchy —
58
+ # and the owner's "v" names no guest os: it answers the languagev
59
+ # compiler seat of the ninety modalities. The container lane itself rides
60
+ # linux (the eleven-architecture standard below); the guest os vocabulary
61
+ # is the sandbox spec surface the console answers, never a host
62
+ # distribution this container installs.
35
63
  #
36
64
  # THE ELEVEN-ARCHITECTURE PLATFORM STANDARD (the pr15b law): every
37
65
  # profile answers the eleven architectures the container standard names —
@@ -46,7 +74,13 @@
46
74
  # loong64 and the wasm pair) answer the standard's honest coded absence
47
75
  # (ARCH_IMAGE_ABSENT), never a pretended build leg — the same honesty
48
76
  # law the containerarch table keeps for its own pinned base: a leg
49
- # never claims a manifest the base does not carry.
77
+ # never claims a manifest the base does not carry. The base stage
78
+ # declares the TARGETPLATFORM/TARGETARCH automatic build-args (the
79
+ # devthink container's standard arch interface) and stamps the honesty
80
+ # law into the build log with its arch guard: a leg the base carries
81
+ # answers its leg line, a legacy builder without a platform axis answers
82
+ # its documented note, and anything else answers the coded refusal
83
+ # before a heavy step runs.
50
84
  #
51
85
  # THE CONTEXT LAW (the family context fix): this file COPYs from two
52
86
  # trees (saddle/ and the engine-ownership sibling vault/), so every build
@@ -81,7 +115,7 @@ ARG NODE_BASE_DIGEST
81
115
  # ===== stage: solbuild — the locked npm tree and the console spa =====
82
116
  FROM --platform=$BUILDPLATFORM ${NODE_BASE_TAG}@sha256:${NODE_BASE_DIGEST} AS solbuild
83
117
 
84
- ARG SADDLE_VERSION=2.0.160
118
+ ARG SADDLE_VERSION=2.0.162
85
119
 
86
120
  WORKDIR /app
87
121
 
@@ -109,7 +143,17 @@ RUN npm run web:build:pages \
109
143
  # ===== stage: base — the hardened runtime floor every profile shares =====
110
144
  FROM ${NODE_BASE_TAG}@sha256:${NODE_BASE_DIGEST} AS base
111
145
 
112
- ARG SADDLE_VERSION=2.0.160
146
+ ARG SADDLE_VERSION=2.0.162
147
+ # the revision stamp the devthink container carries (DEVTHINK_REVISION
148
+ # there): the publish legs pass the release sha at build time and the
149
+ # revision label below declares the default-unknown local shape.
150
+ ARG SADDLE_REVISION=unknown
151
+ # the automatic platform args (the devthink standard arch interface):
152
+ # the multi-arch node base resolves each leg's platform itself, and the
153
+ # arch guard below reads TARGETARCH to stamp the eleven-architecture
154
+ # law into the build log of every leg.
155
+ ARG TARGETPLATFORM
156
+ ARG TARGETARCH
113
157
  ARG SADDLE_PROFILE=max
114
158
  ARG VHE_PROFILE_CPU="epyc9965"
115
159
  ARG VHE_PROFILE_VCPUS="192"
@@ -123,7 +167,8 @@ LABEL org.opencontainers.image.title="saddle" \
123
167
  org.opencontainers.image.version="${SADDLE_VERSION}" \
124
168
  org.opencontainers.image.licenses="GPL-3.0-only" \
125
169
  org.opencontainers.image.source="https://github.com/wenathlan/devthink" \
126
- org.opencontainers.image.documentation="https://github.com/wenathlan/devthink/tree/main/saddle"
170
+ org.opencontainers.image.documentation="https://github.com/wenathlan/devthink/tree/main/saddle" \
171
+ org.opencontainers.image.revision="${SADDLE_REVISION}"
127
172
 
128
173
  WORKDIR /app
129
174
 
@@ -144,6 +189,24 @@ ENV NODE_ENV=production \
144
189
  SADDLE_PROFILE_GPU=${VHE_PROFILE_GPU} \
145
190
  SADDLE_PROFILE_GPUS=${VHE_PROFILE_GPUS}
146
191
 
192
+ # the arch-leg guard of the eleven-architecture standard: the pinned
193
+ # node base resolves the platform of every leg buildx names, and this
194
+ # echo layer stamps the honesty law into the build log — the four seats
195
+ # the base carries answer their leg line, the legacy builder (no
196
+ # platform axis) answers its documented note, and any other seat answers
197
+ # the coded refusal before the heavy steps below could run (buildx
198
+ # itself fails such a leg at base resolution first; the guard keeps the
199
+ # law loud on every builder).
200
+ RUN case "${TARGETARCH:-}" in \
201
+ amd64|arm64|ppc64le|s390x) \
202
+ echo "arch leg ${TARGETARCH} of ${TARGETPLATFORM:-<unresolved>}: one of the four seats the pinned node base carries of the eleven-architecture standard" ;; \
203
+ "") \
204
+ echo "arch leg unresolved: the legacy builder carries no platform axis; buildkit answers TARGETARCH per leg" ;; \
205
+ *) \
206
+ echo "the eleven-architecture standard answers the ${TARGETARCH} seat with the honest coded absence (ARCH_IMAGE_ABSENT): the pinned node base carries no such manifest" >&2; \
207
+ exit 1 ;; \
208
+ esac
209
+
147
210
  COPY --from=solbuild /app/package.json /app/package-lock.json ./
148
211
  RUN apt-get update \
149
212
  && apt-get upgrade -y \
@@ -196,45 +259,123 @@ RUN apt-get update \
196
259
  # the solbuild stage lands it). with the graph closed, node
197
260
  # webserver.ts starts clean; before the fix the container crashed at
198
261
  # boot with module-not-found.
262
+ #
263
+ # The two lazy engine seats ride the same closed graph (the exotic law:
264
+ # the ninety modalities answer real runs inside the container, never the
265
+ # guarded absence): webserver.ts mounts the edge artifact vault through
266
+ # a first-use import of virtual.ts (the guarded loader degrades to null
267
+ # when the module is absent, so a container without it answers no
268
+ # artifact persistence), and edgelandes.ts lazily mounts virtual.ts for
269
+ # its virtual-file, s3 and persistence recipe seats plus tiers.ts for
270
+ # the kernel recipe seat — copying both keeps those seats live inside
271
+ # the container, and their import closures stay inside the set
272
+ # (virtual.ts pulls foundation.ts only; tiers.ts pulls node builtins
273
+ # only).
199
274
  # run the console with: docker run saddle:<version>
200
- COPY --from=solbuild /app/webserver.ts /app/db.ts /app/labels.ts /app/auth.ts /app/mesh.ts /app/sandbox.ts /app/modalityregistry.ts /app/edgeplane.ts /app/workloadlaw.ts /app/foundation.ts /app/edgelandes.ts /app/scriptlanes.ts /app/execution.ts /app/mime.types /app/init.sql /app/schema.prisma ./
275
+ COPY --from=solbuild /app/webserver.ts /app/db.ts /app/labels.ts /app/auth.ts /app/mesh.ts /app/sandbox.ts /app/modalityregistry.ts /app/edgeplane.ts /app/workloadlaw.ts /app/foundation.ts /app/edgelandes.ts /app/scriptlanes.ts /app/execution.ts /app/virtual.ts /app/tiers.ts /app/mime.types /app/init.sql /app/schema.prisma ./
201
276
  # the label store of the console stack: labels.ts (pulled by db.ts)
202
277
  # resolves ../vault/labelstore.ts at /vault - the same family context
203
278
  # fix the solbuild stage applies.
204
279
  COPY --from=solbuild /vault/labelstore.ts /vault/labelstore.ts
205
280
  COPY --from=solbuild /out/spa ./
206
281
 
282
+ # the writable state home of the engine console (the vault engine
283
+ # container's /app/data pattern): db.ts resolves the sqlite store at
284
+ # import time (SADDLE_DB, else ./saddle.db under the working directory)
285
+ # and every directory the COPY chain staged stays root-owned, so the
286
+ # unprivileged node user needs a home it owns before the database file
287
+ # can open — without it the console crashed at boot on the sqlite
288
+ # cantopen refusal. An operator pins the persistent volume here; the
289
+ # engine creates the store on first boot.
290
+ RUN mkdir -p /app/data && chown -R node:node /app/data
291
+
207
292
  USER node
208
293
 
209
294
  # the console service doors: the webserver answers the engine api and the
210
295
  # staged spa on the saddle port (the source-is-library law runs the .ts
211
- # graph natively - node 26 strips the types, zero npm dependencies).
296
+ # graph natively - node 26 strips the types, zero npm dependencies), the
297
+ # port pin keeps the webserver's random-port fallback from ever
298
+ # answering, and the sqlite store opens under the writable state home
299
+ # above.
212
300
  ENV SADDLE_PORT=8080 \
213
- SADDLE_HOST=0.0.0.0
301
+ SADDLE_HOST=0.0.0.0 \
302
+ SADDLE_DB=/app/data/saddle.db
214
303
 
215
304
  EXPOSE 8080
216
305
 
217
- # the health probe rides the public /health route the webserver answers
218
- # (node fetch, zero wget dependency on the slim debian base; the callback
219
- # spells function() — no arrow, no equal sign — so the dockle CIS-DI-0010
220
- # heuristic never reads a credential pair into the recorded healthcheck).
221
- HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 CMD ["node", "-e", "fetch('http://127.0.0.1:8080/health').then(function(r){if(!r.ok)process.exit(1)}).catch(function(){process.exit(1)})"]
306
+ # the health probe rides the PUBLIC /api/v1/health route the webserver
307
+ # answers (the engine's own health surface: the ok flag, the version, the
308
+ # db healthcheck — never the spa shell the extensionless /health path
309
+ # serves, which would answer 200 html even with the engine sealed). The
310
+ # port follows SADDLE_PORT with the 8080 fallback (node fetch, zero wget
311
+ # dependency on the slim debian base; the callback spells function() — no
312
+ # arrow, no equal sign — so the dockle CIS-DI-0010 heuristic never reads
313
+ # a credential pair into the recorded healthcheck).
314
+ HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 CMD ["node", "-e", "fetch('http://127.0.0.1:'+(process.env.SADDLE_PORT||8080)+'/api/v1/health').then(function(r){return r.json()}).then(function(j){if(!j.ok)process.exit(1)}).catch(function(){process.exit(1)})"]
222
315
 
223
316
  ENTRYPOINT ["node", "webserver.ts"]
224
317
 
225
318
  # ===== the variant profile targets (the saddle-exclusive three) =====
226
- # max — the full profile catalog (the default the plain `docker build`
227
- # answers when no target is named is the last stage of the file: lite
228
- # below; the publish lanes always name their target).
229
- FROM base AS max
230
- ENV SADDLE_PROFILE=max
319
+ # Every variant stage re-declares the six plan build-args with its own
320
+ # honest defaults and re-stamps the plan ENV: a local `--target` leg
321
+ # answers its real hardware plan with no args passed (the pre-fix file
322
+ # stamped the max plan under every profile name when the caller passed
323
+ # no VHE_PROFILE_* args), and the publish legs pass the matrix constants
324
+ # per leg and override the defaults — the publish matrix stays the single
325
+ # source of the catalog either way.
231
326
 
232
- # balanced — the threadripper pro class, the b200 plan x2.
233
- FROM base AS balanced
234
- ENV SADDLE_PROFILE=balanced
327
+ # max — the full profile catalog: the epyc 9965 class, 192 vcpu, 384
328
+ # threads, 1024 gib ram, the b200 gpu plan x8 (the bare version tag the
329
+ # registry lane answers).
330
+ FROM base AS max
331
+ ARG VHE_PROFILE_CPU="epyc9965"
332
+ ARG VHE_PROFILE_VCPUS="192"
333
+ ARG VHE_PROFILE_THREADS="384"
334
+ ARG VHE_PROFILE_RAM_GB="1024"
335
+ ARG VHE_PROFILE_GPU="b200"
336
+ ARG VHE_PROFILE_GPUS="8"
337
+ ENV SADDLE_PROFILE=max \
338
+ SADDLE_PROFILE_CPU=${VHE_PROFILE_CPU} \
339
+ SADDLE_PROFILE_VCPUS=${VHE_PROFILE_VCPUS} \
340
+ SADDLE_PROFILE_THREADS=${VHE_PROFILE_THREADS} \
341
+ SADDLE_PROFILE_RAM_GB=${VHE_PROFILE_RAM_GB} \
342
+ SADDLE_PROFILE_GPU=${VHE_PROFILE_GPU} \
343
+ SADDLE_PROFILE_GPUS=${VHE_PROFILE_GPUS}
235
344
 
236
- # lite — the ryzen class, the rtx 5090 plan x1 (the default target: the
237
- # plain `docker build -f saddle/Dockerfile .` with no --target answers
238
- # lite, the lightest leg the family carries).
345
+ # lite — the ryzen 9950x3d class, 16 vcpu, 32 threads, 128 gib ram, the
346
+ # rtx 5090 gpu plan x1 (the -lite suffix tag the registry lane answers).
239
347
  FROM base AS lite
240
- ENV SADDLE_PROFILE=lite
348
+ ARG VHE_PROFILE_CPU="ryzen9950x3d"
349
+ ARG VHE_PROFILE_VCPUS="16"
350
+ ARG VHE_PROFILE_THREADS="32"
351
+ ARG VHE_PROFILE_RAM_GB="128"
352
+ ARG VHE_PROFILE_GPU="rtx5090"
353
+ ARG VHE_PROFILE_GPUS="1"
354
+ ENV SADDLE_PROFILE=lite \
355
+ SADDLE_PROFILE_CPU=${VHE_PROFILE_CPU} \
356
+ SADDLE_PROFILE_VCPUS=${VHE_PROFILE_VCPUS} \
357
+ SADDLE_PROFILE_THREADS=${VHE_PROFILE_THREADS} \
358
+ SADDLE_PROFILE_RAM_GB=${VHE_PROFILE_RAM_GB} \
359
+ SADDLE_PROFILE_GPU=${VHE_PROFILE_GPU} \
360
+ SADDLE_PROFILE_GPUS=${VHE_PROFILE_GPUS}
361
+
362
+ # balanced — the threadripper pro 9995wx class, 96 vcpu, 192 threads,
363
+ # 512 gib ram, the b200 gpu plan x2 (the -balanced suffix tag the
364
+ # registry lane answers). THE DEFAULT TARGET: the last stage of the
365
+ # file — the plain `docker build -f saddle/Dockerfile .` with no
366
+ # --target answers the balanced profile (the SADDLE_PROFILE lane's
367
+ # documented default).
368
+ FROM base AS balanced
369
+ ARG VHE_PROFILE_CPU="threadripperpro9995wx"
370
+ ARG VHE_PROFILE_VCPUS="96"
371
+ ARG VHE_PROFILE_THREADS="192"
372
+ ARG VHE_PROFILE_RAM_GB="512"
373
+ ARG VHE_PROFILE_GPU="b200"
374
+ ARG VHE_PROFILE_GPUS="2"
375
+ ENV SADDLE_PROFILE=balanced \
376
+ SADDLE_PROFILE_CPU=${VHE_PROFILE_CPU} \
377
+ SADDLE_PROFILE_VCPUS=${VHE_PROFILE_VCPUS} \
378
+ SADDLE_PROFILE_THREADS=${VHE_PROFILE_THREADS} \
379
+ SADDLE_PROFILE_RAM_GB=${VHE_PROFILE_RAM_GB} \
380
+ SADDLE_PROFILE_GPU=${VHE_PROFILE_GPU} \
381
+ SADDLE_PROFILE_GPUS=${VHE_PROFILE_GPUS}
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.160 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.162 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
 
@@ -249,7 +249,7 @@ export const MODALITIES: readonly ModalityRecord[] = Object.freeze([
249
249
  label: "deno runtime",
250
250
  family: "packages",
251
251
  isolation: "process",
252
- state: "live",
252
+ state: "planned",
253
253
  drivers: ["deno"],
254
254
  hardening: ["permission-map"],
255
255
  boot: "hot",
@@ -305,7 +305,7 @@ export const MODALITIES: readonly ModalityRecord[] = Object.freeze([
305
305
  label: "v8 isolates",
306
306
  family: "runner",
307
307
  isolation: "process",
308
- state: "live",
308
+ state: "planned",
309
309
  drivers: ["v8"],
310
310
  hardening: ["no-fs", "cpu-quota"],
311
311
  boot: "hot",
@@ -351,7 +351,7 @@ export const MODALITIES: readonly ModalityRecord[] = Object.freeze([
351
351
  label: "rustc toolchain",
352
352
  family: "language",
353
353
  isolation: "process",
354
- state: "live",
354
+ state: "planned",
355
355
  drivers: ["rustc"],
356
356
  hardening: ["crate-allowlist"],
357
357
  boot: "hot",
@@ -365,7 +365,7 @@ export const MODALITIES: readonly ModalityRecord[] = Object.freeze([
365
365
  label: "cargo builds",
366
366
  family: "language",
367
367
  isolation: "process",
368
- state: "live",
368
+ state: "planned",
369
369
  drivers: ["cargo"],
370
370
  hardening: ["crate-allowlist"],
371
371
  boot: "hot",
@@ -379,7 +379,7 @@ export const MODALITIES: readonly ModalityRecord[] = Object.freeze([
379
379
  label: "v compiler",
380
380
  family: "language",
381
381
  isolation: "process",
382
- state: "live",
382
+ state: "planned",
383
383
  drivers: ["v"],
384
384
  hardening: ["package-allowlist"],
385
385
  boot: "hot",
@@ -407,7 +407,7 @@ export const MODALITIES: readonly ModalityRecord[] = Object.freeze([
407
407
  label: "dotnet runtime",
408
408
  family: "language",
409
409
  isolation: "process",
410
- state: "live",
410
+ state: "planned",
411
411
  drivers: ["dotnet"],
412
412
  hardening: ["assembly-allowlist"],
413
413
  boot: "warm",
@@ -435,7 +435,7 @@ export const MODALITIES: readonly ModalityRecord[] = Object.freeze([
435
435
  label: "php interpreter",
436
436
  family: "language",
437
437
  isolation: "process",
438
- state: "live",
438
+ state: "planned",
439
439
  drivers: ["php"],
440
440
  hardening: ["ini-lockdown"],
441
441
  boot: "hot",
@@ -449,7 +449,7 @@ export const MODALITIES: readonly ModalityRecord[] = Object.freeze([
449
449
  label: "lua interpreter",
450
450
  family: "language",
451
451
  isolation: "process",
452
- state: "live",
452
+ state: "planned",
453
453
  drivers: ["lua"],
454
454
  hardening: ["sandbox-env"],
455
455
  boot: "hot",
@@ -463,7 +463,7 @@ export const MODALITIES: readonly ModalityRecord[] = Object.freeze([
463
463
  label: "zig toolchain",
464
464
  family: "language",
465
465
  isolation: "process",
466
- state: "live",
466
+ state: "planned",
467
467
  drivers: ["zig"],
468
468
  hardening: ["package-allowlist"],
469
469
  boot: "hot",
@@ -477,7 +477,7 @@ export const MODALITIES: readonly ModalityRecord[] = Object.freeze([
477
477
  label: "nim compiler",
478
478
  family: "language",
479
479
  isolation: "process",
480
- state: "live",
480
+ state: "planned",
481
481
  drivers: ["nim"],
482
482
  hardening: ["package-allowlist"],
483
483
  boot: "hot",
@@ -536,7 +536,7 @@ export const MODALITIES: readonly ModalityRecord[] = Object.freeze([
536
536
  label: "wasi preview",
537
537
  family: "wasm",
538
538
  isolation: "process",
539
- state: "live",
539
+ state: "planned",
540
540
  drivers: ["wasmtime"],
541
541
  hardening: ["capability-imports"],
542
542
  boot: "hot",
@@ -933,7 +933,7 @@ export const MODALITIES: readonly ModalityRecord[] = Object.freeze([
933
933
  label: "git objects vfs",
934
934
  family: "fs",
935
935
  isolation: "process",
936
- state: "live",
936
+ state: "planned",
937
937
  drivers: ["git", "vfs"],
938
938
  hardening: ["content-hash", "chunk-catalog"],
939
939
  boot: "hot",
@@ -1024,7 +1024,7 @@ export const MODALITIES: readonly ModalityRecord[] = Object.freeze([
1024
1024
  label: "sqlite vault",
1025
1025
  family: "storage",
1026
1026
  isolation: "process",
1027
- state: "live",
1027
+ state: "planned",
1028
1028
  drivers: ["sqlite"],
1029
1029
  hardening: ["file-quota"],
1030
1030
  boot: "hot",
@@ -1124,7 +1124,7 @@ export const MODALITIES: readonly ModalityRecord[] = Object.freeze([
1124
1124
  label: "dns resolver",
1125
1125
  family: "network",
1126
1126
  isolation: "process",
1127
- state: "live",
1127
+ state: "planned",
1128
1128
  drivers: ["dns"],
1129
1129
  hardening: ["resolver-allowlist"],
1130
1130
  boot: "hot",
@@ -1138,7 +1138,7 @@ export const MODALITIES: readonly ModalityRecord[] = Object.freeze([
1138
1138
  label: "dns over https",
1139
1139
  family: "network",
1140
1140
  isolation: "process",
1141
- state: "live",
1141
+ state: "planned",
1142
1142
  drivers: ["doh"],
1143
1143
  hardening: ["resolver-allowlist"],
1144
1144
  boot: "hot",
@@ -1312,7 +1312,7 @@ export const MODALITIES: readonly ModalityRecord[] = Object.freeze([
1312
1312
  label: "make builds",
1313
1313
  family: "build",
1314
1314
  isolation: "process",
1315
- state: "live",
1315
+ state: "planned",
1316
1316
  drivers: ["make"],
1317
1317
  hardening: ["recipe-allowlist"],
1318
1318
  boot: "hot",
@@ -1354,7 +1354,7 @@ export const MODALITIES: readonly ModalityRecord[] = Object.freeze([
1354
1354
  label: "ninja builds",
1355
1355
  family: "build",
1356
1356
  isolation: "process",
1357
- state: "live",
1357
+ state: "planned",
1358
1358
  drivers: ["ninja"],
1359
1359
  hardening: ["recipe-allowlist"],
1360
1360
  boot: "hot",
package/package.json CHANGED
@@ -1,50 +1,7 @@
1
1
  {
2
2
  "name": "@wenathlan/saddle",
3
- "version": "2.0.160",
3
+ "version": "2.0.162",
4
4
  "description": "binary computing engine that turns distributed storage into a publishable working set carrying the merged virtual-hardware engine (the saddle + e2ugh grand merge)",
5
- "type": "module",
6
- "private": false,
7
- "license": "GPL-3.0-only",
8
- "author": "Wenathlan",
9
- "sideEffects": false,
10
- "main": "./index.ts",
11
- "module": "./index.ts",
12
- "browser": "./index.ts",
13
- "types": "./index.ts",
14
- "packageManager": "pnpm@12.3.4",
15
- "publishConfig": {
16
- "access": "public"
17
- },
18
- "overrides": {
19
- "uuid": "^11.1.1",
20
- "deepmerge-ts": "^8.0.0",
21
- "esbuild": "^0.28.2",
22
- "follow-redirects": "^1.16.2",
23
- "minimatch": "^11.0.3",
24
- "glob": "^14.0.3",
25
- "mysql2": "^3.24.6",
26
- "postcss-selector-parser": "^7.1.6",
27
- "source-map-js": "1.2.2",
28
- "proxy-addr": "2.0.8",
29
- "body-parser": "^2.4.0",
30
- "fast-uri": "^3.1.9",
31
- "zod": "^4.6.7",
32
- "undici": "^7.30.1",
33
- "katex": "^0.18.2",
34
- "shell-quote": "^1.11.0",
35
- "xcode": {
36
- "uuid": "11.1.1"
37
- },
38
- "@grpc/grpc-js": "1.14.5"
39
- },
40
- "repository": {
41
- "type": "git",
42
- "url": "https://github.com/wenathlan/devthink"
43
- },
44
- "bugs": {
45
- "url": "https://github.com/wenathlan/saddle/issues"
46
- },
47
- "homepage": "https://github.com/wenathlan/saddle#readme",
48
5
  "keywords": [
49
6
  "distributed computing",
50
7
  "virtual memory",
@@ -61,39 +18,20 @@
61
18
  "mesa",
62
19
  "ghcr"
63
20
  ],
64
- "engines": {
65
- "bare": "^1.18.0",
66
- "bun": "^1.4.4",
67
- "cargo": ">=1.85.0",
68
- "chrome": "^16.0.912",
69
- "cnpm": "^9.4.0",
70
- "corepack": "^0.35.0",
71
- "deno": "^2.9.5",
72
- "dotnet": ">=8.0.0",
73
- "ecmascript": "^5.0.0",
74
- "firefox": ">=0.8.0",
75
- "go": ">=1.24.0",
76
- "iojs": "^1.0.0",
77
- "java": ">=17",
78
- "node": ">=26.13.0 <27",
79
- "npm": "^12.2.1",
80
- "pnpm": "^12.6.0",
81
- "python": ">=3.11.0",
82
- "rust": ">=1.85.0",
83
- "teleport": ">=0.2.0",
84
- "typescript": "^5.8.0",
85
- "v": "^0.5.2",
86
- "vscode": "^1.118.0",
87
- "yarn": "^4.17.1"
88
- },
89
- "peerDependencies": {
90
- "playwright": "^1.63.0"
21
+ "homepage": "https://github.com/wenathlan/saddle#readme",
22
+ "bugs": {
23
+ "url": "https://github.com/wenathlan/saddle/issues"
91
24
  },
92
- "peerDependenciesMeta": {
93
- "playwright": {
94
- "optional": true
95
- }
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "https://github.com/wenathlan/devthink"
96
28
  },
29
+ "license": "GPL-3.0-only",
30
+ "author": "Wenathlan",
31
+ "type": "module",
32
+ "sideEffects": false,
33
+ "main": "./index.ts",
34
+ "types": "./index.ts",
97
35
  "bin": {
98
36
  "saddle": "./cli.ts"
99
37
  },
@@ -117,6 +55,7 @@
117
55
  "./sandboxqueue": "./sandbox.ts",
118
56
  "./sandboxquota": "./sandbox.ts",
119
57
  "./modalities": "./modalityregistry.ts",
58
+ "./modalityregistry": "./modalityregistry.ts",
120
59
  "./workloadlaw": "./workloadlaw.ts",
121
60
  "./browser": "./browser.ts",
122
61
  "./browser-playwright": "./browser.ts",
@@ -203,6 +142,34 @@
203
142
  "tiers.ts",
204
143
  "quantum.ts"
205
144
  ],
145
+ "engines": {
146
+ "bare": "^1.18.0",
147
+ "bun": "^1.4.4",
148
+ "cargo": ">=1.85.0",
149
+ "chrome": "^16.0.912",
150
+ "cnpm": "^9.4.0",
151
+ "corepack": "^0.35.0",
152
+ "deno": "^2.9.5",
153
+ "dotnet": ">=8.0.0",
154
+ "ecmascript": "^5.0.0",
155
+ "firefox": ">=0.8.0",
156
+ "go": ">=1.24.0",
157
+ "iojs": "^1.0.0",
158
+ "java": ">=17",
159
+ "node": ">=26.13.0 <27",
160
+ "npm": "^12.2.1",
161
+ "pnpm": "^12.6.0",
162
+ "python": ">=3.11.0",
163
+ "rust": ">=1.85.0",
164
+ "teleport": ">=0.2.0",
165
+ "typescript": "^5.8.0",
166
+ "v": "^0.5.2",
167
+ "vscode": "^1.118.0",
168
+ "yarn": "^4.17.1"
169
+ },
170
+ "peerDependencies": {
171
+ "playwright": "^1.63.0"
172
+ },
206
173
  "scripts": {
207
174
  "build:engine": "node scriptsbuildengine.ts",
208
175
  "check": "tsc -p tsconfig.json --noEmit",
@@ -247,9 +214,9 @@
247
214
  "vm:create": "node --import tsx ./cli.ts vm",
248
215
  "vm:validate": "node --import tsx ./cli.ts vmlint",
249
216
  "gpu:list": "node --import tsx ./cli.ts gpus",
250
- "docker:build": "docker build -f Dockerfile -t saddle:2.0.159 .",
251
- "docker:run": "docker run --rm --memory-swap=-1 --shm-size=2g saddle:2.0.159",
252
- "docker:health": "docker run --rm saddle:2.0.159 qemu-system-x86_64 --version && curl -fsS http://localhost:8080/health || true",
217
+ "docker:build": "docker build -f Dockerfile -t saddle:2.0.162 .",
218
+ "docker:run": "docker run --rm --memory-swap=-1 --shm-size=2g saddle:2.0.162",
219
+ "docker:health": "docker run --rm saddle:2.0.162 qemu-system-x86_64 --version && curl -fsS http://localhost:8080/health || true",
253
220
  "firecracker:boot": "firecracker --api-sock /tmp/fc-saddle.sock --config-file vmconfig.json 2>/dev/null || echo 'firecracker 1.16.1 warm-pool boot 125ms target'",
254
221
  "py:bridge": "python3 qemubridge.py",
255
222
  "py:test": "python3 -m pytest -q || python3 qemubridge.py",
@@ -295,11 +262,8 @@
295
262
  "@capacitor/cli": "^8.6.0",
296
263
  "@capacitor/core": "^8.6.0",
297
264
  "@capacitor/ios": "^8.6.0",
298
- "@fluentui/react-icons": "^2.0.345",
299
265
  "@hookform/resolvers": "^5.9.2",
300
- "@libsql/client": "^0.18.0",
301
266
  "@opencode-ai/sdk": "^1.19.5",
302
- "@prisma/client": "^7.10.1",
303
267
  "@radix-ui/react-accordion": "^1.3.1",
304
268
  "@radix-ui/react-alert-dialog": "^1.2.1",
305
269
  "@radix-ui/react-aspect-ratio": "^1.2.0",
@@ -342,24 +306,20 @@
342
306
  "add": "^2.0.9",
343
307
  "autoprefixer": "^10.6.2",
344
308
  "axios": "^1.21.0",
345
- "better-sqlite3": "^13.0.3",
346
309
  "bun-types": "^1.4.4",
347
310
  "cheerio": "^1.2.0",
348
311
  "class-variance-authority": "^0.7.1",
349
312
  "clsx": "^2.1.1",
350
313
  "cmdk": "^1.1.1",
351
314
  "drizzle-kit": "^0.31.11",
352
- "drizzle-orm": "^0.45.4",
353
315
  "embla-carousel-react": "^8.6.0",
354
316
  "emittery": "^2.2.0",
355
317
  "esbuild": "^0.28.2",
356
318
  "express": "^5.3.0",
357
319
  "fast-check": "^4.10.2",
358
320
  "framer-motion": "^13.5.1",
359
- "hono": "^4.13.16",
360
321
  "input-otp": "^1.5.0",
361
322
  "jsdom": "26.1.0",
362
- "lucide-react": "^1.68.0",
363
323
  "mysql2": "^3.24.6",
364
324
  "nanoid": "^6.0.2",
365
325
  "next-themes": "^0.4.6",
@@ -370,11 +330,9 @@
370
330
  "postcss": "^8.5.32",
371
331
  "prettier": "^3.9.10",
372
332
  "prisma": "^7.10.1",
373
- "react": "^19.3.1",
374
333
  "react-day-picker": "^10.0.2",
375
- "react-dom": "^19.3.1",
376
334
  "react-hook-form": "^7.91.0",
377
- "react-resizable-panels": "^4.18.0",
335
+ "react-resizable-panels": "^4.18.1",
378
336
  "recharts": "^3.10.1",
379
337
  "sonner": "^2.0.8",
380
338
  "streamdown": "^2.7.1",
@@ -391,10 +349,43 @@
391
349
  "vitest": "^5.1.0",
392
350
  "webpack": "^5.112.2",
393
351
  "webpack-cli": "^6.0.1",
394
- "wouter": "^3.13.0",
395
352
  "yaml": "^2.9.1",
396
353
  "zod": "^4.6.7"
397
354
  },
355
+ "overrides": {
356
+ "uuid": "^11.1.1",
357
+ "deepmerge-ts": "^8.0.0",
358
+ "esbuild": "^0.28.2",
359
+ "follow-redirects": "^1.16.2",
360
+ "minimatch": "^11.0.3",
361
+ "glob": "^14.0.3",
362
+ "mysql2": "^3.24.6",
363
+ "postcss-selector-parser": "^7.1.6",
364
+ "source-map-js": "1.2.2",
365
+ "proxy-addr": "2.0.8",
366
+ "body-parser": "^2.4.0",
367
+ "fast-uri": "^3.1.9",
368
+ "zod": "^4.6.7",
369
+ "undici": "^7.30.1",
370
+ "katex": "^0.18.2",
371
+ "shell-quote": "^1.11.0",
372
+ "xcode": {
373
+ "uuid": "11.1.1"
374
+ },
375
+ "@grpc/grpc-js": "1.14.5"
376
+ },
377
+ "publishConfig": {
378
+ "access": "public"
379
+ },
380
+ "packageManager": "pnpm@12.3.4",
381
+ "private": false,
382
+ "module": "./index.ts",
383
+ "browser": "./index.ts",
384
+ "peerDependenciesMeta": {
385
+ "playwright": {
386
+ "optional": true
387
+ }
388
+ },
398
389
  "optionalDependencies": {
399
390
  "commander": "^15.0.0",
400
391
  "dockerode": "^5.0.1",
package/sandbox.ts CHANGED
@@ -142,11 +142,14 @@ export type SandboxState = {
142
142
  };
143
143
 
144
144
  /** command result matching the api exec contract; clear is set by the
145
- * clear command so the terminal can wipe the screen. */
145
+ * clear command so the terminal can wipe the screen. code carries the
146
+ * machine refusal code of a structured honest answer — the sandbox never
147
+ * pretends a binary the image does not carry. */
146
148
  export type DispatchResult = {
147
149
  output: string;
148
150
  exitCode: number;
149
151
  clear?: boolean;
152
+ code?: string;
150
153
  };
151
154
 
152
155
  /** optional exec context provided by the api host. */
@@ -1452,8 +1455,8 @@ function helpText(): string {
1452
1455
  ['uname -a', 'kernel and architecture line'],
1453
1456
  ['ls /etc/virtual', 'virtual hardware definition files'],
1454
1457
  ['env', 'mesa llvmpipe environment variables'],
1455
- ['docker --version', 'docker engine version'],
1456
- ['qemu-system-x86_64 --version', 'qemu bridge version'],
1458
+ ['docker --version', 'honest refusal: the binary is absent from the sandbox image'],
1459
+ ['qemu-system-x86_64 --version', 'honest refusal: the binary is absent from the sandbox image'],
1457
1460
  ['uptime', 'sandbox uptime and load average'],
1458
1461
  ['whoami', 'current user'],
1459
1462
  ['neofetch', 'saddle ascii logo and sandbox specs'],
@@ -1826,15 +1829,18 @@ export function dispatch(
1826
1829
  }
1827
1830
  case 'env':
1828
1831
  return { output: envText(state), exitCode: 0 };
1832
+ /* the honesty law: the sandbox image carries no container daemon
1833
+ * and no emulator binary, so the shell never fakes a version
1834
+ * banner — the answer is the structured honest refusal, the
1835
+ * machine code rides the result and the message names the absence
1836
+ * and the real surface (the host container and qemu runtimes ride
1837
+ * the edge plane lanes, never a pretended guest binary). */
1829
1838
  case 'docker':
1830
- return { output: 'Docker version 29.7.2, build 5ea4c1f', exitCode: 0 };
1831
1839
  case 'qemu-system-x86_64':
1832
1840
  return {
1833
- output: [
1834
- 'QEMU emulator version 11.1.0 (saddle bridge)',
1835
- 'Copyright (c) 2003-2026 Fabrice Bellard and the QEMU Project developers',
1836
- ].join('\n'),
1837
- exitCode: 0,
1841
+ output: `bash: ${head}: the ${head} binary is absent from this sandbox image (SANDBOX_IMAGE_ABSENT) — the sandbox mounts no container daemon and no emulator, the engine drives the host container and qemu lanes through the edge plane`,
1842
+ exitCode: 127,
1843
+ code: 'SANDBOX_IMAGE_ABSENT',
1838
1844
  };
1839
1845
  case 'uptime':
1840
1846
  return { output: uptimeLine(state), exitCode: 0 };
package/tsconfig.json CHANGED
@@ -8,20 +8,20 @@
8
8
  // one project keeps covering the Sol surface.
9
9
  {
10
10
  "compilerOptions": {
11
- "module": "NodeNext",
12
- "moduleResolution": "NodeNext",
13
11
  "target": "ES2024",
14
- "lib": ["ES2024", "dom", "dom.iterable"],
15
- "jsx": "react-jsx",
16
12
  "noEmit": true,
17
- "allowImportingTsExtensions": true,
13
+ "module": "NodeNext",
18
14
  "strict": false,
19
15
  "noCheck": true,
16
+ "lib": ["es2024", "dom", "dom.iterable"],
17
+ "jsx": "react-jsx",
20
18
  "esModuleInterop": true,
19
+ "allowSyntheticDefaultImports": true,
21
20
  "skipLibCheck": true,
21
+ "allowImportingTsExtensions": true,
22
22
  "isolatedModules": true,
23
- "allowSyntheticDefaultImports": true,
24
23
  "forceConsistentCasingInFileNames": true,
24
+ "moduleResolution": "NodeNext",
25
25
  "types": ["node", "vite/client"],
26
26
  "paths": {
27
27
  "@/*": ["./sol/*", "./*"],