Skip to main content

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}