diff --git a/clawshell.example.toml b/clawshell.example.toml index b97d379..e47460c 100644 --- a/clawshell.example.toml +++ b/clawshell.example.toml @@ -40,7 +40,7 @@ provider = "anthropic" # Data Loss Prevention (DLP) # Each pattern has a name, a regex, and an action. -# action = "redact" (default): Reject the request with 400 Bad Request. +# action = "block" (default): Reject the request with 400 Bad Request. # action = "redact": Replace matches with [REDACTED:] and forward. # scan_responses: When true, upstream responses are also scanned and PII is redacted. [dlp] @@ -58,7 +58,7 @@ patterns = [ # tokens (from non-streaming responses), and per-sender email-filter # activity, and exposes them at GET /admin/stats (loopback-only). [stats] -persist_path = "/etc/clawshell/stats.json" +persist_path = "/var/lib/clawshell/stats.json" # Email read endpoint # If enabled, set exactly one mode: diff --git a/src/main.rs b/src/main.rs index 3a3caf3..c7953ce 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1457,11 +1457,13 @@ fn cmd_onboard() -> Result<(), Box> { // Step 2: Create necessary directories let config_dir = PathBuf::from("/etc/clawshell"); let log_dir_path = PathBuf::from("/var/log/clawshell"); + let state_dir_path = PathBuf::from("/var/lib/clawshell"); tui::print_step(2, TOTAL_STEPS, "Setting up directories..."); std::fs::create_dir_all(&config_dir)?; std::fs::create_dir_all(&log_dir_path)?; + std::fs::create_dir_all(&state_dir_path)?; tui::print_step_done(2, TOTAL_STEPS, "Directories created"); // Step 3: Set permissions and ownership @@ -1488,6 +1490,13 @@ fn cmd_onboard() -> Result<(), Box> { "Failed to set log directory owner" ); } + if let Err(error) = platform::set_owner(&state_dir_path, true) { + warn!( + error = %error, + path = %state_dir_path.display(), + "Failed to set state directory owner" + ); + } tui::print_step_done(3, TOTAL_STEPS, "Permissions set"); // Step 4: Ask the user for configuration details (TUI prompts) diff --git a/src/migration/targets/clawshell_toml/versions/mod.rs b/src/migration/targets/clawshell_toml/versions/mod.rs index 4a07491..266167d 100644 --- a/src/migration/targets/clawshell_toml/versions/mod.rs +++ b/src/migration/targets/clawshell_toml/versions/mod.rs @@ -139,7 +139,7 @@ openai_base_url = "https://api.openai.com" let stats = table.get("stats").unwrap().as_table().unwrap(); assert_eq!( stats.get("persist_path").unwrap().as_str().unwrap(), - "/etc/clawshell/stats.json" + "/var/lib/clawshell/stats.json" ); } diff --git a/src/migration/targets/clawshell_toml/versions/to_v0_2_1.rs b/src/migration/targets/clawshell_toml/versions/to_v0_2_1.rs index f2d2952..53add8c 100644 --- a/src/migration/targets/clawshell_toml/versions/to_v0_2_1.rs +++ b/src/migration/targets/clawshell_toml/versions/to_v0_2_1.rs @@ -16,7 +16,7 @@ pub fn apply( let mut stats = toml::value::Table::new(); stats.insert( "persist_path".to_string(), - toml::Value::String("/etc/clawshell/stats.json".to_string()), + toml::Value::String("/var/lib/clawshell/stats.json".to_string()), ); table.insert("stats".to_string(), toml::Value::Table(stats)); output.applied_steps.push(STEP_ID.to_string()); @@ -62,7 +62,7 @@ openai_base_url = "https://api.openai.com" let stats = table.get("stats").unwrap().as_table().unwrap(); assert_eq!( stats.get("persist_path").unwrap().as_str().unwrap(), - "/etc/clawshell/stats.json" + "/var/lib/clawshell/stats.json" ); } diff --git a/src/onboard/config_render.rs b/src/onboard/config_render.rs index 1d63c2d..285c44b 100644 --- a/src/onboard/config_render.rs +++ b/src/onboard/config_render.rs @@ -78,7 +78,7 @@ patterns = [ ] [stats] -persist_path = "/etc/clawshell/stats.json" +persist_path = "/var/lib/clawshell/stats.json" {oauth_providers_section}"#, version = env!("CARGO_PKG_VERSION"), host = config.server_host, @@ -156,7 +156,7 @@ mod tests { assert!(toml_str.contains(&format!("version = \"{}\"", env!("CARGO_PKG_VERSION")))); assert!(toml_str.contains("[dlp]")); assert!(toml_str.contains("[stats]")); - assert!(toml_str.contains("persist_path =")); + assert!(toml_str.contains("persist_path = \"/var/lib/clawshell/stats.json\"")); assert!(!toml_str.contains("[email]")); assert!(!toml_str.contains("[rate_limit]")); } diff --git a/src/platform/linux.rs b/src/platform/linux.rs index 30d26a0..3427d2e 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -49,6 +49,7 @@ AmbientCapabilities= RestrictAddressFamilies=AF_INET AF_INET6 UMask=0077 ReadOnlyPaths=/etc/clawshell +ReadWritePaths=/var/lib/clawshell StandardOutput=append:/var/log/clawshell/clawshell.log StandardError=append:/var/log/clawshell/clawshell.log @@ -221,6 +222,8 @@ mod tests { assert!(content.contains("WantedBy=multi-user.target")); assert!(content.contains("StandardOutput=append:/var/log/clawshell/clawshell.log")); assert!(content.contains("StandardError=append:/var/log/clawshell/clawshell.log")); + assert!(content.contains("ReadOnlyPaths=/etc/clawshell")); + assert!(content.contains("ReadWritePaths=/var/lib/clawshell")); } #[test]