<feed xmlns='http://www.w3.org/2005/Atom'>
<title>npub1tkq8unhsd5jqx6ueex5lcpsgknrpquxuk44ftpjlpm3ulaake7xs76txrw/ngit-grasp-mirror/src/nostr/policy/announcement.rs, branch v1.0.0</title>
<subtitle>Unnamed repository; edit this file 'description' to name the repository.
</subtitle>
<id>https://upleb.uk/npub1tkq8unhsd5jqx6ueex5lcpsgknrpquxuk44ftpjlpm3ulaake7xs76txrw/ngit-grasp-mirror/atom?h=v1.0.0</id>
<link rel='self' href='https://upleb.uk/npub1tkq8unhsd5jqx6ueex5lcpsgknrpquxuk44ftpjlpm3ulaake7xs76txrw/ngit-grasp-mirror/atom?h=v1.0.0'/>
<link rel='alternate' type='text/html' href='https://upleb.uk/npub1tkq8unhsd5jqx6ueex5lcpsgknrpquxuk44ftpjlpm3ulaake7xs76txrw/ngit-grasp-mirror/'/>
<updated>2026-02-18T09:58:41+00:00</updated>
<entry>
<title>fix: preserve state events when another owner's announcement remains in purgatory</title>
<updated>2026-02-18T09:58:41+00:00</updated>
<author>
<name>DanConwayDev</name>
<email>DanConwayDev@protonmail.com</email>
</author>
<published>2026-02-18T09:58:41+00:00</published>
<link rel='alternate' type='text/html' href='https://upleb.uk/npub1tkq8unhsd5jqx6ueex5lcpsgknrpquxuk44ftpjlpm3ulaake7xs76txrw/ngit-grasp-mirror/commit/?id=2f365fc3b209f6d377d59a6ab8a6891b0350fee6'/>
<id>urn:sha1:2f365fc3b209f6d377d59a6ab8a6891b0350fee6</id>
<content type='text'>
remove_purgatory_announcement() was unconditionally wiping all state
events for an identifier when one owner's announcement was evicted.
State events are keyed by identifier alone, so this incorrectly
discarded state events belonging to a different owner's repository
sharing the same identifier string. Now only removes state events if
no other owner's announcement remains in purgatory for that identifier.
</content>
</entry>
<entry>
<title>fix: only evict purgatory entry when incoming rejected announcement is newer</title>
<updated>2026-02-18T09:43:34+00:00</updated>
<author>
<name>DanConwayDev</name>
<email>DanConwayDev@protonmail.com</email>
</author>
<published>2026-02-18T09:43:34+00:00</published>
<link rel='alternate' type='text/html' href='https://upleb.uk/npub1tkq8unhsd5jqx6ueex5lcpsgknrpquxuk44ftpjlpm3ulaake7xs76txrw/ngit-grasp-mirror/commit/?id=28aa19bc5b196f2259ab8ff0ac8534afe886529f'/>
<id>urn:sha1:28aa19bc5b196f2259ab8ff0ac8534afe886529f</id>
<content type='text'>
An older rejected announcement (e.g. a relay replay of a superseded
event) was incorrectly evicting a newer purgatory entry for the same
pubkey+identifier. Now only evict when the incoming event's created_at
is strictly greater than the stored entry's created_at.
</content>
</entry>
<entry>
<title>fix: handle announcement replacement when original is still in purgatory</title>
<updated>2026-02-18T09:24:01+00:00</updated>
<author>
<name>DanConwayDev</name>
<email>DanConwayDev@protonmail.com</email>
</author>
<published>2026-02-18T09:24:01+00:00</published>
<link rel='alternate' type='text/html' href='https://upleb.uk/npub1tkq8unhsd5jqx6ueex5lcpsgknrpquxuk44ftpjlpm3ulaake7xs76txrw/ngit-grasp-mirror/commit/?id=0c01797812bb77fc81d0efe58f0e7858f2b7af66'/>
<id>urn:sha1:0c01797812bb77fc81d0efe58f0e7858f2b7af66</id>
<content type='text'>
Previously, has_active_announcement() only queried the database, so when
a newer announcement arrived for the same (pubkey, identifier) while the
original was still in purgatory, it was incorrectly routed as a brand-new
announcement (AcceptPurgatory) rather than replacing the existing entry.

