Repository navigation
Replies: 3 comments
[Follow-up] A direction that goes beyond declarative-onlyBuilding on the five options (+ ADRs 0003/0004), digging into the strategy led to a conclusion that is not "keep declarative-only and just raise the expressiveness ceiling," but rather recomposing the "data vs. trusted-only TS" dichotomy itself. Recording it here. 0. Premises we're now fixing
1. The key reframe: break the false dichotomyThe ADRs frame the world as "effect-free data (safe)" vs. "effectful foreign code (dangerous)." That's a false dichotomy. The danger is neither being code nor having effects — it is being able to name an arbitrary effect ( → A third region: run effectful "code" over a closed sim-domain capability, in a realm with zero ambient authority. Authors get a full language, yet cannot structurally name origin authority. XSS / token exfiltration becomes impossible by construction, not by monitoring. Expressiveness far exceeds declarative; safety is the same "by construction." 2. Execution architecture: a separate MDP Worker, async
3. Substrate: spike both SES and QuickJS, decide empirically
4. Per-category routing (handling the asymmetry)Separate the categories where zero-order hold is benign from the one where it isn't:
A clean alignment: "the categories that genuinely need code (Command/Event)" and "the categories where async lag is benign" nearly coincide, while "the one category where async lag hurts (Observation)" is a pure function best served by the synchronous path. This structurally removes the control-loop-latency problem and keeps the MDP Worker lightweight (no need to put policy inference inside it). 5. Authoring: don't make people write a DSL — transpile mjlab PythonTo kill the DX friction (registry learning) at the authoring layer, rather than making authors write a DSL, the main path transpiles the mjlab Python (raw value-extraction functions) directly to TS/JS. Building on the existing Python→IR tracer (
The consistency check (verify numerical agreement between the Python reference and the TS output at build time) is mandatory on the auto-transpiled path too (torch→JS numeric-semantics drift fails silently otherwise). 6. Closing the trust boundary
7. The async contract (to be specified explicitly)
8. Mapping to the five optionsThis direction takes options 2/3 (no-import WASM / WASM-hosted interpreter, i.e. the ocap family) and lands them in a concrete form: "separate async worker + SES/QuickJS + Python-transpile authoring." Option 1 (declarative-only) is retained as the synchronous Observation path; options 4 (CodePen-style iframe/CSP) and 5 (review gate) are dropped from the main line due to their perpetual maintenance tax. 9. Phasing and the next step
Next step (planned spike): a transpiler coverage study over mjlab observation terms
The result reveals, in one shot: how far the IR must expand (the size of the option-B investment), the degree of ocap dependence, and the priority of the SES/QuickJS spike. |
[Follow-up 2] Running user MDP terms as data: ONNX graph capture, per-categoryA concrete refinement of the "third region" idea. The headline: we already load The spine: capture, don't portAn mjlab observation term is Why this dominates the transpiler / interpreter routes:
Per-category strategyPlay-relevant categories only. Reward / Curriculum / Metrics-Recorder are train-only and out of scope.
Notable specifics grounded in the current examples:
The big implication: the sandbox surface shrinks hardWith ONNX capture for Obs/Action/Termination and the existing declarative descriptors for Event, Observation, Action, Termination, and Event all reduce to data and run synchronously inside the engine — no worker. The untrusted-code execution surface collapses to Command tier 4 (bespoke stateful), plus the rare obs/termination that won't lower to a graph. This changes the MVP scope: we do not need to build the asynchronous ocap worker, zero-order-hold, and reset-barrier machinery up front. Those categories are data and run synchronously; the async ocap worker can be deferred until a genuinely bespoke Command term actually appears. "The best code is the code you don't write yet." Open verification
Revised next stepReframe the planned spike from "transpiler coverage" to graph-capture coverage: over several dozen real mjlab obs terms (stratified by family — locomotion / motion-tracking / muscle-myo / height-scan / history), measure how many cleanly |
Uh oh!
There was an error while loading. Please reload this page.
Context
Today mjswan Cloud is declarative-only: it accepts simulations whose MDP terms are composition graphs (pure data the engine interprets) and rejects any build that ships custom TypeScript (
ts_src) — thepublishstep hard-fails onuses_custom_js: true. See ADR 0003 and ADR 0004 §10.The custom-TS escape hatch (
ObservationBinding(ts_src=...), etc.) stays a permanent, trusted-only capability for local /mjswan view/ mjlab / the in-repo app, which load and register author modules directly. To reach Cloud today, an author must PR the needed primitive/slot to the engine.This discussion is the future question: if we wanted to let authors upload simulations that carry custom-TS MDP code and have Cloud render them safely, how would we resolve the security concerns?
Threat model (why declarative-only today)
Custom
ts_srcruns client-side, in a viewer's browser, in the engine's JS realm — no server ever executes author code. So the relevant threat is XSS-class (author code running with the platform's origin authority: reading/writing another origin's DOM/cookies/tokens, executing where it shouldn't) — not server-side RCE/SSRF.The CodePen analogy therefore applies — CodePen safely runs arbitrary user HTML/CSS/JS — but only by paying a permanent isolation cost: a separate sandbox origin, iframe
sandbox, strict CSP, and constant XSS vigilance. Declarative data avoids foreign code by construction, so Cloud pays none of that.Options on the table
sandbox+ separate cookie-isolated origin + strict CSP + resource limits. Maximally compatible, but a perpetual XSS/CSRF-hardening commitment.Concrete concerns any non-declarative path must resolve
sandbox, strict CSP (script-src/frame-src) so author code can't touch the platform origin's DOM/cookies/tokens.SameSitecookies, never ambient credentials.Question
Which path (if any) is worth building, and when? Is the declarative surface plus the "PR a primitive" on-ramp expressive enough that the restriction is rarely felt (the current bet) — or does the RL-research frontier produce enough genuinely-novel per-author logic to justify the perpetual cost of a sandbox, or the engineering of an ocap-WASM compile path?
Background: this question was surfaced while making the Gentle Humanoid demo's MDP terms fully declarative (#79). The declarative surface was widened with new fundamental primitives so more observations compose without author code, but the motion command remains the kind of effectful, stateful logic that motivates this question.
All reactions