Add NRC (Nostr Relay Connect) protocol and web UI (v0.48.9)
Some checks failed
Go / build-and-release (push) Has been cancelled
Some checks failed
Go / build-and-release (push) Has been cancelled
- Implement NIP-NRC protocol for remote relay access through public relay tunnel - Add NRC bridge service with NIP-44 encrypted message tunneling - Add NRC client library for applications - Add session management with subscription tracking and expiry - Add URI parsing for nostr+relayconnect:// scheme with secret and CAT auth - Add NRC API endpoints for connection management (create/list/delete/get-uri) - Add RelayConnectView.svelte component for managing NRC connections in web UI - Add NRC database storage for connection secrets and labels - Add NRC CLI commands (generate, list, revoke) - Add support for Cashu Access Tokens (CAT) in NRC URIs - Add ScopeNRC constant for Cashu token scope - Add wasm build infrastructure and stub files Files modified: - app/config/config.go: NRC configuration options - app/handle-nrc.go: New API handlers for NRC connections - app/main.go: NRC bridge startup integration - app/server.go: Register NRC API routes - app/web/src/App.svelte: Add Relay Connect tab - app/web/src/RelayConnectView.svelte: New NRC management component - app/web/src/api.js: NRC API client functions - main.go: NRC CLI command handlers - pkg/bunker/acl_adapter.go: Add NRC scope mapping - pkg/cashu/token/token.go: Add ScopeNRC constant - pkg/database/nrc.go: NRC connection storage - pkg/protocol/nrc/: New NRC protocol implementation - docs/NIP-NRC.md: NIP specification document 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
84
app/main.go
84
app/main.go
@@ -6,6 +6,7 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -17,6 +18,7 @@ import (
|
||||
"git.mleku.dev/mleku/nostr/crypto/keys"
|
||||
"next.orly.dev/pkg/database"
|
||||
"git.mleku.dev/mleku/nostr/encoders/bech32encoding"
|
||||
"git.mleku.dev/mleku/nostr/encoders/hex"
|
||||
"next.orly.dev/pkg/neo4j"
|
||||
"next.orly.dev/pkg/policy"
|
||||
"next.orly.dev/pkg/protocol/graph"
|
||||
@@ -26,6 +28,7 @@ import (
|
||||
"next.orly.dev/pkg/cashu/issuer"
|
||||
"next.orly.dev/pkg/cashu/keyset"
|
||||
"next.orly.dev/pkg/cashu/verifier"
|
||||
"next.orly.dev/pkg/protocol/nrc"
|
||||
cashuiface "next.orly.dev/pkg/interfaces/cashu"
|
||||
"next.orly.dev/pkg/ratelimit"
|
||||
"next.orly.dev/pkg/spider"
|
||||
@@ -199,6 +202,81 @@ func Run(
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize NRC (Nostr Relay Connect) bridge if enabled
|
||||
nrcEnabled, nrcRendezvousURL, nrcAuthorizedKeys, nrcUseCashu, nrcSessionTimeout := cfg.GetNRCConfigValues()
|
||||
if nrcEnabled && nrcRendezvousURL != "" {
|
||||
// Get relay identity for signing NRC responses
|
||||
relaySecretKey, err := db.GetOrCreateRelayIdentitySecret()
|
||||
if err != nil {
|
||||
log.E.F("failed to get relay identity for NRC bridge: %v", err)
|
||||
} else {
|
||||
// Create signer from secret key
|
||||
relaySigner, sigErr := p8k.New()
|
||||
if sigErr != nil {
|
||||
log.E.F("failed to create signer for NRC bridge: %v", sigErr)
|
||||
} else if sigErr = relaySigner.InitSec(relaySecretKey); sigErr != nil {
|
||||
log.E.F("failed to init signer for NRC bridge: %v", sigErr)
|
||||
} else {
|
||||
// Parse authorized secrets (format: secret:name,secret:name,...)
|
||||
authorizedSecrets := make(map[string]string)
|
||||
for _, entry := range nrcAuthorizedKeys {
|
||||
parts := strings.SplitN(entry, ":", 2)
|
||||
if len(parts) >= 1 {
|
||||
secretHex := parts[0]
|
||||
name := ""
|
||||
if len(parts) == 2 {
|
||||
name = parts[1]
|
||||
}
|
||||
// Derive pubkey from secret
|
||||
secretBytes, decErr := hex.Dec(secretHex)
|
||||
if decErr != nil || len(secretBytes) != 32 {
|
||||
log.W.F("NRC: skipping invalid secret key: %s", secretHex[:8])
|
||||
continue
|
||||
}
|
||||
derivedSigner, signerErr := p8k.New()
|
||||
if signerErr != nil {
|
||||
log.W.F("NRC: failed to create signer: %v", signerErr)
|
||||
continue
|
||||
}
|
||||
if signerErr = derivedSigner.InitSec(secretBytes); signerErr != nil {
|
||||
log.W.F("NRC: failed to init signer: %v", signerErr)
|
||||
continue
|
||||
}
|
||||
derivedPubkeyHex := string(hex.Enc(derivedSigner.Pub()))
|
||||
authorizedSecrets[derivedPubkeyHex] = name
|
||||
}
|
||||
}
|
||||
|
||||
// Construct local relay URL
|
||||
localRelayURL := fmt.Sprintf("ws://localhost:%d", cfg.Port)
|
||||
|
||||
// Create bridge config
|
||||
bridgeConfig := &nrc.BridgeConfig{
|
||||
RendezvousURL: nrcRendezvousURL,
|
||||
LocalRelayURL: localRelayURL,
|
||||
Signer: relaySigner,
|
||||
AuthorizedSecrets: authorizedSecrets,
|
||||
SessionTimeout: nrcSessionTimeout,
|
||||
}
|
||||
|
||||
// Add Cashu verifier if enabled
|
||||
if nrcUseCashu && l.CashuVerifier != nil {
|
||||
bridgeConfig.CashuVerifier = l.CashuVerifier
|
||||
}
|
||||
|
||||
// Create and start the bridge
|
||||
l.nrcBridge = nrc.NewBridge(bridgeConfig)
|
||||
if err := l.nrcBridge.Start(); err != nil {
|
||||
log.E.F("failed to start NRC bridge: %v", err)
|
||||
l.nrcBridge = nil
|
||||
} else {
|
||||
log.I.F("NRC bridge started (rendezvous: %s, authorized: %d, cashu: %v)",
|
||||
nrcRendezvousURL, len(authorizedSecrets), nrcUseCashu && l.CashuVerifier != nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize spider manager based on mode (only for Badger backend)
|
||||
if badgerDB, ok := db.(*database.D); ok && cfg.SpiderMode != "none" {
|
||||
if l.spiderManager, err = spider.New(ctx, badgerDB, l.publishers, cfg.SpiderMode); chk.E(err) {
|
||||
@@ -720,6 +798,12 @@ func Run(
|
||||
log.I.F("bunker server stopped")
|
||||
}
|
||||
|
||||
// Stop NRC bridge if running
|
||||
if l.nrcBridge != nil {
|
||||
l.nrcBridge.Stop()
|
||||
log.I.F("NRC bridge stopped")
|
||||
}
|
||||
|
||||
// Stop WireGuard server if running
|
||||
if l.wireguardServer != nil {
|
||||
l.wireguardServer.Stop()
|
||||
|
||||
Reference in New Issue
Block a user