iroh_services/
api_secret.rs1use std::{
2 collections::BTreeSet,
3 env::VarError,
4 fmt::{self, Display},
5 str::FromStr,
6};
7
8use anyhow::{Context, anyhow};
9use iroh::{EndpointAddr, EndpointId, SecretKey, TransportAddr};
10use iroh_tickets::{ParseError, Ticket};
11use serde::{Deserialize, Serialize};
12
13pub const API_SECRET_ENV_VAR_NAME: &str = "IROH_SERVICES_API_SECRET";
15
16#[derive(Debug, Clone)]
19pub struct ApiSecret {
20 pub secret: SecretKey,
22 pub remote: EndpointAddr,
24}
25
26impl Display for ApiSecret {
27 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28 write!(f, "{}", self.encode_string())
29 }
30}
31
32#[derive(Serialize, Deserialize)]
33struct Variant0EndpointAddr {
34 endpoint_id: EndpointId,
35 addrs: BTreeSet<TransportAddr>,
36}
37
38#[derive(Serialize, Deserialize)]
40enum TicketWireFormat {
41 Variant0(Variant0ServicesTicket),
42}
43
44#[derive(Serialize, Deserialize)]
45struct Variant0ServicesTicket {
46 secret: SecretKey,
47 addr: Variant0EndpointAddr,
48}
49
50impl Ticket for ApiSecret {
51 const KIND: &'static str = "services";
54
55 fn encode_bytes(&self) -> Vec<u8> {
56 let data = TicketWireFormat::Variant0(Variant0ServicesTicket {
57 secret: self.secret.clone(),
58 addr: Variant0EndpointAddr {
59 endpoint_id: self.remote.id,
60 addrs: self.remote.addrs.clone(),
61 },
62 });
63 postcard::to_stdvec(&data).expect("postcard serialization failed")
64 }
65
66 fn decode_bytes(bytes: &[u8]) -> Result<Self, ParseError> {
67 let res: TicketWireFormat = postcard::from_bytes(bytes)?;
68 let TicketWireFormat::Variant0(Variant0ServicesTicket { secret, addr }) = res;
69 Ok(Self {
70 secret,
71 remote: EndpointAddr {
72 id: addr.endpoint_id,
73 addrs: addr.addrs.clone(),
74 },
75 })
76 }
77}
78
79impl FromStr for ApiSecret {
80 type Err = ParseError;
81
82 fn from_str(s: &str) -> Result<Self, Self::Err> {
83 Self::decode_string(s)
84 }
85}
86
87impl ApiSecret {
88 pub fn new(secret: SecretKey, remote: impl Into<EndpointAddr>) -> Self {
90 Self {
91 secret,
92 remote: remote.into(),
93 }
94 }
95
96 pub fn from_env_var(env_var: &str) -> anyhow::Result<Self> {
98 match std::env::var(env_var) {
99 Ok(ticket_string) if ticket_string.is_empty() => {
100 Err(anyhow!("{env_var} environment variable is set but empty"))
101 }
102 Ok(ticket_string) => Self::from_str(&ticket_string)
103 .context(format!("invalid api secret at env var {env_var}")),
104 Err(VarError::NotPresent) => Err(anyhow!("{env_var} environment variable is not set")),
105 Err(VarError::NotUnicode(e)) => Err(anyhow!(
106 "{env_var} environment variable is not valid unicode: {:?}",
107 e
108 )),
109 }
110 }
111
112 pub fn addr(&self) -> &EndpointAddr {
114 &self.remote
115 }
116}