- conforms_to::[[Observation Form Contract]]
- has_epistemic_status::[[Empirical Observation]]
- in_practice_domain::[[eOS Continuum]]
- authored_by::[[Christopher Allen]]
- has_lifecycle::[[Seed Stage]]↗
- has_curation::[[Working Draft]]↗
A Merry-Compiled Room Reaction Executes Within the Sandbox Surface
Claim
In the eOS-Harness MVA running against DGD 1.7.9 plus the eOSContinuum/eOS-kernellib feature/cohesive-lift branch carrying the kernel-layer cohesive lift (Vault + Schema + XML + Index + Merry + property-change dispatcher), a Merry reaction script -- LPC-dialect source compiled at runtime into a sandbox object, bound on a chat-room object as a property, and invoked via run_merry -- executes within the Merry sandbox surface and mutates the binding object's property storage. The dispatch shape is: the boot-time test driver compiles Set($this, "chat-room.message-count", $count); via new_object("/usr/Merry/data/merry", source), binds the resulting script object on room_a under the reaction-property key merry:on:chat-room.message:main, posts a message through the chat daemon, and fires the reaction with run_merry(room_a, "chat-room.message:main", "on", ([ "count": count ])). Inside the script, $this resolves to the binding host (room_a), $count resolves to the passed argument, and the Set merryfun forwards to room_a->set_property("chat-room.message-count", count). The driver reads the marker property back and writes a SANDBOX-ACCEPT OK sentinel. The loaded code was authored as a string, compiled into the running runtime, bound on a live object, and executed against that object's storage -- with no container, no external policy engine, and no redeploy.
Grounds
This is an Empirical Observation. The grounds are a captured cold-boot smoke run 2026-05-30 against the live MVA demonstration instance with examples/chat-app/ (deployed from the eOSContinuum/eOS-kernellib examples/chat-app/) overlaid at the isolated runtime staging tree's src/usr/Chat/.
Setup (verifiable from repo state):
- Driver: DGD 1.7.9 built from
dworkin/dgd; binary symlinked at the isolated runtime staging tree'sbin/dgd. - Kernel layer:
eOSContinuum/eOS-kernellibonfeature/cohesive-liftat HEADa55b77f(the sandboxed-code-load chat-app commit; built atop the persistence commite2c2126). - Application layer:
examples/chat-app/(this commit's deliverable), deployed into the isolated runtime staging tree atsrc/usr/Chat/. - Runtime config: the instance config, telnet
127.0.0.1:8023, binary/HTTP127.0.0.1:8080, the isolated runtime staging tree layout.
Load-bearing source (phase 3 of examples/chat-app/sys/test.c):
object accept_script;
int count;
accept_script = new_object(MERRY_DATA,
"Set($this, \"chat-room.message-count\", $count);");
room_a->set_property("merry:on:chat-room.message:main", accept_script);
CHAT_DAEMON->post_message(bob, room_a, "first post");
count = sizeof(room_a->query_messages());
run_merry(room_a, "chat-room.message:main", "on", ([ "count": count ]));
if (room_a->query_raw_property("chat-room.message-count") != count) {
log_line("ChatApp:test: FAIL: SANDBOX-ACCEPT marker property did not land");
return;
}
MERRY_DATA is /usr/Merry/data/merry; new_object(MERRY_DATA, source) invokes the Merry compiler on the source string and returns a compiled script object. The test driver inherits /usr/Merry/lib/merryapi for the run_merry static surface. The cross-domain clone of ~Merry/data/merry and the cross-domain merryapi inherit ride the global-access grants set in src/usr/System/initd.c.
Smoke transcript (the load-bearing portion of the sentinel result log after the cold boot):
ChatApp:test: starting
ChatApp:test: CAP-REJECT OK
ChatApp:test: CAP-ACCEPT OK
ChatApp:test: SANDBOX-ACCEPT OK
SANDBOX-ACCEPT OK writes to the sentinel file only when:
new_object(MERRY_DATA, source)returns a non-nil compiled script (the in-sandbox source parsed and compiled).run_merry(...)returns without throwing (the reaction fired; no sandbox shadow masked any kfun the source named).room_a->query_raw_property("chat-room.message-count")equals the passedcount(the reaction'sSetactually mutated the room's property storage).
All three are inside a catch{}; a throw at any point writes a distinct FAIL sentinel instead.
Process identity check: a single DGD process served the assertion in one boot. The boot log records no ** System halted. entry before phase 3 (the halt is from the later persistence phase). The runtime instance carrying the reaction execution is the same one initialized at ** Initialization complete..
What the evidence establishes:
- Code authored as a runtime string can be compiled into the running system (
new_objecton the Merry data clonable) and bound on a live object as a property, with no redeploy and no external compile step. - The compiled code executes against the binding object's property storage:
$thisresolves to the host, theSetmerryfun reaches the host'sset_property, and the mutation is observable immediately afterrun_merryreturns. - The execution is in-sandbox: the
Setmerryfun routes throughmerrynode.c::Set(not shadowed), so the script ran to completion. The sandbox is not an external boundary the code crosses -- it is the set of merryfun/kfun symbols the script's namespace can resolve, enforced inside the same runtime.
What the evidence does not establish:
- Automatic firing on message arrival. The reaction is fired explicitly via
run_merryto isolate the sandboxed-code-load claim. Wiring the same reaction to fire automatically off the dispatcher's post-timing observers when a message is posted is a separate async-event demonstration's scope; this Observation does not exercise that path. - Untrusted-author provenance. The reaction source is authored by the test driver, not supplied by an external untrusted party over a transport. The sandbox boundary is the same regardless of authorship, but an end-to-end "an untrusted user submitted this script" flow (transport + authorization + load) is not exercised here.
- Capability-tier narrowing at load. The reaction is compiled under the Chat domain's tier. The Conviction asks that loaded code's capability tier be set at load time and bound every operation; this Observation shows the sandbox holds but does not exercise loading the same reaction under a deliberately narrowed tier and observing a tier-scoped denial distinct from the sandbox-shadow denial.
- Compile-time rejection of malformed source. The source compiles cleanly; a malformed-source probe (syntax error at
new_objecttime) is not exercised. The sandbox boundary is a runtime-fire boundary, not a compile boundary (the paired negative Observation makes this precise).
What Would Revise It
- A demonstration that the reaction fires automatically via the dispatcher. A future async-event demonstration that registers the same reaction as a post-timing observer and posts a message, observing the reaction fire without an explicit
run_merry, would extend the claim from "loaded code runs when invoked" to "loaded code runs as a registered reaction on state change." - A capability-tier-scoped load probe. A probe that loads a reaction under a narrowed capability tier and observes an operation denied by the tier (not by the sandbox shadow set) would extend the claim from "the sandbox holds" to "the load-time capability tier bounds the loaded code," which is the part of the Conviction this Observation leaves to its negative peer's deny-list mechanism.
- An untrusted-transport load path. A probe that accepts a reaction source over the binary/HTTP transport from an unprivileged session, compiles it, and runs it, asserting the sandbox holds against externally-supplied source, would extend the claim from "test-driver-authored source runs in-sandbox" to "externally-supplied source runs in-sandbox."
- A DGD or eOS-kernellib upgrade that changes
new_object-with-source semantics or the merryfun dispatch path. The current evidence rests on DGD 1.7.9 plusfeature/cohesive-liftHEADa55b77f. A runtime release that changes how the Merry compiler resolves merryfun symbols, or howrun_merrybinds$this, would invalidate the snapshot claim; the pinning is part of the grounds.
Sources
examples/chat-app/sys/test.c(ineOS-kernellib feature/cohesive-lift) -- phase 3 is the load-bearing sequence: compile the reaction, bind it, post a message, fire viarun_merry, assert the marker property.src/usr/Merry/lib/merryapi.c(ineOS-kernellib feature/cohesive-lift) -- therun_merry/find_merrystatic surface the test driver inherits.find_merryresolves themerry:<mode>:<signal>property and walksquery_ur_object()ancestry;run_merrycalls the compiled script'sevaluate.src/usr/Merry/lib/merrynode.c(ineOS-kernellib feature/cohesive-lift) -- the per-script base.Set(the in-sandbox merryfun used by the reaction) forwards to the host'sset_property;args["this"]is bound to the host at evaluate time, so$thisresolves toroom_a.examples/chat-app/obj/room.c-- the Room clonable; inherits/lib/util/propertiesand/lib/util/ur, which together are the property-storage + ancestry-walk surface a Merry binding host requires.examples/chat-app/sys/chat.c-- the chat daemon;post_messageappends the message that establishes thecountthe reaction records.examples/chat-app/README.md-- the deployment + verify recipe; expectsSANDBOX-ACCEPT OKafter a clean cold boot.docs/chat-applications.md(ineOS-kernellib feature/cohesive-lift) -- the walkthrough doc; the "Sandboxed reactions" section quotes the in-sandbox reaction source and the load path.docs/merry-language.md(ineOS-kernellib feature/cohesive-lift) -- the deny-list reference; theSetmerryfun is among the in-sandbox surface (not shadowed).- The isolated runtime staging tree's
src/usr/Chat/data/test-result.log-- the sentinel file; carriesSANDBOX-ACCEPT OKafter a clean cold boot. - The instance config and the isolated-runtime materialization used for the smoke.
Relations
-
conforms_to::[[Observation Form Contract]]
- Carries
has_epistemic_status::[[Empirical Observation]], names the measurement (one cold-boot smoke run with a three-part assertion in phase 3), states the limits (explicitrun_merryfire, not dispatcher-driven; test-driver-authored source; sandbox-shadow denial not tier-scoped denial), and lists concrete revision conditions.
- Carries
-
informs_downstream::[[Code Load Compiles Into the Live Runtime, Bounded by Capability Tiers]]
- The sandboxed-code-load Conviction's stance is that the platform accepts new code into the running runtime as a runtime operation, with no external sandbox container. This Observation is the positive half: source authored as a string, compiled in place via
new_object, bound on a live object, and executed within the sandbox surface. The paired negative shows the boundary's deny side.
- The sandboxed-code-load Conviction's stance is that the platform accepts new code into the running runtime as a runtime operation, with no external sandbox container. This Observation is the positive half: source authored as a string, compiled in place via
-
grounded_in::[[Conviction Form Contract]]
- The sandbox is a runtime mechanism (the merryfun/kfun symbol set the script's namespace resolves), not a policy hint the loaded code could opt out of. The reaction cannot reach a shadowed kfun by convention; it simply has no resolvable symbol for one.
-
contrasts_with::[[A Merry Source Calling a Sandbox-Denied Kfun Errors on First Fire]]
- The negative-case peer Observation. The two together cover the accept and deny sides of the same load path: this case shows an in-sandbox reaction compiling and running to completion; the negative shows a sandbox-denied reaction compiling (registration succeeds) but throwing on its first fire. Reading one without the other gives an incomplete picture of the sandbox boundary -- the accept case alone does not show where the boundary is; the deny case alone does not show what successful loaded execution looks like. The paired sentinels (
SANDBOX-ACCEPT OKandSANDBOX-REJECT OK) land in the same result file across the same cold boot.
- The negative-case peer Observation. The two together cover the accept and deny sides of the same load path: this case shows an in-sandbox reaction compiling and running to completion; the negative shows a sandbox-denied reaction compiling (registration succeeds) but throwing on its first fire. Reading one without the other gives an incomplete picture of the sandbox boundary -- the accept case alone does not show where the boundary is; the deny case alone does not show what successful loaded execution looks like. The paired sentinels (