pubhubs/api/discovery.rs
1//! Endpoints for the 'discovery' process by which the pubhubs servers
2//! (pubhubs central, authentication server, and transcryptor)
3//! inform one another of updates in their configuration (URLs, public
4//! key material, etc.) and of updates to their binaries.
5//!
6//! The idea is to minimize the manual coordination required between the
7//! administrators of the different servers. The transcryptor only
8//! has to configure the URL of PHC, not, say, the public keys used by PHC,
9//! which the transcryptor receives instead automatically via discovery.
10//!
11//! ## How it works
12//!
13//! PubHubs central takes the lead, and pulls information from the other
14//! servers via the **[`DiscoveryInfo`] endpoint** (`GET .ph/discovery/info`),
15//! building a **[`Constellation`]** that it makes available via its
16//! own info endpoint.
17//!
18//! The other two servers wait for PHC to publish a constellation,
19//! and check whether the details they advertised in their info
20//! endpoints have been incorporated. If so, they adopt
21//! the constellation. If not, they cue PHC to re-run its discovery
22//! via the **[`DiscoveryRun`] endpoint** (`POST .ph/discovery/run`),
23//! and go back to waiting for PHC to publish a constellation.
24//!
25//! After PHC has assembled its constellation, it checks whether the other
26//! two servers have the same constellation installed. If so, its discovery
27//! finishes (until restarted by a call to the discovery run endpoint).
28//! If not, and if another server, say, the transcryptor,
29//! has an outdated constellation installed, it invokes the discovery run
30//! endpoint of the transcryptor, poking it to re-run its discovery process
31//! (i.e. pulling and checking the constellation from PHC). If the transcryptor
32//! has no constellation installed, PHC leaves it be (assuming its
33//! discovery process is running), and will retry a bit later.
34//!
35//! ## A bit more detail
36//!
37//! The discovery process running inside each server can result in four outcomes:
38//! - [`DiscoverVerdict::Alright`]: My constellation is up-to-date, **discovery done**
39//! - [`DiscoverVerdict::ConstellationOutdated`]: Discovery yielded an updated constellation.
40//! This will cause the server to tear down and **restart** its actix HTTP server with the new constellation.
41//! - [`DiscoverVerdict::BinaryOutdated`]: Discovery revealed that one
42//! of the other servers is running a newer version of the `pubhubs` binary. This will cause
43//! the current server to **exit** in the expectation that the system running this server will
44//! pull and spool up the new pubhubs binary.
45//! - [`ErrorCode::PleaseRetry`]: We're waiting for another server. If this discovery process was
46//! invoked during the start-up of this server, the discovery process will simply be called
47//! again after some pause. If this discovery was invoked by [`DiscoveryRun`], then the
48//! [`ErrorCode::PleaseRetry`] will simply be passed along to the requester.
49//!
50//! # Examples
51//! ## 1. All three servers start blank
52//!
53//! ```text
54//! Transcryptor PHC Auth Server
55//! | | |
56//! |---- info ------------->| |
57//! |<--- no const ----------| |
58//! [ retry later ] | |
59//! | | |
60//! |<--- info --------------| |
61//! |--- info, no const ---->| |
62//! | |---- info ------------->|
63//! | |<--- info, no const ----|
64//! | [ build const, restart ] |
65//! | | |
66//! | |---- info ------------->|
67//! | |<--- info, no const ----|
68//! | [ check A later ] |
69//! | | |
70//! |---- info ------------->| |
71//! |<----- const -----------| |
72//! [ check & adopt const ] | |
73//! [ restart ] | |
74//! |---- info ------------->| |
75//! |<----- const -----------| |
76//! [ matches installed ] | |
77//! [ discovery done ] | |
78//! | |<--- info --------------|
79//! | |------ const ---------->|
80//! | | [ check & adopt const ]
81//! | | [ restart ]
82//! | |<--- info --------------|
83//! | |------ const ---------->|
84//! | | [ matches installed ]
85//! | | [ discovery done ]
86//! |<--- info --------------| |
87//! |--- info, const ------->| |
88//! | |---- info ------------->|
89//! | |<--- info, const -------|
90//! | [ const unchanged, and ] |
91//! | [ installed by A and T ] |
92//! | [ discovery done ] |
93//! | | |
94//! ```
95//!
96//! ## 2. PHC is upgraded to a new version
97//!
98//! All servers are on constellation `C_old` when PHC is restarted and upgraded
99//! from version `V_old` to `V_new`.
100//!
101//! ```text
102//! Transcryptor PHC Auth Server
103//! | | |
104//! | [ upgraded to V_new ] |
105//! | | |
106//! |<--- info --------------|---- info ------------->|
107//! |--- info, C_old ------->|<--- info, C_old -------|
108//! | [ build C_new, restart ] |
109//! | | |
110//! |<--- info --------------|---- info ------------->|
111//! |--- info, C_old ------->|<--- info, C_old -------|
112//! | [ C_new unchanged, but ] |
113//! | [ T, A on C_old: trigger their discovery ] |
114//! | | |
115//! |<--- run ---------------|------------- run ----->|
116//! | [ check A and T later ] |
117//! | | |
118//! |--- info -------------->|<------------ info -----|
119//! |<- C_new (inc. V_new) --|------------- C_new --->|
120//! [ V_old < V_new: exit ] | [ V_old < V_new: exit ]
121//! [ upgraded to V_new ] | [ upgraded to V_new ]
122//! | | |
123//! |--- info -------------->|<------------ info -----|
124//! |<-- C_new --------------|------------- C_new --->|
125//! [ check & adopt C_new ] | [ check & adopt C_new ]
126//! [ restart ] | [ restart ]
127//! | | |
128//! |--- info -------------->|<------------ info -----|
129//! |<--- C_new -------------|------------- C_new --->|
130//! [ matches installed ] | [ matches installed ]
131//! [ discovery done ] | [ discovery done ]
132//! | | |
133//! |<--- info --------------|---- info ------------->|
134//! |--- info, C_new ------->|<--- info, C_new -------|
135//! | [ C_new unchanged, and ] |
136//! | [ installed by A and T ] |
137//! | [ discovery done ] |
138//!
139//! ```
140//!
141//! ## 3. Transcryptor rotates a key
142//!
143//! All servers are on constellation `C_old`, when the transcryptor changes one
144//! of its keys, and restarts.
145//!
146//! ```text
147//! Transcryptor PHC Auth Server
148//! | | |
149//! [ changes key and restarts ] | |
150//! | | |
151//! |--- info -------------->| |
152//! |<-- C_old --------------| |
153//! [ C_old has wrong key ] | |
154//! [ trigger PHC discovery ] | |
155//! | | |
156//! |---- run -------------->| |
157//! [ check PHC later ] | |
158//! | | |
159//! |<--- info --------------|---- info ------------->|
160//! |--- info, C_old ------->|<--- info, C_old -------|
161//! | [ build C_new, restart ] |
162//! | | |
163//! |<--- info --------------|---- info ------------->|
164//! |--- info, C_old ------->|<--- info, C_old -------|
165//! | [ C_new unchanged, but ] |
166//! | [ T, A on C_old: trigger their discovery ] |
167//! | | |
168//! |<--- run ---------------|------------- run ----->|
169//! | [ check A and T later ] |
170//! | | |
171//! |--- info -------------->|<------------ info -----|
172//! |<- C_new ---------------|------------- C_new --->|
173//! [ check & adopt C_new ] | [ check & adopt C_new ]
174//! [ restart ] | [ restart ]
175//! | | |
176//! |--- info -------------->|<------------ info -----|
177//! |<--- C_new -------------|------------- C_new --->|
178//! [ matches installed ] | [ matches installed ]
179//! [ discovery done ] | [ discovery done ]
180//! | | |
181//! |<--- info --------------|---- info ------------->|
182//! |--- info, C_new ------->|<--- info, C_new -------|
183//! | [ C_new unchanged, and ] |
184//! | [ installed by A and T ] |
185//! | [ discovery done ] |
186//! ```
187//!
188//! [`Constellation`]: crate::servers::constellation::Constellation
189//! [`Config::phc_url`]: crate::servers::config::Config::phc_url
190//! [`ServerConfig::self_check_code`]: crate::servers::config::ServerConfig::self_check_code
191//! [`DiscoveryInfo`]: super::DiscoveryInfo
192//! [`DiscoveryInfoResp`]: super::DiscoveryInfoResp
193//! [`constellation_or_id`]: super::DiscoveryInfoResp::constellation_or_id
194//! [`DiscoveryRun`]: super::DiscoveryRun
195//! [`UpToDate`]: super::DiscoveryRunResp::UpToDate
196//! [`Restarting`]: super::DiscoveryRunResp::Restarting
197//! [`PleaseRetry`]: crate::api::ErrorCode::PleaseRetry
198//! [`DiscoverVerdict::Alright`]: crate::servers::DiscoverVerdict::Alright
199//! [`DiscoverVerdict::ConstellationOutdated`]: crate::servers::DiscoverVerdict::ConstellationOutdated
200//! [`DiscoverVerdict::BinaryOutdated`]: crate::servers::DiscoverVerdict::BinaryOutdated
201
202use serde::{Deserialize, Serialize};
203
204use crate::api::*;
205use crate::common::elgamal;
206use actix_web::http;
207
208/// Public details about this server, including its current [`Constellation`].
209/// Used by `PHC` to build and publish its [`Constellation`].
210///
211/// [`Constellation`]: crate::servers::constellation::Constellation
212pub struct DiscoveryInfo {}
213impl EndpointDetails for DiscoveryInfo {
214 type RequestType = NoPayload;
215 type ResponseType = Result<DiscoveryInfoResp>;
216
217 const METHOD: http::Method = http::Method::GET;
218 const PATH: &'static str = ".ph/discovery/info";
219}
220
221/// Has the server run its discovery procedure, if it isn't already.
222pub struct DiscoveryRun {}
223impl EndpointDetails for DiscoveryRun {
224 type RequestType = NoPayload;
225 type ResponseType = Result<DiscoveryRunResp>;
226
227 const METHOD: http::Method = http::Method::POST;
228 const PATH: &'static str = ".ph/discovery/run";
229}
230
231/// What's returned by the [`DiscoveryInfo`].
232///
233/// <div class="warning">
234///
235/// **Warning:** when modifying [`DiscoveryInfoResp`] make sure that
236///
237/// 1. PHC will not crash on the outdated disovery info responses returned by
238/// the transcryptor and authentication server before they are updated;
239///
240/// 2. Old authentication server and transcryptor code will not crash on the updated discovery
241/// info response returned by PHC.
242///
243/// </div>
244#[derive(Serialize, Deserialize, Debug, Clone)]
245#[serde(deny_unknown_fields)]
246#[must_use]
247pub struct DiscoveryInfoResp {
248 pub name: crate::servers::Name,
249
250 /// Random string used by a server to check that it has contact with itself.
251 pub self_check_code: String,
252
253 /// The version of this server (based on git tags).
254 ///
255 /// `None` if not available for some reason
256 pub version: Option<String>,
257
258 /// URL of the PubHubs Central server this server tries to connect to.
259 pub phc_url: url::Url,
260
261 /// Used to sign JWTs from this server.
262 pub jwt_key: VerifyingKey,
263
264 /// Used to encrypt messages to this server, and to create shared secrets with this server
265 /// using Diffie-Hellman
266 pub enc_key: elgamal::PublicKey,
267
268 /// Master encryption key part, that is, `x_PHC B` or `x_T B` in the notation of the
269 /// whitepaper. Only set for PHC or the transcryptor.
270 pub master_enc_key_part: Option<elgamal::PublicKey>,
271
272 /// Details of the other PubHubs servers, according to this server
273 /// `None` when discovery has not been completed.
274 #[serde(rename = "constellation")]
275 pub constellation_or_id: Option<crate::servers::constellation::ConstellationOrId>,
276}
277
278/// What's returned by the [`DiscoveryRun`].
279#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
280#[serde(deny_unknown_fields)]
281#[must_use]
282pub enum DiscoveryRunResp {
283 /// Everything checks out at our side
284 UpToDate,
285 /// Changes were made and we're restarting now. It'd probably be good to check our discovery
286 /// info again in a moment.
287 Restarting,
288}