Navigation
Copy page

Sharing Extensions

On this page

This page is the receiving side. To install an extension someone published at a URL, you name the URL and the git object id it is pinned to, confirm two trust cards, and kli loads only bytes that hash to that id. There are two entry points for the same install:

  • In a running session, /install <url> <git-tree-sha1> loads the extension live into the current image.
  • From a shell or a script, kli install <url> <git-tree-sha1> places it durably for the next session without needing one running.

Both run the identical verification and the same two cards; they differ only in whether the code activates immediately or on the next launch. This is a remote extension install, not the command that installs kli itself — that is the one-time shell install in Installation. For the other side of this exchange — packaging, pinning, and signing an extension to publish — see Publishing extensions. Nix users have a third option that skips runtime install entirely: bake the extension into the image with programs.kli.

Get the URL and the pin

The publisher gives you two things: the URL the extension is served from, and the git object id it is pinned to. For a single-file extension the pin is the git blob id of the file, what git hash-object <file> prints; for a directory extension it is the git tree id over the whole unpacked tree, what git write-tree produces. Either way it is an identity no two different contents can share. If the publisher hands you only a URL, ask for the pin; an install with no pin, or with the wrong pin, is refused rather than loaded.

A published extension is a single Lisp file or a directory bundle — a multi-file unit packaged as one blob — the same kinds you write as a Lisp extension. kli detects which shape it fetched and verifies the matching pin. When it loads it gains eval authority in your session, so the pin is what lets you install code you did not write and still know exactly which bytes ran.

Install in a running session

In an interactive session, type:

/install https://example.com/path/to/extension.lisp 3f8a1c2e9b7d4f60a51e8c2d9f0b4a7c6e1d3b85

The command needs exactly the URL and the hash, two whitespace-separated tokens. Anything else prints Usage: /install <url> <git-tree-sha1>. This slash command runs in an interactive session and loads the extension live; to install without a running session, use the kli install command line.

kli shows a first card before fetching anything. It states the plain fact that author-provided Lisp is about to load into the running image with eval authority, names the URL, and shows the hash you pinned:

Install from https://example.com/path/to/extension.lisp. This loads
author-provided Lisp code into the running image with eval authority,
pinned to git 3f8a1c2e9b7d4f60a51e8c2d9f0b4a7c6e1d3b85.

A two-row menu opens under it: pick install to proceed or cancel to stop. Esc dismisses with no action. Nothing has been fetched at this point, so cancelling here makes no request and loads nothing.

Confirm the verification card

On the first confirm, kli fetches the bytes from the URL and hashes them as a git blob. If that id does not equal the hash you supplied, verification fails and the install stops with a verification failed line naming the URL and reason; no code is placed or loaded. A wrong or missing hash is a hard failure, never a skip.

When the bytes match, a second card reports the verified identity and the trust level, then opens the same install / cancel menu:

Verified bytes match git 3f8a1c2e9b7d4f60a51e8c2d9f0b4a7c6e1d3b85.
Unsigned: integrity-pinned only.

Unsigned: integrity-pinned only is the default state: kli verified that the bytes are the ones the hash names, and nothing more. It does not vouch for who wrote them. If you have configured publisher signing keys, this card instead reads Signed by trusted key <fingerprint> and a download missing or carrying an untrusted signature is refused at this step. Signing is opt-in and off by default; you turn it on by listing keys in trustRoots.

Confirm the second card and kli places the verified file under ~/.config/kli/extensions/, installs it into the running session, and prints Installed <id>. The extension's tools, commands, and keybindings are live immediately, with no restart. Picking cancel at either card prints Install cancelled.; a verified install that fails to index prints Install of <id> rejected (<reason>).

Install from the command line

kli install is the same install as a subcommand of the kli program, for a shell or a script rather than a running session:

kli install https://example.com/path/to/extension.lisp 3f8a1c2e9b7d4f60a51e8c2d9f0b4a7c6e1d3b85 --yes

It takes the URL and the pin as its two positional arguments and runs the identical verification — the git-object pin, then the opt-in signature check when you have configured trust roots. What differs is the result: rather than loading into a running image, it places the verified files durably under ~/.config/kli/extensions/ without activating them, and the next kli session — or kli mcp-serve <id> for a served extension like cairn — discovers them from disk. This is the path a headless install or a provisioning script takes.

The two trust cards print to stderr. --yes (or -y) confirms both stages without prompting; without it, an interactive terminal prompts once per stage, and a non-terminal run refuses rather than blocking on a prompt no one can answer. On success the declared extension id prints to stdout, so a script can capture it cleanly, and Installed <id>. prints to stderr.

The exit code reports the outcome: 0 installed; 2 a malformed invocation, which also prints Usage: kli install <url> <git-tree-sha1> [--yes]; and 3 a refusal, a cancellation, or a verification failure, in which case nothing was placed.

What persists

A confirmed install is recorded as a pin: its URL, the git tree sha1, and the trust level. Later sessions re-fetch the file from its URL and re-verify it against the same hash before loading. If the bytes no longer match the pin, or a previously signed pin comes back unsigned, kli declines to load it and records the refusal rather than running changed code under your old consent. The pin travels with your session, the verification does not weaken on restore, and you confirm the trust cards once, not every session.

  • Publishing extensions — the sending side: packaging, pinning, and signing an extension so others can install it.
  • trustRoots — the setting that turns the signature check from optional into required.
  • Lisp Extensions — what a published extension is and how to write your own.
  • Installation — installing the kli app, which is a different /install.
  • Capabilities and fault barriers — gating what an installed extension's tools can do, and the opt-in signing keys.
  • The Live Image — why an extension can install and run without a restart.