# Ixchel: The Sandbox & Executable Modding

V5 drew one line in the sand and never crossed it: its workshop is **data-only —
it "cannot ship executable code."** That single restriction is what kept a
decade of Oshun UGC safe, and it is exactly the restriction V7 has to lift,
because a creator republic where you can re-skin a car but never write the law
that governs the city is not a republic at all. The whole of V7's identity rests
on one wager — that a community can ship _real code_, behaviours and economy
rules and entire world genomes, and the platform can run that code on an
authoritative server it shares with a thousand strangers **without** the code
being able to touch a file, a socket, another plugin's memory, the real-money
economy, or the realm clock. Ixchel is the subsystem that makes that wager
literally, mechanically true — the part of Mawu that turns "creator republic"
from a slogan into a security property.

Ixchel is named for the Maya goddess of weaving, and it weaves three strands
into one name: a **resource model and lifecycle** that loads, hot-reloads, and
fault-isolates creator code; a **WASM sandbox** that constrains what that code
can reach; and a **dependency-resolution and content-addressed storage** layer
that pins exactly which bytes a realm runs so any client can compose the
byte-identical world. This page is the creator- and feature-facing tour: what
you can build, what the platform guarantees while you build it, and where the
honest edges are. The byte-level engineering is the architecture companion,
[../architecture/ixchel-modding-runtime-and-wasm-sandbox.md](../architecture/ixchel-modding-runtime-and-wasm-sandbox.md);
the server that _hosts_ this sandbox and validates client intent is
[./moremi-roleplay-framework.md](./moremi-roleplay-framework.md); the tools a
creator uses to author the layers Ixchel runs are
[./mawu-studio-creation-tools.md](./mawu-studio-creation-tools.md). For the full
V7 feature scope this slots into, start at the hub:
[../V7_features.md](../V7_features.md).

## What ships, honestly

The monolith describes a complete modding platform; the tree implements the part
that matters most — the sandbox — for real, and is honest about the seams. This
page follows the code, and the code lives in three layers.

- **The WASM sandbox is real, eval-gated, and green today.** It does **not**
  live in `libs/v7` despite where one might look for it — the runtime ships
  inside the Moremi realm server, `apps/v7/moremi-realm-server/src/lib.rs`, a
  single 14,880-line Rust crate built on a genuine **Wasmtime 45.0.0** with the
  Component Model, Cranelift, fuel, epoch, and async features enabled
  (`Cargo.toml:17`), alongside **PubGrub 0.3.0**, `semver`, and SHA-256. It
  carries fifteen `ixchel_*` sandbox tests that exercise real WebAssembly text
  modules against the real engine. Running them is the proof:
  `cargo test -p moremi-realm-server ixchel` reports **`15 passed; 0 failed`** —
  runaway loops that actually trap on fuel exhaustion, oversized memories
  actually refused at instantiation, hostile capability requests actually denied
  before a component instantiates. The zero-escape gate is wired to CI through
  `scripts/v7/verify-adversarial-eval-gates.mjs`.
- **The `libs/maya/forge-*` crates are split between contract and code.**
  `forge-sandbox` is deliberately API-first — its own doc comment says "the full
  implementation will host Wasmtime component-model modules" (`lib.rs:5`) and it
  has no Wasmtime dependency; `forge-resolver` is a deterministic exact-manifest
  resolver "preceding" the PubGrub one (`lib.rs:6`). **Moremi imports neither**;
  it reimplements their surface natively against the real engine. But two of the
  family are real working code: **`forge-compositor`** (835 lines) genuinely
  composes layers with per-field provenance and implements the Maya Loom genome
  system, and **`@maya/forge-assist`** (TypeScript) is a real capability
  firewall that rejects unsafe artifacts before they reach the sandbox.
- **The honest seam is the live intake-and-serve daemon.** The sandbox, the
  resolver, and the content store ship as **verified libraries and eval gates**,
  not as an assembled service that today accepts an uploaded module over the
  network, scans it, and streams it to clients at scale. The hostile-module
  corpus is hand-built WAT fixtures, not a flood of real uploads; the AOT
  precompile step is real, but the network scanner and CDN are the seam. Treat
  the byte-level mechanics below as proven and the production wiring as the
  integration target.

