liminal_server/config/types/open.rs
1//! `[open]`: how far an exchange with no `[auth]` reaches (ACCESS-011 R1).
2//!
3//! ACCESS-010 made an exchange with no `[auth]` open mode and bound it to
4//! loopback. This table is the operator's written choice to let that exchange
5//! listen on the network as well, for building fast on more than one machine;
6//! it changes nothing about what the exchange asks of anyone, which is still
7//! nothing. The table is refused beside `[auth]`, so it can never read as a
8//! way to loosen a gated exchange.
9
10/// The `[open]` table.
11#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Deserialize)]
12#[serde(deny_unknown_fields)]
13pub struct OpenConfig {
14 /// Where the open exchange may listen: loopback (the default without the
15 /// table) or the network.
16 #[serde(default)]
17 pub reach: Reach,
18}
19
20/// How far an open exchange or an open seat reaches.
21#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, serde::Deserialize)]
22#[serde(rename_all = "snake_case")]
23pub enum Reach {
24 /// This machine only: every listener on a loopback address.
25 #[default]
26 Loopback,
27 /// Any address the operator writes; every connection is still admitted.
28 Network,
29}
30
31impl Reach {
32 /// The word the configuration uses.
33 #[must_use]
34 pub const fn word(self) -> &'static str {
35 match self {
36 Self::Loopback => "loopback",
37 Self::Network => "network",
38 }
39 }
40}
41
42impl std::fmt::Display for Reach {
43 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44 formatter.write_str(self.word())
45 }
46}
47
48impl super::ServerConfig {
49 /// How far this exchange reaches: the `[open]` table's word, loopback
50 /// without one. Meaningful in open mode only; a gated exchange listens
51 /// where `[auth]` lets it and this answers loopback for it.
52 #[must_use]
53 pub fn open_reach(&self) -> Reach {
54 self.open.map_or(Reach::Loopback, |open| open.reach)
55 }
56}