fix: sync-bootstrap-relay-url scheme optional

This commit is contained in:
DanConwayDev
2026-01-08 12:14:27 +00:00
parent 64eaf44070
commit 0356740d11
4 changed files with 148 additions and 12 deletions
+1 -1
View File
@@ -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.
+48 -7
View File
@@ -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](./)_
+2
View File
@@ -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>,
+97 -4
View File
@@ -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");
}
}