ralph/config/trust.rs
1//! Repo-local execution trust loading.
2//!
3//! Responsibilities:
4//! - Define the local trust file contract for execution-sensitive project settings.
5//! - Load `.ralph/trust.jsonc` / `.ralph/trust.json` files with JSONC support.
6//! - Provide helpers for source-aware trust checks during config resolution.
7//!
8//! Not handled here:
9//! - Main config layering or schema generation (see `crate::contracts::config`).
10//! - CI command validation or execution (see `crate::config::validation` and `crate::runutil`).
11//!
12//! Invariants/assumptions:
13//! - Trust is local-only and must not be committed to version control.
14//! - Missing trust files mean the repo is untrusted.
15
16use anyhow::{Context, Result};
17use chrono::{DateTime, Utc};
18use serde::{Deserialize, Serialize};
19use std::fs;
20use std::path::{Path, PathBuf};
21
22use super::resolution::prefer_jsonc_then_json;
23
24/// Local trust file for execution-sensitive project configuration.
25#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
26#[serde(default, deny_unknown_fields)]
27pub struct RepoTrust {
28 /// Allow repo-local executable configuration such as `agent.ci_gate`.
29 pub allow_project_commands: bool,
30
31 /// Timestamp for the explicit trust decision.
32 pub trusted_at: Option<DateTime<Utc>>,
33}
34
35impl RepoTrust {
36 pub fn is_trusted(&self) -> bool {
37 self.allow_project_commands
38 }
39}
40
41/// Preferred local trust path for a repository root.
42pub fn project_trust_path(repo_root: &Path) -> PathBuf {
43 prefer_jsonc_then_json(repo_root.join(".ralph").join("trust.jsonc"))
44}
45
46/// Load repo trust if present, otherwise return the default untrusted state.
47pub fn load_repo_trust(repo_root: &Path) -> Result<RepoTrust> {
48 let path = project_trust_path(repo_root);
49 if !path.exists() {
50 return Ok(RepoTrust::default());
51 }
52
53 let raw = fs::read_to_string(&path).with_context(|| format!("read {}", path.display()))?;
54 crate::jsonc::parse_jsonc::<RepoTrust>(&raw, &format!("trust {}", path.display()))
55}