Markdown source for agents: https://npub1d70emggs6jzun5lhvqnfqsd9reqmaarn2qjf6q3r02gryyl4v8sqjn44xe.nsite.lol/docs/sources/zapstore.md

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 live zsp source 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:

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)

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

Relay usage

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.