Skip to content

PATTERN Cited by 1 source

Proxy-based entrypoint encapsulation

Intent

When you need consistent, platform-side interception of every call across an FFI boundary (e.g. Rust↔JS through WebAssembly) but you don't yet have an upstream fix in the binding generator, wrap the boundary with JavaScript Proxy-based indirection so every entrypoint is routed through your platform-controlled interception layer — even exports you didn't write and exports generated dynamically.

Use case

Consistent failure detection + sandbox reinitialisation across a large, auto-generated binding surface. The canonical instance is workers-rs 0.6: Cloudflare needed every call into a Rust-on-Wasm Worker to be wrappable — so a custom Rust panic handler could mark the Worker as failed and a reinitialisation step could run before subsequent requests — but the wasm-bindgen-generated glue exposed dozens of entrypoints per Worker, each of which would otherwise have needed a hand-written wrapper.

The mechanism

A JavaScript Proxy transparently intercepts property access (get) on the Wasm exports object and returns wrapped versions of exported functions. The wrapper calls the real export, catches thrown errors, updates platform-side Worker failure state, and — on detected failure — triggers reinitialisation. The Proxy handles every export without enumeration, including dynamically- added exports.

Cloudflare's 2026-04 retrospective describes the 0.6 recipe directly:

"We introduced a custom Rust panic handler that tracked failure state within a Worker and triggered full application reinitialization before handling subsequent requests. On the JavaScript side, this required wrapping the Rust-JavaScript call boundary using Proxy-based indirection to ensure that all entrypoints were consistently encapsulated. We also made targeted modifications to the generated bindings to correctly reinitialize the WebAssembly module after a failure."

Strengths

  • Zero per-export boilerplate. The Proxy covers arbitrary future exports without modification.
  • Works today on unmodified wasm-bindgen output — doesn't require coordinating changes with the upstream binding generator.
  • Centralises failure handling so the policy lives in one readable place.

Weaknesses and why it's a stepping stone

  • Lives platform-side only. workers-rs users benefit; every other wasm-bindgen consumer stays broken. The fix doesn't compound across the ecosystem.
  • Relies on custom JavaScript logic layered over the stock binding generator — "this approach relied on custom JavaScript logic" — so it diverges from upstream over time.
  • Proxy overhead per call is real even if small; every Rust↔JS hop pays the interception tax.
  • Targeted binding modifications are fragile — the 0.6 recipe required patching generated code; those patches have to be maintained against new wasm-bindgen releases.

These are the standard costs of a platform-private workaround. In Cloudflare's case, the right resolution was to invest in becoming a wasm-bindgen co-maintainer and move the fix upstream — see patterns/upstream-the-fix. The panic=unwind + set_on_abort machinery in concepts/panic-unwind and concepts/abort-recovery makes the Proxy wrapper largely obsolete for workers-rs 0.8+ users.

Applicability beyond Rust Workers

The pattern generalises to any FFI boundary where:

  • The binding generator produces many entrypoints mechanically.
  • You need platform-side interception of every call.
  • You can't (yet) modify the binding generator.
  • JavaScript Proxy (or an equivalent reflection primitive in the host language) is available.

Similar shapes appear in RPC client wrappers, telemetry instrumentation over third-party SDKs, and — adjacent to this post — JS libraries embedding wasm-bindgen-compiled Rust, where the new --reset-state-function now replaces most hand-rolled Proxy wrappers.

Seen in

Last updated · 510 distilled / 1,221 read