mustardscript 0.1.0

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 (99) hide show
  1. package/Cargo.lock +1579 -0
  2. package/Cargo.toml +40 -0
  3. package/LICENSE +201 -0
  4. package/README.md +828 -0
  5. package/SECURITY.md +34 -0
  6. package/crates/mustard/Cargo.toml +31 -0
  7. package/crates/mustard/src/cancellation.rs +28 -0
  8. package/crates/mustard/src/diagnostic.rs +145 -0
  9. package/crates/mustard/src/ir.rs +435 -0
  10. package/crates/mustard/src/lib.rs +21 -0
  11. package/crates/mustard/src/limits.rs +22 -0
  12. package/crates/mustard/src/parser/expressions.rs +723 -0
  13. package/crates/mustard/src/parser/mod.rs +115 -0
  14. package/crates/mustard/src/parser/operators.rs +105 -0
  15. package/crates/mustard/src/parser/patterns.rs +123 -0
  16. package/crates/mustard/src/parser/scope.rs +107 -0
  17. package/crates/mustard/src/parser/statements.rs +298 -0
  18. package/crates/mustard/src/parser/tests/acceptance.rs +339 -0
  19. package/crates/mustard/src/parser/tests/mod.rs +2 -0
  20. package/crates/mustard/src/parser/tests/rejections.rs +107 -0
  21. package/crates/mustard/src/runtime/accounting.rs +613 -0
  22. package/crates/mustard/src/runtime/api.rs +192 -0
  23. package/crates/mustard/src/runtime/async_runtime/mod.rs +5 -0
  24. package/crates/mustard/src/runtime/async_runtime/promises.rs +246 -0
  25. package/crates/mustard/src/runtime/async_runtime/reactions.rs +400 -0
  26. package/crates/mustard/src/runtime/async_runtime/scheduler.rs +224 -0
  27. package/crates/mustard/src/runtime/builtins/arrays.rs +1205 -0
  28. package/crates/mustard/src/runtime/builtins/collections.rs +573 -0
  29. package/crates/mustard/src/runtime/builtins/install.rs +501 -0
  30. package/crates/mustard/src/runtime/builtins/intl.rs +553 -0
  31. package/crates/mustard/src/runtime/builtins/mod.rs +25 -0
  32. package/crates/mustard/src/runtime/builtins/objects.rs +405 -0
  33. package/crates/mustard/src/runtime/builtins/primitives.rs +859 -0
  34. package/crates/mustard/src/runtime/builtins/promises.rs +335 -0
  35. package/crates/mustard/src/runtime/builtins/regexp.rs +356 -0
  36. package/crates/mustard/src/runtime/builtins/strings.rs +803 -0
  37. package/crates/mustard/src/runtime/builtins/support.rs +561 -0
  38. package/crates/mustard/src/runtime/bytecode.rs +123 -0
  39. package/crates/mustard/src/runtime/compiler/assignments.rs +690 -0
  40. package/crates/mustard/src/runtime/compiler/bindings.rs +92 -0
  41. package/crates/mustard/src/runtime/compiler/context.rs +46 -0
  42. package/crates/mustard/src/runtime/compiler/control.rs +342 -0
  43. package/crates/mustard/src/runtime/compiler/expressions.rs +372 -0
  44. package/crates/mustard/src/runtime/compiler/mod.rs +173 -0
  45. package/crates/mustard/src/runtime/compiler/statements.rs +459 -0
  46. package/crates/mustard/src/runtime/conversions/boundary.rs +293 -0
  47. package/crates/mustard/src/runtime/conversions/coercions.rs +217 -0
  48. package/crates/mustard/src/runtime/conversions/errors.rs +118 -0
  49. package/crates/mustard/src/runtime/conversions/mod.rs +14 -0
  50. package/crates/mustard/src/runtime/conversions/operators.rs +334 -0
  51. package/crates/mustard/src/runtime/env.rs +355 -0
  52. package/crates/mustard/src/runtime/exceptions.rs +377 -0
  53. package/crates/mustard/src/runtime/gc.rs +595 -0
  54. package/crates/mustard/src/runtime/mod.rs +318 -0
  55. package/crates/mustard/src/runtime/properties.rs +1762 -0
  56. package/crates/mustard/src/runtime/serialization.rs +127 -0
  57. package/crates/mustard/src/runtime/shared.rs +108 -0
  58. package/crates/mustard/src/runtime/snapshot_validation_tests.rs +93 -0
  59. package/crates/mustard/src/runtime/state.rs +652 -0
  60. package/crates/mustard/src/runtime/tests/async_host.rs +104 -0
  61. package/crates/mustard/src/runtime/tests/collections.rs +50 -0
  62. package/crates/mustard/src/runtime/tests/diagnostics.rs +36 -0
  63. package/crates/mustard/src/runtime/tests/exceptions.rs +122 -0
  64. package/crates/mustard/src/runtime/tests/execution.rs +553 -0
  65. package/crates/mustard/src/runtime/tests/gc.rs +533 -0
  66. package/crates/mustard/src/runtime/tests/mod.rs +56 -0
  67. package/crates/mustard/src/runtime/tests/serialization.rs +170 -0
  68. package/crates/mustard/src/runtime/validation/bytecode.rs +484 -0
  69. package/crates/mustard/src/runtime/validation/mod.rs +14 -0
  70. package/crates/mustard/src/runtime/validation/policy.rs +94 -0
  71. package/crates/mustard/src/runtime/validation/snapshot.rs +406 -0
  72. package/crates/mustard/src/runtime/validation/walk.rs +206 -0
  73. package/crates/mustard/src/runtime/vm.rs +1016 -0
  74. package/crates/mustard/src/span.rs +22 -0
  75. package/crates/mustard/src/structured.rs +107 -0
  76. package/crates/mustard-bridge/Cargo.toml +17 -0
  77. package/crates/mustard-bridge/src/codec.rs +46 -0
  78. package/crates/mustard-bridge/src/dto.rs +99 -0
  79. package/crates/mustard-bridge/src/lib.rs +12 -0
  80. package/crates/mustard-bridge/src/operations.rs +142 -0
  81. package/crates/mustard-node/Cargo.toml +24 -0
  82. package/crates/mustard-node/build.rs +3 -0
  83. package/crates/mustard-node/src/lib.rs +236 -0
  84. package/crates/mustard-sidecar/Cargo.toml +21 -0
  85. package/crates/mustard-sidecar/src/lib.rs +134 -0
  86. package/crates/mustard-sidecar/src/main.rs +36 -0
  87. package/dist/index.js +20 -0
  88. package/dist/install.js +117 -0
  89. package/dist/lib/cancellation.js +124 -0
  90. package/dist/lib/errors.js +46 -0
  91. package/dist/lib/executor.js +555 -0
  92. package/dist/lib/policy.js +292 -0
  93. package/dist/lib/progress.js +356 -0
  94. package/dist/lib/runtime.js +109 -0
  95. package/dist/lib/structured.js +286 -0
  96. package/dist/native-loader.js +227 -0
  97. package/index.d.ts +23 -0
  98. package/mustard.d.ts +220 -0
  99. package/package.json +97 -0
