Download the PHP package eddmann/terrarium without Composer
On this page you can find all versions of the php package eddmann/terrarium. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download eddmann/terrarium
More information about eddmann/terrarium
Files in eddmann/terrarium
Package terrarium
Short Description Typed PHP SDK for running untrusted JS, TypeScript, Python, or PHP sandboxed in WebAssembly
License MIT
Homepage https://github.com/eddmann/terrarium
Informations about the package terrarium
Terrarium
Run untrusted JavaScript, TypeScript, Python, or PHP inside PHP — sandboxed in WebAssembly, against a typed capability SDK you define in plain PHP.
You write capabilities as ordinary typed PHP functions and register() them;
untrusted guest code runs inside a WebAssembly sandbox and calls them by name
as a typed API. The guest reaches exactly what you registered — nothing else — and
because its entire language engine runs inside the WASM boundary, even a
memory-corruption bug in that engine cannot touch your process. No containers,
no microVMs: one PHP extension.
The whole thing is one uniform API — a single Terrarium class. The guest's
language is decided purely by which *_guest.wasm you load; nothing else changes.
Features
- Language-agnostic guests — JavaScript (QuickJS or Python, TypeScript; anything that targets WASM behind a tiny contract.
- Capability allowlist — the guest sees only the PHP functions you register, reached by their dotted names (no synthetic root). That allowlist is the entire trust boundary.
- In-process memory isolation — the engine runs inside the WASM sandbox, so an engine bug stays contained without an outer microVM/container.
- TypeScript checked in-sandbox — the real
tscruns in the guest and checks every eval against the.d.tsof your registered SDK, before it runs. - Typed SDK, three languages — types inferred from your PHP signatures +
PHPDoc and emitted as
.d.ts,.pyi, or a.phpstub. - Typed exceptions + captured output — guest failures surface as a
Terrarium\Exceptionfamily;console.log/printis captured separately and survives a throw. - Bounded and optionally deterministic — memory, time, stack, and fuel limits; fuel gives reproducible runs.
Quick example
Swap quickjs_guest.wasm for boa_guest.wasm, rustpython_guest.wasm, or
php_guest.wasm and the host code is unchanged. Fuller programs — typed
capabilities, four languages over one SDK — in examples/.
TypeScript, checked inside the sandbox
Load typescript_guest.wasm and every eval is type-checked against the .d.ts
generated from your registered SDK before it runs — the real TypeScript compiler
executes inside the wasm guest:
There's also a lint-style mode — check() validates without running and
returns every diagnostic as data ([] = passed). It works on every guest at the
depth its language allows (a full type-check here; a syntax/compile check on the
JS, Python, and PHP guests). See docs/api.md.
analyze() is the same pass with more of the compiler's knowledge handed back.
Name some callees and the TypeScript guest derives a JSON Schema from each
call's type argument — the author writes the type, you get the contract:
Canonical JSON text, identified by call ordinal so reformatting can't repoint it, carrying the call's line for consumers that must find their schema at runtime, and anything with no faithful schema form is refused by member path rather than approximated. See docs/api.md.
The sandbox is synchronous — there is no event loop, so a program that
suspends can never resume. That failure is never silent: a JS/TS eval that
yields a promise, leaves callbacks queued, or registers a promise reaction
raises AsyncIncomplete, and new Terrarium(..., syncOnly: true) makes the
TypeScript guest reject async, await, function*, yield and every use
of a promise at compile time, with the source line and a message naming the
synchronous alternative. Exactly what the run-time guard does and does not catch
is spelled out in
errors.md;
see also synchronous-only guests.
The TypeScript guest's check() also refuses what the sandbox engine cannot
parse even though the compiler accepts it (accessor class members), and its
lib declares nothing the engine lacks (no Intl, no Atomics) — so a clean
check() means "this will run", not merely "this type-checks".
Installation
Prebuilt binaries are attached to each release for PHP 8.4 / 8.5 — self-hosted Linux, AWS Lambda (a ready Bref layer), and macOS (Apple Silicon) — plus the platform-independent PHP library and guest wasm. Enable the extension and point the facade at a guest:
Then pull the PHP library (the Terrarium\Terrarium facade + type inference) via
Composer, and grab a guest engine from the guests.zip release artifact:
The package requires ext-terrarium, so Composer errors clearly if the extension
binary isn't enabled.
Or build from source (Rust 1.96+, clang, PHP dev headers — a plain cargo
cdylib, no phpize; the guest fixtures are committed, so no wasm toolchain is
needed):
→ Full matrix, Docker, and AWS Lambda / Bref instructions: docs/install.md.
How it works
- PHP defines the SDK.
register()typed PHP closures (andgrant()live objects as opaque handles). That allowlist is the entire trust boundary. - The guest runs sandboxed in WASM. A real engine compiled to wasm runs your
source and sees the SDK as frozen, multi-level globals installed from the
registered names — contained by memory / CPU / time limits and zero ambient
authority. Values cross as MessagePack over linear memory through one
host_callimport. - Types flow to the guest author. The SDK's types are inferred from the PHP
signatures (Reflection + PHPDoc, incl. nested
array{…}shapes) and emitted as.d.ts,.pyi, or a.phpstub.
→ docs/architecture.md for the full design.
The engines
Five are bundled; the same bridge serves any language that targets WASM. Each guest pins the upstream version it tracks; the committed fixtures are built from these. See each guest's README for build details and internals.
- QuickJS-ng
v0.16.2— JavaScript, C via the WASI SDK (the reference guest). - Boa
0.20— JavaScript, pure Rust (no C toolchain). - RustPython
0.5— Python, pure Rust. - PHP
8.3.14— real php-src via its embed SAPI: sandboxed PHP inside PHP. - TypeScript — QuickJS-ng +
tsc6.0.3as bytecode; type-checked against the SDK, erased, and run — Wizer-snapshotted so a checked eval is ~20 ms warm.
Scope
Terrarium's boundary is the WebAssembly VM, in-process. Unlike embedding an engine natively, a memory-corruption bug inside the guest engine is contained — it cannot form a pointer outside its linear memory or call a syscall you didn't import. The capability model contains what the guest can reach; the resource limits contain abuse (infinite loops, alloc bombs); and the VM contains the engine itself. That's a stronger default than a natively-embedded interpreter, with no outer microVM/gVisor required for memory safety.
The honest residual: a bug in Wasmtime itself is in the trust base — but that's a small, Rust, memory-safe, heavily-fuzzed surface, a far better bet than trusting each bundled engine's C codebase.
Documentation
- Installation — the three pieces, prebuilt binaries, AWS Lambda (Bref), and building from source.
- API reference — the
Terrariumclass and every method. - Architecture — why WebAssembly, the capability bridge
and
host_callABI, marshaling, type inference, and the trust model. - Execution modes — shared vs. isolated instances.
- Errors — the exception family, the
$errorsentinel, and output capture. - Per-engine notes under
guests/.
License
MIT. The committed guest fixtures (tests/wasm/*.wasm) embed
third-party engines under their own licenses — see
THIRD_PARTY_LICENSES.md.
All versions of terrarium with dependencies
ext-terrarium Version *