## Executable safe scripting — the Ixchel sandbox

### Resources, the six tiers, and the lifecycle

A realm's behaviour is built from **resources** — the FiveM unit, hardened. A
resource bundles assets, data blobs, and WASM scripts, declares its dependencies
and the sandbox tier it needs, and names the events and callbacks it exports and
consumes. That shape is `MoremiResourceManifest` (`lib.rs:741`): a
`resource_id`, a `version`, a `sandbox_tier`, a `hot_reload` flag, and vectors
of `dependencies`, `assets`, `data_blobs`, `wasm_scripts`, `exported_events`,
`exported_callbacks`, `consumed_exports`, and `capability_grants`. The reference
roleplay framework (Nàná) is itself a `BaseFramework` resource that exports
inventory/job/economy primitives; content resources are `Content` resources that
depend on it. A creator never edits a "load order" — they ship a resource that
declares what it needs.

The **six tiers** form a total order (`MoremiResourceSandboxTier`,
`lib.rs:583`): `DataOnly < Scripted < Extended < System < Native < Trusted`. The
lattice is load-bearing, not decorative: `allows_code_execution` is true only
for `tier > DataOnly` (`:617`), and `grants_capability` is
`tier >= capability.minimum_sandbox_tier()` (`:621`). Capabilities are typed
(`MoremiIxchelWitCapability`, `:536`), and each names a WIT interface and the
minimum tier allowed to hold it (`:545`–`:571`): `RealmEmitEvent` needs
**Extended**, `EconomyGrantCurrency` needs **System**, and `PlatformSecretRead`
needs **Trusted**. A realm declares the maximum tier it will host; community
uploads default to Scripted/Extended; System is granted only to framework
authors a realm owner explicitly trusts; and Native/Trusted are review- and
signature-gated, off-limits to community uploads. The tier-gate eval
(`run_moremi_ixchel_tier_gate_eval`, `:7893`) proves a System-tier resource
loaded into an Extended-max realm is refused with a precise
`SandboxTierExceeded { required: System, allowed: Extended }`.

What makes this safe to operate is the **lifecycle manager**
(`MoremiIxchelLifecycleManager`, `:7599`). `hot_reload_resource` (`:7673`) swaps
a resource generation while preserving the prior generation's state hash and
emitting a cleanup receipt — so a realm owner ships a fix or a content drop
without kicking the population. The decisive property is fault isolation:
`isolate_faulting_resource` (`:7702`) sets a faulting resource to `Disabled`,
records the reason, and leaves `realm_running` true with the connected-player
count unchanged. The lifecycle test
(`ixchel_lifecycle_isolates_faulting_resource_without_crashing_realm`) asserts
exactly that — a bad mod takes itself down, not the world. That is the "degrades
safely" promise as code.

### The four-layer sandbox gate

The sandbox is enforced at four composing layers, each backed by an adversarial
eval that I ran green:

1. **Capability-typed host interface.** A plugin receives only the host imports
   its tier and manifest grant — no ambient authority. The capability-deny eval
   (`run_moremi_ixchel_capability_deny_eval`, `:8660`) builds a guest that
   declares `RealmEmitEvent`, instantiates it as a real Wasmtime component, and
   confirms exactly one host call. It then runs a hostile corpus — guests
   reaching for `EconomyGrantCurrency` and `PlatformSecretRead` **without
   declaring them** — and asserts both are denied _before instantiation_, never
   called, with zero host calls recorded. Capability absence is structural:
   expressed through which WIT imports the linker actually provides.
2. **CPU budgeting.** Gameplay-critical plugins run under **fuel** —
   deterministic, instruction-counted metering, so they trap at the same
   instruction on every replay; cosmetic work runs under cheaper **epoch**
   interruption. The budget eval (`run_moremi_ixchel_budget_eval`, `:7958`)
   compiles two real runaway-loop WAT modules; for the deterministic case it
   sets `config.consume_fuel(true)` (`:8030`) and observes a real trap at
   `get_fuel() == 0` classified `FuelExhausted`; for the cosmetic case it sets
   `config.epoch_interruption(true)` (`:8033`) and observes an `EpochDeadline`
   trap. Either way the overrun is `trapped`, `throttled`, and `flagged` — never
   allowed to hang the realm tick.
