mirror of
https://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit-grasp.git
synced 2026-10-05 15:08:24 +00:00
The SelfSubscriber feeds the sync manager's repository index from the service's own accepted events. It dialled our own public WebSocket endpoint with an unauthenticated client, which a private instance's NIP-42 gate correctly refused: the gate runs in the HTTP layer, ahead of LocalRelay, and cannot distinguish our own process from any other anonymous dialler. Announcements accepted at runtime therefore never reached the sync index until a restart rebuilt it from the database, stalling proactive sync and the dynamic membership derived from accepted relay owners — worst on exactly the private-to-private mirroring GRASP-08 exists to enable. Attach the subscriber to the embedded relay in-process instead. A custom WebSocketTransport hands LocalRelay one end of an in-memory duplex pair and keeps the other, so the client gets an ordinary relay session with the same framing, subscription handling, and post-save broadcast, minus the listener, the auth gate, and the network round trip. This replaces the loopback dial in both modes: the public-mode feed is identical in content and strictly more reliable, and it removes a self-directed reconnect loop. Chosen over attaching the relay owner key as a NIP-42 authenticator plus adding the owner pubkey to the effective member set. That alternative works, but widens the member set and the authenticated surface to solve a problem that is not authentication: there is no remote party here. The in-process route needs no key, no membership entry, and no configuration, and nothing reaches the subscriber that the relay did not already accept and persist. Correctness assumptions: LocalRelay applies no NIP-42 or query policy of its own — private-mode access control lives entirely in the HTTP layer — so an in-process session is exactly a local session, not a bypassed remote one. Both ends speak raw WebSocket framing over the duplex with no HTTP upgrade, matching take_connection's Role::Server. The session consumes one connection permit, as the loopback dial did. The GRASP-08 regression test no longer restarts the relay over persistent LMDB: it publishes an announcement at runtime and asserts sync connections to both referenced relays, which is only possible if the live feed reached the index. Verified to fail against the previous implementation (60s deadline, no connections) and pass with this one. req_concurrency's source relay now applies the production outbound target policy. Its scenario lists a proxy URL in the announcement, and with a reliable live feed the source discovers that URL — a distinct host:port that happens to front itself — as an event-directed sync target and opens its own REQ traffic through the proxy, contending for a budget the test means to measure for the syncing relay alone. That behavior is pre-existing and was already reachable after a restart; only its timing changed. The policy keeps the source scenery without weakening the assertion. Deliberately excluded: neg_concurrency shares that topology but passes unchanged, so its fixture is left alone; the duplicate NIP-11 fetch between the pre-dial preflight probe and the post-connect hint fetch is untouched. Validation: cargo clippy --all-targets -D warnings; cargo test --lib (793 passed); cargo test --test private_mode --test sync --test outbound_policy --test purgatory_sync (255 passed, including the full sync suite under parallel load). req_concurrency's startup-burst test passed 4/4 isolated runs after the fixture change.
166 lines
4.2 KiB
TOML
166 lines
4.2 KiB
TOML
[package]
|
|
name = "ngit-grasp"
|
|
version = "2.1.2"
|
|
edition = "2021"
|
|
authors = ["ngit-grasp contributors"]
|
|
license = "MIT"
|
|
description = "A GRASP (Git Relays Authorized via Signed-Nostr Proofs) implementation in Rust"
|
|
repository = "https://gitworkshop.dev/danconwaydev.com/ngit-grasp"
|
|
|
|
[dependencies]
|
|
# Async runtime
|
|
tokio = { version = "1.35", features = ["full"] }
|
|
|
|
# HTTP server (hyper for relay integration)
|
|
hyper = { version = "1.4", features = ["full"] }
|
|
hyper-util = { version = "0.1", features = ["tokio", "server", "http1", "http2"] }
|
|
http-body-util = "0.1"
|
|
|
|
# Nostr
|
|
#
|
|
# The stable 0.45 series contains the upstream NEG-OPEN handling fix used by
|
|
# the embedded relay. Caret requirements accept compatible patch releases.
|
|
nostr = "0.45.0"
|
|
# `local-relay` carries the embedded relay implementation, previously the
|
|
# separate `nostr-relay-builder` crate.
|
|
nostr-sdk = { version = "0.45.0", features = ["local-relay"] }
|
|
nostr-lmdb = "0.45.0"
|
|
nostr-memory = "0.45.0"
|
|
|
|
# Utilities
|
|
# SHA-1 for the `Sec-WebSocket-Accept` handshake. `nostr` uses this same crate
|
|
# internally but stopped re-exporting it as `nostr::hashes` in 0.45,
|
|
# so depend on it directly rather than pulling in a second hash implementation.
|
|
bitcoin_hashes = "0.14"
|
|
futures-util = "0.3"
|
|
tokio-tungstenite = { version = "0.28", default-features = false }
|
|
# Message type in `nostr-sdk`'s `WebSocketTransport` signatures, which the
|
|
# in-process self-subscription transport implements. `nostr-sdk` does not
|
|
# re-export it. No features requested: the build keeps whatever `nostr-sdk`
|
|
# already selects.
|
|
async-wsocket = { version = "0.17", default-features = false }
|
|
base64 = "0.22"
|
|
flate2 = "1.0"
|
|
tar = "0.4"
|
|
fs2 = "0.4"
|
|
ipnet = { version = "2", features = ["serde"] }
|
|
libc = "0.2"
|
|
|
|
# Metrics
|
|
prometheus = { version = "0.14", features = ["process"] }
|
|
dashmap = "6"
|
|
lazy_static = "1.4"
|
|
|
|
# Data structures
|
|
indexmap = "2"
|
|
|
|
# Random (for startup jitter)
|
|
rand = "0.10"
|
|
|
|
# Serialization
|
|
serde = { version = "1.0", features = ["derive"] }
|
|
serde_json = "1.0"
|
|
|
|
# Logging
|
|
tracing = "0.1"
|
|
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
|
|
|
# Configuration
|
|
dotenvy = "0.15"
|
|
clap = { version = "4.5", features = ["derive", "env"] }
|
|
|
|
# Error handling
|
|
anyhow = "1.0"
|
|
|
|
# Async traits
|
|
async-trait = "0.1"
|
|
|
|
# Temporary directories (used for GRASP-06 empty-repo synthesis)
|
|
tempfile = "3"
|
|
reqwest = { version = "0.13", default-features = false, features = ["native-tls"] }
|
|
|
|
# Git (for future use)
|
|
# git-http-backend = "0.3"
|
|
|
|
[dev-dependencies]
|
|
# Testing
|
|
grasp-audit = { path = "grasp-audit", version = "0.2.0" }
|
|
tempfile = "3"
|
|
tokio = { version = "1.35", features = ["full", "test-util"] }
|
|
tokio-tungstenite = "0.28.0"
|
|
|
|
[lib]
|
|
name = "ngit_grasp"
|
|
path = "src/lib.rs"
|
|
|
|
[[bin]]
|
|
name = "ngit-grasp"
|
|
path = "src/main.rs"
|
|
|
|
[[test]]
|
|
name = "nip09_announcement_cascade"
|
|
path = "tests/lifecycle/nip09_announcement_cascade.rs"
|
|
|
|
[[test]]
|
|
name = "nip09_blacklist_ops"
|
|
path = "tests/lifecycle/nip09_blacklist_ops.rs"
|
|
|
|
[[test]]
|
|
name = "nip09_cascade_event_types"
|
|
path = "tests/lifecycle/nip09_cascade_event_types.rs"
|
|
|
|
[[test]]
|
|
name = "nip09_disrespector"
|
|
path = "tests/lifecycle/nip09_disrespector.rs"
|
|
|
|
[[test]]
|
|
name = "nip09_git_archive_cleanup"
|
|
path = "tests/lifecycle/nip09_git_archive_cleanup.rs"
|
|
|
|
[[test]]
|
|
name = "nip09_holding_cleanup"
|
|
path = "tests/lifecycle/nip09_holding_cleanup.rs"
|
|
|
|
[[test]]
|
|
name = "nip09_holding_db"
|
|
path = "tests/lifecycle/nip09_holding_db.rs"
|
|
|
|
[[test]]
|
|
name = "nip09_multi_maintainer"
|
|
path = "tests/lifecycle/nip09_multi_maintainer.rs"
|
|
|
|
[[test]]
|
|
name = "nip09_recovery"
|
|
path = "tests/lifecycle/nip09_recovery.rs"
|
|
|
|
[[test]]
|
|
name = "nip09_state_cascade"
|
|
path = "tests/lifecycle/nip09_state_cascade.rs"
|
|
|
|
[[test]]
|
|
name = "nip09_state_multi_maintainer"
|
|
path = "tests/lifecycle/nip09_state_multi_maintainer.rs"
|
|
|
|
[[test]]
|
|
name = "nip09_validation"
|
|
path = "tests/lifecycle/nip09_validation.rs"
|
|
|
|
[[test]]
|
|
name = "deletion_request_retention"
|
|
path = "tests/lifecycle/deletion_request_retention.rs"
|
|
|
|
[[test]]
|
|
name = "nip62_lifecycle"
|
|
path = "tests/lifecycle/nip62_lifecycle.rs"
|
|
|
|
[[test]]
|
|
name = "nip62_validation"
|
|
path = "tests/lifecycle/nip62_validation.rs"
|
|
|
|
[[test]]
|
|
name = "replaceable_history"
|
|
path = "tests/lifecycle/replaceable_history.rs"
|
|
|
|
[workspace]
|
|
members = [".", "grasp-audit"]
|