package/README.md ADDED
@@ -0,0 +1,828 @@
1
+ # MustardScript
2
+
3
+ `MustardScript` is a small, opinionated JavaScript runtime for executing a deliberately
4
+ limited subset of JavaScript inside a Node.js service with explicit host
5
+ capabilities, bounded resources, and resumable execution.
6
+
7
+ This project is **not** trying to recreate Node.js, V8, npm compatibility, or a
8
+ browser. It is trying to provide a compact execution engine for sandboxed
9
+ agent-style scripts and other constrained guest code.
10
+
11
+ ## Status
12
+
13
+ `MustardScript` is an early-stage design and implementation project.
14
+
15
+ Two warnings belong at the top because they affect almost every technical
16
+ decision:
17
+
18
+ 1. **In-process addon mode is not a hard security boundary.** It is a
19
+ low-latency embedding mode. If the runtime has a memory-safety bug, logic
20
+ bug, or denial-of-service failure, the host process can still be impacted.
21
+ 2. **Sidecar mode is the deployment mode for stronger isolation.** For
22
+ adversarial workloads, sidecar mode should be combined with OS-level controls
23
+ such as process limits, sandboxing, containers, or platform-native jail
24
+ mechanisms.
25
+
26
+ ## Current Baseline
27
+
28
+ The current implementation already supports:
29
+
30
+ - parse -> validate -> IR -> bytecode -> VM execution for the supported subset
31
+ - `let`/`const`, functions and closures, rest parameters, arrays, plain
32
+ objects, loops, and basic control flow
33
+ - array spread and spread arguments over arrays, strings, `Map`, `Set`, and
34
+ supported iterator objects
35
+ - `for...of` plus async `for await...of` over arrays, strings, `Map`, `Set`,
36
+ and supported iterator objects, with the documented loop-header surface,
37
+ destructuring bindings, and snapshot-safe iterator state
38
+ - `Map` and `Set` with supported iterable constructors, SameValueZero key and
39
+ membership semantics, insertion-order-preserving storage, and iterator
40
+ helpers
41
+ - `async` functions, `await`, guest promises, `new Promise(...)`, Promise
42
+ combinators and instance methods, basic thenable adoption, and internal
43
+ microtask scheduling for the supported subset
44
+ - guest-internal `BigInt` literals with exact-integer arithmetic,
45
+ comparison, keyed-collection membership, and string/property-key coercion
46
+ - conservative array, string, object, and Math helper methods, including
47
+ callback-driven array helpers, iterable normalization helpers, and
48
+ string-pattern search/replacement helpers
49
+ - a conservative `Date` subset with UTC formatting/access helpers plus
50
+ `Date.now()` and `new Date(value).getTime()` for realistic SLA and
51
+ freshness checks
52
+ - narrow `Intl.DateTimeFormat` and `Intl.NumberFormat` support for explicit
53
+ `en-US` / `UTC` formatting without widening the ambient runtime surface
54
+ - `throw`, `try`/`catch`/`finally`, and guest-visible `Error` objects
55
+ - `Math` and `JSON` built-ins
56
+ - explicit named host capabilities with `start()` / `resume()` suspension,
57
+ including async guest fan-out across host capability calls
58
+ - deterministic `console.log` / `console.warn` / `console.error` callbacks when
59
+ the host provides them explicitly
60
+ - instruction, call-depth, heap-byte, allocation-count, and
61
+ outstanding-host-call budgets with guest-safe limit errors
62
+ - cooperative cancellation for running compute, suspended progress objects, and
63
+ guest async waits on host promises
64
+ - guest-safe runtime and limit errors with guest function-span tracebacks
65
+ - same-version compiled-program and suspension snapshot round trips
66
+ - a thin Node addon wrapper and a sidecar process that reuse the same Rust core
67
+
68
+ ## Reference Docs
69
+
70
+ - [Security Threat Model](SECURITY_THREAT_MODEL.md)
71
+ - [Security Model](docs/SECURITY_MODEL.md)
72
+ - [Use Case Gaps](USE_CASE_GAPS.md)
73
+ - [Language Contract](docs/LANGUAGE.md)
74
+ - [Host API](docs/HOST_API.md)
75
+ - [Serialization](docs/SERIALIZATION.md)
76
+ - [Limits](docs/LIMITS.md)
77
+ - [Conformance Strategy](docs/CONFORMANCE.md)
78
+ - [Bytecode VM Model](docs/BYTECODE.md)
79
+ - [Runtime Value Model](docs/RUNTIME_MODEL.md)
80
+ - [Sidecar Protocol](docs/SIDECAR_PROTOCOL.md)
81
+ - [Benchmarking Notes and Comparison Plan](benchmarks/README.md)
82
+ - [Release Guide](docs/RELEASE.md)
83
+ - [Architecture ADRs](docs/ADRs/0001-core-architecture.md)
84
+
85
+ ## Installation
86
+
87
+ The release package name is `mustardscript`. The default and fully verified
88
+ path is still source-build installation from a clean checkout or packed source
89
+ tarball, where `npm install` compiles the native addon locally. Optional
90
+ prebuilt binaries now have a separate release flow for the documented target
91
+ matrix, but the loader now only accepts validated `.node` artifacts from the
92
+ expected optional package layout. Source-build fallback remains the baseline
93
+ path, now ships `Cargo.lock`, and builds the addon in release mode. It still
94
+ requires a Rust toolchain plus Node.js on the target machine.
95
+
96
+ From a clean checkout:
97
+
98
+ ```sh
99
+ npm install
100
+ npm test
101
+ ```
102
+
103
+ That flow builds the Rust addon locally and then runs the Node and packaging
104
+ smoke tests. Prebuilt binaries are intentionally deferred until the package
105
+ shape is stable.
106
+
107
+ Release verification and publish guidance live in
108
+ [docs/RELEASE.md](docs/RELEASE.md).
109
+ Maintainers can run `npm run verify:release` to execute the current release
110
+ verification flow end to end.
111
+
112
+ ## Maintainer Helpers
113
+
114
+ Maintainers can run `npm run ralph-loop -- <plan.md>` to repeatedly invoke
115
+ `codex exec` with `gpt-5.4` and `model_reasoning_effort="xhigh"` until the plan
116
+ marks itself with `[PLAN HAS BEEN COMPLETED]` or `[BLOCKED]`. Use
117
+ `--max-iterations <n>` to cap the loop when needed.
118
+
119
+ ## Agent-Style Example
120
+
121
+ See [examples/agent-style.ts](examples/agent-style.ts) for a minimal host loop
122
+ that starts guest execution, persists a suspended `Progress`, reloads it, and
123
+ resumes with a host result.
124
+
125
+ For more realistic guest programs shaped like programmatic tool-calling
126
+ workloads, see [examples/programmatic-tool-calls](examples/programmatic-tool-calls).
127
+
128
+ ## Primary Use Cases
129
+
130
+ `MustardScript` is primarily aimed at agent runtimes that need to execute small,
131
+ bounded guest programs with explicit host tools instead of exposing a large
132
+ ambient runtime.
133
+
134
+ - Server-side "code mode" workloads where an agent writes code against a typed
135
+ SDK or a compact `search()` / `execute()` tool surface instead of loading a
136
+ large API into model context
137
+ - Programmatic tool-calling workloads where an agent fans out across many host
138
+ tools, reduces large intermediate results in code, and only returns the final
139
+ answer to the model
140
+ - Resumable host-mediated workflows where execution must pause at explicit host
141
+ capability boundaries, persist state, and resume later
142
+
143
+ These are the workload shapes described by
144
+ [Cloudflare's Code Mode](https://blog.cloudflare.com/code-mode-mcp/) and
145
+ [Anthropic's Programmatic Tool Calling](https://www.anthropic.com/engineering/advanced-tool-use).
146
+ They are a better fit for `MustardScript` than trying to match general-purpose
147
+ JavaScript runtime behavior.
148
+
149
+ ## Project Goals
150
+
151
+ `MustardScript` should provide:
152
+
153
+ - A small, auditable runtime surface
154
+ - No ambient filesystem, network, environment, module, or subprocess access
155
+ - Explicit host capabilities instead of implicit globals
156
+ - Fast startup and low embedding overhead
157
+ - Good cold-start, memory, and host-call overhead for code-mode and
158
+ programmatic tool-calling workloads
159
+ - Precise accounting for instructions, memory, allocations, and call depth
160
+ - Deterministic or tightly specified behavior for the supported subset
161
+ - Suspension and resume at explicit host boundaries
162
+ - Same-version serialization of compiled programs and execution snapshots
163
+ - A Node.js-first embedding experience with a thin wrapper over a reusable Rust
164
+ core
165
+
166
+ ## Non-Goals
167
+
168
+ `MustardScript` is not intended to be:
169
+
170
+ - A secure wrapper around `node:vm`
171
+ - A general-purpose JavaScript runtime
172
+ - A compatibility layer for npm packages
173
+ - A CommonJS environment
174
+ - An ES module loader
175
+ - A DOM or browser runtime
176
+ - A JIT
177
+ - A drop-in replacement for Node.js or V8
178
+ - A place where unsupported features are partially emulated “well enough”
179
+
180
+ Unsupported features should fail closed with clear diagnostics.
181
+
182
+ ## Design Principles
183
+
184
+ ### 1. No Ambient Authority
185
+
186
+ Guest code starts with no access to the host outside core language semantics and
187
+ the approved built-in surface. There is no ambient `process`, `require`,
188
+ filesystem, network, environment, timers, subprocess API, or native addon
189
+ access.
190
+
191
+ Anything effectful must come from an explicit host capability.
192
+
193
+ ### 2. A Small Language Contract Beats an Implicit One
194
+
195
+ The supported subset must be written down precisely. A small, explicit language
196
+ contract is better than accidental compatibility.
197
+
198
+ ### 3. Safety Properties Must Be Designed Early
199
+
200
+ Instruction budgeting, cancellation, memory accounting, snapshot validation, and
201
+ host-boundary validation are not “polish.” They shape the VM, bytecode, async
202
+ model, and public API and must be designed from the beginning.
203
+
204
+ ### 4. The Core Owns the Semantics
205
+
206
+ The Rust runtime owns guest semantics. The Node wrapper should be thin. Sidecar
207
+ mode should run the same core runtime with a different transport boundary.
208
+
209
+ ### 5. Correctness Before Cleverness
210
+
211
+ For v1, centralized semantics and predictable behavior matter more than advanced
212
+ optimizations. Optimizations such as shapes, inline caches, and specialized
213
+ representations should be introduced only after the baseline semantics are
214
+ correct and well tested.
215
+
216
+ ## Threat Model and Deployment Modes
217
+
218
+ `MustardScript` should document three deployment modes clearly.
219
+
220
+ ### Addon Mode
221
+
222
+ - In-process Node-API addon
223
+ - Lowest latency
224
+ - Shares the host process
225
+ - Best-effort containment only
226
+ - Suitable for trusted or semi-trusted guest workloads where latency matters
227
+ more than isolation
228
+
229
+ ### Sidecar Mode
230
+
231
+ - Separate process running the same Rust core
232
+ - Structured IPC boundary
233
+ - Better crash containment
234
+ - Easier forceful termination
235
+ - Better choice for untrusted or resource-heavy workloads
236
+
237
+ ### Hardened Sidecar Deployment
238
+
239
+ - Sidecar mode plus OS-level controls
240
+ - Recommended for adversarial guest code
241
+ - Examples include CPU and memory limits, restricted syscalls or sandbox
242
+ policies, containerization, job objects, cgroups, or platform-native jail
243
+ mechanisms
244
+
245
+ `MustardScript` itself is responsible for language-level containment. Production
246
+ security for untrusted inputs should assume sidecar mode plus host-managed OS
247
+ controls.
248
+
249
+ ## Core Terminology
250
+
251
+ - **Guest code**: JavaScript executed by `MustardScript`
252
+ - **Host**: The embedding application
253
+ - **Capability**: A named host function intentionally exposed to the guest
254
+ - **Suspension point**: A boundary where guest execution pauses awaiting host
255
+ progress
256
+ - **Snapshot**: A serialized representation of a compiled program or suspended
257
+ execution state
258
+ - **Structured host value**: A value that may legally cross the host boundary
259
+
260
+ ## Technical Choices
261
+
262
+ ### Rust Core
263
+
264
+ The interpreter core should be written in Rust.
265
+
266
+ Reasons:
267
+
268
+ - Strong memory-safety baseline
269
+ - Good tooling for parsers, serialization, fuzzing, and testing
270
+ - Clean path to a Node-API addon
271
+ - Practical fit for a custom VM and capability boundary
272
+
273
+ ### Node-API Native Addon First
274
+
275
+ The primary in-process embedding should be a Node-API native addon, likely via
276
+ `napi-rs`.
277
+
278
+ Reasons:
279
+
280
+ - The target embedder is a Node.js service
281
+ - Node-API keeps the host interop layer stable and relatively small
282
+ - `napi-rs` is a practical way to keep the Node binding thin while leaving the
283
+ runtime in Rust
284
+
285
+ ### Oxc Frontend
286
+
287
+ Use `oxc` as the parser frontend unless evaluation proves it to be a poor fit.
288
+
289
+ Reasons:
290
+
291
+ - It is a strong Rust-native frontend for JavaScript parsing
292
+ - It allows `mustard` to separate parsing from runtime design
293
+ - It is a better fit than building a parser from scratch before the runtime
294
+ architecture exists
295
+
296
+ ### Custom Execution Pipeline
297
+
298
+ The runtime pipeline should be:
299
+
300
+ `source -> parse -> validate -> lowered IR -> bytecode -> VM`
301
+
302
+ The explicit validation phase matters. Parsing alone is not enough because some
303
+ things should be rejected as unsupported semantics rather than syntax errors.
304
+
305
+ ## Language Contract for v1
306
+
307
+ The first useful version should support a strict, intentionally limited subset of
308
+ JavaScript.
309
+
310
+ ### Baseline Semantic Rules
311
+
312
+ - Guest code always runs with strict semantics
313
+ - There is no ambient module system
314
+ - There is no dynamic code loading
315
+ - Unsupported features fail with explicit diagnostics
316
+ - Diagnostics and tracebacks must not leak host paths or host internals
317
+
318
+ ### Current Implemented Baseline
319
+
320
+ - Numbers, booleans, strings, `null`, and `undefined`
321
+ - Arrays, plain objects, `Map`, and `Set`
322
+ - `let` and `const`
323
+ - Functions and closures
324
+ - `async` functions and `await`
325
+ - Arrow functions
326
+ - `if`, `switch`, loops, `break`, and `continue`
327
+ - `throw`, `try`, `catch`, and `finally`
328
+ - Common-case destructuring
329
+ - Template literals
330
+ - Optional chaining and nullish coalescing
331
+ - Internal guest promises and microtask checkpoints for the supported subset
332
+ - Host capability calls
333
+ - Suspension and resume at host boundaries
334
+ - Snapshotting at safe suspension points
335
+
336
+ ### Still Deferred Within The Async Surface
337
+
338
+ - fully general Promise constructor and thenable-adoption edge cases,
339
+ including hostile thenable cycles
340
+ - synchronous host suspensions from Promise executors or adopted thenables
341
+
342
+ ### Explicitly Out of Scope for v1
343
+
344
+ - ES modules
345
+ - CommonJS
346
+ - `eval`
347
+ - `Function` constructor
348
+ - `with`
349
+ - Classes
350
+ - Generators and custom iterator authoring
351
+ - symbol-based custom iterable protocols outside the documented built-ins
352
+ - Symbols
353
+ - `WeakMap`, `WeakSet`
354
+ - Typed arrays, `ArrayBuffer`, shared memory, and atomics
355
+ - Full `Date` parity beyond the documented conservative subset
356
+ - Full `Intl` parity beyond the documented conservative subset
357
+ - `Proxy`
358
+ - Full `RegExp` parity
359
+ - Full property descriptor semantics
360
+ - Accessors
361
+ - Full prototype semantics
362
+ - Implicit host globals such as `process`, `module`, `exports`, `global`,
363
+ `require`, timers, or fetch-like APIs
364
+
365
+ ### Important Clarification About Names Like `require`
366
+
367
+ `mustard` should not reject arbitrary identifiers named `require` or `process`.
368
+ Those names can be legitimate local bindings in JavaScript.
369
+
370
+ What `mustard` should reject is:
371
+
372
+ - module syntax and dynamic import forms
373
+ - dynamic code loading primitives such as `eval` and `Function`
374
+ - unresolved free references to forbidden ambient globals, when static
375
+ resolution can prove they are free references
376
+
377
+ If a program defines its own local `require`, that is ordinary lexical
378
+ JavaScript and should be treated as such.
379
+
380
+ ## Built-In Surface
381
+
382
+ The initial built-in surface should be conservative and explicit.
383
+
384
+ Currently implemented built-ins:
385
+
386
+ - `globalThis`
387
+ - `Object`
388
+ - `Array`
389
+ - `Map`
390
+ - `Set`
391
+ - `Promise`
392
+ - `RegExp`
393
+ - `Date`
394
+ - `String`
395
+ - `Error`
396
+ - `TypeError`
397
+ - `ReferenceError`
398
+ - `RangeError`
399
+ - `Number`
400
+ - `Boolean`
401
+ - `Intl`
402
+ - `Math`
403
+ - `JSON`
404
+ - A placeholder `console` global object
405
+
406
+ Current Promise support is intentionally narrow:
407
+
408
+ - async functions return internal guest promises
409
+ - `new Promise(executor)` is supported when `executor` is callable and
410
+ completes synchronously from the runtime's perspective
411
+ - `Promise.resolve(...)`, `Promise.reject(...)`, `Promise.all(...)`,
412
+ `Promise.race(...)`, `Promise.any(...)`, and `Promise.allSettled(...)` are
413
+ supported
414
+ - promise instance methods `then(...)`, `catch(...)`, and `finally(...)` are
415
+ supported
416
+ - promise resolution and `await` adopt guest promises plus guest object or
417
+ array thenables whose `.then` property is callable
418
+ - Promise executor and thenable resolve/reject functions keep first-settlement
419
+ semantics; later resolve/reject calls and post-settlement throws do not
420
+ override the settled result
421
+ - `Promise.any(...)` rejects with a guest-visible `AggregateError` carrying an
422
+ `errors` array when every input rejects
423
+ - async Promise executors and async adopted `.then` handlers reject with an
424
+ explicit `TypeError`
425
+ - synchronous host suspensions from Promise executors and adopted thenables
426
+ still fail closed
427
+
428
+ Current BigInt support is intentionally conservative:
429
+
430
+ - guest code supports `123n` literals plus exact-integer `+`, `-`, `*`, `/`,
431
+ `%`, truthiness, `typeof`, string coercion, and property-key coercion
432
+ - `Map` and `Set` treat `BigInt` values as stable guest keys using the same
433
+ equality surface as other guest values
434
+ - mixed `BigInt` / `Number` arithmetic and relational comparisons fail closed
435
+ - `Number(1n)` and unary `+1n` fail closed instead of implicitly coercing
436
+ - `JSON.stringify(...)` rejects `BigInt` values with an explicit `TypeError`
437
+ - `BigInt` values remain guest-internal and still cannot cross the structured
438
+ host boundary
439
+
440
+ Current built-in helper support is intentionally conservative:
441
+
442
+ - arrays support `push`, `pop`, `slice`, `join`, `includes`, `indexOf`,
443
+ `values`, `keys`, `entries`, `forEach`, `map`, `filter`, `find`,
444
+ `findIndex`, `findLast`, `findLastIndex`, `some`, `every`, `reduce`, and
445
+ `reduceRight`
446
+ - strings support `trim`, `trimStart`, `trimEnd`, `includes`, `startsWith`,
447
+ `endsWith`, `slice`, `substring`, `toLowerCase`, `toUpperCase`,
448
+ `padStart`, `padEnd`, `split`, `replace`, `replaceAll`, `search`, and
449
+ `match`
450
+ - `Array(...)` and `new Array(...)` follow JavaScript's single-length
451
+ constructor behavior for one numeric argument and reject invalid lengths
452
+ with `RangeError`
453
+ - `Object(value)` preserves supported object-like guest values and boxes
454
+ primitive strings, numbers, and booleans into conservative wrapper objects
455
+ - `Object.keys`, `Object.values`, `Object.entries`, and `Object.hasOwn`
456
+ support plain objects, arrays, supported callables, and conservative boxed
457
+ strings
458
+ - `Math.pow`, `Math.sqrt`, `Math.trunc`, and `Math.sign` are supported
459
+ - `Date.now()`, `new Date(value).getTime()`, `Date.prototype.toISOString()`,
460
+ `Date.prototype.toJSON()`, and the documented UTC field accessors are
461
+ supported
462
+ - `Number.parseInt`, `Number.parseFloat`, `Number.isNaN`, and
463
+ `Number.isFinite` are supported
464
+ - `Intl.DateTimeFormat` and `Intl.NumberFormat` are available in a narrow
465
+ `en-US` / `UTC` subset with explicit fail-closed behavior for unsupported
466
+ locales and options
467
+ - array callback helpers currently support guest callbacks, built-in callbacks,
468
+ and async host callbacks reached from an async guest boundary; synchronous
469
+ host suspensions from those helpers fail closed
470
+ - string pattern helpers accept string-coercible patterns and real `RegExp`
471
+ instances, including callback replacements for `replace` and `replaceAll`
472
+ - string replacement callbacks are synchronous-only; host suspensions fail
473
+ closed, and only `g`, `i`, `m`, `s`, `u`, and `y` flags are supported
474
+ - full `RegExp` parity and symbol-based match/replace protocol hooks remain
475
+ unsupported
476
+ - descriptor/prototype helpers remain unsupported
477
+ - proxy-backed host values, accessor-backed handler registries, and cyclic host
478
+ values fail closed at the JavaScript wrapper boundary before guest execution
479
+
480
+ Current function-call support is intentionally narrow:
481
+
482
+ - non-arrow guest member calls bind the computed receiver as `this`
483
+ - arrow functions capture lexical `this` from the surrounding supported guest
484
+ frame
485
+ - rest parameters are supported for functions and arrow functions
486
+ - default parameters and conservative default destructuring are supported
487
+ - implicit free `arguments` is rejected with a validation diagnostic
488
+ - `new` remains limited to the documented conservative built-in constructors
489
+
490
+ Current legacy-binding and prototype-related exclusions are deliberate:
491
+
492
+ - `var` is intentionally not part of the v1 contract. The runtime keeps only
493
+ lexical `let` / `const` bindings and does not emulate function/global
494
+ hoisting or legacy redeclaration rules.
495
+ - the `delete` operator is intentionally unavailable for plain objects and
496
+ arrays. Supporting it would require explicit rules for own-property absence,
497
+ sparse arrays, and descriptor/configurability semantics; until then guest
498
+ code must rebuild values instead. This does not affect the supported
499
+ `Map.prototype.delete` and `Set.prototype.delete` methods.
500
+ - full prototype inheritance remains unavailable, but conservative
501
+ `instanceof` checks work for the documented built-in constructors,
502
+ primitive-wrapper objects, and `Object` checks over supported callables.
503
+
504
+ Current keyed-collection support is intentionally narrow:
505
+
506
+ - `new Map()` and `new Set()` accept the supported iterable surface
507
+ - `Map` supports `get`, `set`, `has`, `delete`, `clear`, and `size`
508
+ - `Set` supports `add`, `has`, `delete`, `clear`, and `size`
509
+ - `Map` keys and `Set` membership use SameValueZero semantics, so `NaN`
510
+ matches `NaN` and `-0` is treated the same as `0`
511
+ - `Map` and `Set` preserve first-in insertion order internally
512
+ - public collection iterator helpers `entries()`, `keys()`, and `values()`
513
+ return guest iterator objects with `.next()`
514
+ - `for...of` supports arrays, strings, `Map`, `Set`, and iterator objects
515
+ produced by the supported helper surface
516
+
517
+ The guest runtime intentionally remains narrow at the host boundary: it does
518
+ not expose filesystem, network, timers, or environment access by default. The
519
+ documented built-ins `Date.now()` and `Math.random()` are the current
520
+ exceptions, both intentionally nondeterministic and not reproducible across
521
+ runs or resumes. If the host wants broader capabilities, it must provide them
522
+ explicitly.
523
+
524
+ ## Structured Host Boundary
525
+
526
+ The host boundary should be narrowly defined and documented as its own contract.
527
+
528
+ ### Allowed Structured Host Values
529
+
530
+ - `undefined`
531
+ - `null`
532
+ - booleans
533
+ - strings
534
+ - numbers, including non-finite values and `-0`
535
+ - arrays of structured host values, including sparse arrays with preserved hole
536
+ positions up to 1,000,000 elements
537
+ - plain objects with string keys and structured host values
538
+
539
+ ### Rejected at the Host Boundary
540
+
541
+ - functions
542
+ - symbols
543
+ - guest `BigInt` values and host bigints; the current `BigInt` surface remains
544
+ guest-internal only
545
+ - class instances
546
+ - host objects
547
+ - dates
548
+ - regex objects
549
+ - maps, sets, typed arrays, buffers, and array buffers
550
+ - cycles
551
+ - objects with accessors or custom prototypes
552
+
553
+ This is intentionally narrower than general JavaScript values.
554
+
555
+ The sidecar wire format and snapshot format should **not** rely on plain JSON if
556
+ that would lose information such as `undefined`, `NaN`, `Infinity`, or `-0`.
557
+ Use a tagged internal encoding instead.
558
+
559
+ ## Architecture
560
+
561
+ ### Frontend and Validation
562
+
563
+ Responsibilities:
564
+
565
+ - Parse source text
566
+ - Reject modules and unsupported syntax with clear diagnostics
567
+ - Preserve spans for tracebacks and error reporting
568
+ - Run a validation pass that rejects forbidden dynamic forms and unsupported
569
+ semantic constructs
570
+ - Lower valid programs into an internal IR
571
+
572
+ ### IR
573
+
574
+ The IR should make control flow and semantics explicit.
575
+
576
+ Design goals:
577
+
578
+ - Explicit scopes and bindings
579
+ - Explicit function and closure boundaries
580
+ - Explicit exception regions
581
+ - Explicit host-call suspension points
582
+ - A structure that is convenient for validation and bytecode generation
583
+
584
+ ### Bytecode
585
+
586
+ The bytecode should be:
587
+
588
+ - Easy to interpret
589
+ - Easy to validate
590
+ - Easy to serialize
591
+ - Instrumentable for instruction budgeting
592
+ - Private to `mustard`, not a public stable standard
593
+
594
+ Compiled programs only need to round-trip within the same `mustard` version.
595
+
596
+ ### VM
597
+
598
+ The VM should initially be stack-based unless profiling proves a different choice
599
+ worth the complexity.
600
+
601
+ Responsibilities:
602
+
603
+ - Execute bytecode
604
+ - Maintain call frames and lexical environments
605
+ - Handle control flow and exceptions
606
+ - Enforce instruction budgets and cancellation checks
607
+ - Suspend and resume around host interactions
608
+ - Produce guest-safe tracebacks
609
+
610
+ ### Values, Objects, and Heap
611
+
612
+ `mustard` should define an explicit internal `JsValue` type and a heap object
613
+ model with a disciplined rooting strategy.
614
+
615
+ For v1, object semantics should prioritize correctness and centralization over
616
+ aggressive optimization:
617
+
618
+ - centralized property get, set, and deletion rules
619
+ - plain-object and array behavior first
620
+ - explicit decisions around enumeration order
621
+ - explicit decisions around prototype support and deferrals
622
+
623
+ A simple dictionary-backed representation is a sensible starting point. Shapes or
624
+ hidden-class-style metadata can be added later if profiling shows a real need.
625
+ They should be treated as an optimization, not as the foundation of the v1
626
+ semantic model.
627
+
628
+ ### Garbage Collection
629
+
630
+ Use a non-moving mark-sweep collector in v1.
631
+
632
+ Requirements:
633
+
634
+ - explicit root-set design
635
+ - no raw guest references crossing host boundaries without rooting
636
+ - test coverage for cyclic data
637
+ - accounting hooks for heap limits and allocation tracking
638
+
639
+ ### Exceptions
640
+
641
+ Guest exceptions must be guest-facing objects with guest-safe rendering.
642
+
643
+ Requirements:
644
+
645
+ - support `throw`, `try`, `catch`, and `finally`
646
+ - standard error hierarchy for the supported subset
647
+ - tracebacks mapped to guest source spans
648
+ - no host paths, internal filenames, or Rust panic details in guest output
649
+
650
+ ### Async Runtime
651
+
652
+ `mustard` owns its own async model.
653
+
654
+ Current behavior:
655
+
656
+ - internal promise representation for async guest execution
657
+ - internal microtask queue with explicit checkpoint draining
658
+ - host-boundary suspension and resume for async guest capability calls
659
+ - enforced maximum outstanding host calls for async guest fan-out
660
+ - no reentrancy into the same VM unless explicitly designed and documented
661
+ - cooperative cancellation now fails top-level execution, including while guest
662
+ async code is awaiting host promises
663
+ - same-thread addon `AbortSignal` delivery remains cooperative rather than
664
+ preemptive
665
+
666
+ The host should not need to understand VM internals to resume guest execution.
667
+
668
+ ### Serialization and Snapshots
669
+
670
+ Two formats matter:
671
+
672
+ 1. **Compiled program format**
673
+ 2. **Suspended execution snapshot format**
674
+
675
+ Requirements:
676
+
677
+ - same-version round trips only
678
+ - explicit version tags
679
+ - defensive validation on load
680
+ - corruption-safe failure behavior
681
+ - no raw host pointers, JS callback handles, or native references in serialized
682
+ state
683
+
684
+ Snapshots should only be allowed at safe suspension points. If a suspended
685
+ execution depends on ongoing external work, the host must represent that work
686
+ through an explicit continuation token or equivalent resumable contract. `mustard`
687
+ must not attempt to serialize opaque host futures.
688
+
689
+ ## Resource Model
690
+
691
+ Resource controls should be designed into the runtime from the beginning.
692
+
693
+ The public model should include at least:
694
+
695
+ - instruction budget
696
+ - heap byte limit
697
+ - allocation limit or allocation accounting
698
+ - call-depth limit
699
+ - maximum outstanding host calls
700
+ - cancellation support
701
+
702
+ Default limits should be explicit and documented.
703
+
704
+ Limit failures should be deterministic, guest-safe, and distinguishable from
705
+ ordinary guest exceptions.
706
+
707
+ ## Public API Shape
708
+
709
+ The public Node API should stay small.
710
+
711
+ Illustrative shape:
712
+
713
+ ```ts
714
+ type HostValue =
715
+ | undefined
716
+ | null
717
+ | boolean
718
+ | number
719
+ | string
720
+ | HostValue[]
721
+ | { [k: string]: HostValue }
722
+
723
+ type Capability = (...args: HostValue[]) => HostValue | Promise<HostValue>
724
+
725
+ const program = new Mustard(source, {
726
+ inputs: ['x'],
727
+ })
728
+
729
+ const result = await program.run({
730
+ inputs: { x: 1 },
731
+ capabilities: {
732
+ fetch_data: async (url) => '...',
733
+ },
734
+ limits: {
735
+ instructionBudget: 100_000,
736
+ heapLimitBytes: 8 << 20,
737
+ callDepthLimit: 256,
738
+ },
739
+ })
740
+ ```
741
+
742
+ Lower-level control should exist for advanced hosts:
743
+
744
+ - `new Mustard(...)`
745
+ - `Mustard.validateProgram(...)`
746
+ - `run(...)`
747
+ - `start(...)`
748
+ - `progress.resume(...)`
749
+ - `progress.resumeError(...)`
750
+ - `dump()`
751
+ - `Mustard.load(...)`
752
+ - `progress.dump()`
753
+ - `Progress.load(...)`
754
+
755
+ For hosts managing a large backlog of resumable jobs, the Node wrapper also
756
+ exports `MustardExecutor` plus `InMemoryMustardExecutorStore` as a thin
757
+ queue-oriented layer over `start()` / `Progress.dump()` / `Progress.load()`.
758
+ The design and invariants for that layer are documented in
759
+ [MUSTARD_EXECUTOR.md](MUSTARD_EXECUTOR.md).
760
+
761
+ Native failures are surfaced in Node as typed JavaScript errors:
762
+ `MustardParseError`, `MustardValidationError`, `MustardRuntimeError`,
763
+ `MustardLimitError`, and `MustardSerializationError`.
764
+
765
+ `Mustard.validateProgram(source)` checks that a guest program parses, stays
766
+ inside the supported language subset, and lowers to an executable compiled
767
+ program. It does not prove that a later `run()` or `start()` call will succeed
768
+ with a particular host policy, input set, capability map, or runtime limit.
769
+
770
+ `Progress.load(...)` always requires explicit restore authority: the host must
771
+ pass `capabilities` or `console`, explicit `limits` as an object (use `{}` for
772
+ default runtime limits), and the original `snapshotKey`. The dumped token
773
+ authenticates the snapshot bytes before any loaded capability metadata is
774
+ trusted, and same-process dumps stay single-use.
775
+
776
+ The common path should be easy. The advanced path should remain explicit.
777
+
778
+ ## Repository Shape
779
+
780
+ ```text
781
+ mustard/
782
+ crates/
783
+ mustard/
784
+ mustard-node/
785
+ mustard-sidecar/
786
+ docs/
787
+ SECURITY_MODEL.md
788
+ LANGUAGE.md
789
+ HOST_API.md
790
+ SERIALIZATION.md
791
+ LIMITS.md
792
+ ADRs/
793
+ tests/
794
+ examples/
795
+ ```
796
+
797
+ The extra documentation matters. The risk in a project like this is not only code
798
+ complexity. It is semantic ambiguity.
799
+
800
+ ## What Must Be True Before Production Use
801
+
802
+ Before `mustard` is described as production-ready for untrusted workloads, the
803
+ following should be true:
804
+
805
+ - the supported subset is written down and tested
806
+ - forbidden features fail closed
807
+ - resource limits are enforced predictably
808
+ - snapshots and compiled programs are validated before load
809
+ - guest diagnostics never leak host internals
810
+ - sidecar mode is available for stronger isolation
811
+ - kill and cancellation behavior is well defined
812
+ - the capability boundary is narrow, tested, and documented
813
+
814
+ ## End State
815
+
816
+ The desired end state is a reusable Rust core plus thin Node-facing wrappers that
817
+ together provide:
818
+
819
+ - safe-by-default host embedding
820
+ - explicit and testable feature boundaries
821
+ - good diagnostics and tracebacks
822
+ - snapshot and resume across process boundaries
823
+ - predictable behavior under limits
824
+ - a practical path to production use for constrained guest code
825
+
826
+ The finished project should feel small, opinionated, and predictable. It should
827
+ optimize for sandboxed execution of constrained JavaScript, not for language
828
+ maximalism.