3. **Memory and stack limits.** The per-store `StoreLimitsBuilder` (`:8165`)
   caps linear-memory size, table elements, and instance count and sets
   `trap_on_grow_failure(true)` (`:8170`). The probe proves an oversized
   linear-memory module is refused at instantiation, a recursive stack traps,
   and — the data-leak guard — two instances of the same module are isolated, so
   the second reads `0` for a secret the first wrote
   (`no_cross_instance_memory_leak`).
4. **Host hardening and AOT.** Borrowing Luau's frozen-host posture, the
   host-tamper eval (`run_moremi_ixchel_host_tamper_eval`, `:8517`) runs five
   attacks — mutate a host global, mutate a host metatable, write another
   plugin's environment, disable the interrupt callback, load untrusted bytecode
   — and asserts all five blocked with the shared host-state hash byte-identical
   before and after. Vetted plugins are AOT-compiled at upload via
   `precompile_component()` (`:8317`) and run on a production engine built with
   the compiler disabled — speed and attack-surface reduction in one move.

All four roll into one adversarial gate. `run_moremi_ixchel_sandbox_escape_eval`
(`:8726`) aggregates the capability, budget, and host-tamper evidence into a
single fixture corpus and marks each `blocked` / `detected` / `escaped`. The
report's `passed()` (`:7564`) is true only when the corpus is non-empty, every
fixture is blocked and detected, and **`escaped_fixtures == 0`**. The pinned
test `ixchel_sandbox_escape_eval_blocks_all_hostile_modules` (`:14717`) asserts
that over ≥6 hostile fixtures — and it is green. That zero-escape bar is the
launch gate, not a hope.

### The scripting surface and the capability firewall

Three on-ramps compile to the same WASM sandbox target — **visual logic** (the
no-code node graph), a **TypeScript/JavaScript API** for advanced creators, and
**vetted native modules** for performance-critical paths only — so the safety
properties above hold no matter which on-ramp a creator picked. The firewall
that keeps a creator (or an AI co-creator) from declaring its way around those
properties is `@maya/forge-assist`'s `CapabilityPolicy`
(`forge-assist/src/capability-policy.ts:54`). Its `evaluate(declared, source)`
enforces three rules: a capability that names a platform-owned namespace is a
`PLATFORM_NAMESPACE` violation; a declared capability outside the realm
allowlist is `CAPABILITY_NOT_ALLOWED`; and a capability the source _invokes_ but
never declared is `UNDECLARED_INVOKE`. The `DEFAULT_REALM_CAPABILITY_POLICY`
(`:121`) makes `platform.identity`, `platform.economy`, `platform.moderation`,
and `platform.payments` forbidden to any realm artifact — there is no
declaration that makes a realm script mint currency.

The identity half of that boundary is enforced independently in
`libs/v7/substrate-bridge/src/lib.rs`. `V7IdentityFirewall` (`:325`) projects a
platform principal into an **opaque per-realm handle** (`realm-user:…`, `:363`)
via a peppered digest, and its test asserts the _same_ player gets _different_
handles in two different realms (`:2167`) — so realm code can never see a
platform account id, and a ban-evader cannot correlate themselves across realms.
Currency lives in Aje behind that boundary; even a System-tier script that holds
`EconomyGrantCurrency` only mutates realm-local, non-fungible play-currency.

## The modding framework — composable layers, not load order

### SAT resolution and the content-addressed lock

A realm is reproducible only if "which exact bytes run" is pinned, and that is
two subsystems, both real. **Resolution is PubGrub.**
`run_moremi_forge_resolver_eval` (`lib.rs:9934`) drives a real
`pubgrub::resolve` over a package registry with SemVer ranges
(`VersionReq::parse`), **optional dependencies**, **feature gates**, and
**capability alternatives** that expand to a cartesian product of providers
satisfying an abstract capability id. It proves three behaviours: a satisfiable
graph locks the expected versions; disabling a feature drops its optional
dependency from the lock; and an unsatisfiable graph fails with a **precise root
cause** — the failure report carries `plain_english`, `root_causes`, and a
`pubgrub_report` (`:1616`–`:1618`), not a silent crash. The solution freezes to
a `MoremiForgeLockFile` (`:1590`) carrying a `lock_hash`.

