liminal_server/services/site/
records.rs1use serde::{Deserialize, Serialize};
4use std::collections::BTreeMap;
5use std::num::NonZeroUsize;
6
7#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
9#[serde(deny_unknown_fields)]
10pub struct SiteLimits {
11 pub sites: NonZeroUsize,
13 pub subjects: NonZeroUsize,
15 pub schema_bytes: NonZeroUsize,
17 pub text_bytes: NonZeroUsize,
19 pub steps: NonZeroUsize,
21 pub transitions: NonZeroUsize,
23 pub drafts: NonZeroUsize,
25 pub approvers: NonZeroUsize,
27 pub snapshot_bytes: NonZeroUsize,
29 pub request_bytes: NonZeroUsize,
31}
32
33#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
35#[serde(deny_unknown_fields)]
36pub struct ConstructionConfig {
37 pub limits: SiteLimits,
39 pub grants: super::effects::GrantSources,
41}
42
43#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
45#[serde(deny_unknown_fields)]
46pub struct Contract {
47 #[serde(default)]
49 pub input_schema_json: String,
50 #[serde(default)]
52 pub output_schema_json: String,
53}
54
55#[derive(Clone, Serialize, Deserialize)]
57#[serde(deny_unknown_fields)]
58pub struct SiteSpec {
59 pub name: String,
61 pub purpose: String,
63 pub subjects: Vec<String>,
65 #[serde(default)]
67 pub contracts: BTreeMap<String, Contract>,
68}
69
70#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
72#[serde(deny_unknown_fields)]
73pub struct BuilderGrants {
74 pub plan: String,
76 pub serve: String,
78}
79
80#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
82#[serde(deny_unknown_fields)]
83pub struct Approval {
84 pub draft: String,
86 pub digest: String,
88 pub approved_by: Vec<String>,
90 pub executed_at_ms: u64,
92 #[serde(default, skip_serializing_if = "Option::is_none")]
94 pub receipt: Option<lys_pass::drafts::Execution>,
95}
96impl From<crate::auth_lys::live::drafts::ApprovalTransition> for Approval {
97 fn from(value: crate::auth_lys::live::drafts::ApprovalTransition) -> Self {
98 Self {
99 draft: value.draft,
100 digest: value.digest,
101 approved_by: value.approved_by,
102 executed_at_ms: value.executed_at_ms,
103 receipt: None,
104 }
105 }
106}
107
108#[derive(Clone, Serialize, Deserialize)]
110#[serde(
111 tag = "act",
112 content = "detail",
113 rename_all = "snake_case",
114 deny_unknown_fields
115)]
116pub enum SiteChange {
117 CreateSite(SiteSpec),
119 BindBuilder { agent: String },
121 UnbindBuilder,
123 CreateTemplate(super::super::templates::TemplateProposal),
125 UpdateTemplate(super::super::templates::TemplateProposal),
127 RetireTemplate,
129 Complete,
131}
132
133impl SiteChange {
134 #[must_use]
136 pub const fn action(&self) -> &'static str {
137 match self {
138 Self::CreateSite(_) => "create_site",
139 Self::BindBuilder { .. } => "bind_builder",
140 Self::UnbindBuilder => "unbind_builder",
141 Self::CreateTemplate(_) => "create",
142 Self::UpdateTemplate(_) => "update",
143 Self::RetireTemplate => "retire",
144 Self::Complete => "complete",
145 }
146 }
147 #[must_use]
149 pub const fn kind(&self) -> &'static str {
150 match self {
151 Self::CreateSite(_) => "liminal.network",
152 Self::CreateTemplate(_) | Self::UpdateTemplate(_) | Self::RetireTemplate => {
153 "liminal.template"
154 }
155 _ => "liminal.service",
156 }
157 }
158}
159
160#[derive(Clone, Serialize, Deserialize)]
162#[serde(deny_unknown_fields)]
163pub struct SiteAct {
164 pub target: String,
166 pub holder: String,
168 pub responsible: String,
170 pub change: SiteChange,
172}
173
174impl SiteAct {
175 pub fn prepare(
180 target: String,
181 change: SiteChange,
182 pass: &lys_pass::VerifiedPass,
183 ) -> Result<Self, super::SiteError> {
184 let holder = &pass.claims().holder;
185 let responsible = match holder.kind.as_str() {
186 "person" => holder.id.clone(),
187 "agent" => holder
188 .responsible
189 .clone()
190 .ok_or(super::SiteError::Invalid("responsible person"))?,
191 _ => return Err(super::SiteError::Invalid("construction holder kind")),
192 };
193 if responsible.is_empty() {
194 return Err(super::SiteError::Invalid("responsible person"));
195 }
196 Ok(Self {
197 target,
198 holder: holder.id.clone(),
199 responsible,
200 change,
201 })
202 }
203}
204
205impl SiteAct {
206 #[must_use]
208 pub fn permission_id<'a>(&'a self, network: &'a str) -> &'a str {
209 if matches!(self.change, SiteChange::CreateSite(_)) {
210 network
211 } else {
212 &self.target
213 }
214 }
215}