Files
zeekayandHanzo Dev 3e5f509c69 One plugin runtime, four languages, one interface
The contract was written down twice — zip declares Module and Loader, and the
loader beside base's runners declared its own copy. Structural typing was the
only thing keeping them equal, which is a property nobody can see and a compiler
will not check.

This implements zip's. The loader, the manifest and the runners live here so
cloud can stop carrying a second goja runtime of its own — a shim, a bundler and
a third registry that answered the same question this one does.

native, goja, wazero and pyvm. wazero is the reason this is not a list of four
languages: anything compiled to WebAssembly ahead of time arrives without a new
runner, and Rust walks through that door rather than getting one built for it.
pyvm is real CPython through cgo with PEP 684 sub-interpreters, behind a build
tag, because embedding the language beats reimplementing a subset of it.

Its one dependency on base was a single call for an unguessable string, which
crypto/rand says directly.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-16 12:33:40 -07:00

1.7 KiB

runtime

One plugin runtime for the Hanzo estate: a module declares what it exports, and a caller invokes an export by name with JSON in and JSON out. What language the module is written in is a property of the module, not of the caller.

type Module interface {
	Name() string
	Runtime() string
	Exports() []string
	Invoke(ctx context.Context, fn string, payload []byte) ([]byte, error)
}

That interface is zip's. This module implements it rather than declaring its own copy, which is the whole reason it exists: the same contract was written down twice — once in zip, once beside the loader — and only structural typing kept the two honest.

The runtimes

runtime language how
native Go in-process, no boundary
goja JavaScript, TypeScript in-process, no Node
wazero Rust, and anything else compiled to WebAssembly ahead of time
pyvm Python CPython via cgo, PEP 684 sub-interpreters — build tag pyvm

wazero is why this list is not a list of four languages. Anything that compiles to WebAssembly arrives without a new runner, and Rust is the first caller of that door rather than a special case built for it.

A module declares itself

extension.json, beside the module:

{
  "name": "brave-search",
  "version": "1.0.0",
  "runtime": "goja",
  "module": "index.js",
  "exports": ["search"]
}

Exports is the contract. A caller reads it to know what can be invoked, and nothing else has to be registered anywhere — which is what lets one registry publish a Go action and a Python one side by side.