@@ -27,9 +27,10 @@ struct Cli {
2727 #[ command( subcommand) ]
2828 command : Option < Commands > ,
2929
30- /// SQLite database path (default: $MIMIR_DB_PATH or ~/.mimir/data/perseus-vault.db,
31- /// falling back to an existing ~/.mimir/data/mneme.db or ~/.mimir/data/mimir.db
32- /// from before the Perseus Vault rename). Used when running the server directly
30+ /// SQLite database path (default: $PERSEUS_VAULT_DB_PATH / $MIMIR_DB_PATH or
31+ /// ~/.perseus-vault/data/perseus-vault.db, falling back to an existing
32+ /// ~/.mimir/data/{perseus-vault,mneme,mimir}.db from before the rename).
33+ /// Used when running the server directly
3334 /// without the `serve` subcommand — matches the documented MCP host config:
3435 /// `perseus-vault --db /path/to/perseus-vault.db`.
3536 #[ arg( long) ]
@@ -238,7 +239,8 @@ enum Commands {
238239
239240 /// Generate a new AES-256-GCM encryption key and write it to a file
240241 Keygen {
241- /// Path to write the key file (default: ~/.mimir/secret.key)
242+ /// Path to write the key file (default: ~/.perseus-vault/secret.key, or
243+ /// an existing ~/.mimir/secret.key from before the rename)
242244 #[ arg( long, default_value_t = default_key_file( ) ) ]
243245 key_file : String ,
244246 } ,
@@ -356,7 +358,7 @@ enum Commands {
356358 ObsidianSync {
357359 /// Target Obsidian vault directory (created if needed)
358360 vault_path : String ,
359- /// SQLite database path (defaults to $MIMIR_DB_PATH or ~/.mimir /data/perseus-vault.db)
361+ /// SQLite database path (defaults to $PERSEUS_VAULT_DB_PATH / $ MIMIR_DB_PATH or ~/.perseus-vault /data/perseus-vault.db)
360362 #[ arg( long) ]
361363 db : Option < String > ,
362364 /// Continuously re-export whenever memory changes
@@ -509,12 +511,18 @@ struct DbResolution {
509511/// pass `--db` or set `$MIMIR_DB_PATH`.
510512///
511513/// Precedence (first existing wins):
512- /// 1. `~/.mimir/data/perseus-vault.db` (canonical, current name)
513- /// 2. `~/.mimir/data/mneme.db` (pre-rename)
514- /// 3. `~/.mimir/data/mimir.db` (pre-rename)
515- /// 4. `~/mimir.db` (legacy single-user install location)
514+ /// 1. `~/.perseus-vault/data/perseus-vault.db` (canonical, current brand)
515+ /// 2. `~/.mimir/data/perseus-vault.db` (pre-dir-rename, #427)
516+ /// 3. `~/.mimir/data/mneme.db` (pre-rename)
517+ /// 4. `~/.mimir/data/mimir.db` (pre-rename)
518+ /// 5. `~/mimir.db` (legacy single-user install location)
516519/// If none exist, fall back to creating (1), the canonical path.
517520///
521+ /// #427 is a *precedence-only* directory rename: fresh installs land in
522+ /// `~/.perseus-vault/`, while any existing `~/.mimir/` install keeps being
523+ /// adopted via the fallback chain — no data is moved. `~/.mimir/` stays in the
524+ /// chain indefinitely so upgraders are never orphaned.
525+ ///
518526/// Crucially `~/mimir.db` is chosen *before* falling through to create a fresh
519527/// canonical DB, so an existing single-user install is picked up instead of
520528/// silently starting empty. `other_candidates` reports every *other* database
@@ -535,15 +543,18 @@ fn resolve_default_db(
535543 exists : & dyn Fn ( & str ) -> bool ,
536544 entity_count : & dyn Fn ( & str ) -> Option < i64 > ,
537545) -> DbResolution {
538- let dir = format ! ( "{}/.mimir/data" , home) ;
539- let vault_path = format ! ( "{}/perseus-vault.db" , dir) ;
540- let mneme_path = format ! ( "{}/mneme.db" , dir) ;
541- let mimir_path = format ! ( "{}/mimir.db" , dir) ;
546+ let new_dir = format ! ( "{}/.perseus-vault/data" , home) ;
547+ let legacy_dir = format ! ( "{}/.mimir/data" , home) ;
548+ let vault_path = format ! ( "{}/perseus-vault.db" , new_dir) ; // #427 canonical
549+ let legacy_vault_path = format ! ( "{}/perseus-vault.db" , legacy_dir) ;
550+ let mneme_path = format ! ( "{}/mneme.db" , legacy_dir) ;
551+ let mimir_path = format ! ( "{}/mimir.db" , legacy_dir) ;
542552 let home_legacy_path = format ! ( "{}/mimir.db" , home) ;
543553
544554 // Ordered candidate list; the first that exists is chosen.
545555 let candidates = [
546556 vault_path. clone ( ) ,
557+ legacy_vault_path,
547558 mneme_path,
548559 mimir_path,
549560 home_legacy_path,
@@ -619,16 +630,24 @@ fn probe_entity_count(path: &str) -> Option<i64> {
619630/// emitted separately by `normalize_default_db`, which runs once at real
620631/// startup and only when the default path was actually used.
621632fn default_db_path ( ) -> String {
633+ // #427: PERSEUS_VAULT_DB_PATH is the current-brand override; MIMIR_DB_PATH
634+ // stays honored for back-compat (checked second).
635+ if let Ok ( explicit) = std:: env:: var ( "PERSEUS_VAULT_DB_PATH" ) {
636+ return explicit;
637+ }
622638 if let Ok ( explicit) = std:: env:: var ( "MIMIR_DB_PATH" ) {
623639 return explicit;
624640 }
625641 let home = std:: env:: var ( "HOME" )
626642 . or_else ( |_| std:: env:: var ( "USERPROFILE" ) )
627643 . unwrap_or_else ( |_| {
628- eprintln ! ( "perseus-vault: could not determine home directory. Set MIMIR_DB_PATH or HOME/USERPROFILE." ) ;
644+ eprintln ! ( "perseus-vault: could not determine home directory. Set PERSEUS_VAULT_DB_PATH or HOME/USERPROFILE." ) ;
629645 std:: process:: exit ( 1 ) ;
630646 } ) ;
631- let dir = format ! ( "{}/.mimir/data" , home) ;
647+ // Create the current-brand canonical data dir for fresh installs. Existing
648+ // ~/.mimir installs are still adopted by resolve_default_db via the fallback
649+ // chain (this only ever creates an empty dir alongside them).
650+ let dir = format ! ( "{}/.perseus-vault/data" , home) ;
632651 let _ = std:: fs:: create_dir_all ( & dir) ;
633652
634653 // Path-only here: clap evaluates this eagerly for *every* invocation (even
@@ -652,7 +671,10 @@ fn default_db_path() -> String {
652671/// rather than only the handful of sites that used to call `check_legacy_db`.
653672fn normalize_default_db ( cli : & mut Cli ) {
654673 // Explicit selection (env or top-level `--db`) is never second-guessed.
655- if std:: env:: var_os ( "MIMIR_DB_PATH" ) . is_some ( ) || cli. db . is_some ( ) {
674+ if std:: env:: var_os ( "PERSEUS_VAULT_DB_PATH" ) . is_some ( )
675+ || std:: env:: var_os ( "MIMIR_DB_PATH" ) . is_some ( )
676+ || cli. db . is_some ( )
677+ {
656678 return ;
657679 }
658680 let Ok ( home) = std:: env:: var ( "HOME" ) . or_else ( |_| std:: env:: var ( "USERPROFILE" ) ) else {
@@ -698,7 +720,7 @@ fn normalize_default_db(cli: &mut Cli) {
698720 eprintln ! ( "perseus-vault: also present (ignored): {}" , other) ;
699721 }
700722 eprintln ! (
701- "perseus-vault: pass --db <path> or set MIMIR_DB_PATH to choose explicitly and silence this warning."
723+ "perseus-vault: pass --db <path> or set PERSEUS_VAULT_DB_PATH to choose explicitly and silence this warning."
702724 ) ;
703725 }
704726
@@ -717,7 +739,18 @@ fn default_key_file() -> String {
717739 let home = std:: env:: var ( "HOME" )
718740 . or_else ( |_| std:: env:: var ( "USERPROFILE" ) )
719741 . unwrap_or_else ( |_| "/root" . to_string ( ) ) ;
720- format ! ( "{}/.mimir/secret.key" , home)
742+ // #427 precedence-only: prefer whichever secret.key already exists so an
743+ // existing encrypted install NEVER loses its key (a wrong default would
744+ // silently make the vault undecryptable). Fresh installs use the new dir.
745+ let new_key = format ! ( "{}/.perseus-vault/secret.key" , home) ;
746+ let legacy_key = format ! ( "{}/.mimir/secret.key" , home) ;
747+ if std:: path:: Path :: new ( & new_key) . exists ( ) {
748+ new_key
749+ } else if std:: path:: Path :: new ( & legacy_key) . exists ( ) {
750+ legacy_key
751+ } else {
752+ new_key
753+ }
721754}
722755
723756/// Open a database for a CLI maintenance command, or exit(1) with a message.
@@ -2028,14 +2061,41 @@ mod tests {
20282061
20292062 #[ test]
20302063 fn resolve_default_db_falls_back_to_canonical_when_none_exist ( ) {
2031- // Fresh install: nothing exists -> create canonical path, no warning.
2064+ // Fresh install: nothing exists -> create the #427 canonical path under
2065+ // ~/.perseus-vault/, no warning.
20322066 let home = "/home/tester" ;
2033- let vault = format ! ( "{}/.mimir /data/perseus-vault.db" , home) ;
2067+ let vault = format ! ( "{}/.perseus-vault /data/perseus-vault.db" , home) ;
20342068 let r = resolve_default_db ( home, & present ( & [ ] ) , & unknown) ;
20352069 assert_eq ! ( r. chosen, vault) ;
20362070 assert ! ( r. other_candidates. is_empty( ) ) ;
20372071 }
20382072
2073+ #[ test]
2074+ fn resolve_default_db_427_prefers_new_dir_when_present ( ) {
2075+ // Both the new ~/.perseus-vault and a legacy ~/.mimir DB exist: the new
2076+ // canonical dir wins; the legacy one is reported as an also-present.
2077+ let home = "/home/tester" ;
2078+ let new_vault = format ! ( "{}/.perseus-vault/data/perseus-vault.db" , home) ;
2079+ let legacy_vault = format ! ( "{}/.mimir/data/perseus-vault.db" , home) ;
2080+ let existing = vec ! [ new_vault. clone( ) , legacy_vault. clone( ) ] ;
2081+ let r = resolve_default_db ( home, & present ( & existing) , & unknown) ;
2082+ assert_eq ! ( r. chosen, new_vault) ;
2083+ assert_eq ! ( r. other_candidates, vec![ legacy_vault] ) ;
2084+ }
2085+
2086+ #[ test]
2087+ fn resolve_default_db_427_adopts_legacy_mimir_dir_on_upgrade ( ) {
2088+ // Upgrade path: only the legacy ~/.mimir DB exists (no ~/.perseus-vault
2089+ // yet). It must be adopted, NOT shadowed by a fresh empty new-dir DB —
2090+ // no data is moved.
2091+ let home = "/home/tester" ;
2092+ let legacy_vault = format ! ( "{}/.mimir/data/perseus-vault.db" , home) ;
2093+ let existing = vec ! [ legacy_vault. clone( ) ] ;
2094+ let r = resolve_default_db ( home, & present ( & existing) , & unknown) ;
2095+ assert_eq ! ( r. chosen, legacy_vault) ;
2096+ assert ! ( r. other_candidates. is_empty( ) ) ;
2097+ }
2098+
20392099 #[ test]
20402100 fn resolve_default_db_reports_multiple_candidates ( ) {
20412101 // Multiple candidate DBs -> chosen is highest-precedence, others named
0 commit comments