**Storage is content-addressed, Nix-model.** Every artifact's id is a SHA-256
hash of its content _and_ its full dependency closure: a `content_hash` over the
package blobs, a `closure_hash` over sorted dependency hashes (`:2027`),
combined into the `artifact_hash` (`:2028`) that becomes the store path. This
buys deduplication, tamper-evidence (swap one dependency and the artifact hash
changes, so the lock no longer verifies), and atomic install/rollback for free.

The pinned lock is what crosses to the client.
`V7/ue/Config/RealmLocks/dedicated-smoke.lock.json` is a real realm lock whose
`artifactClosure` lists each artifact with a `sha256:` `contentHash`, its
`dependencyRefs`, and a `payloadHash`. On the UE side,
`UMawuRealmLockFileLibrary::ParseLockFileJson`
(`V7/ue/Source/MawuRealm/Private/MawuRealmLockTypes.cpp:407`) parses and
validates it — rejecting any layer without a `sha256:` content hash (`:278`) —
and `AMawuComposedRealmActor::ApplyLockFile` (`MawuComposedRealmActor.cpp:27`)
composes the realm geometry from that content-addressed closure. The server
resolves and pins; the client renders exactly the pinned composition, or
refuses.

### Composition, provenance, and the Loom

Once resolved, layers compose in a fixed priority. `forge-compositor`
(`libs/maya/forge-compositor/src/lib.rs`) applies layers in the band order
`Engine < BaseGame < ContentPack < ServerRealm < CommunityVariant < PersonalOverride`,
breaking ties by layer id, and records a `source_layer_id` (`:62`) for every
composed field — so a realm operator can see _which_ layer supplied each value.
"Load order" never appears in the creator's vocabulary; per-field provenance
replaces it.

On top sits the **Maya Loom**. `compile_world_genome_layer` (`:300`) turns a
shareable world genome — physics constants, biome/dimension/narrative rules —
into a deterministic Ixchel layer, so applying a genome to a base game yields an
entirely different experience without forking code. `express_world_genome`
(`:424`) applies it **non-destructively** (`fork_created: false`, `:446`),
reporting only the changed paths — the "variants without forking" capability —
and `compose_runtime_expression_view` (`:480`) layers per-player runtime
expressions with visibility scopes, its test proving one player's "eternal
winter" does not bleed into another's view and that a realm-wide expression
requires explicit owner consent.

### What is real, and what is feature-level spec

Being precise here matters. **Real today:** the sandbox, the six tiers, the
lifecycle, the PubGrub resolver, the content-addressed lock, the compositor, and
the Loom genome/expression system. **Feature-level spec — described in the
monolith, no implementing code on disk yet:** Collections (Nexus/Wabbajack-style
one-click reproducible modlists), automatic dependency-revenue chains, full
git-like world **forking** with attribution links (the Loom's non-destructive
expression is the nearest real primitive, but forking-as-a-product is the
Abundantia layer), and **AI balance verification** (Maya's "Crucible" — fleets
of agents that stress-test mod combinations for exploits). A grep for a Crucible
or balance-verifier implementation finds only unrelated engine stress tests;
treat it as roadmap, not as a checkbox you can point at here.

## What creators can build safely

Put together, the guarantee a creator gets is concrete: **ship real logic and
the platform contains its worst case for you.** A creator writes a heist
resource in TypeScript, declares the capabilities it touches, and uploads it.
`forge-assist` rejects it at authoring time if it reaches for `platform.*` or
invokes something undeclared; PubGrub resolves its dependencies into a lock (or
a precise conflict error); the content store hashes its bytes and closure so a
swapped dependency can't slip in post-publish; and the AOT-precompiled module
runs behind the four-layer gate. If it faults in production, the lifecycle
manager disables that one resource and the realm keeps running, rendering only
the pinned, provenance-stamped composition — the flow below.

