A Merry Source Calling a Sandbox-Denied Kfun Errors on First Fire

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 whose source calls a sandbox-denied kfun compiles and binds without error but throws on its first invocation, and the denied operation does not occur. The dispatch shape is: the boot-time chat-app test driver compiles write_file("/usr/Chat/data/hack.txt", "pwned"); via new_object("/usr/Merry/data/merry", source) and binds the resulting script object on room_a under the reaction-property key merry:on:chat-room.intrusion:main. The registration (compile + bind) succeeds: the Merry compiler resolves write_file to the local SANDBOX(write_file) shadow method defined in merrynode.c, which exists, so the symbol is found and the script binds. The error fires only on the first invocation via run_merry, when the shadow body runs and raises function 'write_file' not allowed in merry code. The driver's catch{} converts the throw into a SANDBOX-REJECT OK sentinel and confirms no hack.txt was created. The boundary is enforced at execution, not at load: an untrusted author cannot tell at registration time whether their reaction will be stopped, because the deny is a runtime-fire deny.

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/ overlaid at the isolated runtime staging tree's src/usr/Chat/. Same boot and same setup as the paired positive Observation.

Setup (verifiable from repo state):

Load-bearing source (phase 4 of examples/chat-app/sys/test.c):

object reject_script;

reject_script = new_object(MERRY_DATA,
        "write_file(\"" + HACK_FILE + "\", \"pwned\");");
room_a->set_property("merry:on:chat-room.intrusion:main", reject_script);
/* (the compile + bind above are in their own catch{}; a throw there
 *  is a FAIL -- registration is supposed to succeed) */

catch(remove_file(HACK_FILE));
rejected = 0;
catch {
    run_merry(room_a, "chat-room.intrusion:main", "on", ([ ]));
} : {
    rejected = 1;
}
if (!rejected) {
    log_line("ChatApp:test: FAIL: SANDBOX-REJECT denied kfun did not throw");
    return;
}
if (file_info(HACK_FILE)) {
    log_line("ChatApp:test: FAIL: SANDBOX-REJECT forbidden write created a file");
    return;
}

HACK_FILE is /usr/Chat/data/hack.txt. The shadow that fires is defined in src/usr/Merry/lib/merrynode.c via the SANDBOX(f) macro: # define SANDBOX(f) mixed f(mixed args...) { error("function '" + #f + "' not allowed in merry code"); }, with SANDBOX(write_file) among the 51 entries.

Smoke transcript (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
ChatApp:test: SANDBOX-REJECT OK

Boot-log evidence of the deny mechanism (the [caught] error and its call stack):

** function 'write_file' not allowed in merry code [caught]
                      /usr/Chat/sys/test
  123   setup_and_run    /usr/Chat/sys/test
  288 * run_tests        /usr/Chat/sys/test
  103   run_merry        /usr/Merry/lib/merryapi
                      /usr/Merry/data/merry#-1
  299   evaluate         /usr/Merry/data/merry
                      /usr/Merry/merry/<hash>
   73   evaluate         /usr/Merry/lib/merrynode
    6   merry            /usr/Merry/merry/<hash>
  448   write_file       /usr/Merry/lib/merrynode

The stack terminates at merrynode.c line 448 (the SANDBOX(write_file) shadow), reached through the compiled merry program, merrynode::evaluate, merryapi::run_merry, and the test driver's run_tests. The [caught] annotation confirms the throw was caught by phase 4's catch{} (the expected outcome), not an uncaught runtime error.

SANDBOX-REJECT OK writes to the sentinel file only when:

  1. new_object(MERRY_DATA, source) returns a non-nil compiled script (the source with the forbidden call still COMPILES -- the shadow symbol exists, so compilation succeeds).
  2. run_merry(...) throws (the shadow body raised on first fire).
  3. file_info(HACK_FILE) returns nil (the forbidden write never reached the filesystem).

What the evidence establishes:

What the evidence does not establish:

What Would Revise It

Sources

Relations