commit 4d65e37568b074d8de5ae3af14e3449c68198baa
parent b9f53411f42a6e1eb50f29d376bd9298c9ef97c0
Author: triesap <triesap@radroots.dev>
Date: Wed, 7 Jan 2026 15:44:25 +0000
nip46: add session list endpoint
- add session store list helper that filters expired entries
- expose nip46.session.list JSON-RPC method
- return session metadata with remaining TTL
- add session store test for list filtering
Diffstat:
3 files changed, 85 insertions(+), 0 deletions(-)
diff --git a/src/core/nip46/session.rs b/src/core/nip46/session.rs
@@ -81,6 +81,14 @@ impl Nip46SessionStore {
None => false,
}
}
+
+ pub async fn list(&self) -> Vec<Nip46Session> {
+ let mut sessions = self.inner.lock().await;
+ sessions.retain(|_, session| !session.is_expired());
+ let mut listed: Vec<Nip46Session> = sessions.values().cloned().collect();
+ listed.sort_by(|left, right| left.id.cmp(&right.id));
+ listed
+ }
}
impl Nip46Session {
@@ -159,4 +167,24 @@ mod tests {
let found = store.get("active").await;
assert!(found.is_some());
}
+
+ #[tokio::test]
+ async fn session_store_list_filters_expired() {
+ let store = Nip46SessionStore::new();
+ store
+ .insert(build_session(
+ "expired",
+ Some(Instant::now() - Duration::from_secs(1)),
+ ))
+ .await;
+ store
+ .insert(build_session(
+ "active",
+ Some(Instant::now() + Duration::from_secs(10)),
+ ))
+ .await;
+ let listed = store.list().await;
+ assert_eq!(listed.len(), 1);
+ assert_eq!(listed[0].id, "active");
+ }
}
diff --git a/src/transport/jsonrpc/methods/nip46/mod.rs b/src/transport/jsonrpc/methods/nip46/mod.rs
@@ -12,6 +12,7 @@ pub mod nip44;
pub mod ping;
pub mod sign_event;
pub mod session_close;
+pub mod session_list;
pub mod session_status;
pub mod status;
@@ -26,5 +27,6 @@ pub fn module(ctx: RpcContext, registry: MethodRegistry) -> Result<RpcModule<Rpc
sign_event::register(&mut m, ®istry)?;
session_status::register(&mut m, ®istry)?;
session_close::register(&mut m, ®istry)?;
+ session_list::register(&mut m, ®istry)?;
Ok(m)
}
diff --git a/src/transport/jsonrpc/methods/nip46/session_list.rs b/src/transport/jsonrpc/methods/nip46/session_list.rs
@@ -0,0 +1,55 @@
+use std::time::{Instant};
+
+use anyhow::Result;
+use jsonrpsee::server::RpcModule;
+use serde::Serialize;
+
+use crate::transport::jsonrpc::{MethodRegistry, RpcContext};
+
+#[derive(Clone, Serialize)]
+struct Nip46SessionListEntry {
+ session_id: String,
+ client_pubkey: String,
+ remote_signer_pubkey: String,
+ user_pubkey: Option<String>,
+ relays: Vec<String>,
+ perms: Vec<String>,
+ name: Option<String>,
+ url: Option<String>,
+ image: Option<String>,
+ expires_in_secs: Option<u64>,
+}
+
+pub fn register(m: &mut RpcModule<RpcContext>, registry: &MethodRegistry) -> Result<()> {
+ registry.track("nip46.session.list");
+ m.register_async_method("nip46.session.list", |_params, ctx, _| async move {
+ let sessions = ctx.state.nip46_sessions.list().await;
+ let entries = sessions
+ .into_iter()
+ .map(|session| Nip46SessionListEntry {
+ session_id: session.id,
+ client_pubkey: session.client_pubkey.to_hex(),
+ remote_signer_pubkey: session.remote_signer_pubkey.to_hex(),
+ user_pubkey: session.user_pubkey.map(|pubkey| pubkey.to_hex()),
+ relays: session.relays,
+ perms: session.perms,
+ name: session.name,
+ url: session.url,
+ image: session.image,
+ expires_in_secs: session
+ .expires_at
+ .map(|expires_at| remaining_secs(expires_at)),
+ })
+ .collect::<Vec<_>>();
+ Ok::<Vec<Nip46SessionListEntry>, crate::transport::jsonrpc::RpcError>(entries)
+ })?;
+ Ok(())
+}
+
+fn remaining_secs(expires_at: Instant) -> u64 {
+ if expires_at <= Instant::now() {
+ 0
+ } else {
+ expires_at.saturating_duration_since(Instant::now()).as_secs()
+ }
+}