//! Node identity endpoint — exposes this node's Ed25519 public key. //! //! `GET /identity` returns the node's ID and public key as JSON. No //! authentication is required; only the public half of the keypair is //! disclosed. use poem::handler; use poem::web::Json; use serde::Serialize; /// JSON response body for `GET /identity`. #[derive(Serialize)] pub struct IdentityResponse { /// Node ID: lowercase hex-encoding of the 32-byte Ed25519 public key. pub node_id: String, /// Lowercase hex-encoding of the 32-byte Ed25519 public key. pub pubkey: String, } /// `GET /identity` — return this node's Ed25519 public key. /// /// Returns `{"node_id": "<64-hex>", "pubkey": "<64-hex>"}`. /// No authentication required; the private key is never exposed. #[handler] pub fn identity_handler() -> Json { match crate::node_identity::get_identity() { Some(id) => Json(IdentityResponse { node_id: id.node_id.clone(), pubkey: id.pubkey_hex.clone(), }), None => Json(IdentityResponse { node_id: "uninitialized".to_string(), pubkey: "uninitialized".to_string(), }), } } #[cfg(test)] mod tests { use super::*; use poem::{Route, get, test::TestClient}; #[tokio::test] async fn identity_endpoint_returns_json() { // Initialise a temporary key file so get_identity() returns Some. let tmp = tempfile::tempdir().unwrap(); let key_path = tmp.path().join("node_identity.key"); crate::node_identity::init_identity(&key_path).unwrap(); let app = Route::new().at("/identity", get(identity_handler)); let cli = TestClient::new(app); let resp = cli.get("/identity").send().await; resp.assert_status_is_ok(); let body: serde_json::Value = resp.json().await.value().deserialize(); let node_id = body["node_id"].as_str().unwrap(); let pubkey = body["pubkey"].as_str().unwrap(); assert_eq!(node_id.len(), 64); assert!(node_id.chars().all(|c| c.is_ascii_hexdigit())); assert_eq!(node_id, pubkey); } }