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";
14
15#[derive(Debug, Clone)]
18pub struct ApiSecret {
19 pub secret: SecretKey,
21 pub remote: EndpointAddr,
23}
24
25impl Display for ApiSecret {
26 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27 write!(f, "{}", self.encode_string())
28 }
29}
30
31#[derive(Serialize, Deserialize)]
32struct Variant0EndpointAddr {
33 endpoint_id: EndpointId,
34 addrs: BTreeSet<TransportAddr>,
35}
36
37#[derive(Serialize, Deserialize)]
39enum TicketWireFormat {
40 Variant0(Variant0ServicesTicket),
41}
42
43#[derive(Serialize, Deserialize)]
44struct Variant0ServicesTicket {
45 secret: SecretKey,
46 addr: Variant0EndpointAddr,
47}
48
49impl Ticket for ApiSecret {
50 const KIND: &'static str = "services";
53
54 fn encode_bytes(&self) -> Vec<u8> {
55 let data = TicketWireFormat::Variant0(Variant0ServicesTicket {
56 secret: self.secret.clone(),
57 addr: Variant0EndpointAddr {
58 endpoint_id: self.remote.id,
59 addrs: self.remote.addrs.clone(),
60 },
61 });
62 postcard::to_stdvec(&data).expect("postcard serialization failed")
63 }
64
65 fn decode_bytes(bytes: &[u8]) -> Result<Self, ParseError> {
66 let res: TicketWireFormat = postcard::from_bytes(bytes)?;
67 let TicketWireFormat::Variant0(Variant0ServicesTicket { secret, addr }) = res;
68 Ok(Self {
69 secret,
70 remote: EndpointAddr {
71 id: addr.endpoint_id,
72 addrs: addr.addrs.clone(),
73 },
74 })
75 }
76}
77
78impl FromStr for ApiSecret {
79 type Err = ParseError;
80
81 fn from_str(s: &str) -> Result<Self, Self::Err> {
82 Self::decode_string(s)
83 }
84}
85
86impl ApiSecret {
87 pub fn new(secret: SecretKey, remote: impl Into<EndpointAddr>) -> Self {
89 Self {
90 secret,
91 remote: remote.into(),
92 }
93 }
94
95 pub fn from_env_var(env_var: &str) -> anyhow::Result<Self> {
97 match std::env::var(env_var) {
98 Ok(ticket_string) => Self::from_str(&ticket_string)
99 .context(format!("invalid api secret at env var {env_var}")),
100 Err(VarError::NotPresent) => Err(anyhow!("{env_var} environment variable is not set")),
101 Err(VarError::NotUnicode(e)) => Err(anyhow!(
102 "{env_var} environment variable is not valid unicode: {:?}",
103 e
104 )),
105 }
106 }
107
108 pub fn addr(&self) -> &EndpointAddr {
110 &self.remote
111 }
112}