Files
ngit-grasp/CHANGELOG.md
T
DanConwayDev 4665a00ea9 fix(grasp06): inline zero-ref /prs/ cleanup under per-path lock
Replaces the periodic /prs/ cleanup sweep (reverted in the previous
commit) with inline zero-ref cleanups at the three sites that can
leave a /prs/<submitter>/<identifier>.git bare repo empty:

  1. The /prs/ receive handler at the end of a push, already in place
     prior to the revert.
  2. The PR-event policy when it discards a scoped placeholder whose
     incoming event fails the (signer, identifier, commit) check —
     deletes refs/nostr/<event-id> and, if that empties the repo,
     removes the bare directory in the same step.
  3. The standard 30-minute purgatory expiry sweep when a scoped
     placeholder times out without a matching PR event arriving —
     same shape as (2), but reached from the synchronous cleanup
     loop, so the per-path lock is taken with try_lock and the
     filesystem cleanup is skipped (leaving a harmless dangling ref)
     if a push is currently in flight to the same path.

All three sites share a single Arc<DashMap<PathBuf, Arc<Mutex<()>>>>
of per-`(submitter, identifier)` locks. The receive handler now holds
its entry for the entire pipeline — `git init --bare` →
`git-receive-pack` → per-ref validation → zero-ref cleanup — instead
of only for the init step, so a concurrent push or off-push cleanup
cannot remove the bare repo while it is still being written. The
same lock map is plumbed into PolicyContext (used by pr_event.rs)
and Purgatory (via a one-shot `set_prs_cleanup_ctx` setter wired in
main, so tests can leave it unset and get the previous behaviour for
in-memory entries).

This delivers what the reverted commit was reaching for without the
370-line periodic walker, the mtime heuristic, or the
`has_prs_scope`/`DEFAULT_EXPIRY` API surface area on Purgatory: the
last ref always implies an immediate (or, under lock contention, a
next-cycle) repo removal, and the only code path that ever deletes
a /prs/ repo dir is one that already holds the per-path lock.

Docs:

- CHANGELOG.md, docs/how-to/enable-grasp-06.md: describe the three
  inline cleanup sites; drop the "periodic 10-minute sweep" line.
- docs/explanation/architecture.md: drop the src/grasp06/cleanup.rs
  bullet; document the lock as held for the whole pipeline and
  shared with off-push cleanup paths.
- docs/explanation/grasp-06-contributor-pr-submission.md: replace
  the "Periodic /prs/ cleanup" subsection with a "Zero-ref /prs/
  cleanup" subsection enumerating the three sites and the shared
  lock map; update "On-demand bare repo creation" to reflect the
  wider lock scope.
2026-05-15 18:49:45 +00:00

4.5 KiB

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

Unreleased

Added

  • GRASP-06 contributor PR submission endpoint (NGIT_GRASP06_ENABLE, default off). When enabled, the relay accepts unauthenticated git push of refs/nostr/<event-id> to /prs/<npub>/<identifier>.git from any contributor, even for repositories this relay has no accepted announcement for. The corresponding PR (kind 1618) or PR Update (kind 1619) event is accepted into purgatory when its clone tag names this relay's /prs/<signer>/<d>.git endpoint and its a tag's d-tag matches the URL identifier. When the event and the push match (signer, d-tag, c-tag commit) the event is released from purgatory and the ref is mirrored into any accepted-announcement repos on this relay. Empty /prs/ repos (probe pushes, mismatched events) are garbage-collected inline at the three sites that can leave them empty: the receive handler at the end of a push, the PR-event policy when discarding a mismatched scoped placeholder, and the purgatory sweep when a scoped placeholder expires without a matching event. GRASP-06 is advertised in NIP-11 supported_grasps when enabled. See how-to/enable-grasp-06.md and explanation/grasp-06-contributor-pr-submission.md.

1.0.2 - 2026-04-10

Fixed

  • Replacement announcements (kind 30617) for a purgatory entry were being saved to the database immediately, bypassing the purgatory gate. When a second copy of the same announcement arrived (e.g. via sync from another relay) while the original was still in purgatory awaiting git data, the policy returned Accept instead of AcceptPurgatory, causing the event to be stored without the corresponding git data or state events ever arriving. The fix returns AcceptPurgatory for replacements of purgatory entries so the updated event is held in purgatory until git data arrives.

  • Repository identifiers containing characters that require percent-encoding in URLs (e.g. spaces, emoji) are now accepted and served correctly. NIP-01 places no restriction on d tag values and NIP-34 only recommends kebab-case without mandating it, so rejecting non-kebab identifiers was overly strict. Identifiers are stored verbatim on disk and percent-encoded when used in URLs, per the nostr:// clone URL spec formalised in NIP-34 PR #2312 and the GRASP-01 HTTP path spec. The landing page clone URL now also correctly percent-encodes the identifier.

  • --git-dir is now passed as a global git option (before the subcommand) in check_repo_empty, fixing compatibility with git versions that require global options to precede the subcommand.

Changed

  • Remove arbitrary default max connections limit; when NGIT_MAX_CONNECTIONS is unset the relay imposes no connection cap, deferring to OS fd limits and infrastructure controls

  • Added cleanup-empty-repos subcommand to remove stale events for empty git repositories

1.0.1 - 2026-02-27

Fixed

  • Push authorization now correctly ignores refs/tags/<name>^{} peeled-tag entries in state events (kind 30618). These entries are git's internal notation for the dereferenced commit behind an annotated tag and are never sent as part of a push. Previously, their presence in the state event caused can_satisfy_state to reject valid annotated-tag pushes because the would-be ref state after the push did not include the spurious ^{} entry, making the exact-equality check fail.

Changed

  • Push auth rejections now send the reason to the git client via ERR pkt-line (e.g. "authorisation failed: No state events in purgatory") instead of a generic HTTP 403, so users see actionable error messages directly in their terminal

1.0.0 - 2026-02-26

Initial release of ngit-grasp, a GRASP relay implementation in Rust.