Skip to content

Releases

ngit release adds GitHub Releases-like functionality to a Nostr repository. It ties release notes and downloadable files to an exact Git tag, uploads local files to Blossom, and publishes signed metadata that does not depend on one forge. Android releases use NIP-82 records understood by Zapstore.

ngit represents each publication as three related records:

  • an application identifies the software and its publication authority;
  • a release identifies an exact version, source commit, channel, and notes;
  • an asset describes a downloadable file referenced by a release.

The metadata is published as signed Nostr events. Local files are uploaded to Blossom and referenced by those events.

Preview workflow

The v3 release workflow is still a release candidate. Its generated pages are labelled preview until the final v3 release is pinned.

Create .ngit/release.yaml

For repeatable releases, commit a manifest at .ngit/release.yaml. The filename is singular. It tells ngit which build output to publish and keeps stable application, storage, and catalog settings out of the release command.

This example publishes one local APK:

yaml
schema: 1
identifier: com.example.myapp
name: Example App
summary: A short description for app listings
description: |
  A longer description of Example App.
repository: nostr://npub1.../example-app
release_notes: CHANGELOG.md
release_source: dist/example-app-{version}.apk
publication:
  blossom_servers:
    - https://blossom.example.com
  zapstore_relay: true

Replace the repository URL and Blossom server with the ones you use. release_source is the Zapstore-compatible shorthand for one local APK. {version} expands to the release version, so version 1.0.0 reads dist/example-app-1.0.0.apk.

ngit inspects the exact APK and derives its package and version data, SDK levels, ABI platforms, signing-certificate hashes, file hash, and size. Values you also declare in the manifest are checked against the APK rather than used to override it.

release_notes: CHANGELOG.md selects the level-two section matching the exact release version, accepting one optional leading v in the heading. Missing, duplicate, or empty sections fail before publication. Use notes for literal text instead.

Publish a tagged version

Build the file at the path declared by the manifest, then tag and push the exact commit:

bash
git tag v1.0.0
git push origin v1.0.0
ngit release publish

In this common path, the command finds .ngit/release.yaml, derives version 1.0.0 and the commit from the exact v1.0.0 tag, finds the APK through release_source, uploads it, and publishes the linked application, asset, and release records. There is no hidden source for the binary.

No exact tag is an error. If the commit has more than one exact tag, select one with --tag. An explicitly supplied version is not normalised: 1.0.0 and v1.0.0 identify different releases. Only a version derived from a tag strips one leading lowercase v.

Use --manifest <PATH> for a manifest outside the default location. Automatic discovery applies when creating a release without direct asset flags; pass --manifest explicitly when combining manifest and CLI assets or when editing.

One-off release without a manifest

For a single release, name the APK directly:

bash
ngit release publish 1.0.0 \
  --tag v1.0.0 \
  --file dist/example-app-1.0.0.apk \
  --notes-file release-notes.md \
  --zapstore-relay

Here the binary comes from --file. APK metadata and platforms are still derived from that file. This form is useful for an experiment; the committed manifest is clearer once the process repeats.

Zapstore compatibility

ngit publishes the NIP-82 application, asset, and release records Zapstore understands. The manifest reuses Zapstore-compatible names where their meanings agree, including identifier, application metadata, and release_source.

Set publication.zapstore_relay: true, as in the manifest above, or pass --zapstore-relay to publish the complete release batch to the Zapstore catalog relay. This is additive: it keeps the repository and application relays, and it does not silently change the Blossom servers.

After Zapstore's one-time publisher acceptance and APK certificate-linking steps are complete, APK releases published this way become available in Zapstore automatically. You do not need a second zsp publish for each version. New publishers should complete Zapstore's publisher setup; ngit does not perform those onboarding steps.

Application identity

The first manifest-backed publication can create the linked application from repository, APK, and manifest metadata. Use the explicit application flow only when you want to define or inspect that identity before publishing:

bash
ngit release app init
ngit release app view <APP>

An application can also be linked to an existing repository.

Add downloadable assets

Attach local files while publishing with repeatable --file options:

bash
ngit release publish 1.0.0 \
  --file linux-x86_64=dist/my-tool \
  --file macos-aarch64=dist/my-tool-macos

The PLATFORM=PATH prefix associates each file with a target platform. The command also supports URL-backed assets and existing asset events; see the generated ngit release publish reference.

For a local APK, ngit derives package and version data, SDK levels, signing certificate hashes, and ABI platforms from the exact file snapshot. Matching values in the manifest are assertions; they cannot override the APK.

ngit attempts every requested Blossom server. Publication can continue once each blob has at least one exact confirmed copy, with incomplete replication reported as a warning. Automation that requires every requested placement must inspect the JSON warnings and placement results rather than relying on a zero exit status alone.

To attach one asset after publication, use the explicit ngit release asset add workflow. It requires --edit because it replaces the release record.

Inspect and verify

Populate the cache from relays, then inspect a release and its assets:

bash
ngit release list
ngit release view 1.0.0 --verify

--verify downloads referenced assets and verifies their hashes and sizes. After the online read, --offline can skip another relay fetch:

bash
ngit release view 1.0.0 --verify --offline

--verify still downloads the release assets. Here, offline describes event retrieval, not asset verification.

For automation, request one JSON document:

bash
ngit release view 1.0.0 --verify --json

If a bare version is ambiguous, provide --app <APP>.

Editing is explicit

Publishing an existing version requires --edit; it does not silently replace a release. Review the existing record first:

bash
ngit release view 1.0.0
ngit release publish 1.0.0 --edit --notes-file corrected-notes.md

Containers

Releases and containers do not inherit the repository's multiple-maintainer model. A NIP-34 repository can have several maintainers, but each release application and container repository is owned by one author npub.

Keep one publishing identity

After the first publication, every release for that application and every update to that container tag map must be signed by the same npub. Another repository maintainer can publish a separate application or container coordinate, but cannot continue the original publication history with their own key. Choose a long-lived publisher before the first release or container publication, and make that signer available to trusted release automation.

Use ngit container publish when the result should be a pullable OCI image. It uploads the OCI content graph to Blossom and publishes a signed, mutable tag map through Nostr. This is separate from attaching a container archive to a release: a release asset is a downloadable file, not a pullable container repository.

See the generated ngit container publish reference for layouts, .ngit/containers.yaml, Blossom placement, tag updates, and JSON output.

Exact reference

Git collaboration, without the platform.