mirror of
https://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit-grasp.git
synced 2026-10-05 15:08:24 +00:00
fix: sync-bootstrap-relay-url scheme optional
This commit is contained in:
@@ -321,7 +321,7 @@ NGIT_OWNER_NPUB=npub1... ngit-grasp --domain relay.example.com
|
||||
|
||||
**Sync Notes:**
|
||||
|
||||
- **Bootstrap relay**: Optional starting point for relay discovery. System automatically discovers additional relays from repository announcements.
|
||||
- **Bootstrap relay**: Optional starting point for relay discovery. System automatically discovers additional relays from repository announcements. URL scheme is optional - if not provided, `wss://` is assumed (e.g., `git.shakespeare.diy` → `wss://git.shakespeare.diy`).
|
||||
- **Backoff settings**: Controls exponential backoff for failed connections (`base * 2^(failures-1)`, capped at max).
|
||||
- **Negentropy**: Can be disabled for testing REQ+EOSE fallback behavior.
|
||||
- **Batch window**: Self-subscriber batches events for this duration before triggering sync filters.
|
||||
|
||||
@@ -29,6 +29,7 @@ Configuration is loaded at startup and validated before the server starts.
|
||||
**Required:** No
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
# Localhost only (development)
|
||||
NGIT_BIND_ADDRESS=127.0.0.1:8080
|
||||
@@ -44,6 +45,7 @@ NGIT_BIND_ADDRESS=127.0.0.1:3000
|
||||
```
|
||||
|
||||
**Notes:**
|
||||
|
||||
- Use `127.0.0.1` for local development
|
||||
- Use `0.0.0.0` for production (behind reverse proxy)
|
||||
- Ensure firewall rules allow the port
|
||||
@@ -58,6 +60,7 @@ NGIT_BIND_ADDRESS=127.0.0.1:3000
|
||||
**Required:** Yes
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
NGIT_DOMAIN=gitnostr.com
|
||||
NGIT_DOMAIN=git.example.org
|
||||
@@ -65,12 +68,14 @@ NGIT_DOMAIN=localhost:8080 # Development only
|
||||
```
|
||||
|
||||
**Used for:**
|
||||
|
||||
- NIP-11 relay information document
|
||||
- Generating repository URLs
|
||||
- CORS configuration
|
||||
- Webhook URLs (future)
|
||||
|
||||
**Notes:**
|
||||
|
||||
- Must be accessible from the internet for production
|
||||
- Include port if non-standard (e.g., `localhost:8080`)
|
||||
- Used in repository clone URLs: `https://{NGIT_DOMAIN}/{npub}/{repo}.git`
|
||||
@@ -87,16 +92,19 @@ NGIT_DOMAIN=localhost:8080 # Development only
|
||||
**Required:** Yes
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
NGIT_OWNER_NPUB=npub1alice...
|
||||
```
|
||||
|
||||
**Used for:**
|
||||
|
||||
- NIP-11 relay information document
|
||||
- Contact information
|
||||
- Administrative operations (future)
|
||||
|
||||
**Notes:**
|
||||
|
||||
- Must be valid npub format (starts with `npub1`)
|
||||
- Can be generated with Nostr tools
|
||||
- Publicly visible in relay metadata
|
||||
@@ -111,12 +119,14 @@ NGIT_OWNER_NPUB=npub1alice...
|
||||
**Required:** No
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
NGIT_RELAY_NAME="GitNostr Community Relay"
|
||||
NGIT_RELAY_NAME="Alice's GRASP Server"
|
||||
```
|
||||
|
||||
**Used for:**
|
||||
|
||||
- NIP-11 relay information document
|
||||
- Client display
|
||||
- Relay discovery
|
||||
@@ -131,12 +141,14 @@ NGIT_RELAY_NAME="Alice's GRASP Server"
|
||||
**Required:** No
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
NGIT_RELAY_DESCRIPTION="Public GRASP relay for open source projects"
|
||||
NGIT_RELAY_DESCRIPTION="Private relay for ACME Corp repositories"
|
||||
```
|
||||
|
||||
**Used for:**
|
||||
|
||||
- NIP-11 relay information document
|
||||
- User information
|
||||
- Relay selection
|
||||
@@ -153,6 +165,7 @@ NGIT_RELAY_DESCRIPTION="Private relay for ACME Corp repositories"
|
||||
**Required:** No
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
# Relative path (development)
|
||||
NGIT_GIT_DATA_PATH=./data/git
|
||||
@@ -165,6 +178,7 @@ NGIT_GIT_DATA_PATH=/mnt/storage/git-repos
|
||||
```
|
||||
|
||||
**Storage structure:**
|
||||
|
||||
```
|
||||
{NGIT_GIT_DATA_PATH}/
|
||||
├── {npub1}/
|
||||
@@ -178,6 +192,7 @@ NGIT_GIT_DATA_PATH=/mnt/storage/git-repos
|
||||
```
|
||||
|
||||
**Notes:**
|
||||
|
||||
- Directory must be writable by ngit-grasp process
|
||||
- Ensure sufficient disk space
|
||||
- Consider backup strategy
|
||||
@@ -193,6 +208,7 @@ NGIT_GIT_DATA_PATH=/mnt/storage/git-repos
|
||||
**Required:** No
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
# Relative path (development)
|
||||
NGIT_RELAY_DATA_PATH=./data/relay
|
||||
@@ -205,6 +221,7 @@ NGIT_RELAY_DATA_PATH=/mnt/ssd/relay-data
|
||||
```
|
||||
|
||||
**Storage structure:**
|
||||
|
||||
```
|
||||
{NGIT_RELAY_DATA_PATH}/
|
||||
├── events/
|
||||
@@ -217,6 +234,7 @@ NGIT_RELAY_DATA_PATH=/mnt/ssd/relay-data
|
||||
```
|
||||
|
||||
**Notes:**
|
||||
|
||||
- Directory must be writable
|
||||
- Consider SSD for better query performance
|
||||
- Size grows with event count
|
||||
@@ -232,11 +250,13 @@ NGIT_RELAY_DATA_PATH=/mnt/ssd/relay-data
|
||||
**Required:** No
|
||||
|
||||
**Valid Values:**
|
||||
|
||||
- `memory` - In-memory database (default, fastest, no persistence)
|
||||
- `nostrdb` - NostrDB backend (persistent, optimized for Nostr) [Not yet implemented]
|
||||
- `lmdb` - LMDB backend (persistent, general purpose) [Not yet implemented]
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
# Development (default, no persistence)
|
||||
NGIT_DATABASE_BACKEND=memory
|
||||
@@ -250,13 +270,14 @@ NGIT_DATABASE_BACKEND=lmdb
|
||||
|
||||
**Comparison:**
|
||||
|
||||
| Backend | Persistence | Performance | Use Case |
|
||||
|---------|-------------|-------------|----------|
|
||||
| memory | No | Fastest | Development, testing |
|
||||
| nostrdb | Yes | High | Production (Nostr-optimized) |
|
||||
| lmdb | Yes | High | Production (general purpose) |
|
||||
| Backend | Persistence | Performance | Use Case |
|
||||
| ------- | ----------- | ----------- | ---------------------------- |
|
||||
| memory | No | Fastest | Development, testing |
|
||||
| nostrdb | Yes | High | Production (Nostr-optimized) |
|
||||
| lmdb | Yes | High | Production (general purpose) |
|
||||
|
||||
**Notes:**
|
||||
|
||||
- `memory` backend loses all data on restart
|
||||
- NostrDB and LMDB backends will use `NGIT_RELAY_DATA_PATH` for storage
|
||||
- NostrDB and LMDB are planned features, not yet available
|
||||
@@ -277,6 +298,7 @@ These options configure the proactive sync feature that synchronizes events from
|
||||
**Required:** No
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
# Sync from a public relay
|
||||
NGIT_SYNC_BOOTSTRAP_RELAY_URL=wss://relay.example.com
|
||||
@@ -289,11 +311,12 @@ NGIT_SYNC_BOOTSTRAP_RELAY_URL=ws://127.0.0.1:8081
|
||||
```
|
||||
|
||||
**Notes:**
|
||||
|
||||
- Bootstrap relay provides initial sync source on startup
|
||||
- Additional relays are **automatically discovered** from repository announcements that list our service
|
||||
- Even without a bootstrap relay, sync will discover relays from stored announcements
|
||||
- Synced events go through the same validation as directly-submitted events
|
||||
- Use WebSocket protocol (`ws://` or `wss://`)
|
||||
- Use WebSocket protocol (`ws://` or `wss://`) or defaults to wss://
|
||||
|
||||
---
|
||||
|
||||
@@ -305,6 +328,7 @@ NGIT_SYNC_BOOTSTRAP_RELAY_URL=ws://127.0.0.1:8081
|
||||
**Required:** No
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
# Default: 1 hour max backoff
|
||||
NGIT_SYNC_MAX_BACKOFF_SECS=3600
|
||||
@@ -317,6 +341,7 @@ NGIT_SYNC_MAX_BACKOFF_SECS=7200
|
||||
```
|
||||
|
||||
**Notes:**
|
||||
|
||||
- Backoff starts at 5 seconds and doubles on each failure
|
||||
- Capped at this maximum value
|
||||
- After 24 hours of failures, relay is marked "dead" and retried daily
|
||||
@@ -332,6 +357,7 @@ NGIT_SYNC_MAX_BACKOFF_SECS=7200
|
||||
**Required:** No
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
# Default: 30 second delay
|
||||
NGIT_SYNC_STARTUP_DELAY_SECS=30
|
||||
@@ -344,6 +370,7 @@ NGIT_SYNC_STARTUP_DELAY_SECS=60
|
||||
```
|
||||
|
||||
**Notes:**
|
||||
|
||||
- Allows connections to stabilize before catchup
|
||||
- Reduces load on remote relays at startup
|
||||
- Set to 0 for immediate catchup (not recommended)
|
||||
@@ -358,6 +385,7 @@ NGIT_SYNC_STARTUP_DELAY_SECS=60
|
||||
**Required:** No
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
# Default: 10 second delay
|
||||
NGIT_SYNC_RECONNECT_DELAY_SECS=10
|
||||
@@ -370,6 +398,7 @@ NGIT_SYNC_RECONNECT_DELAY_SECS=30
|
||||
```
|
||||
|
||||
**Notes:**
|
||||
|
||||
- Prevents rate limiting from remote relays
|
||||
- Applied after each successful reconnection
|
||||
- Only catches up on recent events (see lookback days)
|
||||
@@ -384,6 +413,7 @@ NGIT_SYNC_RECONNECT_DELAY_SECS=30
|
||||
**Required:** No
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
# Default: 3 days lookback
|
||||
NGIT_SYNC_RECONNECT_LOOKBACK_DAYS=3
|
||||
@@ -396,6 +426,7 @@ NGIT_SYNC_RECONNECT_LOOKBACK_DAYS=7
|
||||
```
|
||||
|
||||
**Notes:**
|
||||
|
||||
- Limits catchup queries to recent events only
|
||||
- Reduces load compared to full historical sync
|
||||
- Balance between completeness and performance
|
||||
@@ -413,6 +444,7 @@ NGIT_SYNC_RECONNECT_LOOKBACK_DAYS=7
|
||||
**Required:** No
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
# Simple levels
|
||||
RUST_LOG=error # Errors only
|
||||
@@ -429,6 +461,7 @@ RUST_LOG=debug,hyper=info,tokio=warn
|
||||
```
|
||||
|
||||
**Log levels (most to least verbose):**
|
||||
|
||||
1. `trace` - Very detailed, performance impact
|
||||
2. `debug` - Detailed debugging information
|
||||
3. `info` - General information (default)
|
||||
@@ -436,6 +469,7 @@ RUST_LOG=debug,hyper=info,tokio=warn
|
||||
5. `error` - Errors only
|
||||
|
||||
**Production recommendation:**
|
||||
|
||||
```bash
|
||||
RUST_LOG=info,ngit_grasp=debug
|
||||
```
|
||||
@@ -452,6 +486,7 @@ RUST_LOG=info,ngit_grasp=debug
|
||||
**Status:** 🔜 Planned
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
NGIT_AUTH_REQUIRED=true # Require auth
|
||||
NGIT_AUTH_REQUIRED=false # Public relay
|
||||
@@ -467,6 +502,7 @@ NGIT_AUTH_REQUIRED=false # Public relay
|
||||
**Status:** 🔜 Planned
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
NGIT_RATE_LIMIT_ENABLED=true
|
||||
NGIT_RATE_LIMIT_ENABLED=false
|
||||
@@ -491,6 +527,7 @@ RUST_LOG=debug
|
||||
```
|
||||
|
||||
**Notes:**
|
||||
|
||||
- Never commit `.env` to version control
|
||||
- Use `.env.example` as a template
|
||||
- Environment variables override `.env` values
|
||||
@@ -510,6 +547,7 @@ Error: Invalid configuration
|
||||
```
|
||||
|
||||
**Validation checks:**
|
||||
|
||||
- Required fields are present
|
||||
- Values have correct format
|
||||
- Paths are accessible and writable
|
||||
@@ -533,6 +571,7 @@ RUST_LOG=info,ngit_grasp=debug
|
||||
```
|
||||
|
||||
**Additional production considerations:**
|
||||
|
||||
- Use reverse proxy (nginx, Caddy) for HTTPS
|
||||
- Set up log rotation
|
||||
- Configure monitoring
|
||||
@@ -573,6 +612,7 @@ RUST_LOG=debug
|
||||
```
|
||||
|
||||
**Testing notes:**
|
||||
|
||||
- Use temporary directories
|
||||
- Use non-standard ports
|
||||
- Clean up after tests
|
||||
@@ -590,6 +630,7 @@ When multiple configuration sources exist:
|
||||
4. **Default values** (lowest priority)
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
# .env file
|
||||
NGIT_BIND_ADDRESS=127.0.0.1:8080
|
||||
@@ -610,4 +651,4 @@ NGIT_BIND_ADDRESS=0.0.0.0:3000 cargo run
|
||||
|
||||
---
|
||||
|
||||
*Part of the [ngit-grasp reference documentation](./)*
|
||||
_Part of the [ngit-grasp reference documentation](./)_
|
||||
|
||||
@@ -94,6 +94,8 @@ pub struct Config {
|
||||
|
||||
/// URL of bootstrap relay to sync from on startup (optional)
|
||||
/// Sync discovers additional relays from repository announcements that list our service
|
||||
/// If no scheme is provided (wss:// or ws://), wss:// is assumed
|
||||
/// Examples: "relay.example.com" -> "wss://relay.example.com", "wss://relay.example.com" -> unchanged
|
||||
#[arg(long, env = "NGIT_SYNC_BOOTSTRAP_RELAY_URL")]
|
||||
pub sync_bootstrap_relay_url: Option<String>,
|
||||
|
||||
|
||||
@@ -79,14 +79,38 @@ pub struct RelayConnection {
|
||||
}
|
||||
|
||||
impl RelayConnection {
|
||||
/// Normalize a relay URL to include a scheme (wss:// or ws://)
|
||||
///
|
||||
/// If the URL already has a scheme, it's returned as-is.
|
||||
/// If no scheme is provided, wss:// is assumed (secure by default).
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `url` - The relay URL (with or without scheme)
|
||||
///
|
||||
/// # Returns
|
||||
/// The normalized URL with scheme
|
||||
///
|
||||
/// # Examples
|
||||
/// - `"relay.example.com"` -> `"wss://relay.example.com"`
|
||||
/// - `"wss://relay.example.com"` -> `"wss://relay.example.com"`
|
||||
/// - `"ws://relay.example.com"` -> `"ws://relay.example.com"`
|
||||
fn normalize_url(url: &str) -> String {
|
||||
if url.starts_with("wss://") || url.starts_with("ws://") {
|
||||
url.to_string()
|
||||
} else {
|
||||
format!("wss://{}", url)
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new relay connection (not yet connected)
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `url` - The relay URL to connect to (e.g., "wss://relay.example.com")
|
||||
/// * `url` - The relay URL to connect to (with or without scheme, e.g., "relay.example.com" or "wss://relay.example.com")
|
||||
pub fn new(url: String) -> Self {
|
||||
let normalized_url = Self::normalize_url(&url);
|
||||
let client = Client::default();
|
||||
Self {
|
||||
url,
|
||||
url: normalized_url,
|
||||
client,
|
||||
database: None,
|
||||
nip77_warning_logged: std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false)),
|
||||
@@ -96,12 +120,13 @@ impl RelayConnection {
|
||||
/// Create a new relay connection with database for negentropy sync
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `url` - The relay URL to connect to (e.g., "wss://relay.example.com")
|
||||
/// * `url` - The relay URL to connect to (with or without scheme, e.g., "relay.example.com" or "wss://relay.example.com")
|
||||
/// * `database` - Shared database for local event comparison during negentropy sync
|
||||
pub fn new_with_database(url: String, database: SharedDatabase) -> Self {
|
||||
let normalized_url = Self::normalize_url(&url);
|
||||
let client = Client::default();
|
||||
Self {
|
||||
url,
|
||||
url: normalized_url,
|
||||
client,
|
||||
database: Some(database),
|
||||
nip77_warning_logged: std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false)),
|
||||
@@ -491,3 +516,71 @@ impl RelayConnection {
|
||||
self.database.is_some()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_normalize_url_with_wss_scheme() {
|
||||
let url = "wss://relay.example.com";
|
||||
assert_eq!(RelayConnection::normalize_url(url), "wss://relay.example.com");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_normalize_url_with_ws_scheme() {
|
||||
let url = "ws://relay.example.com";
|
||||
assert_eq!(RelayConnection::normalize_url(url), "ws://relay.example.com");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_normalize_url_without_scheme() {
|
||||
let url = "relay.example.com";
|
||||
assert_eq!(RelayConnection::normalize_url(url), "wss://relay.example.com");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_normalize_url_without_scheme_with_port() {
|
||||
let url = "relay.example.com:8080";
|
||||
assert_eq!(RelayConnection::normalize_url(url), "wss://relay.example.com:8080");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_normalize_url_with_path() {
|
||||
let url = "relay.example.com/nostr";
|
||||
assert_eq!(RelayConnection::normalize_url(url), "wss://relay.example.com/nostr");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_new_normalizes_url() {
|
||||
let conn = RelayConnection::new("relay.example.com".to_string());
|
||||
assert_eq!(conn.url(), "wss://relay.example.com");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_new_preserves_wss_scheme() {
|
||||
let conn = RelayConnection::new("wss://relay.example.com".to_string());
|
||||
assert_eq!(conn.url(), "wss://relay.example.com");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_new_preserves_ws_scheme() {
|
||||
let conn = RelayConnection::new("ws://relay.example.com".to_string());
|
||||
assert_eq!(conn.url(), "ws://relay.example.com");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_new_with_database_normalizes_url() {
|
||||
// This test just verifies the URL normalization works
|
||||
// We can't easily test with_database without a real database
|
||||
let conn = RelayConnection::new("git.shakespeare.diy".to_string());
|
||||
assert_eq!(conn.url(), "wss://git.shakespeare.diy");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_normalize_url_real_world_example() {
|
||||
// Test the exact case from the bug report
|
||||
let url = "git.shakespeare.diy";
|
||||
assert_eq!(RelayConnection::normalize_url(url), "wss://git.shakespeare.diy");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user