Skip to main content

liminal_server/services/site/
outright.rs

1//! A construction act Lys answers outright is carried out at once. Lys answers outright only when
2//! a person granted the act outright, so no person decided this act: its receipt names the grant as
3//! its authority and Lys as the decider, never a person who did not see it. The grant's responsible
4//! person is recorded as answerable for the site, which is what lets them call its trial. (Lys never
5//! lets a grant's responsible person decide a draft, so a person working alone could otherwise
6//! never build anything.) On an open exchange the declared holder's own act is applied through the
7//! same path (ACCESS-015).
8
9use super::super::Lifecycle;
10use super::{SiteAct, SiteError};
11use crate::auth_lys::live::drafts::ApprovalTransition;
12
13impl Lifecycle {
14    /// Applies `act` under the outright `grant`, keyed by the caller's `operation`.
15    ///
16    /// # Errors
17    /// The act's own refusal (an existing site, a service not building), or a store failure.
18    pub fn apply_outright(
19        &self,
20        act: &SiteAct,
21        grant: &str,
22        operation: &str,
23        words: &str,
24        at_ms: u64,
25    ) -> Result<serde_json::Value, SiteError> {
26        self.apply_now(
27            act,
28            &ApprovalTransition {
29                draft: format!("grant:{grant}"),
30                digest: lys_pass::drafts::request_digest(words),
31                approved_by: vec!["lys".to_owned()],
32                executed_at_ms: at_ms,
33            },
34            at_ms,
35        )?;
36        Ok(serde_json::json!({
37            "state": "executed",
38            "grant": grant,
39            "approved_by": "lys",
40            "responsible": act.responsible,
41            "operation": operation,
42        }))
43    }
44
45    /// Applies `act` as its declared holder's own (ACCESS-015 R2): on an open
46    /// exchange the holder who asks is the one who approves, and the record
47    /// says so.
48    ///
49    /// # Errors
50    /// The `request_bytes` bound, the act's own refusal, or a store failure.
51    pub fn apply_declared(
52        &self,
53        act: &SiteAct,
54        operation: &str,
55        at_ms: u64,
56    ) -> Result<serde_json::Value, SiteError> {
57        let words = serde_json::to_string(act).map_err(|_| SiteError::Store)?;
58        if words.len() > self.site_bindings()?.limits.request_bytes.get() {
59            return Err(SiteError::Bound("request_bytes"));
60        }
61        self.apply_now(
62            act,
63            &ApprovalTransition {
64                draft: format!("{}:{operation}", act.holder),
65                digest: lys_pass::drafts::request_digest(&words),
66                approved_by: vec![act.holder.clone()],
67                executed_at_ms: at_ms,
68            },
69            at_ms,
70        )?;
71        Ok(serde_json::json!({
72            "state": "executed",
73            "grant": crate::agents::open_session::OPEN_GRANT,
74            "approved_by": act.holder,
75            "responsible": act.responsible,
76            "operation": operation,
77        }))
78    }
79
80    /// The one apply both answers share: the act on a candidate snapshot,
81    /// one commit, then the retained values refreshed.
82    fn apply_now(
83        &self,
84        act: &SiteAct,
85        transition: &ApprovalTransition,
86        at_ms: u64,
87    ) -> Result<(), SiteError> {
88        let mut registry = self.lock()?;
89        let mut snapshot = Self::snapshot(&registry);
90        self.apply_site_act(&mut snapshot, act, transition, at_ms, &registry.trials)?;
91        super::storage::commit(self, &mut registry, &snapshot)?;
92        drop(registry);
93        self.refresh_sites()
94    }
95}
96
97#[cfg(test)]
98#[path = "outright_tests.rs"]
99mod tests;