feat(relay): messaging-plane config + provider seam + pure admission policy engine

First slice of the optional Nostr relay (messaging plane), behind a swappable
seam mirroring the PaymentProvider pattern so nostr-rs-relay can be replaced by
another relay (strfry/external) or another messaging protocol later.

- config: MessagingConfig { relay: RelayConfig } — RELAY_ENABLED (off by default),
  mode (bundled|external), advertised ws(s):// URL, write_policy
  (inbox-outbox default | author-allowlist | open), inbound_pow_bits (NIP-13),
  admission_bind, resource caps. validate() fail-closes (URL required + ws(s)://
  + wss in prod, known policy, PoW <= 40, admission bind for non-open). +7 tests.
- infra/relay: RelayProvider trait + from_config (feature-gated, fail-closed) +
  the default nostr-rs-relay/external adapter; AppState.relay wired
  (lib/main/tests).
- infra/relay/policy: PURE admission engine (WritePolicy, decide, NIP-13
  leading_zero_bits): open permits all; author-allowlist gates on a registered
  npub; inbox-outbox permits a registered author's outbox OR a kind-1059 gift-wrap
  addressed to a registered user (inbox), the external-inbound branch optionally
  behind a PoW gate. Registration lookups are resolved by the caller and passed as
  booleans so the decision is pure + fully unit-tested (10 tests).

The gRPC event-admission service that calls this engine, capabilities/NIP-05
advertising, packaging, and the client DM stack land in the following slices.
This commit is contained in:
jwinterm
2026-07-02 06:29:38 -04:00
parent 1181b5938a
commit a8f1a9e50c
7 changed files with 507 additions and 0 deletions
+3
View File
@@ -72,6 +72,8 @@ pub async fn try_app() -> Option<TestApp> {
let chains = ChainClients::from_config(&config).expect("build chain clients");
let payment =
smirk_backend_core::infra::payment::from_config(&config).expect("build payment provider");
let relay =
smirk_backend_core::infra::relay::from_config(&config).expect("build relay provider");
let prices = Arc::new(tokio::sync::RwLock::new(
smirk_backend_core::infra::prices::PriceSnapshot::empty(&config.features.prices_currency),
));
@@ -85,6 +87,7 @@ pub async fn try_app() -> Option<TestApp> {
sessions,
chains,
payment,
relay,
web_challenges: Arc::default(),
prices,
admin_sessions,