Skip to main content

fur_cli/security/
state.rs

1use serde_json::json;
2use serde_json::Value;
3use std::fs;
4use std::path::Path;
5
6use crate::schema::rebuild::LOCK_SENTINEL;
7
8pub fn lock_file() -> &'static str {
9    ".fur/lock.json"
10}
11
12/// True when either the disposable flag in `.fur/` or the durable sentinel in
13/// `chats/` says so.
14///
15/// viceroy: previously consulted `.fur/lock.json` alone. Once `.fur/` became
16/// rebuildable, deleting it discarded the only record that `chats/` was
17/// ciphertext — `fur unlock` then reported "already unlocked" and refused to
18/// decrypt. The sentinel lives with the data it describes.
19pub fn is_locked() -> bool {
20    if Path::new("chats").join(LOCK_SENTINEL).exists() {
21        return true;
22    }
23
24    let path = Path::new(lock_file());
25
26    if !path.exists() {
27        return false;
28    }
29
30    let content = fs::read_to_string(path).unwrap_or_default();
31
32    if let Ok(v) = serde_json::from_str::<Value>(&content) {
33        return v["locked"].as_bool().unwrap_or(false);
34    }
35
36    false
37}
38
39pub fn write_lock() {
40    let data = json!({
41        "locked": true,
42        "algorithm": "AES-256-GCM",
43        "version": 1
44    });
45
46    if Path::new(".fur").exists() {
47        let _ = fs::write(lock_file(), serde_json::to_string_pretty(&data).unwrap());
48    }
49}
50
51pub fn remove_lock() {
52    let path = Path::new(lock_file());
53
54    if path.exists() {
55        let _ = fs::remove_file(path);
56    }
57}