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.
- package/Cargo.lock +1579 -0
- package/Cargo.toml +40 -0
- package/LICENSE +201 -0
- package/README.md +828 -0
- package/SECURITY.md +34 -0
- package/crates/mustard/Cargo.toml +31 -0
- package/crates/mustard/src/cancellation.rs +28 -0
- package/crates/mustard/src/diagnostic.rs +145 -0
- package/crates/mustard/src/ir.rs +435 -0
- package/crates/mustard/src/lib.rs +21 -0
- package/crates/mustard/src/limits.rs +22 -0
- package/crates/mustard/src/parser/expressions.rs +723 -0
- package/crates/mustard/src/parser/mod.rs +115 -0
- package/crates/mustard/src/parser/operators.rs +105 -0
- package/crates/mustard/src/parser/patterns.rs +123 -0
- package/crates/mustard/src/parser/scope.rs +107 -0
- package/crates/mustard/src/parser/statements.rs +298 -0
- package/crates/mustard/src/parser/tests/acceptance.rs +339 -0
- package/crates/mustard/src/parser/tests/mod.rs +2 -0
- package/crates/mustard/src/parser/tests/rejections.rs +107 -0
- package/crates/mustard/src/runtime/accounting.rs +613 -0
- package/crates/mustard/src/runtime/api.rs +192 -0
- package/crates/mustard/src/runtime/async_runtime/mod.rs +5 -0
- package/crates/mustard/src/runtime/async_runtime/promises.rs +246 -0
- package/crates/mustard/src/runtime/async_runtime/reactions.rs +400 -0
- package/crates/mustard/src/runtime/async_runtime/scheduler.rs +224 -0
- package/crates/mustard/src/runtime/builtins/arrays.rs +1205 -0
- package/crates/mustard/src/runtime/builtins/collections.rs +573 -0
- package/crates/mustard/src/runtime/builtins/install.rs +501 -0
- package/crates/mustard/src/runtime/builtins/intl.rs +553 -0
- package/crates/mustard/src/runtime/builtins/mod.rs +25 -0
- package/crates/mustard/src/runtime/builtins/objects.rs +405 -0
- package/crates/mustard/src/runtime/builtins/primitives.rs +859 -0
- package/crates/mustard/src/runtime/builtins/promises.rs +335 -0
- package/crates/mustard/src/runtime/builtins/regexp.rs +356 -0
- package/crates/mustard/src/runtime/builtins/strings.rs +803 -0
- package/crates/mustard/src/runtime/builtins/support.rs +561 -0
- package/crates/mustard/src/runtime/bytecode.rs +123 -0
- package/crates/mustard/src/runtime/compiler/assignments.rs +690 -0
- package/crates/mustard/src/runtime/compiler/bindings.rs +92 -0
- package/crates/mustard/src/runtime/compiler/context.rs +46 -0
- package/crates/mustard/src/runtime/compiler/control.rs +342 -0
- package/crates/mustard/src/runtime/compiler/expressions.rs +372 -0
- package/crates/mustard/src/runtime/compiler/mod.rs +173 -0
- package/crates/mustard/src/runtime/compiler/statements.rs +459 -0
- package/crates/mustard/src/runtime/conversions/boundary.rs +293 -0
- package/crates/mustard/src/runtime/conversions/coercions.rs +217 -0
- package/crates/mustard/src/runtime/conversions/errors.rs +118 -0
- package/crates/mustard/src/runtime/conversions/mod.rs +14 -0
- package/crates/mustard/src/runtime/conversions/operators.rs +334 -0
- package/crates/mustard/src/runtime/env.rs +355 -0
- package/crates/mustard/src/runtime/exceptions.rs +377 -0
- package/crates/mustard/src/runtime/gc.rs +595 -0
- package/crates/mustard/src/runtime/mod.rs +318 -0
- package/crates/mustard/src/runtime/properties.rs +1762 -0
- package/crates/mustard/src/runtime/serialization.rs +127 -0
- package/crates/mustard/src/runtime/shared.rs +108 -0
- package/crates/mustard/src/runtime/snapshot_validation_tests.rs +93 -0
- package/crates/mustard/src/runtime/state.rs +652 -0
- package/crates/mustard/src/runtime/tests/async_host.rs +104 -0
- package/crates/mustard/src/runtime/tests/collections.rs +50 -0
- package/crates/mustard/src/runtime/tests/diagnostics.rs +36 -0
- package/crates/mustard/src/runtime/tests/exceptions.rs +122 -0
- package/crates/mustard/src/runtime/tests/execution.rs +553 -0
- package/crates/mustard/src/runtime/tests/gc.rs +533 -0
- package/crates/mustard/src/runtime/tests/mod.rs +56 -0
- package/crates/mustard/src/runtime/tests/serialization.rs +170 -0
- package/crates/mustard/src/runtime/validation/bytecode.rs +484 -0
- package/crates/mustard/src/runtime/validation/mod.rs +14 -0
- package/crates/mustard/src/runtime/validation/policy.rs +94 -0
- package/crates/mustard/src/runtime/validation/snapshot.rs +406 -0
- package/crates/mustard/src/runtime/validation/walk.rs +206 -0
- package/crates/mustard/src/runtime/vm.rs +1016 -0
- package/crates/mustard/src/span.rs +22 -0
- package/crates/mustard/src/structured.rs +107 -0
- package/crates/mustard-bridge/Cargo.toml +17 -0
- package/crates/mustard-bridge/src/codec.rs +46 -0
- package/crates/mustard-bridge/src/dto.rs +99 -0
- package/crates/mustard-bridge/src/lib.rs +12 -0
- package/crates/mustard-bridge/src/operations.rs +142 -0
- package/crates/mustard-node/Cargo.toml +24 -0
- package/crates/mustard-node/build.rs +3 -0
- package/crates/mustard-node/src/lib.rs +236 -0
- package/crates/mustard-sidecar/Cargo.toml +21 -0
- package/crates/mustard-sidecar/src/lib.rs +134 -0
- package/crates/mustard-sidecar/src/main.rs +36 -0
- package/dist/index.js +20 -0
- package/dist/install.js +117 -0
- package/dist/lib/cancellation.js +124 -0
- package/dist/lib/errors.js +46 -0
- package/dist/lib/executor.js +555 -0
- package/dist/lib/policy.js +292 -0
- package/dist/lib/progress.js +356 -0
- package/dist/lib/runtime.js +109 -0
- package/dist/lib/structured.js +286 -0
- package/dist/native-loader.js +227 -0
- package/index.d.ts +23 -0
- package/mustard.d.ts +220 -0
- 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.
|