diff options
| author | Your Name <you@example.com> | 2026-05-26 16:11:05 +0530 |
|---|---|---|
| committer | Your Name <you@example.com> | 2026-05-26 16:11:05 +0530 |
| commit | 8816a192c95cf539b65975469a2d61aed46f0414 (patch) | |
| tree | 7590318244a56fabbfa6919ef6d0fab5be529134 /src/signing.rs | |
feat: initial implementation of grasp-mirror daemon
GRASP mirror daemon that discovers repos from watched npubs and mirrors
git data + Nostr events across all known GRASP servers for redundancy.
Features:
- Configurable npub watch list via .env (MIRROR_NPUBS)
- TOML config for GRASP server list, index relays, storage paths
- NIP-11 verification of GRASP servers on startup
- Discovery of repos via kind:30617 announcements on index relays
- Git mirroring (bare clone + push --mirror) to missing GRASP servers
- Nostr event forwarding to all GRASP server embedded relays
- SQLite state tracking for sync status and event dedup
- Optional signing key for updating announcements with new clone URLs
- CLI subcommands: daemon, status, verify, mirror-once
Architecture:
config.rs - TOML + .env config loading
db.rs - SQLite state tracking
health.rs - NIP-11 GRASP server verification
discovery.rs - Relay subscription, kind:30617 parsing
git_mirror.rs - Bare clone + push to GRASP servers
nostr_mirror.rs - Event forwarding to all GRASP relays
signing.rs - Optional announcement updates
main.rs - CLI entry point, daemon loop
Diffstat (limited to 'src/signing.rs')
| -rw-r--r-- | src/signing.rs | 76 |
1 files changed, 76 insertions, 0 deletions
diff --git a/src/signing.rs b/src/signing.rs new file mode 100644 index 0000000..7ec796a --- /dev/null +++ b/src/signing.rs | |||
| @@ -0,0 +1,76 @@ | |||
| 1 | use crate::discovery::DiscoveredRepo; | ||
| 2 | use crate::health::GraspServer; | ||
| 3 | use anyhow::{Context, Result}; | ||
| 4 | use nostr_sdk::prelude::*; | ||
| 5 | |||
| 6 | pub struct OptionalSigner { | ||
| 7 | keys: Option<Keys>, | ||
| 8 | } | ||
| 9 | |||
| 10 | impl OptionalSigner { | ||
| 11 | pub fn none() -> Self { | ||
| 12 | Self { keys: None } | ||
| 13 | } | ||
| 14 | |||
| 15 | pub fn from_nsec(nsec: &str) -> Result<Self> { | ||
| 16 | let keys = Keys::parse(nsec).context("failed to parse nsec")?; | ||
| 17 | Ok(Self { keys: Some(keys) }) | ||
| 18 | } | ||
| 19 | |||
| 20 | pub fn is_available(&self) -> bool { | ||
| 21 | self.keys.is_some() | ||
| 22 | } | ||
| 23 | |||
| 24 | pub async fn build_updated_announcement( | ||
| 25 | &self, | ||
| 26 | original: &DiscoveredRepo, | ||
| 27 | additional_servers: &[GraspServer], | ||
| 28 | ) -> Result<Option<Event>> { | ||
| 29 | let keys = match &self.keys { | ||
| 30 | Some(k) => k, | ||
| 31 | None => { | ||
| 32 | tracing::debug!("no signer available, skipping announcement update"); | ||
| 33 | return Ok(None); | ||
| 34 | } | ||
| 35 | }; | ||
| 36 | |||
| 37 | if additional_servers.is_empty() { | ||
| 38 | return Ok(None); | ||
| 39 | } | ||
| 40 | |||
| 41 | let pk_hex = original.pubkey.to_hex(); | ||
| 42 | |||
| 43 | let mut new_clone_urls: Vec<String> = original.clone_urls.clone(); | ||
| 44 | for server in additional_servers { | ||
| 45 | let url = server.clone_url(&pk_hex, &original.identifier); | ||
| 46 | if !new_clone_urls.contains(&url) { | ||
| 47 | new_clone_urls.push(url); | ||
| 48 | } | ||
| 49 | } | ||
| 50 | |||
| 51 | let mut tags: Vec<Tag> = vec![ | ||
| 52 | Tag::custom(TagKind::Custom("d".into()), [&original.identifier]), | ||
| 53 | ]; | ||
| 54 | |||
| 55 | for url in &new_clone_urls { | ||
| 56 | tags.push(Tag::custom(TagKind::Custom("clone".into()), [url.as_str()])); | ||
| 57 | } | ||
| 58 | for url in &original.relay_urls { | ||
| 59 | tags.push(Tag::custom( | ||
| 60 | TagKind::Custom("relays".into()), | ||
| 61 | [url.as_str()], | ||
| 62 | )); | ||
| 63 | } | ||
| 64 | |||
| 65 | let builder = EventBuilder::new(Kind::Custom(30617), "").tags(tags); | ||
| 66 | let event = builder.sign_with_keys(keys)?; | ||
| 67 | |||
| 68 | tracing::info!( | ||
| 69 | identifier = %original.identifier, | ||
| 70 | added = additional_servers.len(), | ||
| 71 | "built updated announcement with additional clone URLs" | ||
| 72 | ); | ||
| 73 | |||
| 74 | Ok(Some(event)) | ||
| 75 | } | ||
| 76 | } | ||