A convention for MNEME databases that serve as installable Lua module
packages for Lilush. A single .lilpack file carries Lua source code,
metadata, and shell integration descriptors.
The current convention version is 1.
Lilush replaces the standard Lua filesystem-based module loading with a three-stage searcher chain:
Preload -- built-in modules compiled into the binary
LILPACK -- modules from installed .lilpack databases
Script-local -- ./?.lua from the current directory
Standard filesystem searchers (package.path, package.cpath) are
disabled entirely. This prevents accidental loading of system Lua
modules (e.g. LuaRocks packages) that might clash with Lilush modules.
__lilpackA conforming LILPACK has a keyspace named __lilpack containing
well-known keys that describe the package. The __lilpack keyspace
name is reserved.
| Key | Value type | Description |
|---|---|---|
version | integer | Convention version (currently 1) |
name | string | Unique package identifier (e.g. "llm") |
pkg_version | string | Semantic version (e.g. "0.1.0") |
description | string | Short human-readable description |
modules | string (JSON) | Array of module names this package provides |
signature | bytes (64B) | Ed25519 signature over canonical digest |
pubkey | bytes (32B) | Signer's Ed25519 public key |
| Key | Value type | Description |
|---|---|---|
author | string | Author name or contact |
license | string | License identifier |
dependencies | string (JSON) | Array of dependency descriptors |
builtins | string (JSON) | Array of builtin family descriptors |
modes | string (JSON) | Array of shell mode descriptors |
cli | string (JSON) | Array of CLI script command names |
theme | string (JSON) | Theme section builders and/or theme definitions |
modulesLua source code is stored in a regular KV keyspace named modules.
Each key is a dot-separated module name matching what you pass to
require(). The value is the Lua source text.
Example keys for a package named llm:
llm -> source of llm.lua llm.oaic -> source of llm/oaic.lua llm.tools.bash -> source of llm/tools/bash.lua
Source is stored as text (not bytecode) to keep packages portable and
debuggable. Stack traces show @lilpack:<pack>:<module> chunk names.
cliStandalone Lua scripts intended for installation under ~/.local/bin/
are stored in a separate cli keyspace. Each key is a command name,
each value is the Lua source text.
Example keys for a package named mytools:
mytool -> source of cli/mytool.lua myutil -> source of cli/myutil.lua
Scripts in the cli keyspace are not loadable via require(). They
are extracted as files during lilpack install.
[
{"name": "llm", "version": ">=0.1.0"}
]
Dependencies are informational. The registry warns if a dependency is missing but does not prevent loading.
A package can provide shell builtins by declaring them in the manifest:
[{"module": "shell.builtins.k8s", "commands": ["ktl"]}]
The module field names the module within the package that returns a
builtin family table (same format as built-in families like
shell.builtins.fs). The module is loaded via require() during
shell startup.
A package can provide shell modes:
[{
"name": "agent",
"path": "agent.mode.agent",
"history": true,
"prompt": "agent.mode.agent.prompt",
"completion": {
"path": "agent.completion.slash",
"sources": ["agent.completion.source.slash"]
}
}]
Mode descriptors do NOT include shortcut assignments. Users map F-keys
to package modes via ~/.config/lilush/modes.json:
{
"F4": "agent",
"F5": "k8s-dashboard"
}
Each key is an F-key, each value is a LILPACK name whose first mode descriptor is activated on that key.
A package can provide standalone CLI commands that are installed as
executable scripts in ~/.local/bin/:
["mytool", "myutil"]
Each entry is a command name. The corresponding Lua source must exist in
the cli/ subdirectory of the source tree (e.g. cli/mytool.lua). On
install, each script is written to ~/.local/bin/<command> with a
#!/usr/bin/env lilush shebang prepended (if not already present) and
made executable.
CLI scripts are independent files -- they do not participate in module loading. Scripts that need LILPACK modules must initialise the searcher chain themselves:
local lilpack = require("lilpack")
lilpack.init()
local mylib = require("mypackage.utils")
A package can extend the theme system by providing section builders (styles for a specific mode) and/or complete theme definitions (palette + overrides).
The theme manifest field is an object with two optional sub-fields:
Section builders — provide styles for a named theme section:
{
"theme": {
"sections": [
{"name": "agent", "builder": "agent.theme"}
]
}
}
The builder field names a module that returns a function. The function
receives the active palette table and returns an RSS table for the section.
It is called each time the theme changes to rebuild section styles from the
new palette.
Theme definitions — provide a complete selectable theme:
{
"theme": {
"definition": {
"name": "solarized",
"module": "solarized.theme"
}
}
}
The module field names a module that returns a table in the same format as
bundled catalog entries: { palette = {...}, shell = {...}, markdown = {...} }.
A single package can declare both sections and definition if needed.
See THEMES for the full theme architecture and API.
Packages are managed with lilpack install/lilpack remove builtins.
Packages are installed to ~/.local/share/lilush/packages/<name>.lilpack.
Packages declaring CLI scripts also install executable files under
~/.local/bin/. Ensure ~/.local/bin is in your PATH.
By default lilpack install will try to install
remote packages from the official Lilush Packages HTTP Repo.
This can be overriden with the LILPACK_REPO_URL environment variable.
Packages in the official HTTP repo come from lilpacks repo.
lilpack.jsonEach source directory contains a lilpack.json at its root:
{
"name": "llm",
"version": "0.1.0",
"description": "LLM client factory with backends and tools",
"author": "Vladimir Zorin",
"license": "LicenseRef-OWL-1.0-or-later",
"dependencies": [],
"builtins": [],
"modes": [],
"cli": []
}
The directory layout maps to module names. The cli/ subdirectory is
reserved for CLI scripts and excluded from module scanning:
llm/
lilpack.json <- manifest (not packed)
llm.lua <- module "llm"
llm/
oaic.lua <- module "llm.oaic"
tools/
bash.lua <- module "llm.tools.bash"
cli/
llmq.lua <- CLI script "llmq" (not a module)
All packages must be signed with Ed25519 SSH keys. Unsigned packages are rejected at every stage: packing, installation, and load time.
Lilush ships with the author's public
key built in. Set LILPACK_PUB_KEY to
verify against a different trusted key. If a package fails
verification, it is rejected and none of its modules, builtins, modes,
or CLI scripts are made available.
| Variable | Used by | Value |
|---|---|---|
LILPACK_SIGN_KEY | lilpack pack | Required. Path to unencrypted OpenSSH ed25519 private key |
LILPACK_PUB_KEY | lilpack install, lilpack verify | Path to SSH .pub file (overrides built-in key) |
lilpack pack)lilpack pack requires LILPACK_SIGN_KEY to be set:
Reads the OpenSSH ed25519 private key (must be unencrypted)
Computes a canonical SHA-256 digest over all package content
Signs the digest with Ed25519
Stores the 64-byte signature and 32-byte pubkey in __lilpack
The embedded pubkey must exactly match the trusted verification key.
Packages with a mismatched embedded key are rejected.
lilpack install)lilpack install always verifies signatures before installing:
Verification passes -- installed
Verification fails -- rejected
No trusted key available -- rejected
lilpack.init)Every package is verified when the package system initialises. The
built-in public key is used by default; LILPACK_PUB_KEY overrides it.
Verification requires both:
the embedded pubkey matches the trusted key
the signature verifies against that key
Verification passes -- loaded
Verification fails -- skipped with error
No trusted key available -- no packages loaded
The digest is a SHA-256 hash over a deterministic serialization of all
package content (excluding signature and pubkey):
Manifest fields in fixed order: version, name, pkg_version,
description, modules, author, license, dependencies,
builtins, modes, cli, theme. Each present field contributes
key=tostring(value)\0.
Module sources in sorted name order. Each module contributes
module:name=source\0.
CLI script sources in sorted name order. Each script contributes
cli:name=source\0.
All parts are concatenated and hashed with SHA-256.
Public key fingerprints are the first 16 hex characters of SHA-256 over
the raw 32-byte public key. Displayed by lilpack info and
lilpack verify.
lilpack builtin| Subcommand | Description |
|---|---|
lilpack list | List installed packages |
lilpack info <name> | Show detailed package info (includes signature status) |
lilpack modules [-n name] | List registered modules |
lilpack pack <dir> [-o path] | Create .lilpack from source directory |
lilpack install <path> | Install a .lilpack file |
lilpack remove <name> | Remove an installed package |
lilpack verify <path> [-k key] | Verify package signature |
# Pack and sign a source directory:
setenv LILPACK_SIGN_KEY ${HOME}/.ssh/lilpack_sign.ed
lilpack pack /path/to/lilpack/source
# Install a package (verified against the built-in key):
lilpack install llm-0.1.0.lilpack
# Install with a custom trusted key:
setenv LILPACK_PUB_KEY ${HOME}/.ssh/lilpack_sign.pub
lilpack install llm-0.1.0.lilpack
# Verify a package without installing:
lilpack verify llm-0.1.0.lilpack -k ~/.ssh/lilpack_sign.pub
local lilpack = require("lilpack")
-- Initialize the package system (called automatically at startup)
local extensions = lilpack.init()
-- extensions.builtins -- array of builtin descriptors
-- extensions.modes -- table: mode_name -> mode config
-- Query the registry
local pkgs = lilpack.packages() -- pack_name -> {db_path, manifest}
local mods = lilpack.modules() -- module_name -> {db_path, pack_name}
local exts = lilpack.extensions() -- {builtins = ..., modes = ..., cli = ..., theme = ...}
local manifest = lilpack.read_manifest(db_path) -- read and validate manifest
-- Constants
lilpack.CONVENTION_VERSION -- integer (current: 1)
lilpack.PACKAGES_DIR -- packages directory path
-- Signature utilities
local digest = lilpack.compute_digest(db) -- SHA-256 canonical digest from open MNEME db
local pubkey, err = lilpack.load_trusted_pubkey(path) -- load trusted pubkey from file, env, or built-in
local fp = lilpack.pubkey_fingerprint(pubkey) -- first 16 hex chars of SHA-256(pubkey)
local ok, err, pubkey = lilpack.verify_signature(path, trusted_pk) -- verify .lilpack file