Skip to main content

liminal_server/services/templates/
records.rs

1//! Exact template data remains flat on disk with a closed decoding boundary.
2
3use serde::{Deserialize, Serialize};
4use std::path::PathBuf;
5
6/// Immutable exact proposal; subjects come from the site, never tool arguments.
7#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
8#[serde(deny_unknown_fields)]
9pub struct TemplateProposal {
10    /// One template name, normally the site's name.
11    pub name: String,
12    /// Site this proposal was prepared against.
13    pub service: String,
14    /// Absolute canonical path approved by the person.
15    pub command: PathBuf,
16    /// Exact argument vector.
17    pub args: Vec<String>,
18    /// Site's exact subjects.
19    pub subjects: Vec<String>,
20    /// Only environment names, never values.
21    pub env: Vec<String>,
22    /// Positive termination deadline in milliseconds.
23    pub stop_timeout_ms: u64,
24    /// SHA-256 of command bytes at preparation, rechecked on approval.
25    pub digest: String,
26}
27
28/// A live or retired record with all approvals kept.
29#[derive(Clone, PartialEq, Eq, Serialize)]
30pub struct TemplateRecord {
31    /// Exact approved program and invocation.
32    #[serde(flatten)]
33    pub proposal: TemplateProposal,
34    /// Actual people whose authenticated approvals authorized this record.
35    pub approved_by: Vec<String>,
36    /// Execution time of the approval in epoch milliseconds.
37    pub approved_at_ms: u64,
38    /// Retirement prevents every subsequent deployment and restart.
39    pub retired: bool,
40    /// All execution transitions with draft ids and word digests.
41    pub transitions: Vec<super::super::site::Approval>,
42}
43
44impl<'de> Deserialize<'de> for TemplateRecord {
45    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
46        #[derive(Deserialize)]
47        #[serde(deny_unknown_fields)]
48        struct Fields {
49            name: String,
50            service: String,
51            command: PathBuf,
52            args: Vec<String>,
53            subjects: Vec<String>,
54            env: Vec<String>,
55            stop_timeout_ms: u64,
56            digest: String,
57            approved_by: Vec<String>,
58            approved_at_ms: u64,
59            retired: bool,
60            transitions: Vec<super::super::site::Approval>,
61        }
62        let value = Fields::deserialize(deserializer)?;
63        Ok(Self {
64            proposal: TemplateProposal {
65                name: value.name,
66                service: value.service,
67                command: value.command,
68                args: value.args,
69                subjects: value.subjects,
70                env: value.env,
71                stop_timeout_ms: value.stop_timeout_ms,
72                digest: value.digest,
73            },
74            approved_by: value.approved_by,
75            approved_at_ms: value.approved_at_ms,
76            retired: value.retired,
77            transitions: value.transitions,
78        })
79    }
80}