radrootsd

JSON-RPC bridge for Radroots event publishing
git clone https://radroots.dev/git/radrootsd.git
Log | Files | Refs | README | LICENSE

commit 7c4d6c4a518b242e299b27fd5e8550dae25a4cd3
parent ec5d8d1d80f1d1137930d1435f98f4c6c342e121
Author: triesap <triesap@radroots.dev>
Date:   Tue,  6 Jan 2026 18:09:05 +0000

nip46: add jsonrpc sign_event

- add nip46.sign_event rpc method
- sign events via nip46 client request flow
- validate session perms and signer pubkey
- register sign_event endpoint

Diffstat:
Msrc/transport/jsonrpc/methods/nip46/mod.rs | 2++
Asrc/transport/jsonrpc/methods/nip46/sign_event.rs | 49+++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 51 insertions(+), 0 deletions(-)

diff --git a/src/transport/jsonrpc/methods/nip46/mod.rs b/src/transport/jsonrpc/methods/nip46/mod.rs @@ -8,6 +8,7 @@ use crate::transport::jsonrpc::{MethodRegistry, RpcContext}; pub mod connect; pub mod get_public_key; pub mod ping; +pub mod sign_event; pub mod session_close; pub mod session_status; pub mod status; @@ -18,6 +19,7 @@ pub fn module(ctx: RpcContext, registry: MethodRegistry) -> Result<RpcModule<Rpc connect::register(&mut m, &registry)?; ping::register(&mut m, &registry)?; get_public_key::register(&mut m, &registry)?; + sign_event::register(&mut m, &registry)?; session_status::register(&mut m, &registry)?; session_close::register(&mut m, &registry)?; Ok(m) diff --git a/src/transport/jsonrpc/methods/nip46/sign_event.rs b/src/transport/jsonrpc/methods/nip46/sign_event.rs @@ -0,0 +1,49 @@ +use anyhow::Result; +use jsonrpsee::server::RpcModule; +use serde::{Deserialize, Serialize}; + +use crate::core::nip46::session::Nip46Session; +use crate::transport::jsonrpc::nip46::client; +use crate::transport::jsonrpc::{MethodRegistry, RpcContext, RpcError}; +use nostr::UnsignedEvent; + +#[derive(Debug, Deserialize)] +struct Nip46SignEventParams { + session_id: String, + event: UnsignedEvent, +} + +#[derive(Clone, Debug, Serialize)] +struct Nip46SignEventResponse { + event: nostr::Event, +} + +pub fn register(m: &mut RpcModule<RpcContext>, registry: &MethodRegistry) -> Result<()> { + registry.track("nip46.sign_event"); + m.register_async_method("nip46.sign_event", |params, ctx, _| async move { + let Nip46SignEventParams { session_id, event } = params + .parse() + .map_err(|e| RpcError::InvalidParams(e.to_string()))?; + let session = ctx + .state + .nip46_sessions + .get(&session_id) + .await + .ok_or_else(|| RpcError::InvalidParams("unknown session".to_string()))?; + if !has_permission(&session, "sign_event") { + return Err(RpcError::Other("unauthorized sign_event".to_string())); + } + if event.pubkey != session.remote_signer_pubkey { + return Err(RpcError::InvalidParams( + "event pubkey does not match remote signer".to_string(), + )); + } + let event = client::sign_event(&session, event, "sign_event").await?; + Ok::<Nip46SignEventResponse, RpcError>(Nip46SignEventResponse { event }) + })?; + Ok(()) +} + +fn has_permission(session: &Nip46Session, perm: &str) -> bool { + session.perms.iter().any(|entry| entry == perm) +}