a simple rust terminal ui (tui) for setting up alternative plc rotation keys driven by: secure enclave hardware (not synced) or software-based keys (synced to icloud)
plc secure-enclave touchid icloud atproto
at main 96 lines 2.8 kB view raw
1use anyhow::{Context, Result}; 2use crate::plc::PlcState; 3 4const DEFAULT_PLC_DIRECTORY: &str = "https://plc.directory"; 5 6pub struct PlcDirectoryClient { 7 client: reqwest::Client, 8 base_url: String, 9} 10 11impl PlcDirectoryClient { 12 pub fn new() -> Self { 13 Self { 14 client: reqwest::Client::new(), 15 base_url: DEFAULT_PLC_DIRECTORY.to_string(), 16 } 17 } 18 19 /// Fetch the current PLC state for a DID. 20 pub async fn get_state(&self, did: &str) -> Result<PlcState> { 21 let url = format!("{}/{}/data", self.base_url, did); 22 let resp = self 23 .client 24 .get(&url) 25 .send() 26 .await 27 .context("Failed to fetch PLC state")?; 28 29 if !resp.status().is_success() { 30 let status = resp.status(); 31 let body = resp.text().await.unwrap_or_default(); 32 anyhow::bail!("PLC directory returned {}: {}", status, body); 33 } 34 35 let mut state: PlcState = resp 36 .json() 37 .await 38 .context("Failed to parse PLC state")?; 39 40 // The /data endpoint doesn't include the DID in the response body, 41 // so set it from the request 42 if state.did.is_empty() { 43 state.did = did.to_string(); 44 } 45 46 Ok(state) 47 } 48 49 /// Fetch the audit log for a DID. 50 pub async fn get_audit_log(&self, did: &str) -> Result<Vec<serde_json::Value>> { 51 let url = format!("{}/{}/log/audit", self.base_url, did); 52 let resp = self 53 .client 54 .get(&url) 55 .send() 56 .await 57 .context("Failed to fetch audit log")?; 58 59 if !resp.status().is_success() { 60 let status = resp.status(); 61 let body = resp.text().await.unwrap_or_default(); 62 anyhow::bail!("PLC directory returned {}: {}", status, body); 63 } 64 65 let log: Vec<serde_json::Value> = resp 66 .json() 67 .await 68 .context("Failed to parse audit log")?; 69 70 Ok(log) 71 } 72 73 /// Submit a signed PLC operation. 74 pub async fn submit_operation( 75 &self, 76 did: &str, 77 operation: &serde_json::Value, 78 ) -> Result<String> { 79 let url = format!("{}/{}", self.base_url, did); 80 let resp = self 81 .client 82 .post(&url) 83 .json(operation) 84 .send() 85 .await 86 .context("Failed to submit PLC operation")?; 87 88 if !resp.status().is_success() { 89 let status = resp.status(); 90 let body = resp.text().await.unwrap_or_default(); 91 anyhow::bail!("PLC directory returned {}: {}", status, body); 92 } 93 94 Ok("Operation submitted successfully".to_string()) 95 } 96}