npack is a Nostr-native package manager. It distributes signed software
release metadata over Nostr relays and stores immutable .npk artifacts on
Blossom-compatible servers.
The package format is independent of APT, RPM, and native package databases.
An .npk is a deterministic tar archive compressed with zstd. npack
resolves dependencies, verifies signatures and SHA-256 hashes, and installs
files directly into the host system or the user's standard local prefix.
This is an early working prototype. The local package lifecycle, Nostr release events, NIP-94 artifact metadata, Blossom discovery, dependency resolution, lockfiles, revocations, offline replay, and GitHub Actions release workflow are implemented. The wire format is a project protocol and is not yet a registered NIP.
Nostr relays signed package metadata and discovery
│
▼
kind:9900 release ─── kind:1063 NIP-94 artifact metadata
│ │
└──────── SHA-256 ─────────────┘
│
▼
Blossom / content-addressed storage
The publisher's Nostr key identifies the release. Relays and download URLs are transport; clients verify the event signatures and artifact hash locally.
See:
- Using npack
- Package protocol v1
- Event fixtures
- Release-key notes
- GitHub Actions release workflow
- Development roadmap
This is a Cargo workspace: npack-cli (the npack CLI and npackd daemon)
and npack-gui (a small reference GUI, see below). The commands below build
and check both from the repo root.
cargo build --release
cargo test
cargo clippy --all-targets --all-features -- -D warningsnpack-gui is a small egui/eframe desktop app that exercises npackd's
JSON-RPC service API (Search, Details, Install, Installed, Updates,
Remove) purely as a client of its documented Unix-socket protocol -- it has
no special access to npack-cli's internals, proving npackd is
frontend-independent before integrating with an existing desktop store.
npack daemon & # start npackd-user in the background
cargo run --release -p npack-guiThe first npack package is npack itself. A bootstrap binary can be built
from source or downloaded from a GitHub Release. That binary installs future
.npk releases, including updates to itself.
System installation is the default and targets the host filesystem. It
normally requires privilege and stores package state in /var/lib/npack.
Use --user for the host's user-local prefix ($HOME/.local) and user-local
state. Use --store for isolated development or test installations.
Generate or view the command reference:
npack man > npack.1
man ./npack.1The .npk release also installs the page at share/man/man1/npack.1.
# Build and inspect an artifact
npack init ./myapp --name myapp --version 1.0.0 --publisher npub1...
npack pack ./myapp --output ./myapp-1.0.0.npk
npack manifest ./myapp-1.0.0.npk --output ./myapp-1.0.0.manifest.json
npack hash ./myapp-1.0.0.npk
npack inspect ./myapp-1.0.0.npk
# Generate AppStream metadata from a manifest's `app` metadata (icons,
# categories, screenshots, homepage, licence, summary, description) for
# desktop application stores
npack appstream ./myapp-1.0.0.npk --output ./myapp.metainfo.xml
# Install and inspect local packages
# The package metadata is embedded in the .npk.
npack install ./myapp-1.0.0.npk --user
npack verify ./myapp-1.0.0.npk
npack list --user
npack verify-installed --user
npack remove <publisher>/myapp --user
# A standalone manifest remains useful for publishing and verification
npack verify ./myapp.manifest.json
# Create and verify signed release metadata
npack register
npack release-event ./myapp.manifest.json --secret-key <secret-key>
npack verify-event ./release.json ./myapp.manifest.json
npack revoke-event ./release.json --secret-key <secret-key> \
--reason "security issue"
# Rebuild the local package catalogue from Nostr relays, then browse it
# without any further relay round trips
npack refresh --relay wss://relay.example
npack search myapp
npack info <publisher>/myapp
# Discover and install from Nostr
npack search myapp --refresh # rebuild the catalogue from relays, then search it
npack install <publisher>/myapp --relay wss://relay.example --user
npack update <publisher>/myapp --relay wss://relay.example --user
npack update --user # check all installed packages
npack update --user --check # report available updates without installing
# Resolve and verify release metadata without installing it -- useful for
# declarative package managers (e.g. Nix) that want to fetch and manage the
# artifact themselves
npack resolve <publisher>/myapp --relay wss://relay.example
# Run npackd, a local JSON-RPC service over a Unix socket for a GUI store or
# other tool that shouldn't need to know about Nostr, Blossom, or .npk
npack daemon --socket $XDG_RUNTIME_DIR/npackd.sock
# npackd-system: a privileged instance for the system store, normally run as
# a systemd service (see packaging/npackd-system.service). Gates Install,
# Remove, and Update from non-root peers through PolicyKit.
npack daemon --system
# Publish an artifact and its Nostr events
npack publish ./myapp.manifest.json \
--secret-key <secret-key> \
--relay wss://relay.example \
--server https://blossom.example
# Publish a signed Nostr announcement using the registered key
npack announce "npack is now available!"
# Generate and register a new publisher key
npack generate-key
# Announce a signed package release with a nostr:nevent link
npack announce --release-event ./release.json "npack 0.2.6 is available!"Publisher keys can be supplied as user-facing npub values where a public
key is accepted; internal comparisons use canonical hex keys.
Configuration is read from npack/config.toml in the platform's user config
directory, normally $XDG_CONFIG_HOME/npack/config.toml:
[network]
relays = ["wss://relay.example"]
[storage]
blossom = ["https://blossom.example"]
[identity]
pubkey = "npub1..."
[trust]
publishers = ["npub1..."]
[install]
user = falseWhen an identity is configured, npack reads the user's NIP-65 relay list
and adds read-capable relays to discovery. For artifact retrieval it also
checks the publisher's Blossom kind:10063 server list before configured
fallback servers.
Search results are cached locally for five minutes. Use --refresh to query
relays immediately, or --no-cache to disable both reading and writing the
cache. Search returns only the newest valid SemVer release for each publisher
and package; platform-specific artifacts at that version are retained.
Progress is written to stderr so search results remain clean on stdout.
- Release events are publisher-signed Nostr
kind:9900events. - Artifact metadata uses signed NIP-94
kind:1063events. - Artifact bytes are accepted only when their SHA-256 matches the release.
- Revocations are signed
kind:9901events and remain effective if relays retain copies of the original release. - Dependencies and runtime capabilities are declared in signed metadata.
- Post-install actions are declarative and capability-gated; services are installed but not enabled or started automatically.
- GitHub Actions uses a dedicated
NOSTR_SECRET_KEYin a protected release environment for the current publication prototype. Do not use a personal primary Nostr identity key.
The tag-driven workflow builds the first npack .npk package, extracts ELF
runtime requirements, generates SHA-256 and SPDX metadata, creates a GitHub
provenance attestation, and publishes the artifact and Nostr events when the
protected release environment is configured.
Required repository configuration is documented in docs/github-actions.md.
MIT