Skip to content

Commit fdd6c1a

Browse files
committed
vault wiring
1 parent 135c37f commit fdd6c1a

File tree

7 files changed

+229
-80
lines changed

7 files changed

+229
-80
lines changed

Cargo.lock

Lines changed: 15 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ members = [
1818
]
1919

2020
[workspace.package]
21-
version = "0.4.3"
21+
version = "0.4.4"
2222
edition = "2021"
2323
license = "Apache-2.0 OR MIT"
2424
repository = "https://github.com/RightNow-AI/openfang"

crates/openfang-api/src/routes.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6953,7 +6953,10 @@ pub async fn set_provider_key(
69536953
})
69546954
};
69556955

6956-
// Write to secrets.env file
6956+
// Store in vault (best-effort — no-op if vault not initialized)
6957+
state.kernel.store_credential(&env_var, &key);
6958+
6959+
// Write to secrets.env file (dual-write for backward compat / vault corruption recovery)
69576960
let secrets_path = state.kernel.config.home_dir.join("secrets.env");
69586961
if let Err(e) = write_secret_env(&secrets_path, &env_var, &key) {
69596962
return (
@@ -7115,6 +7118,9 @@ pub async fn delete_provider_key(
71157118
);
71167119
}
71177120

7121+
// Remove from vault (best-effort)
7122+
state.kernel.remove_credential(&env_var);
7123+
71187124
// Remove from secrets.env
71197125
let secrets_path = state.kernel.config.home_dir.join("secrets.env");
71207126
if let Err(e) = remove_secret_env(&secrets_path, &env_var) {
@@ -10267,7 +10273,10 @@ pub async fn copilot_oauth_poll(
1026710273
Json(serde_json::json!({"status": "pending"})),
1026810274
),
1026910275
openfang_runtime::copilot_oauth::DeviceFlowStatus::Complete { access_token } => {
10270-
// Save to secrets.env
10276+
// Store in vault (best-effort)
10277+
state.kernel.store_credential("GITHUB_TOKEN", &access_token);
10278+
10279+
// Save to secrets.env (dual-write)
1027110280
let secrets_path = state.kernel.config.home_dir.join("secrets.env");
1027210281
if let Err(e) = write_secret_env(&secrets_path, "GITHUB_TOKEN", &access_token) {
1027310282
return (

crates/openfang-cli/src/main.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4813,6 +4813,10 @@ fn cmd_config_set_key(provider: &str) {
48134813
return;
48144814
}
48154815

4816+
// Try vault first (best-effort)
4817+
save_credential_prefer_vault(&env_var, &key);
4818+
4819+
// Always save to dotenv as fallback
48164820
match dotenv::save_env_key(&env_var, &key) {
48174821
Ok(()) => {
48184822
ui::success(&format!("Saved {env_var} to ~/.openfang/.env"));
@@ -4835,6 +4839,18 @@ fn cmd_config_set_key(provider: &str) {
48354839
fn cmd_config_delete_key(provider: &str) {
48364840
let env_var = provider_to_env_var(provider);
48374841

4842+
// Remove from vault (best-effort)
4843+
{
4844+
let home = openfang_home();
4845+
let vault_path = home.join("vault.enc");
4846+
if vault_path.exists() {
4847+
let mut vault = openfang_extensions::vault::CredentialVault::new(vault_path);
4848+
if vault.unlock().is_ok() {
4849+
let _ = vault.remove(&env_var);
4850+
}
4851+
}
4852+
}
4853+
48384854
match dotenv::remove_env_key(&env_var) {
48394855
Ok(()) => ui::success(&format!("Removed {env_var} from ~/.openfang/.env")),
48404856
Err(e) => {
@@ -4864,6 +4880,26 @@ fn cmd_config_test_key(provider: &str) {
48644880
}
48654881
}
48664882

4883+
/// Try to store a credential in the vault first; silently falls through if vault
4884+
/// is not initialized or cannot be unlocked. The caller should always also
4885+
/// write to dotenv as a fallback.
4886+
fn save_credential_prefer_vault(env_var: &str, value: &str) {
4887+
use zeroize::Zeroizing;
4888+
4889+
let home = openfang_home();
4890+
let vault_path = home.join("vault.enc");
4891+
if !vault_path.exists() {
4892+
return;
4893+
}
4894+
let mut vault = openfang_extensions::vault::CredentialVault::new(vault_path);
4895+
if vault.unlock().is_err() {
4896+
return;
4897+
}
4898+
if let Ok(()) = vault.set(env_var.to_string(), Zeroizing::new(value.to_string())) {
4899+
println!(" {}", "Also stored in encrypted vault".dimmed());
4900+
}
4901+
}
4902+
48674903
// ---------------------------------------------------------------------------
48684904
// Quick chat (OpenClaw alias)
48694905
// ---------------------------------------------------------------------------

crates/openfang-extensions/src/credentials.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,17 @@ impl CredentialResolver {
126126
))
127127
}
128128
}
129+
130+
/// Remove a credential from the vault (if available).
131+
pub fn remove_from_vault(&mut self, key: &str) -> ExtensionResult<bool> {
132+
if let Some(ref mut vault) = self.vault {
133+
vault.remove(key)
134+
} else {
135+
Err(crate::ExtensionError::Vault(
136+
"No vault configured".to_string(),
137+
))
138+
}
139+
}
129140
}
130141

131142
/// Load a dotenv file into a HashMap.

crates/openfang-kernel/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ rand = { workspace = true }
3434
hex = { workspace = true }
3535
reqwest = { workspace = true }
3636
cron = "0.15"
37+
zeroize = { workspace = true }
3738

3839
[target.'cfg(unix)'.dependencies]
3940
libc = "0.2"

0 commit comments

Comments
 (0)