This change splits the logic into two cases:
- If the existing entry is in the database: return Accept (replacement) as before
- If the existing entry is only in purgatory: replace the purgatory entry via
  add_announcement() (which overwrites by key) and extend expiries for both the
  announcement and any waiting state events, then return Accept
- If the owner sends a Reject-classified announcement (service removed) but has
  a purgatory entry: clear the purgatory entry, delete the bare repo, and remove
  any waiting state events before rejecting

Also add an explicit comment to find_accepted_repository() in related.rs
clarifying that it intentionally only checks the database. Related events
should only be accepted after the repository announcement has been promoted
(validated via git data) - this is correct behaviour, not a missing check.
</content>
</entry>
<entry>
<title>fix: check purgatory in maintainer announcement lookup</title>
<updated>2026-02-18T08:59:52+00:00</updated>
<author>
<name>DanConwayDev</name>
<email>DanConwayDev@protonmail.com</email>
</author>
<published>2026-02-18T08:59:52+00:00</published>
<link rel='alternate' type='text/html' href='https://upleb.uk/npub1tkq8unhsd5jqx6ueex5lcpsgknrpquxuk44ftpjlpm3ulaake7xs76txrw/ngit-grasp-mirror/commit/?id=467690f33bbbfd442852e61de221e4e5e161b878'/>
<id>urn:sha1:467690f33bbbfd442852e61de221e4e5e161b878</id>
<content type='text'>
is_maintainer_in_any_announcement only queried the database, missing
announcements still in purgatory. A maintainer's announcement (which
lists the recursive maintainer) may arrive and enter purgatory before
the recursive maintainer's announcement does, causing the maintainer
exception check to return false and reject the recursive maintainer's
announcement.
</content>
</entry>
<entry>
<title>feat: implement announcement purgatory core (breaks archive sync test)</title>
<updated>2026-02-13T17:29:23+00:00</updated>
<author>
<name>DanConwayDev</name>
<email>DanConwayDev@protonmail.com</email>
</author>
<published>2026-02-13T13:24:46+00:00</published>
<link rel='alternate' type='text/html' href='https://upleb.uk/npub1tkq8unhsd5jqx6ueex5lcpsgknrpquxuk44ftpjlpm3ulaake7xs76txrw/ngit-grasp-mirror/commit/?id=1d09e4bdea7e328cf2740818df9df660c5532a99'/>
<id>urn:sha1:1d09e4bdea7e328cf2740818df9df660c5532a99</id>
<content type='text'>
Route new announcements to purgatory instead of accepting immediately.
Announcements are promoted to the database when git data arrives,
ensuring we only serve announcements for repos with actual content.

Implemented:
- AnnouncementPurgatoryEntry type and DashMap store
- Route new announcements to purgatory (replacement announcements skip)
- Promote announcements on git data arrival (process_purgatory_announcements)
- Authorization checks purgatory announcements (fetch_repository_data_with_purgatory)
- State policy uses purgatory announcements for maintainer validation
- Cleanup task handles announcement expiry
- Updated count()/cleanup() to 3-tuples

Known broken:
- test_archive_read_only_creates_bare_repo fails: sync module does not
  treat purgatory announcements as confirmed repos, so per-repo sync
  (state events, PRs) is never triggered for purgatory announcements
- Announcement persistence (save/restore) not implemented
- SyncLevel (StateOnly vs Full) not implemented
- Soft expiry two-phase not implemented
- Expiry extension on state event / git auth not wired up
</content>
</entry>
<entry>
<title>feat(config): add repository whitelist for curated GRASP-01 acceptance</title>
<updated>2026-01-12T21:21:52+00:00</updated>
<author>
<name>DanConwayDev</name>
<email>DanConwayDev@protonmail.com</email>
</author>
<published>2026-01-12T21:06:39+00:00</published>
<link rel='alternate' type='text/html' href='https://upleb.uk/npub1tkq8unhsd5jqx6ueex5lcpsgknrpquxuk44ftpjlpm3ulaake7xs76txrw/ngit-grasp-mirror/commit/?id=82b56c37b26a2fac1a294873e539b19b9325dca6'/>
<id>urn:sha1:82b56c37b26a2fac1a294873e539b19b9325dca6</id>
<content type='text'>
Adds NGIT_REPOSITORY_WHITELIST option for curated relay operation that
accepts only whitelisted repositories while maintaining GRASP-01 compliance
(announcements must list the service). This differs from archive whitelist
which enables GRASP-05 mode and doesn't require service listing.

