Extending it
The question a module graph does not answer: how does somebody who is not the author change what this does? Three systems, three units of extension.
Three units
| the unit | machinery | shipped content | |
|---|---|---|---|
| pi | a JavaScript file | ~160,000 lines — an embedded engine, a Node compatibility layer, a capability policy, a package manager | 107 embedded modules, ~78 example extensions |
| deepseek | a Rust crate | ~800 lines. Its plugin port has exactly one implementor, named UnavailablePluginRuntime | none |
| nerv | a Lua declaration | four VMs, one sandbox each — a removal list, not a permission model | ~9,000 lines of Lua, examples in every repository |
deepseek documents a plugin seam and ships the “unavailable” answer for it. That is honest, and it is the whole story. pi is thirteen times our size and is the only one of the three that has actually been extended by strangers — which is the fact worth taking seriously, and the reason for everything below.
What a third party can reach
| and how | |
|---|---|
| a tool | magi.tool, or casper.tool for one that runs a program |
| a watcher | magi.watch — eight events, told after the fact, answering nothing |
| a wire protocol | melchior.api. The registry is readable, so a dialect that differs from a shipped one in one function borrows the other three |
| a provider | melchior.provider — an endpoint, a credential, and either a model list or discover = true |
| a memory source | balthasar.source. No Rust file in balthasar names a harness; a new one is a file, not a release |
| what a model is told | balthasar.section — weight, order, filter, and a confidence floor |
Where it goes
neovim's runtimepath, unchanged, in all four. Twenty years of real plugins have been written against it and most people arriving already know it; deviating buys nothing and costs everyone the transfer.
~/.config/<program>/plugin/*.lua alphabetical, each on its own ~/.local/share/<program>/site/pack/*/start/*/plugin/*.lua installed packages ~/.config/<program>/after/plugin/*.lua the last word
Every registrar replaces by name, so the order is the precedence. No manifest, no
registration, no entry point to edit — requiring an edit to your own init.lua to
enable somebody's package makes every package a merge conflict with your configuration.
What contains it
pi gives its extensions a default grant of read, write, http, events and session, and never asks. It has a capability policy because it embedded a language that can do anything; the policy exists to take that back.
nerv never handed it over. The sandbox is a removal list — os.execute,
io.popen, io entirely, require, dofile,
loadfile — applied to the VM before any file runs. What is left cannot spawn or
touch a file. Running a command goes through the same ledger the shell tool passes; writing
one goes through the same gate the write tool passes. A discovered file is held to exactly
what a named one is.
os.execute. The removal list applies in all four now, and discovery arrived after it rather than before.What a package costs to trust
pi's package manager is 8,579 lines: npm and git sources, user and project scopes, a lockfile with digests and trust states. Most of that is a package ecosystem nobody here wants to be in. The ten per cent worth having is the part about trust rather than fetching, and it is what pi conspicuously does for MCP servers and not for its own extensions.
| a file you wrote | runs on sight. A prompt about your own configuration is one nobody reads — it trains people to say yes. |
| a package you fetched | held back until <program> acknowledge records its SHA-256, and held back again the moment it changes |
Fetching is git clone. The lockfile is the idea. A manifest that will not
parse reads as empty, which holds everything back rather than letting everything through.
What is promised
<program> verbs reports surface, separate from
family: one versions the wire between these programs, the other versions what an
extension is written against. Both move only when something already published stops working.
Five registrars were published, dead and unversioned for most of this project's life. A number an extension can read is what closes that window deliberately rather than by accident — and the examples in every repository are run by that repository's own test suite, because an example that does not load is worse than no example.