Nostr Agent Onboarding · start here · index · source:
nostr-dev/docs/sources/zapstore.md· snapshot 2026-10-10
Source: Zapstore + NIP-82 (Software Applications)
Reference layer for the protocol Zapstore implements. Primary implementation:
github.com/zapstore. The spec is being codified as NIP-82 (PR nostr-protocol/nips#1336, in flight at the time of writing — 2026-05-12). Always cross-check the livezspsource if a tag or field disagrees with this summary.
The model
Zapstore is not a server. It's a convention over three things every Nostr client already has: addressable events, file blobs, and outbox-style relay routing. A "published app" is:
- a kind 32267 event (app metadata, replaceable by package id) signed by the publisher,
- one or more kind 30063 events (one per release version, replaceable per
package_id@version), each pointing at… - one or more kind 3063 events (per-binary metadata: hash, URL, size,
cert), whose
urltags resolve to Blossom blobs.
Discovery is just an nostr+REQ for kind: 32267 with a tag filter. The
Zapstore app and zapstore.dev are one client over that data; anyone can
build another.
Event kinds
kind 32267 — Software Application (addressable, d = package id)
Required tags: d (package id, e.g. com.example.app), name, icon (URL).
Common tags emitted by zsp v0.4.10:
| Tag | Meaning |
|---|---|
d |
package id (e.g. com.otsuite.lite.debug) |
name, summary, icon |
display fields |
t |
topic/category (repeatable) |
repository |
URL or NIP-34 naddr to the source repo |
a |
full NIP-34 coordinate 30617:<pubkey>:<d> for the repo announcement |
f |
platform/arch filter (android-arm64-v8a, android-x86_64, …) |
h |
platform-grouped hash (used by clients to detect changes across releases) |
content is the long description (markdown OK).
kind 30063 — Software Release (addressable, d = package_id@version)
A set event in the NIP-51 sense. Links one or more kind-3063 assets that together make up a release.
| Tag | Meaning |
|---|---|
d |
<package_id>@<version> |
i |
package id (so clients can filter releases for a given app) |
version |
semver string |
c |
channel (main, beta, nightly, dev) |
f |
arch filter (repeated per arch the release supports) |
e |
event id of each kind-3063 asset (with relay hint) |
kind 30509 — NIP-C1 Cryptographic Identity Link (addressable, d = cert SHA-256)
Used by zsp identity --link-key to bind an APK signing certificate to a
Nostr identity. Clients that walk the kind 32267 → 30063 → 3063 chain compare
the 3063's apk_certificate_hash to the latest 30509 d-tag under the same
publisher pubkey — a mismatch signals a possible repo hijack.
| Tag | Meaning |
|---|---|
d |
SHA-256 of the certificate (DER form) |
signature |
RSA (or ECDSA, depending on the key) signature over the publisher's npub, signed by the APK signing key. The cryptographic proof of co-control. |
expiry |
Unix timestamp after which the proof is no longer considered valid. Default 1 year; tunable via zsp identity --link-key-expiry. |
kind 3063 — Software Asset (regular, one per binary)
This is the file-metadata event. It is zsp's own kind — not NIP-94's
kind 1063. The NIP-82 PR draft discussed 1063; the shipped zsp v0.4.10 uses
3063. If you only have the spec PR open, you'll be misled — check the wire.
| Tag | Meaning |
|---|---|
i |
package id |
x |
SHA-256 of the asset bytes (the Blossom address) |
url |
Blossom URL (<server>/<sha256>) |
m |
MIME (e.g. application/vnd.android.package-archive) |
size |
bytes |
f |
arch filter (one tag per arch in the binary; fat APKs get multiple) |
min_platform_version / target_platform_version |
Android min/target SDK |
filename, version_code, version |
display + ordering metadata |
apk_certificate_hash |
SHA-256 of the APK signing cert (used by NIP-C1) |
Blossom usage
- Default upload target:
https://cdn.zapstore.dev. - Override:
BLOSSOM_URL=<server-base-url>env var. - The CLI uploads icons, screenshots, and the APK itself — anything the events
reference by
url. Blobs are content-addressed, so re-uploading an unchanged asset is a no-op.
Relay usage
- Default publication target:
wss://relay.zapstore.dev. - Override:
RELAY_URLS=<comma-separated-list>env var. relay.zapstore.devenforces a whitelist on first-time publisher pubkeys. Either (a) commit azapstore.yamlwith the publisher pubkey at the root of the linked source repo so the relay can verify pubkey↔repo, or (b) accumulate prior reputation via their Vertex system.
Signing
zsp reads the signer from SIGN_WITH. Supported forms:
| Value | Meaning |
|---|---|
nsec1… |
Raw nsec |
<hex> |
Raw hex private key |
bunker://<pubkey>?relay=…&secret=… |
NIP-46 remote signer |
browser |
NIP-07 (opens a localhost preview page that signs via your browser extension) |
npub1… |
Use the given identity for preview only; pairs with --offline |
Cert linking — NIP-C1
zsp identity --link-key key.p12 (or the interactive prompt on first
publish) produces a NIP-C1 proof that ties the APK signing certificate to
the Nostr identity. Clients use it to detect repository hijacks: a new
release signed by a different cert under the same npub flags as suspicious.
Skip with --skip-certificate-linking when:
- The cert is a debug keystore (the default Android debug cert is shared
across many machines and effectively meaningless as a proof).
- You're producing a sandbox publish that won't be consumed by clients
checking provenance.
For any release you intend to ship to the public Zapstore, do the linking with the actual release keystore.
Format note. zsp identity --link-key accepts .p12/.pfx/.pem/.crt
directly. .jks (Java KeyStore) needs a one-shot conversion first —
zsp will error with a keytool -importkeystore command if you point it at
a JKS. Sample:
keytool -importkeystore -noprompt \
-srckeystore release.jks -srcstoretype JKS -srcstorepass "$PW" -alias <key-alias> \
-destkeystore release.p12 -deststoretype PKCS12 -deststorepass "$PW"
CLI: zsp
go install github.com/zapstore/zsp@latest # binary lands in ~/go/bin/
Modes:
| Invocation | Use |
|---|---|
zsp publish --wizard |
Interactive setup. Writes a zapstore.yaml for you. |
zsp publish zapstore.yaml |
Config-driven. The form to script against. |
zsp publish app.apk -r github.com/u/r |
Local APK + forge URL. -r does not accept naddr; use the yaml. |
zsp publish ... --offline |
Sign events without uploading or publishing. Events to stdout, upload manifest to stderr. The standard dry-run. |
zsp utils extract-apk app.apk |
Read-only: dump package id / version / cert / icon. |
zsp utils check-releases <src> |
Detect whether upstream has a newer version than what's published. |