-
Notifications
You must be signed in to change notification settings - Fork 0
Fix Windows startup recovery and SurrealDB boot path #213
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,9 +3,7 @@ use crate::protected_topics::{ | |
| ProtectedTopicSummary, SecretKind, SecretMovePlan, TriangleEthicPreview, | ||
| }; | ||
| use abigail_core::{AppConfig, HybridUnlockProvider, PassphraseUnlockProvider, UnlockProvider}; | ||
| use abigail_persistence::{ | ||
| migrate_legacy_layout, EntityScope, PersistenceError, PersistenceHandle, QueryBinding, | ||
| }; | ||
| use abigail_persistence::{EntityScope, PersistenceError, PersistenceHandle, QueryBinding}; | ||
| use chrono::Utc; | ||
| use serde::{Deserialize, Serialize}; | ||
| use sha2::{Digest, Sha256}; | ||
|
|
@@ -198,13 +196,8 @@ impl MemoryStore { | |
| } | ||
|
|
||
| pub fn open_with_config(config: &AppConfig) -> Result<Self> { | ||
| if uses_legacy_layout(config) { | ||
| migrate_legacy_layout(config) | ||
| .map_err(|error| StoreError::Migration(error.to_string()))?; | ||
| } | ||
|
|
||
| let shared_path = shared_db_path(config); | ||
| if let Some(parent) = shared_path.parent() { | ||
| let db_path = config.db_path.clone(); | ||
| if let Some(parent) = db_path.parent() { | ||
| std::fs::create_dir_all(parent) | ||
| .map_err(|error| StoreError::Migration(error.to_string()))?; | ||
|
Comment on lines
198
to
202
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This change removes the migration step before opening the new Surreal store. Useful? React with 👍 / 👎. |
||
| } | ||
|
|
@@ -218,14 +211,24 @@ impl MemoryStore { | |
| .and_then(|name| name.to_str()) | ||
| .map(str::to_string) | ||
| }; | ||
| tracing::info!( | ||
| "MemoryStore opening SurrealDB store: scope={} path={} data_dir={}", | ||
| if config.is_hive { "hive" } else { "entity" }, | ||
| db_path.display(), | ||
| config.data_dir.display() | ||
| ); | ||
| Self::open_internal( | ||
| shared_path, | ||
| db_path, | ||
| Arc::new(HybridUnlockProvider::new()), | ||
| entity_id, | ||
| None, | ||
| ) | ||
| } | ||
|
|
||
| pub fn path(&self) -> &Path { | ||
| self.persistence.path() | ||
| } | ||
|
|
||
| pub fn capture_secret_message( | ||
| &self, | ||
| entity_id: &str, | ||
|
|
@@ -829,44 +832,6 @@ struct PreparedProtectedSecret { | |
| created_at: chrono::DateTime<Utc>, | ||
| } | ||
|
|
||
| fn uses_legacy_layout(config: &AppConfig) -> bool { | ||
| legacy_primary_db_path(config).is_some() | ||
| || config.data_dir.join("jobs.db").exists() | ||
| || config.data_dir.join("calendar.db").exists() | ||
| || config.data_dir.join("kb.db").exists() | ||
| } | ||
|
|
||
| fn shared_db_path(config: &AppConfig) -> PathBuf { | ||
| if is_shared_db_path(&config.db_path) { | ||
| return config.db_path.clone(); | ||
| } | ||
|
|
||
| if uses_legacy_layout(config) { | ||
| config | ||
| .data_dir | ||
| .parent() | ||
| .and_then(|parent| parent.parent()) | ||
| .map(|root| root.join("memory.db")) | ||
| .unwrap_or_else(|| config.data_dir.join("memory.db")) | ||
| } else { | ||
| config.db_path.clone() | ||
| } | ||
| } | ||
|
|
||
| fn legacy_primary_db_path(config: &AppConfig) -> Option<&Path> { | ||
| if is_shared_db_path(&config.db_path) || !config.db_path.exists() { | ||
| return None; | ||
| } | ||
|
|
||
| Some(config.db_path.as_path()) | ||
| } | ||
|
|
||
| fn is_shared_db_path(path: &Path) -> bool { | ||
| path.file_name() | ||
| .and_then(|name| name.to_str()) | ||
| .is_some_and(|name| name == "memory.db") | ||
| } | ||
|
|
||
| fn infer_entity_id(path: &Path) -> Option<String> { | ||
| path.parent() | ||
| .and_then(|parent| parent.file_name()) | ||
|
|
@@ -1015,27 +980,13 @@ mod tests { | |
| } | ||
|
|
||
| #[test] | ||
| fn test_shared_db_path_honors_explicit_non_legacy_path() { | ||
| fn test_open_with_config_uses_configured_db_path() { | ||
| let tmp = std::env::temp_dir().join(format!("abigail_store_path_{}", Uuid::new_v4())); | ||
| let db_path = tmp.join("sandbox").join("test.db"); | ||
| let config = test_config(&tmp, db_path.clone()); | ||
| let store = MemoryStore::open_with_config(&config).unwrap(); | ||
|
|
||
| assert!(!uses_legacy_layout(&config)); | ||
| assert_eq!(shared_db_path(&config), db_path); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_shared_db_path_uses_hive_root_for_existing_legacy_db() { | ||
| let tmp = std::env::temp_dir().join(format!("abigail_store_legacy_{}", Uuid::new_v4())); | ||
| let agent_dir = tmp.join("identities").join(Uuid::new_v4().to_string()); | ||
| let legacy_db = agent_dir.join("abigail_memory.db"); | ||
| std::fs::create_dir_all(&agent_dir).unwrap(); | ||
| std::fs::write(&legacy_db, b"legacy").unwrap(); | ||
|
|
||
| let config = test_config(&tmp, legacy_db); | ||
|
|
||
| assert!(uses_legacy_layout(&config)); | ||
| assert_eq!(shared_db_path(&config), tmp.join("memory.db")); | ||
| assert_eq!(store.path(), db_path.as_path()); | ||
|
|
||
| let _ = std::fs::remove_dir_all(&tmp); | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the fallback-recovery path, the KEK loaded from
vault.kek.dpapiis written back into the OS keyring beforeverify_or_create_sentinel()proves that it can actually decrypt the existing vault. If that fallback file is corrupted or stale, startup not only fails, it also overwrites the previously-good keyring entry with the bad key, leaving later launches stuck on the same recovery error until the user manually repairs the keyring. The keyring repair needs to happen only after sentinel validation succeeds.Useful? React with 👍 / 👎.