Living Document Notice
Published 2026-09-16. The evolving architecture, revisions, and connected notes for this dispatch live in the Stax Digital Garden.
Sandboxing AST Extensions with Outrigger
Summary
Allowing user-written plugins to alter Markdown syntax trees introduces stability and security vulnerabilities. Conventional script runtimes that execute third-party code with full host filesystem privileges expose user vaults to malicious file access and unbounded CPU execution loops.
Bosun isolates third-party transformation hooks within the Outrigger Protocol WebAssembly System Interface (WASI) runtime. By constraining plugins to isolated memory spaces with explicit fuel metering, extensions can transform syntax nodes without obtaining access to the operating system or blocking the host engine thread.
Capability-Based WASI Isolation
Outrigger executes plugins as compiled WebAssembly modules with zero default capabilities. The host engine denies access to host environment variables, system clocks, network sockets, and directory trees.
pub struct SandboxConfig {
pub max_memory_pages: u32,
pub initial_fuel: u64,
pub allowed_directories: Vec<PathBuf>,
}
impl SandboxConfig {
pub fn restricted_ast_transform() -> Self {
Self {
max_memory_pages: 128, // 8 MB limit
initial_fuel: 50_000_000,
allowed_directories: Vec::new(),
}
}
}If an extension module attempts to open file handles or invoke OS syscalls, the WASI host layer returns an access denied error code, aborting execution immediately.
Host functions exposed to guest modules are restricted to pure mathematical operations and string span evaluations. No raw pointers into host memory are ever shared with the guest execution environment.
Deterministic Fuel Metering and Instruction Limits
Unbounded recursion or infinite loops inside user plugins can stall processing pipelines. Outrigger prevents denial-of-service conditions by injecting fuel consumption checks into compiled WebAssembly basic blocks.
store.set_fuel(50_000_000).unwrap();
let result = instance
.get_typed_func::<(), ()>(&mut store, "transform_ast")
.unwrap()
.call(&mut store, ());
match result {
Ok(_) => println!("Transformation completed successfully"),
Err(trap) if store.get_fuel().unwrap_or(0) == 0 => {
eprintln!("Plugin exceeded execution fuel allocation");
}
Err(trap) => eprintln!("Plugin trapped: {trap}"),
}Each executed instruction decrements the fuel counter. When fuel reaches zero, the WebAssembly runtime traps, allowing the host engine to log the offending plugin and continue processing remaining vault files.
Fuel bounds are calibrated to allow complex syntax transformations across 10,000-line files while capping runtime execution to under 25 milliseconds per document.
AST Serialization and Host-Guest Memory Exchange
AST nodes pass into the sandbox via shared linear memory buffers using a flat binary representation. The host serializes target subtrees into guest memory, passes the offset, and reads back modified node buffers upon completion.
This memory boundary prevents guest modules from corrupting host pointer addresses or tampering with adjacent document memory spaces.
The serialized format uses compact integer offsets and enum tags, avoiding JSON serialization overhead and keeping inter-boundary transfer latency below 15 microseconds.
Sandbox Execution Overhead and Fuel Consumption
The table below details performance and overhead metrics for sandboxed plugin executions under Outrigger.
| Extension Operation | Sandbox Instantiation | Fuel Consumed | Execution Overhead | Memory Quota Ceiling |
|---|---|---|---|---|
| Tag Normalizer Hook | 0.42 ms | 120,400 units | 48 μs | 8 MB |
| Wikilink Alias Expander | 0.44 ms | 310,200 units | 85 μs | 8 MB |
| Callout Block Transformer | 0.45 ms | 890,500 units | 164 μs | 16 MB |
| Custom Math Renderer | 0.51 ms | 2,410,000 units | 420 μs | 32 MB |
- Directus Target: bosunpkm-blog
- Garden Source Reference: MOC - Bosun PKM Engine, MOC - Bosun PKM Tools, MOC - Outrigger Protocol, MOC - Agentic Containment and Sandbox Boundaries, [OUT-1001 - Verifying Autonomous Agent Bounds with Pure Standard Library](OUT-1001 - Verifying Autonomous Agent Bounds with Pure Standard Library)