Skip to main content

liminal_server/config/types/
lys.rs

1use std::net::SocketAddr;
2
3/// `[auth.lys]`: the Lys issuer whose passes are admitted (ACCESS-001 R2).
4#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize)]
5#[serde(deny_unknown_fields)]
6pub struct LysConfig {
7    #[serde(default, skip_serializing_if = "Option::is_none")]
8    pub drafts: Option<crate::auth_lys::live::drafts::DraftConfig>,
9    /// The issuer URL exactly as the pass's `iss` names it.
10    pub issuer: String,
11    /// The Lys network liminal's subjects are placed under.
12    pub network_id: String,
13    /// Path to the issuer's published key set as JSON, read once at start.
14    pub keys_path: std::path::PathBuf,
15    /// Ask the issuer live for deliberate decisions (opening a link,
16    /// deploying a service, an administrator's act) with the holder's own
17    /// pass. Off, Lys cannot be asked and every deliberate route refuses by
18    /// that name; it never falls back to the pass.
19    #[serde(default)]
20    pub live: bool,
21    /// This liminal as a machine Lys knows, for links (ACCESS-002 R1).
22    #[serde(default)]
23    pub machine: Option<MachineConfig>,
24    /// Argus's functions as liminal services, bound to Lys and the server's
25    /// publish path (ACCESS-003 R1).
26    #[serde(default)]
27    pub services: Option<LysServicesConfig>,
28    /// Address-only broker and its observed-address UDP echo.
29    #[serde(default)]
30    pub broker: Option<BrokerConfig>,
31    /// Blind forwarding of marked relay streams.
32    #[serde(default)]
33    pub relay: Option<crate::services::relay::RelayConfig>,
34    /// Channel membership from Lys's one shared membership contract
35    /// (ACCESS-006 R1): the explicit product mapping of liminal channels to
36    /// placed Lys resources, and the provider route it is asked through.
37    /// Absent, no channel is membership-protected and every channel keeps
38    /// its prior contract.
39    #[serde(default)]
40    pub membership: Option<MembershipConfig>,
41    /// One bounded reader for this log; required beside membership protection.
42    #[serde(default)]
43    pub grant_stream: Option<super::GrantStreamConfig>,
44    /// Agent station bounds; required when any agent is placed.
45    #[serde(default)]
46    pub agents: Option<crate::agents::config::AgentsConfig>,
47    /// The templates services are created from (SRV-028 R1): the only
48    /// command lines this server will ever run for a seat.
49    #[serde(default)]
50    pub templates: crate::services::lifecycle::templates::TemplateSources,
51    /// Required operator bounds and machine-held builder delegation sources.
52    #[serde(default)]
53    pub construction: Option<crate::services::lifecycle::site::ConstructionConfig>,
54    /// The base of Lys's API routes (`.../api/`) for live asks, placements
55    /// and the service routes, when they are not served at the issuer
56    /// itself; absent, the issuer is the base, as before.
57    #[serde(default)]
58    pub api_url: Option<String>,
59    /// Path to this liminal's Lys app credential, read once at start; a new
60    /// service and its subjects are placed under the network with it
61    /// (SRV-028 R3). Absent, every create is refused by name.
62    #[serde(default)]
63    pub app_credential_path: Option<std::path::PathBuf>,
64}
65
66/// `[[auth.lys.templates]]`: one command a seat may create services from
67/// (SRV-028 R1). Read-only at run time.
68#[derive(Clone, PartialEq, Eq, serde::Deserialize)]
69#[serde(deny_unknown_fields)]
70pub struct ServiceTemplateConfig {
71    /// The template's name, one segment.
72    pub name: String,
73    /// The program, an absolute path to an executable file.
74    pub command: std::path::PathBuf,
75    /// Its arguments, passed as written.
76    #[serde(default)]
77    pub args: Vec<String>,
78    /// The subject roots a service of this template serves; each service
79    /// serves `<root>.<service name>`.
80    pub subjects: Vec<String>,
81    /// Names of variables passed through from the server's environment;
82    /// nothing else of it reaches the process.
83    #[serde(default)]
84    pub env: Vec<String>,
85    /// How long a stop waits after SIGTERM before SIGKILL; positive.
86    pub stop_timeout_ms: u64,
87}
88
89/// `[auth.lys.membership]`: channel membership bound to the verified holder
90/// and the placed resource (ACCESS-006 R1).
91///
92/// The mapping is explicit: a channel is membership-protected only when it is
93/// listed here with its workspace and placed resource, and a participant
94/// conversation is placed under a channel only when it is listed here. No
95/// mapping is guessed from a subject string, and a readable parent is never
96/// taken as membership of a child. Every bound is required and positive;
97/// none has a default. Validated whole when the membership gate is built at
98/// start ([`crate::auth_membership::MembershipGate::new`]).
99#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize)]
100#[serde(deny_unknown_fields)]
101pub struct MembershipConfig {
102    /// The membership contract version this liminal speaks; it must equal
103    /// `lys_pass::membership::CONTRACT_VERSION`.
104    pub contract: u32,
105    /// The identity of the Lys grant log decisions are asked from.
106    pub log_identity: String,
107    /// The reset epoch of that grant log.
108    pub log_epoch: u64,
109    /// The product schema's workspace kind, as Lys places it.
110    pub workspace_kind: String,
111    /// The product schema's channel kind, as Lys places it.
112    pub channel_kind: String,
113    /// Path to this liminal's app credential for the Lys membership routes,
114    /// read once at start. Its bytes never appear in a `Debug`.
115    pub credential_path: std::path::PathBuf,
116    /// Most rows one membership page carries; positive.
117    pub page_rows: u32,
118    /// Most encoded row bytes and HTTP answer bytes; positive. The same cap
119    /// bounds reading the app credential before opening any protected route.
120    pub page_bytes: u32,
121    /// Most decisions the local membership index retains at once; positive.
122    /// A decision past it is refused by name before anything is allocated
123    /// for it, never evicted to make room.
124    pub max_decisions: usize,
125    /// The workspace a Lys-admitted `Connect` must hold a current grant in
126    /// (scoped guest admission). Absent, `Connect` asks no admission and a
127    /// protected channel is still decided per act.
128    #[serde(default)]
129    pub connect_workspace: Option<String>,
130    /// The membership-protected channels.
131    pub channels: Vec<MembershipChannelConfig>,
132    /// Participant conversations placed under a protected channel.
133    #[serde(default)]
134    pub conversations: Vec<MembershipConversationConfig>,
135}
136
137/// One membership-protected channel: the liminal channel name, the
138/// workspace it is placed under and the placed resource id.
139#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize)]
140#[serde(deny_unknown_fields)]
141pub struct MembershipChannelConfig {
142    /// The liminal channel (subject) name, matched exactly.
143    pub channel: String,
144    /// The workspace resource id the channel is placed under.
145    pub workspace: String,
146    /// The placed channel resource id.
147    pub resource: String,
148}
149
150/// One participant conversation placed under a protected channel.
151#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize)]
152#[serde(deny_unknown_fields)]
153pub struct MembershipConversationConfig {
154    /// The participant conversation id.
155    pub conversation: u64,
156    /// The protected channel it is placed under, as listed in `channels`.
157    pub channel: String,
158}
159
160/// `[auth.lys.services]`: the production bindings of the ACCESS-003
161/// services. They ask Lys as this machine, so `[auth.lys.machine]` is
162/// required beside it.
163#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize)]
164#[serde(deny_unknown_fields)]
165pub struct LysServicesConfig {
166    /// The machine id Lys records, on whose runner `seats.<name>` starts.
167    pub machine: String,
168    /// The host name service messages are published as (`liminal.<host>`).
169    pub host: String,
170    /// Run the context watch over the `context-watch` channel, reporting
171    /// each reading to Lys.
172    #[serde(default)]
173    pub context_watch: bool,
174}
175
176/// `[auth.lys.broker]`: the address-only broker's UDP echo listener.
177#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize)]
178#[serde(deny_unknown_fields)]
179pub struct BrokerConfig {
180    /// The socket on which a device asks for its observed address.
181    pub udp_listen: SocketAddr,
182}
183
184/// `[auth.lys.machine]`: this liminal's agent identity and pass.
185///
186/// Its links are keyed from the Ed25519 identity Lys recorded for it,
187/// its own end of every crossing is decided from its own pass, and the
188/// holders that may open links to it are named with their public keys.
189/// The link identity is an agent's own key and pass. The machine section
190/// name is retained so existing file-pass configurations still read.
191#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize)]
192#[serde(deny_unknown_fields)]
193pub struct MachineConfig {
194    /// Carrier order and message-scoped direct deadlines.
195    #[serde(default)]
196    pub carrier_choice: crate::link_open::ChoiceConfig,
197    /// The device this machine is a district of, when configured.
198    #[serde(default)]
199    pub parent: Option<String>,
200    /// Path to the agent's Ed25519 seed, 64 hexadecimal digits, read once
201    /// at start. The link handshake keys from its X25519 derivation.
202    pub identity_path: std::path::PathBuf,
203    /// Path to the agent's own Lys pass, verified at start; absent when
204    /// the signed agent dial is configured.
205    #[serde(default)]
206    pub pass_path: std::path::PathBuf,
207    /// The agent whose capability certificate names this identity's key.
208    /// With it set, the pass is asked of Lys at start and again at any link
209    /// open while none is held or the held one is expired or due for
210    /// renewal; a start whose ask Lys refuses is logged once and asks again
211    /// at the next open, never by a restart (ACCESS-009).
212    #[serde(default)]
213    pub agent_id: String,
214    /// The app the agent asks the issuer for a pass to.
215    #[serde(default)]
216    pub audience: String,
217    /// The public key recorded for each holder that may open a link,
218    /// holder id to Ed25519 public key as 64 hexadecimal digits.
219    #[serde(default)]
220    pub machines: std::collections::BTreeMap<String, String>,
221    /// Outbound links keyed from this machine, each configured by name.
222    #[serde(default)]
223    pub links: Vec<OutboundLinkConfig>,
224}
225
226/// One exporting link; an absent wire address leaves carrier choice to the caller.
227#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize)]
228#[serde(deny_unknown_fields)]
229pub struct OutboundLinkConfig {
230    /// The link id placed under the configured network.
231    pub link_id: String,
232    /// The holder whose Ed25519 key is recorded in this machine's directory mirror.
233    pub peer_holder: String,
234    /// The peer's wire listener as host:port, if this link uses the wire.
235    #[serde(default)]
236    pub peer_address: Option<String>,
237    /// Placed subject prefixes exported over this link.
238    pub exports: Vec<String>,
239}