```mermaid
flowchart TD
  C["Creator: TS / visual logic / native"] --> FA["@maya/forge-assist<br/>CapabilityPolicy.evaluate"]
  FA -- "platform.* or undeclared invoke" --> REJ[[rejected at authoring<br/>never reaches sandbox]]
  FA -- "realm.* only, declared" --> MAN["MoremiResourceManifest<br/>tier + capability_grants + wasm_scripts"]
  MAN --> RES["PubGrub resolve<br/>semver + features + capability alts"]
  RES -- unsat --> FAIL[[plain-English root cause]]
  RES -- locked --> CAS["SHA-256 content store<br/>artifact_hash = content + closure"]
  CAS --> LOCK["MoremiForgeLockFile (lock_hash)"]
  LOCK --> GATE{{"Wasmtime four-layer gate"}}
  subgraph GATE_INNER [enforced + eval-gated]
    T["tier gate: tier ≥ capability min"] --> CAP["capability deny:<br/>undeclared imports refused"]
    CAP --> FUEL["fuel / epoch: runaway → trap + flag"]
    FUEL --> LIM["StoreLimits: memory / stack / no leak"]
    LIM --> HARD["frozen host: no tamper, no untrusted bytecode"]
  end
  GATE --> GATE_INNER
  GATE_INNER --> ESC{{"sandbox-escape gate<br/>escaped_fixtures == 0"}}
  ESC --> COMP["forge-compositor<br/>priority bands + per-field provenance"]
  COMP --> UE["UE: ApplyLockFile renders pinned closure"]
  LOCK -.->|pinned lock| UE
```

## Edge cases and failure modes

- **A mod reaches for the economy.** A guest invoking `EconomyGrantCurrency`
  without System tier and a declared grant is denied before instantiation — no
  host call ever happens (`:8660`). Currency lives in Aje, never in realm WASM.
- **A mod tries to read a platform secret.** `PlatformSecretRead` is
  Trusted-tier, first-party only; a community resource cannot declare it, and
  the `V7IdentityFirewall` independently shows realm code only an opaque
  per-realm handle.
- **An infinite loop.** A runaway gameplay plugin exhausts its fuel and traps at
  a deterministic instruction (`FuelExhausted`); it is throttled and flagged,
  the tick proceeds, and a golden replay reproduces the exact state hash.
- **A memory bomb or data exfiltration.** Oversized linear memory is refused at
  instantiation, a recursive stack traps, and per-instance isolation means a
  freed plugin can't leak bytes into the next (`no_cross_instance_memory_leak`).
- **A swapped dependency.** Because an artifact id hashes its content _and_ its
  closure, substituting a dependency changes the hash; the lock no longer
  verifies and the realm refuses to compose the altered closure.
- **A forged client event.** Orthogonal but adjacent: the realm protocol treats
  every cross-trust message as hostile —
  `rejects_forged_payload_even_with_valid_token` and
  `rejects_forged_security_token_even_when_envelope_is_resigned`
  (`libs/v7/realm-protocol/src/lib.rs:3788`, `:3799`) drop a tampered intent
  before it mutates authoritative state.
- **`forge-sandbox` is a contract, not the runtime.** Cite
  `apps/v7/moremi-realm-server` for the real sandbox; the
  `libs/maya/forge-sandbox` crate is the API scaffold Moremi reimplements, by
  its own doc comment.

## Where this connects

- [Moremi: The Server-Authoritative Roleplay Framework](./moremi-roleplay-framework.md)
  — the authoritative Rust server that _hosts_ this sandbox, validates client
  intent, and runs the Nàná resources Ixchel composes.
- [Mawu Studio: Creation Tools](./mawu-studio-creation-tools.md) — the visual
  logic, TypeScript, and AI-assisted on-ramps that emit the Ixchel layers and
  manifests this page resolves and runs (sibling).
- [../architecture/ixchel-modding-runtime-and-wasm-sandbox.md](../architecture/ixchel-modding-runtime-and-wasm-sandbox.md)
  — the byte-level companion: every eval, the four-layer enforcement, the
  PubGrub/content-store mechanics, and the end-to-end pipeline in depth.
- The feature hub: [../V7_features.md](../V7_features.md).
