User guide and surface reference for loading external HashiCorp go-plugin modules into go-go-goja runtimes.
This page explains how plugin-backed JavaScript modules work from a user point of view.
The short version is simple: a plugin is a separate Go binary that speaks the HashiCorp go-plugin protocol, publishes a manifest that describes one JavaScript module, and responds to function calls from the host runtime. The host goja runtime stays inside go-go-goja. The plugin does not own a goja.Runtime; it only exposes a process boundary and an RPC surface.
That distinction matters because it tells you what to expect operationally:
require("plugin:...") inside JavaScript.From JavaScript, a plugin-backed module looks like a normal native module:
const echo = require("plugin:echo")
echo.ping("hello")
echo.math.add(2, 3)
echo.pid()
The module name is the manifest module name published by the plugin. In the current design, plugin modules are expected to live in the plugin: namespace, so plugin:echo is valid and echo is rejected.
The exports currently supported by the host are:
echo.ping(...)echo.math.add(...)The first implementation intentionally keeps the export surface small. That keeps plugin manifests easy to validate and makes it obvious how a plugin export maps onto a JavaScript call site.
This section shows the smallest working flow for testing a plugin manually.
From the repository root:
mkdir -p ~/.go-go-goja/plugins/examples
go build -o ~/.go-go-goja/plugins/examples/goja-plugin-examples-greeter ./plugins/examples/greeter
If you want to install the whole example catalog at once, run:
make install-modules
The binary name matters. Discovery uses the pattern goja-plugin-* by default, so goja-plugin-examples-greeter is picked up automatically.
go run ./cmd/goja-repl tui
At runtime, goja-repl tui scans ~/.go-go-goja/plugins/... by default, starts matching plugin binaries through go-plugin, validates their manifests, and registers them as runtime-scoped CommonJS modules.
If you want to use a different location for one run, pass one or more explicit flags:
go run ./cmd/goja-repl --plugin-dir /tmp/goja-plugins tui
let greeter = require("plugin:examples:greeter")
greeter.greet("hello")
greeter.strings.upper("hello")
greeter.meta.pid()
Expected results:
greeter.greet("hello") returns "hello, hello"greeter.strings.upper("hello") returns "HELLO"greeter.meta.pid() returns the plugin subprocess PIDThe example directory now contains several SDK-authored plugins, each meant to teach one part of the surface:
| Example | Module name | What to learn from it |
|---|---|---|
plugins/examples/greeter | plugin:examples:greeter | Baseline module structure, metadata, function exports, object methods |
plugins/examples/clock | plugin:examples:clock | Zero-argument handlers and structured result objects |
plugins/examples/validator | plugin:examples:validator | sdk.Call helpers, defaults, map/slice inputs, and validation failures |
plugins/examples/kv | plugin:examples:kv | Stateful object methods that keep process-local state |
plugins/examples/system-info | plugin:examples:system-info | Mixed export shapes and nested JSON-like responses |
plugins/examples/failing | plugin:examples:failing | Explicit handler errors and how failures surface to JavaScript |
If you want a single starting point, copy plugins/examples/greeter. If you want a plugin that looks more like input validation or process-local services, read validator and kv next.
When you leave the REPL or the runtime is closed, the plugin subprocesses created for that runtime are shut down by runtime cleanup hooks.
The canonical interactive entrypoint is goja-repl tui.
goja-repl tuigoja-repl tui is the Bobatea TUI REPL with completion and help widgets.
It supports:
~/.go-go-goja/plugins/...--plugin-dir flags for explicit directories--allow-plugin-module flags for module-name allowlisting--profileExample:
go run ./cmd/goja-repl tui
This section explains how the host decides which binaries count as plugins.
By default, the host-side plugin config uses:
goja-plugin-*plugin:The host scans the configured directories and filters for regular executable files that match the discovery pattern. For each candidate, it:
go-plugin client,require.Registry.If any plugin has an invalid manifest, runtime creation fails early instead of partially registering a broken module set.
goja-repl tui exposes plugin visibility through runtime startup and its in-session JavaScript environment:
goja-repl,This section is the user-facing contract for what a plugin may expose to JavaScript.
plugin:Examples:
plugin:echoplugin:examples:greeterplugin:dbtoolsechofsCurrent export kinds:
FUNCTIONOBJECTRules:
FUNCTION export must not declare methods.OBJECT export must declare one or more method names.Every JavaScript call is forwarded over RPC as:
structpb.Value values.The result comes back as one protobuf value and is converted back into a JavaScript value by the host runtime.
That means the most reliable data shapes are the ones that already round-trip cleanly through JSON-like values:
null.If you are building your own plugin binary, follow these rules first before you optimize anything else.
goja-plugin-*.plugin: namespace.pkg/hashiplugin/sdk package instead of implementing contract.JSModule manually.plugin:foo.math.add.These rules are not arbitrary. They align with how the first host implementation validates manifests and how it currently marshals values across the process boundary.
The example catalog under plugins/examples/... now follows the richer SDK path:
sdk.MustModule(...) defines the module,sdk.Function(...) defines top-level functions,sdk.Object(...sdk.Method(...)) defines object-method exports,sdk.MethodSummary(...), sdk.MethodDoc(...), and sdk.MethodTags(...) attach richer method metadata,sdk.Serve(...) boots the shared transport.The examples are intentionally varied. greeter is the small baseline, clock emphasizes structured return values, validator shows sdk.Call helper usage, kv shows plugin-local state, system-info shows nested responses, and failing shows explicit error returns.
This feature is process isolation, not sandboxing.
Loading a plugin means executing a local Go binary that you selected via --plugin-dir. The plugin runs outside the host process, which is useful for isolation and lifecycle control, but it is still native code you are choosing to execute. Treat plugin directories as trusted execution inputs.
What the current system does provide:
What it does not currently provide by default:
If you want to allow only a specific set of modules for one run, use the allowlist flag:
go run ./cmd/goja-repl --allow-plugin-module plugin:examples:greeter tui
You can repeat the flag to allow multiple module names.
| Problem | Cause | Solution |
|---|---|---|
Cannot find module 'plugin:examples:greeter' | The plugin binary was not discovered or the manifest was rejected | Confirm the binary is named goja-plugin-examples-greeter, placed in the configured directory, and that you passed --plugin-dir /path/to/dir |
Runtime creation fails with must use namespace | The plugin manifest published a module name outside plugin: | Update the plugin manifest to use a name like plugin:echo |
Runtime creation fails with not in the allowlist | The plugin loaded successfully but its module name was not on the requested allowlist | Add --allow-plugin-module plugin:your-module or remove the allowlist restriction |
| The plugin binary exists but still is not loaded | The file is not executable or does not match the discovery pattern | Run chmod +x if needed and keep the binary name under goja-plugin-* |
| Calls fail on argument conversion | The JS values do not cleanly round-trip through protobuf structpb.Value | Use JSON-like values and avoid host-specific Goja objects/functions as arguments |
goja-repl tui does not see plugins | The plugin was not built under the default tree and no explicit directory was passed | Build into ~/.go-go-goja/plugins/... or pass one or more --plugin-dir flags |
goja-repl help plugin-tutorial-build-install — Step-by-step tutorial for building and installing a plugingoja-repl help goja-plugin-developer-guide — Internal architecture and integration guidegoja-repl help repl-usage — General REPL usagegoja-repl help creating-modules — Native in-process module authoring guide