Plugins
A plugin adds a new top-level command to hull. Once installed, hull greet or
hull backup runs your code as if it were built in.
What a plugin actually is
A hull plugin is a directory containing:
- a
plugin.yamlmanifest, and - an executable (or a shell one-liner) that the manifest points at.
That’s the whole contract. Some specifics, because they’re the questions every plugin author asks first:
- Does it have to be Go? No. The plugin can be any executable — a bash or Python script, a compiled binary in any language, anything your OS can run. hull itself is Go, but it never loads your code into its own process.
- Is it linked in / a Go plugin (
.so)? No. hull does not use Go’spluginpackage. Your plugin is a separate program. - Is it a pipe / RPC / IO protocol? No. hull runs your program as a normal
child process with
exec, and wires its stdin, stdout, and stderr straight to the terminal. You read stdin and print to stdout exactly as you would in any CLI. There is no framing, no JSON-RPC, no socket. - Is it attached / long-running? No. hull execs it, waits, and returns its exit code. When your program exits, the command is done.
This is the same model git, kubectl, and Helm use for their plugins — and hull sets Helm’s environment variables too, so many existing Helm plugins run unchanged.
Build your first plugin
A plugin is a directory. Create one:
mkdir greet && cd greet
Write the manifest, plugin.yaml:
name: greet
version: 0.1.0
usage: "greet [name]"
description: Print a friendly greeting
command: greet.sh
Write the executable it points at, greet.sh:
#!/usr/bin/env bash
echo "Hello, ${1:-world}! (plugin dir: $HULL_PLUGIN_DIR)"
Install it from the local directory:
hull plugin install ./greet
Installed plugin: greet v0.1.0
Now run it — everything after the plugin name is passed to your program as arguments:
hull greet Ada
Hello, Ada! (plugin dir: /home/you/.config/hull/plugins/greet)
hull greet
Hello, world! (plugin dir: /home/you/.config/hull/plugins/greet)
hull greet Ada ran greet.sh with Ada as $1; with no argument, the
script’s own ${1:-world} default produced world. hull added
HULL_PLUGIN_DIR to the environment (see below).
The plugin.yaml manifest
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | yes | The command users type: hull <name>. Must match [a-zA-Z0-9][a-zA-Z0-9._-]*. |
command |
string | yes | What to run. Either a filename in the plugin dir, or a shell expression (see below). |
version |
string | no | Shown in hull plugin list. |
description |
string | no | Shown in hull plugin list. |
usage |
string | no | Short usage line for your own help text. |
hooks |
map | no | install / update / delete shell commands run on lifecycle events. |
downloaders |
list | no | Custom protocol handlers (see Custom downloaders). |
Unknown top-level keys are rejected at install time (strict parsing), so a typo in a field name fails loudly instead of being silently ignored.
The command field
command has two shapes, and hull picks automatically:
-
A filename — a single token naming a file inside the plugin directory, e.g.
command: greet.sh. hull runs that file directly, appending the user’s arguments. This is the common case. hull makes the file executable (chmod 0755) on install. -
A shell expression — anything containing whitespace or shell characters (
| & ; < > $ ( ) * ?), e.g.command: "echo hello"orcommand: "python3 $HULL_PLUGIN_DIR/main.py". hull runs it through/bin/sh -c(cmd /Con Windows), so pipes,$HULL_PLUGIN_DIR, and multi-word commands all work.
For safety, a plain filename must resolve to a real file in the plugin dir; it
cannot contain a path separator or .., and a symlink is refused. A
filename that doesn’t exist is an error (it catches typos) rather than silently
falling through to the shell.
What hull passes your plugin
Arguments. Everything after hull <name> is passed to your program in
order. hull greet Ada --loud calls your program with Ada and --loud.
Standard streams. stdin, stdout, and stderr are the terminal’s. Read input, print output, and set your exit code normally.
Environment variables. hull adds these on top of the inherited environment:
| Variable | Value |
|---|---|
HULL_PLUGIN_DIR |
Absolute path to your plugin’s installed directory. |
HULL_BIN |
Path to the hull executable — call back into hull with "$HULL_BIN" template ., etc. |
HULL_NAMESPACE |
The active namespace (-n / HULL_NAMESPACE / HELM_NAMESPACE). |
HULL_KUBECONFIG |
The active KUBECONFIG path. |
HELM_PLUGIN_DIR, HELM_BIN, HELM_NAMESPACE |
Helm-compatible aliases of the above, so Helm plugins work. |
Your program runs with its working directory set to the plugin directory.
Lifecycle hooks
Declare shell commands that run on install, update, and remove — useful for fetching a binary, compiling, or cleaning up:
name: backup
version: 1.0.0
description: Back up a release
command: backup
hooks:
install: "go build -o backup ./cmd"
update: "go build -o backup ./cmd"
delete: "rm -f backup"
Each hook runs through /bin/sh -c (cmd /C on Windows) with the working
directory set to the plugin directory and the same environment variables listed
above. The install hook runs after the files are in place; if it fails,
hull removes the half-installed plugin. hull logs the exact hook command before
running it, because a hook is the plugin author’s code running with your
privileges.
Custom downloaders
A plugin can teach hull to fetch packages over a custom protocol. Declare one or more downloaders:
downloaders:
- command: myproto-downloader
protocols: ["myproto"]
hull invokes the downloader as:
<command> <cert> <key> <ca> <full-url>
where the first three are TLS material (may be empty) and the last is the URL the user requested. The downloader writes the package bytes to stdout.
Installing and distributing
Install from a local directory (copied into place; symlinks are refused) or
from a git repository (hull plugin install runs git clone --depth 1):
hull plugin install ./greet # local
hull plugin install https://github.com/you/hull-greet.git # git
Recognized git sources end in .git or start with git@, git://, ssh://,
or file://. Everything else is treated as a local path.
Plugins install under ~/.config/hull/plugins/<dir>. Manage them with:
hull plugin list # NAME / VERSION / DESCRIPTION
hull plugin update greet # git plugins: git pull --ff-only, then update hook
hull plugin remove greet # runs the delete hook, then deletes the directory
To publish for others, push the plugin directory (manifest + executable, or a
manifest plus an install hook that builds it) to a git repo and share the
clone URL. Signed plugins can be distributed through the
marketplace.
Security
Installing a plugin means running its author’s code on your machine — the
command, any shell-form command, and every lifecycle hook execute with your
privileges. hull reduces surprises (strict manifest parsing, no path traversal
in command, no symlinks copied in, hooks logged before they run), but it
cannot make untrusted code safe. Install plugins only from sources you trust,
and prefer signed marketplace plugins.
See also
hull plugin— the plugin management commandshull plugin install·list·update·removehull marketplace— discover and verify signed plugins