ngit is a command-line client and Git remote helper for decentralized code collaboration over Nostr. It keeps repository state, issues, and pull requests on Nostr while using interchangeable Git servers for object storage.
First release: Dec 20, 2024, 8 total releases.
Most recent release: Sep 21, 2026.
Appears in 0 app stacks.
0 sats / 0 zaps received in the past year.
Sats Received
Underlying data available via MCP: app_zaps, app_releases.
Zap Count
Underlying data available via MCP: app_zaps, app_releases.
Releases
- Sep 21, 2026 3.0.3Fix proposal base selection for PR pushes and draft patches, refresh dependencies, and clarify agent guidance for repository data refreshes. ### Changed - Refresh Rust dependencies and the lockfile, including Nostr SDK 0.45.4, Reqwest 0.13.5, Rustls 0.23.45, apk-info 1.0.13, mailparse 0.17.0, keyring 4.2.0, and the test harness dependencies. Relevant upstream fixes: - Rustls rejects TLS 1.3 handshake messages received at the wrong encryption level ([GHSA-2mjx-qc3c-rqvc](https://github.com/rustls/rustls/releases/tag/v%2F0.23.45)).
More…
- Reqwest selects the correct credentials when multiple proxies match a URL and recognizes timeouts wrapped in response-body decoding errors ([release notes](https://github.com/seanmonstar/reqwest/releases/tag/v0.13.5)). - APK analysis can continue when the resource table is corrupt, resolves sparse resource entries correctly, and reads ZIP entries lazily to reduce memory use with large archives ([release notes](https://github.com/delvinru/apk-info/blob/master/CHANGELOG.md#1013---2026-09-10)). - The Nostr SDK's local relay, used by the test harness, closes subscriptions when their live-event buffer overflows so clients can detect missing events ([crate changelog](https://docs.rs/crate/nostr-sdk/0.45.4/source/CHANGELOG.md)). - Clarify the agent skill's shared repository-data refresh behavior across git and ngit commands, and when to reuse cached data with `--offline` to avoid redundant relay fetching. ### Fixed - Fix PR pushes when changes are already on your local default branch or another remote but have not been accepted by the Nostr destination. A PR that points to the same commit as local `main` or `master` works too. - Support maintainers who build PRs on a newer local default branch. For a new PR sharing local commits that have not been published to Nostr, explain how `-o base=<commit-or-ref>` chooses where the proposed changes start. Earlier commits remain in Git history. Local changes outside the PR need no confirmation. - Keep `ngit send` working for drafts made directly on `main` or `master`. Rejection messages explain how `--base <commit-or-ref>` lets you choose which earlier commits to exclude. Remote names such as `origin` or `github` do not establish ownership or accepted history. - Sep 18, 2026 3.0.2Fix flaky tests and improve updates, cache recovery, push consistency, relay publishing, and Android TLS compatibility. ### Changed - Make rapid updates to replaceable events, such as Git state announcements, more robust by preferring IDs that are easier to replace again. The extra work adapts to recent activity and replacement difficulty and stays bounded. - Publish updated nsites without waiting for the clock to advance, consistent with PR and patch revisions. Manifest timestamps advance past the previous version, including when that version is future-dated. Previously, manifests
More…
more than five seconds ahead were refused; updates now advance past them and may be rejected by relays that limit future timestamps. ### Fixed - Freeze push source objects before planning and publication so concurrent local branch or tag changes cannot make repository state and Git server pushes refer to different objects within the same ngit push. - Prevent HTTPS requests, including Nostr web address resolution, from panicking in standalone Android and Termux builds. Use bundled CA roots on Android while retaining certificate and hostname verification. - Recover corrupt or incompatible repository and global event caches, including queries failing with `Not found`. Keep damaged files for diagnosis, coordinate recovery across ngit processes, and restart online fetch planning after a reset. Permission, disk-space and other operational errors do not trigger cache replacement. - Return a nonzero exit status and JSON error when no relay accepts a command's publication, including account creation and issue or PR edits. Errors include the failed relay URLs and reasons; publication still succeeds when at least one relay accepts the complete batch. - Cache outgoing issue and PR subject, cover-note, and label edits only after a relay accepts them, so undelivered edits no longer change local views. New-account profiles and relay lists likewise enter the cache only after relay acceptance. - Include the relay's rejection reason when a GRASP server is skipped because its relay did not accept the state event. Quiet and non-interactive runs previously reported only that the event failed to reach the relay. - Ignore unauthorized status events when ordering issue and PR status changes, including merge statuses. An outsider's future-dated event can no longer force a legitimate update to adopt a timestamp that relays may reject. - Ensure rapid metadata and status updates supersede earlier events, including PR subject and cover-note edits, automatic issue resolution, and statuses that close patches upgraded to PRs. These writers now apply the same ordering and permission checks as their readers. - Align the public GRASP-list library writer and reader on event ordering. This writer is not currently exposed through a CLI command. - The one-line installer now works with older ngit versions that do not have `ngit update`. - If you installed ngit with Cargo, `ngit update` and the one-line installer now upgrade it automatically using Cargo's default registry and default features. This keeps your installation location, not custom source or feature selections. You can still choose downloaded binaries with `--method standalone`; the installer warns if your terminal is still using the old copy. - Make installation and repair safer on Unix and Windows: check downloaded programs before replacing existing files, recover from failed replacements, and require explicit permission to install an older version. - Fix `ngit update` failing when ngit's Git integration tool (`git-remote-nostr`) is missing. - Try hinted relays alongside cached decrypted private relay lists first when cloning a `nostr://` repository; only request private-list decryption if a known-private repository's announcement is still missing. Public and unclassified discovery failures no longer trigger decryption. Explicitly public clones skip private relay caches, and unrelated cached relay failures cannot veto a found announcement. Follow discovered announcements to their repository relays for canonical refs and proposals, even when the announcement came from a separate URL hint. - Keep usable accounts visible in `ngit account whoami` when a stale inventory entry cannot resolve its credentials. Report unavailable candidates separately in human and JSON output, with backend-specific lookup diagnostics. - Prevent fast profile and indexer responses from prematurely timing out repository-history fetches. Repository completion thresholds now count only repository requests in the current discovery round. Keep deadlines within their original budget and report actual elapsed time in timeout errors. - Reject incomplete relay queries that disconnect or expire before EOSE instead of reporting partial history as a successful fetch. - Sep 10, 2026 3.0.1### Changed - `ngit skill install` and `upgrade` now leave managed files uncommitted so each repository can apply its own validation and commit workflow, with exact non-ignored changed paths available in both human and JSON output. - The bundled skill uses conservative CommonMark formatting to reduce avoidable formatter churn without imposing formatter configuration on consuming repositories.
- Sep 8, 2026 3.0.0### Release overview ngit v3 is its biggest release yet. With CI, private repositories, software releases, and plenty more, ngit, powered by GRASP, is quickly becoming a fully fledged development solution. It launches in tandem with ngit-grasp v3, GitWorkshop v4, and the new ngit-ci 0.1 as one coordinated release across the ngit family of tools. These are the changes that define the release. [Read the full launch story](https://ngit.dev/v3) to see how all four releases fit together; the detailed entries below record every feature, compatibility change, and fix.
More…
- **CI is here:** One of the biggest gaps holding projects back from ngit is now closed. Discover maintainer-selected coordinators, request, trigger, and stop runs, follow workflows down to individual job logs and trust evidence, and optionally require a trusted CI result before merging. - **Private repositories without surrendering identity:** Clone, fetch, push, and collaborate on GRASP-08 private repositories using scoped Nostr and Git authentication. The repository remains a signed Nostr identity instead of becoming an account owned by its server. Basic Buzz support extends clone, pull-request, and lifecycle-status workflows to Buzz repositories. - **Maintainers and repository authority:** Introduce an explicit lead, role history, and non-maintainer moderators, with clearer invitations, handovers, departures, roster changes, and historical authorization. These substantial improvements are backwards compatible for existing repositories and confirmed maintainer relationships. The one narrow breaking change is that an invited maintainer has no Git-state authority until they accept. That pending-invitation boundary is the SemVer reason for the v3 major version bump. - **Pull requests that fit real projects:** Target non-default branches, build inferred or explicit pull-request stacks, check out contributor branches on demand, record issue-resolution provenance, and merge with stronger safety checks. - **Software releases without the platform:** Think GitHub Releases without GitHub, made resilient through Blossom replication. Publish signed NIP-82 applications, releases, and assets with Zapstore-compatible metadata, then update ngit itself from its verified releases. #### Additional ngit highlights - **Credential protection:** Move `nsec` and bunker credentials out of Git config by default and into the OS credential store, with an ngit-specific, user-only file as fallback. This reflects a changed threat model: coding agents and other tools now routinely read Git config, making inadvertent disclosure much more likely. - **Nsite and container publishing:** Publish nsites and OCI container images through Nostr and Blossom. - **Accounts and automation:** Add portable local and remote signer selection, deliberate credential export, global JSON output and repository selection, plus a versioned agent skill installer and upgrader. - **Repository addresses:** Resolve path-scoped NIP-AD mappings from memorable bare domains, with NIP-65 relay discovery when the repository announcement is not already cached. - **Everyday Git compatibility:** Support guarded force pushes with `--force-with-lease`, raw object IDs in refspecs, no-op pushes, Git-managed tracking refs, and installed remote helpers for additional server protocols. - **Safer upgrades and recovery:** Update from exact signed NIP-82 releases, preserve staged, unstaged, and untracked work through merges, and provide an actionable merge recipe when synchronized refs have diverged. - **Private-network access:** Connect opportunistically to `.onion` relays and clone URLs through Tor, and optionally trust host-installed certificate authorities for private or development infrastructure. ### Added - Bare-domain `nostr://` repository URLs can resolve path-scoped NIP-AD mappings, including protocol and SSH-key prefixes. NIP-AD mappings and the existing hint-less `nostr://<npub>/<identifier>` form now bootstrap missing repository announcements through the author's NIP-65 write relays. - `ngit release publish` can derive its version and source commit from one exact Git tag, so tagged CI builds need no positional arguments. Schema-1 manifests accept Zapstore-compatible local APK `release_source`; ngit extracts package/version, SDK, certificate, and ABI metadata from the stable Blossom snapshot and rejects conflicts with declarative assertions. - Schema-1 release manifests can declare Zapstore-style application metadata. Tracked local icons and screenshots publish through Blossom, while existing HTTP(S) image URLs remain direct references and are never downloaded. - Add `ngit update`, `ngit update --check`, and exact-version selection backed by ngit's signed repository state and trusted NIP-82 release assets. Only receipted standalone Unix installations are replaced automatically; Nix, Cargo, and unreceipted installations receive non-mutating guidance. The canonical website installer template now shares this update path. - Release manifests can set `release_notes: CHANGELOG.md` to publish the matching Keep a Changelog version section. Missing, duplicate, and empty sections fail rather than falling back to the complete changelog. - Add `ngit nsite publish <DIRECTORY>` for publishing an already-built static site as a root or named NIP-5A manifest. The command snapshots files deterministically, confirms every unique blob on every selected Blossom server with batched BUD-11 authorization, then signs and publishes the manifest with local or remote signers. Blossom servers may be supplied explicitly, read with site metadata, fallback routing, and relay hints from nsyte's `.nsite/config.json`, or discovered from the account's kind-10063 server list. - Add `ngit container publish <OCI_LAYOUT>` for publishing verified OCI image layouts through Blossom and repository-bound Nostr kind-30624 state. The command uploads every reachable blob, preserves existing remote tags by default, publishes through repository relays, and supports NIP-42 and structured CI output. - **Private repositories via GRASP-08**: discover private repositories through encrypted kind-10318 relay lists, then clone, fetch, push, and collaborate using NIP-42 relay authentication and repository-scoped NIP-98 Git HTTP credentials. Private repository events stay on repository relays instead of leaking through indexers, fallback relays, or account relay fanout. Copied repository URLs can also be classified as private from GRASP-08 NIP-11 metadata before discovery begins. - **Basic Buzz support**: clone Buzz repositories, view pull requests and their lifecycle status, and push new or updated pull requests and status changes through authenticated Buzz relay and Git transport. Normal branch and tag pushes are not supported, and Buzz pull-request comments cannot be viewed or published because Buzz uses kind 1 while ngit uses NIP-22 kind 1111. - Stored accounts can be selected for one ngit command with global `--signer <npub|alias|profile-name>`, or for one Git command with `git -c nostr.signer=<account>`, without changing the configured login. `ngit account login <account>` reactivates retained credentials and `--alias` adds a portable signer name. An explicit selector that is missing, invalid, or ambiguous fails closed. - Add portable one-shot and export paths for both signer types: `--nsec-file` reads a local key, while interoperable `--nbunksec` and `--nbunksec-file` reuse an established NIP-46 session. Secret-file targets must be small regular files and, on Unix, use mode `0400` or `0600`. `ngit account export-keys` returns the selected account's npub plus `nsec` for a local signer or `nbunksec` for a remote signer. - `--json` is now a global option for all ngit commands. Machine-readable results are written to stdout only after command completion, diagnostics and progress stay on stderr, and mutating commands return useful event IDs and metadata. JSON output cannot be combined with interactive mode. - Pull requests can target a non-default branch with `git push -o target-branch=<branch>` or `ngit send --target-branch <branch>`. Root PR events expose the target as an indexed `b` tag, and listing, viewing, updating, merging, and applied-status detection honor the immutable target; omitting the option retains default-branch behavior. `ngit merge` resolves an explicit target against the latest Nostr repository state, fetching the target commit without mutating tracking refs, so a stale local clone cannot merge onto old target history. - Pull requests automatically select the unique most-advanced tip of the author's other open or draft PRs when it is in the new proposal's history and ahead of the target branch. Existing children follow later authorized parent updates after they are rebased, while stale children and ambiguous unrelated parents fail closed. `git push -o base=<commit|branch|event>` and `ngit send --base <commit|branch|event>` override inference for that publication; PR roots select their latest authorized update, specific PR-update events select their historical commit, and unique event-ID prefixes are accepted. Repeat an explicit historical base on later child updates to keep that deliberate pin instead of following the open parent lineage. - Issue-resolution status events now identify the triggering commit and, when attribution is unambiguous, the proposal and merge commit that caused the status change. - Add a multi-stage `Containerfile` for building a minimal Alpine-based ngit image, plus a CI smoke test that builds the image and runs `ngit --version`. - Add an opt-in `native-tls-roots` build feature that trusts certificate authorities installed on the host alongside WebPKI roots, enabling WSS connections to relays and GRASP servers using private, corporate, or development CAs. - `ngit repo --json` now exposes `selected_maintainer`, `confirmed_maintainers`, `invited_maintainers`, `lead_maintainer`, and the directional `maintainer_edges` alongside the backward-compatible full `maintainers` set. - Add `ngit skill install`, `upgrade`, and `status` commands plus local and global reminder opt-outs for repository-managed coding-agent guidance. Installs add a slim versioned `SKILL.md` and eight on-demand `reference/*.md` guides to both Codex and Claude discovery paths, including static-site publication with `ngit nsite`, and append a compact pointer only to existing `AGENTS.md` or `CLAUDE.md` files that do not already mention ngit. Installs and upgrades preserve supported symlinks, protect modified or newer copies unless forced, and create distinct guidance-only commits; non-maintainers are advised to push the commit as a pull request, while `ngit init` leaves installation as an explicit suggested follow-up. - Expand `--repo-relay-only` to all ngit commands that publish nostr events. - Git server clone URLs can use installed `git-remote-<scheme>` helpers for listing, fetching, and pushing. Installing a helper is treated as consent for signed repository announcements to invoke it, subject to Git's protocol policy; recursive `nostr`, internal `fd`, and GRASP-reserved `ws`/`wss` schemes are not delegated. - Global `--repo <REMOTE|NADDR|NOSTR-URL>` argument selects the target repository for repo-scoped operations (`send`, `issue`, `pr`, `repo`, `sync`, and every other command that resolves a repository coordinate). Available at any command position (`ngit --repo upstream issue create`, `ngit issue --repo upstream create`, `ngit issue create --repo upstream`). Value is first matched against configured remote names, then parsed as an naddr, then as a `nostr://` URL. - Repo-coordinate resolution now prints a `target repository: <naddr> (source: ...)` diagnostic when publishing repo-scoped events, so an incorrect target is visible before the event is signed. - Opportunistic Tor support for `.onion` relays and clone URLs. ngit uses an available SOCKS5 proxy from `NGIT_TOR_PROXY` or probes the common system Tor and Tor Browser ports (`127.0.0.1:9050` and `127.0.0.1:9150`). Unavailable onion entries fail immediately so they do not delay clearnet alternatives. See `docs/onion.md`. ### Changed - The bundled coding-agent skill is rewritten around the guides published at https://ngit.dev. `SKILL.md` keeps the rules agents get wrong and points each task slice at its bundled reference, the matching guide, and `ngit <command> --help`; the reference files drop material the guides cover better, describe capabilities instead of choosing workflow policy, and gain a rule to preserve choices the user or repository already made. The skill now states that it documents ngit v3 and how to install or update an older host, merges `reference/repo-settings.md` into `reference/repositories.md`, and corrects the `setup-ngit` action tag (`v3`), the issue auto-resolution keywords, and the canonical `ngit pr merge` behaviour. - `ngit skill install` and `upgrade` delete reference files that a later ngit no longer bundles, resolving the same canonical symlinks as bundled files, and stage the deletion in the guidance commit. A lingering retired file is reconciled even when the skill version is already current. - The top-level `ngit merge` command is now a compatibility alias for the canonical `ngit pr merge` command. Both spellings create the same local no-ff merge on the PR's declared target (or repository default), share conflict handling and options, and refuse closed or already-applied PRs before changing Git state. Preparing the local merge no longer requires a maintainer identity; the later Git push enforces repository authority and publishes the Nostr applied status. Ungated JSON keeps the PR `id`, `entity`, and `action` (`merged` or `conflicted`) while omitting the unrequested CI projection and unpublished event. - JSON command-result envelopes now use top-level `command_status: "ok" | "error"` instead of the ambiguous `status` field or `ok` boolean. Nested fields continue to describe domain results, so a successful `ci status` query can pair `command_status: "ok"` with `ci.conclusion: "failure"`. Release, nsite, and container publication envelopes advance to format version 2 for this breaking schema change. - `ngit merge` now accepts `--require-ci-trust`, matching `ngit pr merge`. - Repository hosting flags now distinguish grasp-derived infrastructure from deliberate additions. `ngit init` uses `--additional-relay` and `--additional-clone`; `ngit repo edit` replaces whole-list `--grasp-server`, `--relay`, `--clone`, and `--hashtag` flags with repeatable `--add-*` and `--remove-*` actions. Repository identifiers can no longer be changed through `repo edit` because doing so creates a new coordinate. - GRASP service URLs may include a non-root base path. Repository announcements, `nostr://` relay hints, Git push/fetch, and explicit GRASP-06 PR endpoints preserve the configured path. - `ngit account whoami` now inventories every usable stored account from local, global, and system Git config, `credentials.json`, and the OS credential store; groups identities by npub; lists distinct local-key and remote-signer connections with their npub/alias selectors and connection-specific scope badges; and prints shared guidance for one-shot ngit or Git use, local/global activation, alias creation, and removing a local override. `ngit account list` is a visible alias, while `ngit account login <account>` adds a concise activation form alongside the existing `--signer` spelling. A non-secret account index makes newly stored OS-keyring identities discoverable without relying on platform-specific keyring enumeration. - Logged-in accounts answer NIP-42 authentication challenges from repository relays, and from the account's inbox or outbox relays while publishing, but decline challenges from unrelated relays such as indexers and fallbacks. - Now that ngit-grasp repository-relay synchronization has matured, repository state and collaboration events are fetched exclusively from relays declared by the repository. This makes repository relays the authoritative collaboration view and allows grasp servers to provide moderation in the future without filtered events being restored from user relays. URL hints, fallback relays, and announcement indexers are limited to repository announcements; user relays are limited to profile metadata, relay lists, and user GRASP lists. Publishing still fans out to relevant user relays unless `--repo-relay-only` or `nostr.repo-relay-only` is set. - Open and draft PRs from other users are no longer downloaded as branches by default. `ngit pr checkout` opts an individual PR back in with its familiar shorthand-suffixed branch name and keeps later fetch, pull, and push tracking intact. The new default applies to fresh and existing clones; existing clones keep previously downloaded tracking refs until `git fetch --prune`. Set `nostr.auto-pr-branches=true` explicitly to restore automatic downloading. - Global event caching now falls back to an in-memory cache when persistent storage is unavailable, allowing ngit to operate in restricted or sandboxed environments. Set `NGIT_CACHE_DIR` to select a writable persistent cache directory; repository caches remain strict and require the Git common directory to be writable. - Upgrade NostrDevKit dependencies from the `0.45.0-alpha.2` prerelease series to the stable `0.45.0` release. - Align maintainer terminology with gitworkshop: every pubkey in the directional maintainer graph has maintainer rights, while "invited" identifies an unreciprocated relationship rather than reduced authority. Reciprocal graph membership confirms co-maintainers, and a unique highest-listed confirmed maintainer is shown as a coordination-only lead. `ngit repo` describes whom each confirmed maintainer lists and, when informative, who invited an unconfirmed maintainer. Acceptance defaults now reciprocate the sole confirmed maintainer or unique lead, retaining the selected maintainer only for ambiguous non-interactive cases. - Accepting co-maintainership (`ngit repo accept`, and the auto-accept that runs during push and status commands) no longer rewrites `nostr.repo` or the `origin` remote to point at the accepter's own coordinate. The coordinate a repository resolves from is the root of trust; re-rooting it on your own announcement — which always lists you as a maintainer — would make it impossible to observe the inviter removing you later. Resolution stays on the inviter's coordinate, so a removal surfaces naturally (for example as a refused push); only `ngit repo edit` / `ngit init` change the resolved coordinate deliberately. When `origin` is not a `nostr://` remote, `ngit repo accept` now prints how to add a nostr remote for the inviter's coordinate instead of claiming pushes will work. - `ngit account login` and `ngit account create` now store local keys and typed remote-signer records in the OS credential store, falling back to a user-only file store in ngit's data directory when no OS store is available. With credential-backed storage, Git config keeps only public account-selection and alias metadata. Plaintext values in Git config remain supported indefinitely and are never rewritten by reads; run `ngit account login` to move an existing plaintext login into a credential store. The `nostr.secret-storage` Git config item, `NGIT_SECRET_STORAGE` env var, or `--secret-storage` flag on `ngit account login` / `create` selects `auto` (OS store then file store), `file`, or `git-config` (plaintext, the previous behaviour); when a secret cannot be stored, login fails with guidance instead of silently saving plaintext. `ngit account logout` keeps the stored secret — the store may hold the only copy of the key — and prints the `ngit account forget-keys <entry>` command that removes it; `ngit account logout --forget` does both in one step. Generated keys are no longer printed by `ngit account create`; use `ngit account export-keys` when the secret needs to be revealed deliberately. See `docs/credential-storage.md`. - Repository-coordinate resolution now follows a documented priority: (1) explicit `--repo`, (2) `git config nostr.repo`, (3) current branch's tracked upstream if a `nostr://` remote, (4) `origin` if a `nostr://` remote, (5) sole remaining distinct nostr coordinate. When multiple distinct coordinates remain and none of the earlier rules match, ngit errors by default and prints how to disambiguate, instead of silently picking one at HashMap-iteration random. Interactive selection is offered only when `-i` is explicitly requested and uses deterministic (name-sorted) ordering. - `ngit init` first-time use now recreates the existing `origin` as a remote named after the git server's domain (e.g. `github` for github.com; deeper hosts drop the public suffix and dash-join the rest, like `git-fiatjaf` for git.fiatjaf.com; name collisions get a numeric suffix) instead of discarding its URL entirely when `origin` is repointed at the nostr URL. Branch upstreams and existing tracking refs are untouched. - `ngit init` now pushes git data and publishes the repository state in-process instead of spawning `git push` / `ngit sync` subprocesses. The state is cached and broadcast only after a Git server accepts the data and a relay accepts the event. A failure while pushing or establishing the state is a real error that reports that the announcement was already published and names the follow-up command (`git push -u origin <branch>` or `ngit sync`), instead of being downgraded to a warning. - Repeat `ngit init` on a repository that already has a state event republishes it as a fresh event (identical refs, new event id) through the same acceptance-gated flow as a push, so relays and git servers newly added to the announcement receive the repository state immediately instead of only on the next `git push`. Establishing the state during init now requires at least one reachable git server. - `ngit init` on a repository with a pre-existing reachable `origin` now records `refs/remotes/origin/*` remote-tracking refs for the branches covered by the published state, so ahead/behind reporting against the repointed nostr `origin` is correct before the first push. - The remote helper no longer creates, updates or deletes `refs/remotes/<remote>/*` tracking refs itself: git's own transport layer performs those updates for every ref the helper reports `ok`, mapping the destination through `remote.<name>.fetch`. The helper's only remaining local ref bookkeeping is deleting legacy tag tracking refs written by old ngit versions. Two behaviours deliberately change to match vanilla git semantics: a remote whose `remote.<name>.fetch` refspec has been narrowed no longer receives tracking refs for pushed branches outside that refspec, and `git push <nostr-url>` with no configured remote now completes without the helper's previous bookkeeping error, writing no tracking refs. ### Removed - Remove the former `ngit pr merge --squash` option so the canonical command and its top-level alias expose one identical no-ff merge interface. Use Git's manual squash workflow when a single-parent result is desired. ### Fixed - Invalid repository self-`defer` role records are now author-scoped health problems instead of repository-wide failures. A strictly later signed self-role restores current authority without rewriting the malformed history; otherwise only that signer is gated and can explicitly repair the record with `ngit repo edit --repair-self-defer` or accept a fresh role. A numeric repair now merges a same-role successor into one multi-interval record so the signed replacement does not retain a duplicate-role conflict. - Unparseable repository role records, including records with non-hex subjects, no longer abort repository parsing or read as a signed departure. They remain byte-for-byte on the source announcement but never grant authority or enter copied history. An author whose own role records are exclusively malformed is blocked — excluded from authority as an author-scoped health problem that gates only that signer's announcement edits (no automated repair exists yet) — while the repository stays readable through the selected coordinate instead of suggesting a follow-lead redirect that cannot resolve. A valid numeric departure still departs, and still redirects, even beside malformed tags. - The `nostr://` Git remote helper now accepts raw commit object IDs on the source side of push refspecs, matching Git's normal behavior for commands such as `git push origin <oid>:refs/heads/recovery`. Branch and lightweight tag destinations are recorded in repository state without requiring a temporary local ref. - Blossom presence checks and upload responses now treat a server's MIME type as representation metadata rather than blob identity. Content-addressed bytes can be reused by files with different media types, so an existing empty blob labelled `inode/x-empty` no longer blocks publication of an empty CSS file; hash, size, and descriptor URL validation remain strict. - `ngit update` now recognizes signed prerelease events whose NIP-82 channel matches the first SemVer prerelease identifier (`rc.7` uses `rc`, `beta.2` uses `beta`) for both automatic and exact-version updates, while continuing to accept backward-compatible `main`-channel events. Stable releases remain restricted to `main`, and pending-release warnings now use the correct singular or plural verb. - Remote-signer login no longer overwrites a different NIP-46 connection for the same npub before failing to persist Git config. A bare npub retains its default signer, while `--alias <name>` stores and selects additional signer sessions independently for login, push, and `account export-keys`. Credential-store alias values remain raw npubs so older ngit versions can continue reading them. If only one session remains and it was stored for an alias, either the npub or alias can still select it; multiple sessions with no default require an alias. - `ngit account export-keys --secret` prints only the selected account's nsec or nbunksec, providing a direct human-readable export without a JSON wrapper or interactive menu. - `ngit repo edit` now refreshes the maintainer's current announcement from every NIP-65 write relay before deriving a replacement. A cold cache can no longer let a stale indexer announcement make a targeted `--add-*` edit drop existing grasp servers, relays, clone URLs, hashtags, or membership data; incomplete account-relay reads fail before anything is signed or published. - `ngit merge` now preserves staged, unstaged, and untracked changes across the merge commit and branch switch, including the staged/unstaged boundary. If those changes conflict with the merged target, ngit rolls the merge back and restores the original branch and worktree. Linked worktrees are supported when the command runs from the worktree that owns the target branch; if another worktree owns it, ngit refuses before changing either worktree or the shared target ref. - `ngit init --defaults` no longer drops grasp hosting when `--additional-clone` or `--additional-relay` is supplied. Additional infrastructure supplements the account's preferred grasp servers (or ngit's defaults) instead of silently replacing them, so the announcement keeps its grasp-derived clone URLs and relays. Publishing without any grasp server is now stated explicitly with an empty value, `--grasp-server ""`, which also requires an additional relay and clone URL of its own. Republishing an announcement that genuinely declares no grasp servers no longer grafts the defaults onto it. - `ngit init` and `ngit repo edit` refuse to publish a repository announcement whose relay or clone field would be empty, naming `--grasp-server`, `--additional-relay` and `--additional-clone` as the ways to supply the missing half. The check runs on the resolved announcement before anything is signed, so it covers every flag shape and repository configuration, including a metadata-only edit of an announcement that already lacks hosting — which previously republished the unusable announcement and then failed while pushing git data with no git server to connect to. - Prevent TLS client initialization from panicking after Reqwest 0.13 selected AWS-LC alongside rust-nostr's Ring provider. Reqwest remains provider-neutral while ngit explicitly installs Ring before constructing either its application or library HTTP clients. - Repository-conditioned global signer identities, aliases, and secret-storage policies selected through Git `includeIf` `gitdir` conditions are now resolved in repository context. Conditional signers work for `ngit account whoami` and ordinary signed commands, while `nostr.secret-storage` and legacy `nostr.credential-store` settings control login storage and plaintext hints. - ngit now honours `GIT_CONFIG_GLOBAL`, `GIT_CONFIG_SYSTEM` and `GIT_CONFIG_NOSYSTEM` when resolving Git config scopes outside the repository. libgit2 does not apply these to the config ngit opens, so a login stored, read or removed with `--global` (or any global-scope read, such as the secret-storage policy) previously went to `~/.gitconfig` regardless of the redirect, silently overwriting the real global login of anyone who sandboxes ngit with the documented variables. - Keep `--json` output parseable by sending relay-fetch summaries such as `no updates` and `updates: ...` to stderr instead of stdout. - `git push --force-with-lease` now works with `nostr://` remotes: matching leases authorize guarded non-fast-forward updates, while stale leases reject the push before a conflicting repository state can be published. - `git push` of a `pr/` branch alongside branch or tag changes now reports the proposal ref as failed when no git server accepts the pushed git data, matching the branch/tag refs. Proposal events are broadcast to the repository and user relays only after a git server accepts the pushed data; previously the helper told git `ok` and recorded a `refs/remotes/<remote>/pr/<branch>` tracking ref in that total-failure case even though the proposal events were never broadcast. - `git push` to a nostr remote now treats a git server that already has every requested change as a successful push target, so no-op pushes (re-pushing a tag that is already present, deleting an already-deleted branch) succeed instead of erroring, including under `nostr.nostate`. Git data is no longer pushed to a GRASP server whose paired relay did not accept the staged state event, and the locally cached repository state is only updated after a git server accepted the pushed data and a relay accepted the state event, so a failed or interrupted push leaves the previous state authoritative instead of caching an unpublished replacement. - Git pushes that encounter libgit2's internal protocol assertion now retry through the system Git transport, allowing affected SSH servers such as tangled.org to accept the same push. - Make ahead/behind commit walks topology-complete so merged side-branch commits with equal timestamps are not skipped, restoring every issue-resolution status generated from a multi-merge push. - Fast successive repository and proposal updates now order reliably despite Nostr's whole-second timestamps. Repository state, announcements, and statuses use bounded nonce grinding with a timestamp fallback; GRASP now honors the lower-event-ID tie-break for same-second state replacements. Patch revisions and pull-request upgrades or updates remain strictly ordered by timestamp. - `ngit init` first-time use no longer fails to publish git data for existing `origin` refs that were never downloaded locally (e.g. tags after a `--no-tags` or single-branch clone): the missing objects are now fetched from the origin by ref name before the repository state is signed, and refs whose objects still cannot be obtained are excluded from the state event instead of being advertised as oids no git server holds. - Fix silent mis-targeting of repo-scoped events (`ngit send`, `ngit issue create`, `ngit pr *`, `ngit repo`, etc.) when a repository had multiple `nostr://` remotes with disagreeing coordinates. Previously the resolver iterated a `HashMap` and picked the first key it saw, ignored `nostr.repo`, and printed no diagnostic; the effect was that PRs and issues could be published against the wrong repository coordinate without warning. See the documented priority under "Changed". - `ngit merge` run with no argument on a bare `pr/<name>` branch now falls back to matching the branch's tip commit against the published tips of open and draft PRs when the logged-in-author mapping finds zero or several candidates. A maintainer merging a contributor's PR from a hand-made bare branch, and a logged-out user merging their own, now resolve the PR instead of erroring; when several open PRs share both the branch name and the tip commit, merge still asks for an explicit event-id. - Sep 4, 2026 3.0.0-rc.8 rc- Eighth v3 release candidate, hardening malformed maintainer-role recovery, accepting raw object IDs in push refspecs, recognizing prerelease update channels, adding canonical bootstrap installers, and reusing identical Blossom blobs across MIME representations.
- Sep 3, 2026 3.0.0-rc.7 rc- Seventh v3 release candidate, preserving backward-compatible signer records, distinguishing multiple remote-signer connections for one identity, adding direct credential export, and publishing ngit's complete cross-platform release through NIP-82 from the GitHub release workflow.
- Jun 26, 2026 2.6.0### Added - `ngit merge` merges a PR into the default branch as a no-ff merge commit (recording the PR nevent and author in the commit message) without pushing - `ngit pr` and `ngit issue` commands that accept a PR/issue event ID now accept unique hex prefixes, with or without a leading `#`; ambiguous prefixes fail with the matching items listed - `ngit init -u ...` / `--u ...` / `--upstream ...` publishes the informational NIP-34 `u` tag for subordinate forks, and `ngit repo` now displays/serializes existing `u` metadata without ever inventing it by default - `ngit account connect` as an alias for `ngit account login -i` (interactive nostr connect login) [hzd149] - Pushing commits that include issue-closing keywords (e.g. `fixes` / `resolves`) followed by #<hex-event-id-or-8-char-prefix> or nostr:nevent123 now auto-resolves referenced issues - Added env-var and git-config options to increase libgit2 HTTP connect and per-socket I/O timeouts, helpful for large pushes with git servers that may be silent for longer than the default timeout; `NGIT_HTTP_CONNECT_TIMEOUT_MS` / `NGIT_HTTP_IO_TIMEOUT_MS` override `nostr.http-connect-timeout-ms` / `nostr.http-io-timeout-ms` for one-off commands — thanks to new contributor mstrofnone - warn when newer ngit version available
More…
### Changed - Use git index relays along side relay hints to find repositories during cloning - Bump dependencies: updated to rust-nostr v0.45.0-alpha.2 (required significant internal refactoring); semver-compatible lockfile updates across the board; dev-dependencies rstest 0.23 → 0.26 and mockall 0.13 → 0.14 - The interactive remote-signer login menu now has a single "bunker" option that shows the QR code and the nostrconnect:// connection string together while it waits for the signer to connect, and concurrently offers options to manually paste a `bunker://` url, change the signer relays, or cancel; previously these were separate menu entries gated behind ctrl+c [hzd149] ### Fixed - Merge status detection now handles updated PRs correctly - Better default branch and merge-base detection when force-pushing PRs - `cargo install ngit` was failing to build due to an upstream dependency picking up an incompatible `git2` version - Pushing a tag through a `nostr://` remote wrote a stray ref at `refs/remotes/<remote>/<tagname>` — git's remote-tracking branch namespace — so tags appeared as remote branches in `git branch -r`, IDE listings, completion, etc. The remote helper no longer writes a per-remote tracking ref for tags (the local `refs/tags/<name>` is git's single source of truth), and `ngit sync` sources tag push refspecs directly from the nostr state event oid. On the next push or `ngit sync`, any stray legacy entries are deleted automatically. - Invited co-maintainers auto-accept maintainership during push and auto-update the remote to reflect their npub. This no longer breaks the push. - `ngit send --in-reply-to` now errors before publishing an update to an existing PR when the signer is neither the proposal author nor a repository maintainer. - Push reporting no longer returns `ok` before any git server has successfully received the git data. - State events are no longer broadcast before the git data is successfully pushed: ngit now publishes the state event to GRASP servers using their purgatory support, pushes the git data, then only broadcasts to additional relays after at least one git server succeeds, so `ok` requires the latest state on at least one relay and the git data on at least one git server. - `.onion` hosts supplied as a relay-hint segment in a `nostr://<npub>/<host>/<repo>` URL no longer get a stray `wss://` prefix, which previously made onion relays unreachable. Same fix applies to the `?relay=` query-string form. ### Removed - NIP-05 signer-URL lookup removed from the remote-signer login flow; it was unused and added an extra step before reaching the bunker / QR-code screen