Key features:
- Supports three whitelist formats: npub, npub/identifier, identifier
- Enforces mutual exclusivity with archive read-only mode
- Updates NIP-11 curation field when whitelist is enabled
- Maintains GRASP-01 compliance (doesn't add GRASP-05 support)

Configuration synced across all four sources: src/config.rs, docs/reference/configuration.md,
nix/module.nix, and .env.example as required by AGENTS.md.
</content>
</entry>
<entry>
<title>feat(grasp-05): implement archive mode for backup/mirror operation</title>
<updated>2026-01-12T17:40:25+00:00</updated>
<author>
<name>DanConwayDev</name>
<email>DanConwayDev@protonmail.com</email>
</author>
<published>2026-01-12T17:40:25+00:00</published>
<link rel='alternate' type='text/html' href='https://upleb.uk/npub1tkq8unhsd5jqx6ueex5lcpsgknrpquxuk44ftpjlpm3ulaake7xs76txrw/ngit-grasp-mirror/commit/?id=c29191b1e1239e931c575a926ec9480e594476d6'/>
<id>urn:sha1:c29191b1e1239e931c575a926ec9480e594476d6</id>
<content type='text'>
Implements GRASP-05 specification for accepting repository announcements
that don't list this relay, enabling archive, mirror, and backup use cases.

Core Features:
- Three whitelist formats: &lt;npub&gt;, &lt;npub&gt;/&lt;identifier&gt;, &lt;identifier&gt;
- Archive-all mode for complete ecosystem mirrors
- Fail-fast npub validation at startup
- Read-only enforcement (archived repos reject pushes)
- Full GRASP-02 sync (git data + Nostr events)
- Dynamic archive status (no flags/metadata)

Implementation:
- Add ArchiveWhitelistEntry enum with Pubkey/Repository/Identifier variants
- Add ArchiveConfig with validation and matching logic
- Update AnnouncementResult to include AcceptArchive variant
- Refactor validate_announcement() to return AnnouncementResult with archive check
- Update AnnouncementPolicy with catch-all pattern for cleaner code
- Wire archive config through builder and policy layers

Configuration:
- NGIT_ARCHIVE_ALL: Accept all announcements (⚠️ storage risk)
- NGIT_ARCHIVE_WHITELIST: Comma-separated whitelist entries
- Updated docs, .env.example, and nix/module.nix

Testing:
- 28 unit tests for config parsing and whitelist matching
- 7 integration tests for archive mode validation
- All 296 tests passing

Validation Priority:
1. Lists our service → Accept (GRASP-01, read/write)
2. Is maintainer → AcceptMaintainer (multi-maintainer, read/write)
3. Matches archive config → AcceptArchive (GRASP-05, read-only)
4. None of above → Reject

Security Considerations:
- Archive-all mode has storage/bandwidth DoS risk
- Identifier-only format matches any pubkey (use npub/identifier for high-value)
- Invalid npubs cause startup failure (fail-fast)

Documentation:
- Concise explanation focused on rationale
- Reference docs updated with all config options
- README updated to reflect completed feature
- Removed from roadmap, added to compliance section

See docs/explanation/grasp-05-archive.md for details.
</content>
</entry>
<entry>
<title>refactor: replace hardcoded Kind constants with rust-nostr variants</title>
<updated>2026-01-08T11:23:28+00:00</updated>
<author>
<name>DanConwayDev</name>
<email>DanConwayDev@protonmail.com</email>
</author>
<published>2026-01-08T11:20:35+00:00</published>
<link rel='alternate' type='text/html' href='https://upleb.uk/npub1tkq8unhsd5jqx6ueex5lcpsgknrpquxuk44ftpjlpm3ulaake7xs76txrw/ngit-grasp-mirror/commit/?id=5d02ad6b893f9059044914c115d77cf9d8e589c3'/>
<id>urn:sha1:5d02ad6b893f9059044914c115d77cf9d8e589c3</id>
<content type='text'>
- Replace KIND_REPOSITORY_ANNOUNCEMENT with Kind::GitRepoAnnouncement
- Replace KIND_REPOSITORY_STATE with Kind::RepoState
- Replace KIND_PR with Kind::GitPullRequest
- Replace KIND_PR_UPDATE with Kind::GitPullRequestUpdate
- Replace KIND_USER_GRASP_LIST with Kind::GitUserGraspList
- Replace KIND_PATCH with Kind::GitPatch
- Replace KIND_ISSUE with Kind::GitIssue
- Replace KIND_COMMENT with Kind::Comment
- Replace all Kind::Custom(30617|30618|1617|1618|1619|1621|1111|10317) patterns
- Remove all hardcoded KIND_* constants from events.rs
- Update all match statements to use Kind enum directly
- Update all filter builders to use Kind variants
- Update all test helpers and assertions

Benefits:
- Type safety: compiler prevents wrong kind numbers
- Readability: Kind::GitRepoAnnouncement is self-documenting
- Maintainability: single source of truth (rust-nostr)
- IDE support: full autocompletion and refactoring
- Standards: aligns with rust-nostr best practices

Files modified: 21
Constants removed: 9
Patterns replaced: 100+
Tests passing: 222/222
</content>
</entry>
<entry>
<title>fix: resolve all fmt and clippy warnings</title>
<updated>2025-12-11T16:53:03+00:00</updated>
<author>
<name>DanConwayDev</name>
<email>DanConwayDev@protonmail.com</email>
</author>
<published>2025-12-11T16:53:03+00:00</published>
<link rel='alternate' type='text/html' href='https://upleb.uk/npub1tkq8unhsd5jqx6ueex5lcpsgknrpquxuk44ftpjlpm3ulaake7xs76txrw/ngit-grasp-mirror/commit/?id=2a9160836bb87fdea3ae891563b0169c68d1c2ab'/>
<id>urn:sha1:2a9160836bb87fdea3ae891563b0169c68d1c2ab</id>
<content type='text'>
Main lib (src/):
- Add #[allow(dead_code)] for build_info field (stored to prevent Prometheus unregistration)
- Add #[allow(dead_code)] for first_seen field (reserved for future rate limiting)
- Replace .or_insert_with(RelaySyncNeeds::default) with .or_default()
- Replace manual div_ceil implementations with .div_ceil(100)

Test code (tests/):
- Replace .expect(&amp;format!(...)) with .unwrap_or_else(|_| panic!(...))
- Remove needless borrows in fetch_metrics() calls
- Add #[allow(dead_code)] and #[allow(unused_imports)] to test helpers module

grasp-audit:
- Apply cargo fmt to fix formatting
</content>
</entry>
<entry>
<title>refactor: split Nip34WritePolicy into focused sub-policies</title>
<updated>2025-12-04T15:42:00+00:00</updated>
<author>
<name>DanConwayDev</name>
<email>DanConwayDev@protonmail.com</email>
</author>
<published>2025-12-04T15:42:00+00:00</published>
<link rel='alternate' type='text/html' href='https://upleb.uk/npub1tkq8unhsd5jqx6ueex5lcpsgknrpquxuk44ftpjlpm3ulaake7xs76txrw/ngit-grasp-mirror/commit/?id=819866330c7e2f535a155d1d7efaf2e12dc15dc2'/>
<id>urn:sha1:819866330c7e2f535a155d1d7efaf2e12dc15dc2</id>
<content type='text'>
Split the ~900 line Nip34WritePolicy into focused sub-policies for improved
testability and maintainability:

- AnnouncementPolicy - Repository announcement validation
- StatePolicy - State event validation + ref alignment
- PrEventPolicy - PR/PR Update validation
- RelatedEventPolicy - Forward/backward reference checking

The main Nip34WritePolicy now delegates to these sub-policies via a shared
PolicyContext that provides domain, database, and git_data_path.

Also updates:
- README.md: Accurate project structure reflecting actual implementation
- docs/learnings: Marks this technical debt item as complete
</content>
</entry>
</feed>
