codetether_agent/rlm/oracle/storage/manager/mod.rs
1//! Core storage manager definition and constructors.
2//!
3//! [`OracleTraceStorage`] owns the local spool directory and an
4//! optional remote backend. Construction is via
5//! [`from_env_or_vault`](OracleTraceStorage::from_env_or_vault).
6//!
7//! # Examples
8//!
9//! ```ignore
10//! let storage = OracleTraceStorage::from_env_or_vault().await;
11//! ```
12
13mod internals;
14mod persist;
15
16use super::helpers::default_spool_dir;
17use super::remote::{MinioOracleRemote, OracleRemote};
18use crate::bus::s3_sink::BusS3SinkConfig;
19use std::path::PathBuf;
20use std::sync::Arc;
21
22pub(in super::super) const DEFAULT_MAX_SPOOL_BYTES: u64 = 500 * 1024 * 1024;
23
24/// Coordinates local-spool-first persistence with optional
25/// upload to a MinIO/S3 remote backend.
26///
27/// Every trace is atomically written to the spool directory.
28/// If a remote is configured, the record is uploaded and the
29/// local copy removed on success.
30///
31/// # Examples
32///
33/// ```ignore
34/// let storage = OracleTraceStorage::from_env_or_vault().await;
35/// let result = storage.persist_result(&oracle_result).await?;
36/// ```
37pub struct OracleTraceStorage {
38 pub(in super::super) spool_dir: PathBuf,
39 pub(in super::super) max_spool_bytes: u64,
40 pub(in super::super) remote: Option<Arc<dyn OracleRemote>>,
41}
42
43impl OracleTraceStorage {
44 /// Build from environment variables and Vault-backed MinIO
45 /// credentials, falling back to local-only mode on failure.
46 ///
47 /// # Examples
48 ///
49 /// ```ignore
50 /// let storage = OracleTraceStorage::from_env_or_vault().await;
51 /// ```
52 pub async fn from_env_or_vault() -> Self {
53 let spool_dir = default_spool_dir();
54 let max = std::env::var("CODETETHER_ORACLE_SPOOL_MAX_BYTES")
55 .ok()
56 .and_then(|v| v.parse().ok())
57 .unwrap_or(DEFAULT_MAX_SPOOL_BYTES);
58 let remote = match BusS3SinkConfig::from_env_or_vault().await {
59 Ok(cfg) => match MinioOracleRemote::from_bus_config(cfg) {
60 Ok(r) => Some(Arc::new(r) as Arc<dyn OracleRemote>),
61 Err(e) => {
62 tracing::warn!(error = %e, "Remote init failed");
63 None
64 }
65 },
66 Err(e) => {
67 tracing::warn!(error = %e, "Remote not configured");
68 None
69 }
70 };
71 Self {
72 spool_dir,
73 max_spool_bytes: max,
74 remote,
75 }
76 }
77
78 /// Construct with injected dependencies for testing.
79 #[cfg(test)]
80 pub(in super::super) fn new_for_test(
81 spool_dir: PathBuf,
82 max_spool_bytes: u64,
83 remote: Option<Arc<dyn OracleRemote>>,
84 ) -> Self {
85 Self {
86 spool_dir,
87 max_spool_bytes,
88 remote,
89 }
90 }
91}