~/nullapt

Documentation

INSTALLATION

The fastest way to install NullApt on macOS or Linux:

$ curl -fsSL https://nullapt.dev/install.sh | sh

Go (requires Go 1.23+)

$ go install github.com/nullapt/nullapt/cmd/nullapt@latest

Windows

Download the latest .exe from the releases page.

QUICK START

# Install a skill from the registry
$ nullapt get web-search
# Short form (same as get)
$ nullapt i web-search
# Install a specific version
$ nullapt i web-search@1.2.0
# List installed skills
$ nullapt list
# Remove a skill
$ nullapt remove web-search

Skills install to ~/.nullapt/skills/. Any MCP-compliant client (LM Studio, AnythingLLM, Ollama) pointed at that directory picks them up automatically — no restart required.

COMMAND REFERENCE

nullapt get <skill[@ver]>Install a skill from the registry. Alias: i, install
nullapt remove <skill>Uninstall a skill
nullapt listList installed skills
nullapt verify <SKILL.json>Verify a manifest's Ed25519 signature
nullapt publish <SKILL.json>Publish a skill (requires login)
nullapt loginAuthenticate with the registry
nullapt logoutRemove stored credentials

Flags

--registry <url>Use a custom or self-hosted registry
--skip-verifySkip signature check (development only)

BUILDING A SKILL

Skills are WASM-WASI binaries compiled from any language that targets wasm32-wasi. Rust is recommended.

# 1. Create a new Rust library
$ cargo new --lib my-skill && cd my-skill
$ cargo add extism-pdk
# 2. Build for WASM
$ cargo build --target wasm32-wasi --release
$ cp target/wasm32-wasi/release/my_skill.wasm skill.wasm

Your skill exports named functions that match the tool names in interface.tools:

// src/lib.rs
use extism_pdk::*;
#[plugin_fn]
pub fn web_search(input: Json<SearchInput>) -> FnResult<Json<SearchOutput>> {
// your logic here
}

Write a SKILL.json manifest that declares exactly which permissions your skill needs:

{
  "schema_version": "1.0",
  "name": "my-skill",
  "version": "0.1.0",
  "description": "What your skill does",
  "author": "your-username",
  "license": "MIT",
  "permissions": {
    "network": {
      "allowed": false
    },
    "filesystem": {
      "read": [],
      "write": []
    },
    "env": []
  },
  "entry": "skill.wasm",
  "interface": {
    "tools": [
      {
        "name": "my_tool",
        "description": "Tool description",
        "input_schema": {
          "type": "object",
          "properties": {
            "query": {
              "type": "string"
            }
          },
          "required": [
            "query"
          ]
        }
      }
    ]
  }
}

SIGNING & PUBLISHING

Every skill must be signed before publishing. NullApt uses Ed25519 — your private key never leaves your machine.

# Generate a key pair (one-time setup)
$ nullapt keygen --output ~/.nullapt/keys
# Sign your manifest (embeds signature in SKILL.json)
$ nullapt sign ./SKILL.json --key ~/.nullapt/keys/private.pem
# Verify the signature locally
$ nullapt verify ./SKILL.json
# Publish — uploads manifest + WASM, records in transparency log
$ nullapt login
$ nullapt publish ./SKILL.json

Your public key is permanently recorded in the transparency log so users can audit key history and detect compromise.

SELF-HOSTING

The registry API is a single Go binary. Run your own private registry in minutes.

# Clone and configure
$ git clone https://github.com/nullapt/nullapt
$ cd nullapt
$ cp .env.example .env
# Edit DATABASE_URL and BLOB_READ_WRITE_TOKEN
# Apply schema
$ psql $DATABASE_URL -f internal/db/schema.sql
# Run
$ go run ./cmd/registry-api

Point the CLI at your instance:

$ nullapt i my-skill --registry https://my-registry.internal
$ nullapt publish SKILL.json --registry https://my-registry.internal