diff --git a/internal/secret/passphrase_test.go b/internal/secret/passphrase_test.go
new file mode 100644
index 0000000..75b4a61
--- /dev/null
+++ b/internal/secret/passphrase_test.go
@@ -0,0 +1,193 @@
+package secret_test
+
+import (
+	"os"
+	"path/filepath"
+	"testing"
+	"time"
+
+	"filippo.io/age"
+	"git.eeqj.de/sneak/secret/internal/secret"
+	"git.eeqj.de/sneak/secret/pkg/agehd"
+	"github.com/spf13/afero"
+)
+
+func TestPassphraseUnlockKeyWithRealFS(t *testing.T) {
+	// Skip this test if CI=true is set, as it uses real filesystem
+	if os.Getenv("CI") == "true" {
+		t.Skip("Skipping test with real filesystem in CI environment")
+	}
+
+	// Create a temporary directory for our tests
+	tempDir, err := os.MkdirTemp("", "secret-passphrase-test-")
+	if err != nil {
+		t.Fatalf("Failed to create temp dir: %v", err)
+	}
+	defer os.RemoveAll(tempDir) // Clean up after test
+
+	// Use the real filesystem
+	fs := afero.NewOsFs()
+
+	// Test data
+	testMnemonic := "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
+	testPassphrase := "test-passphrase-123"
+
+	// Create the directory structure
+	keyDir := filepath.Join(tempDir, "unlock-key")
+	if err := os.MkdirAll(keyDir, secret.DirPerms); err != nil {
+		t.Fatalf("Failed to create key directory: %v", err)
+	}
+
+	// Set up test metadata
+	metadata := secret.UnlockKeyMetadata{
+		ID:        "test-passphrase",
+		Type:      "passphrase",
+		CreatedAt: time.Now(),
+		Flags:     []string{},
+	}
+
+	// Create passphrase unlock key
+	unlockKey := secret.NewPassphraseUnlockKey(fs, keyDir, metadata)
+
+	// Generate a test age identity
+	ageIdentity, err := age.GenerateX25519Identity()
+	if err != nil {
+		t.Fatalf("Failed to generate age identity: %v", err)
+	}
+	agePrivateKey := ageIdentity.String()
+	agePublicKey := ageIdentity.Recipient().String()
+
+	// Test writing public key
+	t.Run("WritePublicKey", func(t *testing.T) {
+		pubKeyPath := filepath.Join(keyDir, "pub.age")
+		if err := afero.WriteFile(fs, pubKeyPath, []byte(agePublicKey), secret.FilePerms); err != nil {
+			t.Fatalf("Failed to write public key: %v", err)
+		}
+
+		// Verify the file exists
+		exists, err := afero.Exists(fs, pubKeyPath)
+		if err != nil {
+			t.Fatalf("Failed to check if public key exists: %v", err)
+		}
+		if !exists {
+			t.Errorf("Public key file should exist at %s", pubKeyPath)
+		}
+	})
+
+	// Test encrypting private key with passphrase
+	t.Run("EncryptPrivateKey", func(t *testing.T) {
+		privKeyData := []byte(agePrivateKey)
+		encryptedPrivKey, err := secret.EncryptWithPassphrase(privKeyData, testPassphrase)
+		if err != nil {
+			t.Fatalf("Failed to encrypt private key: %v", err)
+		}
+
+		privKeyPath := filepath.Join(keyDir, "priv.age")
+		if err := afero.WriteFile(fs, privKeyPath, encryptedPrivKey, secret.FilePerms); err != nil {
+			t.Fatalf("Failed to write encrypted private key: %v", err)
+		}
+
+		// Verify the file exists
+		exists, err := afero.Exists(fs, privKeyPath)
+		if err != nil {
+			t.Fatalf("Failed to check if private key exists: %v", err)
+		}
+		if !exists {
+			t.Errorf("Encrypted private key file should exist at %s", privKeyPath)
+		}
+	})
+
+	// Test writing long-term key
+	t.Run("WriteLongTermKey", func(t *testing.T) {
+		// Derive a long-term identity from the test mnemonic
+		ltIdentity, err := agehd.DeriveIdentity(testMnemonic, 0)
+		if err != nil {
+			t.Fatalf("Failed to derive long-term identity: %v", err)
+		}
+
+		// Encrypt long-term private key to the unlock key's recipient
+		recipient, err := age.ParseX25519Recipient(agePublicKey)
+		if err != nil {
+			t.Fatalf("Failed to parse recipient: %v", err)
+		}
+
+		ltPrivKeyData := []byte(ltIdentity.String())
+		encryptedLtPrivKey, err := secret.EncryptToRecipient(ltPrivKeyData, recipient)
+		if err != nil {
+			t.Fatalf("Failed to encrypt long-term private key: %v", err)
+		}
+
+		ltPrivKeyPath := filepath.Join(keyDir, "longterm.age")
+		if err := afero.WriteFile(fs, ltPrivKeyPath, encryptedLtPrivKey, secret.FilePerms); err != nil {
+			t.Fatalf("Failed to write encrypted long-term private key: %v", err)
+		}
+
+		// Verify the file exists
+		exists, err := afero.Exists(fs, ltPrivKeyPath)
+		if err != nil {
+			t.Fatalf("Failed to check if long-term key exists: %v", err)
+		}
+		if !exists {
+			t.Errorf("Encrypted long-term key file should exist at %s", ltPrivKeyPath)
+		}
+	})
+
+	// Save original environment variables and set test ones
+	oldPassphrase := os.Getenv(secret.EnvUnlockPassphrase)
+	os.Setenv(secret.EnvUnlockPassphrase, testPassphrase)
+
+	// Clean up after test
+	defer func() {
+		if oldPassphrase != "" {
+			os.Setenv(secret.EnvUnlockPassphrase, oldPassphrase)
+		} else {
+			os.Unsetenv(secret.EnvUnlockPassphrase)
+		}
+	}()
+
+	// Test getting identity from environment variable
+	t.Run("GetIdentityFromEnv", func(t *testing.T) {
+		identity, err := unlockKey.GetIdentity()
+		if err != nil {
+			t.Fatalf("Failed to get identity from env: %v", err)
+		}
+
+		// Verify the identity matches what we expect
+		expectedPubKey := ageIdentity.Recipient().String()
+		actualPubKey := identity.Recipient().String()
+		if actualPubKey != expectedPubKey {
+			t.Errorf("Public key mismatch. Expected %s, got %s", expectedPubKey, actualPubKey)
+		}
+	})
+
+	// Unset the environment variable to test interactive prompt
+	os.Unsetenv(secret.EnvUnlockPassphrase)
+
+	// Test getting identity from prompt (this would require mocking the prompt)
+	// For real integration tests, we'd need to provide a way to mock the passphrase input
+	// Here we'll just verify the error is what we expect when no passphrase is available
+	t.Run("GetIdentityWithoutEnv", func(t *testing.T) {
+		// This should fail since we're not in an interactive terminal
+		_, err := unlockKey.GetIdentity()
+		if err == nil {
+			t.Errorf("Should have failed to get identity without passphrase env var")
+		}
+	})
+
+	// Test removing the unlock key
+	t.Run("RemoveUnlockKey", func(t *testing.T) {
+		err := unlockKey.Remove()
+		if err != nil {
+			t.Fatalf("Failed to remove unlock key: %v", err)
+		}
+
+		// Verify the directory is gone
+		exists, err := afero.DirExists(fs, keyDir)
+		if err != nil {
+			t.Fatalf("Failed to check if key directory exists: %v", err)
+		}
+		if exists {
+			t.Errorf("Key directory should not exist after removal")
+		}
+	})
+}
diff --git a/internal/secret/pgpunlock_test.go b/internal/secret/pgpunlock_test.go
new file mode 100644
index 0000000..3af8712
--- /dev/null
+++ b/internal/secret/pgpunlock_test.go
@@ -0,0 +1,226 @@
+package secret_test
+
+import (
+	"encoding/json"
+	"fmt"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"strings"
+	"testing"
+	"time"
+
+	"git.eeqj.de/sneak/secret/internal/secret"
+	"github.com/spf13/afero"
+)
+
+func TestPGPUnlockKeyWithRealFS(t *testing.T) {
+	// Skip tests if gpg is not available
+	if _, err := exec.LookPath("gpg"); err != nil {
+		t.Skip("GPG not available, skipping PGP unlock key tests")
+	}
+
+	// Create a temporary directory for our tests
+	tempDir, err := os.MkdirTemp("", "secret-pgp-test-")
+	if err != nil {
+		t.Fatalf("Failed to create temp dir: %v", err)
+	}
+	defer os.RemoveAll(tempDir) // Clean up after test
+
+	// Create a temporary GNUPGHOME
+	gnupgHomeDir := filepath.Join(tempDir, "gnupg")
+	if err := os.MkdirAll(gnupgHomeDir, 0700); err != nil {
+		t.Fatalf("Failed to create GNUPGHOME: %v", err)
+	}
+
+	// Save original GNUPGHOME
+	origGnupgHome := os.Getenv("GNUPGHOME")
+
+	// Set new GNUPGHOME
+	os.Setenv("GNUPGHOME", gnupgHomeDir)
+
+	// Clean up environment after test
+	defer func() {
+		if origGnupgHome != "" {
+			os.Setenv("GNUPGHOME", origGnupgHome)
+		} else {
+			os.Unsetenv("GNUPGHOME")
+		}
+	}()
+
+	// Create GPG batch file for key generation
+	batchFile := filepath.Join(tempDir, "gen-key-batch")
+	batchContent := `%echo Generating a test key
+Key-Type: RSA
+Key-Length: 2048
+Name-Real: Test User
+Name-Email: test@example.com
+Expire-Date: 0
+Passphrase: test123
+%commit
+%echo Key generation completed
+`
+	if err := os.WriteFile(batchFile, []byte(batchContent), 0600); err != nil {
+		t.Fatalf("Failed to write batch file: %v", err)
+	}
+
+	// Generate GPG key
+	t.Log("Generating GPG key...")
+	cmd := exec.Command("gpg", "--batch", "--gen-key", batchFile)
+	output, err := cmd.CombinedOutput()
+	if err != nil {
+		t.Fatalf("Failed to generate GPG key: %v\nOutput: %s", err, output)
+	}
+	t.Log("GPG key generated successfully")
+
+	// Get the key ID
+	cmd = exec.Command("gpg", "--list-secret-keys", "--with-colons")
+	output, err = cmd.CombinedOutput()
+	if err != nil {
+		t.Fatalf("Failed to list GPG keys: %v\nOutput: %s", err, output)
+	}
+
+	// Parse output to get key ID
+	var keyID string
+	lines := strings.Split(string(output), "\n")
+	for _, line := range lines {
+		if strings.HasPrefix(line, "sec:") {
+			fields := strings.Split(line, ":")
+			if len(fields) >= 5 {
+				keyID = fields[4]
+				break
+			}
+		}
+	}
+
+	if keyID == "" {
+		t.Fatalf("Failed to find GPG key ID in output: %s", output)
+	}
+	t.Logf("Generated GPG key ID: %s", keyID)
+
+	// Export GNUPGHOME variable to ensure subprocesses inherit it
+	err = os.Setenv("GNUPGHOME", gnupgHomeDir)
+	if err != nil {
+		t.Fatalf("Failed to set GNUPGHOME environment variable: %v", err)
+	}
+
+	// Use the real filesystem
+	fs := afero.NewOsFs()
+
+	// Test data
+	testMnemonic := "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
+
+	// Save original environment variable
+	oldMnemonic := os.Getenv(secret.EnvMnemonic)
+	oldGPGKeyID := os.Getenv(secret.EnvGPGKeyID)
+
+	// Set test environment variables
+	os.Setenv(secret.EnvMnemonic, testMnemonic)
+	os.Setenv(secret.EnvGPGKeyID, keyID)
+
+	// Clean up after test
+	defer func() {
+		if oldMnemonic != "" {
+			os.Setenv(secret.EnvMnemonic, oldMnemonic)
+		} else {
+			os.Unsetenv(secret.EnvMnemonic)
+		}
+
+		if oldGPGKeyID != "" {
+			os.Setenv(secret.EnvGPGKeyID, oldGPGKeyID)
+		} else {
+			os.Unsetenv(secret.EnvGPGKeyID)
+		}
+	}()
+
+	// Create the directory structure for test
+	keyDir := filepath.Join(tempDir, "unlock-key")
+	if err := os.MkdirAll(keyDir, secret.DirPerms); err != nil {
+		t.Fatalf("Failed to create key directory: %v", err)
+	}
+
+	// Set up test metadata
+	metadata := secret.UnlockKeyMetadata{
+		ID:        fmt.Sprintf("%s-pgp", keyID),
+		Type:      "pgp",
+		CreatedAt: time.Now(),
+		Flags:     []string{"gpg", "encrypted"},
+	}
+
+	// We'll skip the CreatePGPUnlockKey test since it requires registered vault functions
+	t.Run("CreatePGPUnlockKey", func(t *testing.T) {
+		t.Skip("Skipping test that requires registered vault functions")
+	})
+
+	// Create a PGP unlock key for the remaining tests
+	unlockKey := secret.NewPGPUnlockKey(fs, keyDir, metadata)
+
+	// Test getting GPG key ID
+	t.Run("GetGPGKeyID", func(t *testing.T) {
+		// Create PGP metadata with GPG key ID
+		type PGPUnlockKeyMetadata struct {
+			secret.UnlockKeyMetadata
+			GPGKeyID string `json:"gpg_key_id"`
+		}
+
+		pgpMetadata := PGPUnlockKeyMetadata{
+			UnlockKeyMetadata: metadata,
+			GPGKeyID:          keyID,
+		}
+
+		// Write metadata file
+		metadataPath := filepath.Join(keyDir, "unlock-metadata.json")
+		metadataBytes, err := json.MarshalIndent(pgpMetadata, "", "  ")
+		if err != nil {
+			t.Fatalf("Failed to marshal metadata: %v", err)
+		}
+		if err := afero.WriteFile(fs, metadataPath, metadataBytes, secret.FilePerms); err != nil {
+			t.Fatalf("Failed to write metadata: %v", err)
+		}
+
+		// Get GPG key ID
+		retrievedKeyID, err := unlockKey.GetGPGKeyID()
+		if err != nil {
+			t.Fatalf("Failed to get GPG key ID: %v", err)
+		}
+
+		// Verify key ID
+		if retrievedKeyID != keyID {
+			t.Errorf("Expected GPG key ID '%s', got '%s'", keyID, retrievedKeyID)
+		}
+	})
+
+	// Test getting identity from PGP unlock key
+	t.Run("GetIdentity", func(t *testing.T) {
+		// For this test, we'll do a simplified version since GPG operations
+		// can be tricky in automated tests
+		t.Skip("Skipping GetIdentity test due to complex GPG operations in automated testing")
+	})
+
+	// Test removing the unlock key
+	t.Run("RemoveUnlockKey", func(t *testing.T) {
+		// Ensure key directory exists before removal
+		keyExists, err := afero.DirExists(fs, keyDir)
+		if err != nil {
+			t.Fatalf("Failed to check if key directory exists: %v", err)
+		}
+		if !keyExists {
+			t.Fatalf("Key directory does not exist: %s", keyDir)
+		}
+
+		// Remove unlock key
+		err = unlockKey.Remove()
+		if err != nil {
+			t.Fatalf("Failed to remove unlock key: %v", err)
+		}
+
+		// Verify directory is gone
+		keyExists, err = afero.DirExists(fs, keyDir)
+		if err != nil {
+			t.Fatalf("Failed to check if key directory exists: %v", err)
+		}
+		if keyExists {
+			t.Errorf("Key directory still exists after removal: %s", keyDir)
+		}
+	})
+}
diff --git a/internal/vault/integration_test.go b/internal/vault/integration_test.go
new file mode 100644
index 0000000..e2570d3
--- /dev/null
+++ b/internal/vault/integration_test.go
@@ -0,0 +1,425 @@
+package vault_test
+
+import (
+	"os"
+	"path/filepath"
+	"testing"
+
+	"git.eeqj.de/sneak/secret/internal/secret"
+	"git.eeqj.de/sneak/secret/internal/vault"
+	"git.eeqj.de/sneak/secret/pkg/agehd"
+	"github.com/spf13/afero"
+)
+
+func TestVaultWithRealFilesystem(t *testing.T) {
+	// Create a temporary directory for our tests
+	tempDir, err := os.MkdirTemp("", "secret-test-")
+	if err != nil {
+		t.Fatalf("Failed to create temp dir: %v", err)
+	}
+	defer os.RemoveAll(tempDir) // Clean up after test
+
+	// Use the real filesystem
+	fs := afero.NewOsFs()
+
+	// Test mnemonic
+	testMnemonic := "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
+
+	// Save original environment variables
+	oldMnemonic := os.Getenv(secret.EnvMnemonic)
+	oldPassphrase := os.Getenv(secret.EnvUnlockPassphrase)
+
+	// Set test environment variables
+	os.Setenv(secret.EnvMnemonic, testMnemonic)
+	os.Setenv(secret.EnvUnlockPassphrase, "test-passphrase")
+
+	// Clean up after test
+	defer func() {
+		if oldMnemonic != "" {
+			os.Setenv(secret.EnvMnemonic, oldMnemonic)
+		} else {
+			os.Unsetenv(secret.EnvMnemonic)
+		}
+
+		if oldPassphrase != "" {
+			os.Setenv(secret.EnvUnlockPassphrase, oldPassphrase)
+		} else {
+			os.Unsetenv(secret.EnvUnlockPassphrase)
+		}
+	}()
+
+	// Test symlink handling
+	t.Run("SymlinkHandling", func(t *testing.T) {
+		stateDir := filepath.Join(tempDir, "symlink-test")
+		if err := os.MkdirAll(stateDir, 0700); err != nil {
+			t.Fatalf("Failed to create state dir: %v", err)
+		}
+
+		// Create a test vault
+		vlt, err := vault.CreateVault(fs, stateDir, "test-vault")
+		if err != nil {
+			t.Fatalf("Failed to create vault: %v", err)
+		}
+
+		// Get the vault directory
+		vaultDir, err := vlt.GetDirectory()
+		if err != nil {
+			t.Fatalf("Failed to get vault directory: %v", err)
+		}
+
+		// Create a symlink to the vault directory in a different location
+		symlinkPath := filepath.Join(tempDir, "test-symlink")
+		if err := os.Symlink(vaultDir, symlinkPath); err != nil {
+			t.Fatalf("Failed to create symlink: %v", err)
+		}
+
+		// Test that we can resolve the symlink correctly
+		resolvedPath, err := vault.ResolveVaultSymlink(fs, symlinkPath)
+		if err != nil {
+			t.Fatalf("Failed to resolve symlink: %v", err)
+		}
+
+		// On some platforms, the resolved path might have different case or format
+		// We'll use filepath.EvalSymlinks to get the canonical path for comparison
+		expectedPath, err := filepath.EvalSymlinks(vaultDir)
+		if err != nil {
+			t.Fatalf("Failed to evaluate symlink: %v", err)
+		}
+		actualPath, err := filepath.EvalSymlinks(resolvedPath)
+		if err != nil {
+			t.Fatalf("Failed to evaluate resolved path: %v", err)
+		}
+
+		if actualPath != expectedPath {
+			t.Errorf("Expected symlink to resolve to %s, got %s", expectedPath, actualPath)
+		}
+	})
+
+	// Test secret operations with deeply nested paths
+	t.Run("DeepPathSecrets", func(t *testing.T) {
+		stateDir := filepath.Join(tempDir, "deep-path-test")
+		if err := os.MkdirAll(stateDir, 0700); err != nil {
+			t.Fatalf("Failed to create state dir: %v", err)
+		}
+
+		// Create a test vault
+		vlt, err := vault.CreateVault(fs, stateDir, "test-vault")
+		if err != nil {
+			t.Fatalf("Failed to create vault: %v", err)
+		}
+
+		// Derive long-term key from mnemonic
+		ltIdentity, err := agehd.DeriveIdentity(testMnemonic, 0)
+		if err != nil {
+			t.Fatalf("Failed to derive long-term key: %v", err)
+		}
+
+		// Get the vault directory
+		vaultDir, err := vlt.GetDirectory()
+		if err != nil {
+			t.Fatalf("Failed to get vault directory: %v", err)
+		}
+
+		// Write long-term public key
+		ltPubKeyPath := filepath.Join(vaultDir, "pub.age")
+		pubKey := ltIdentity.Recipient().String()
+		if err := afero.WriteFile(fs, ltPubKeyPath, []byte(pubKey), secret.FilePerms); err != nil {
+			t.Fatalf("Failed to write long-term public key: %v", err)
+		}
+
+		// Unlock the vault
+		vlt.Unlock(ltIdentity)
+
+		// Create a secret with a deeply nested path
+		deepPath := "api/credentials/production/database/primary"
+		secretValue := []byte("supersecretdbpassword")
+
+		err = vlt.AddSecret(deepPath, secretValue, false)
+		if err != nil {
+			t.Fatalf("Failed to add secret with deep path: %v", err)
+		}
+
+		// List secrets and verify our deep path secret is there
+		secrets, err := vlt.ListSecrets()
+		if err != nil {
+			t.Fatalf("Failed to list secrets: %v", err)
+		}
+
+		found := false
+		for _, s := range secrets {
+			if s == deepPath {
+				found = true
+				break
+			}
+		}
+
+		if !found {
+			t.Errorf("Deep path secret not found in listed secrets")
+		}
+
+		// Retrieve the secret and verify its value
+		retrievedValue, err := vlt.GetSecret(deepPath)
+		if err != nil {
+			t.Fatalf("Failed to retrieve deep path secret: %v", err)
+		}
+
+		if string(retrievedValue) != string(secretValue) {
+			t.Errorf("Retrieved value doesn't match. Expected %q, got %q",
+				string(secretValue), string(retrievedValue))
+		}
+	})
+
+	// Test key caching in GetOrDeriveLongTermKey
+	t.Run("KeyCaching", func(t *testing.T) {
+		stateDir := filepath.Join(tempDir, "key-cache-test")
+		if err := os.MkdirAll(stateDir, 0700); err != nil {
+			t.Fatalf("Failed to create state dir: %v", err)
+		}
+
+		// Create a test vault
+		vlt, err := vault.CreateVault(fs, stateDir, "test-vault")
+		if err != nil {
+			t.Fatalf("Failed to create vault: %v", err)
+		}
+
+		// Derive long-term key from mnemonic
+		ltIdentity, err := agehd.DeriveIdentity(testMnemonic, 0)
+		if err != nil {
+			t.Fatalf("Failed to derive long-term key: %v", err)
+		}
+
+		// Get the vault directory
+		vaultDir, err := vlt.GetDirectory()
+		if err != nil {
+			t.Fatalf("Failed to get vault directory: %v", err)
+		}
+
+		// Write long-term public key
+		ltPubKeyPath := filepath.Join(vaultDir, "pub.age")
+		pubKey := ltIdentity.Recipient().String()
+		if err := afero.WriteFile(fs, ltPubKeyPath, []byte(pubKey), secret.FilePerms); err != nil {
+			t.Fatalf("Failed to write long-term public key: %v", err)
+		}
+
+		// Verify the vault is locked initially
+		if !vlt.Locked() {
+			t.Errorf("Vault should be locked initially")
+		}
+
+		// First call to GetOrDeriveLongTermKey should derive and cache the key
+		firstKey, err := vlt.GetOrDeriveLongTermKey()
+		if err != nil {
+			t.Fatalf("Failed to get long-term key: %v", err)
+		}
+
+		// Verify the vault is now unlocked
+		if vlt.Locked() {
+			t.Errorf("Vault should be unlocked after GetOrDeriveLongTermKey")
+		}
+
+		// Second call should return the cached key without re-deriving
+		secondKey, err := vlt.GetOrDeriveLongTermKey()
+		if err != nil {
+			t.Fatalf("Failed to get cached long-term key: %v", err)
+		}
+
+		// Verify both keys are the same instance
+		if firstKey != secondKey {
+			t.Errorf("Second key call should return same instance as first call")
+		}
+
+		// Verify the public key matches what we expect
+		expectedPubKey := ltIdentity.Recipient().String()
+		actualPubKey := firstKey.Recipient().String()
+		if actualPubKey != expectedPubKey {
+			t.Errorf("Public key mismatch. Expected %s, got %s", expectedPubKey, actualPubKey)
+		}
+
+		// Now clear the key and verify it's locked again
+		vlt.ClearLongTermKey()
+		if !vlt.Locked() {
+			t.Errorf("Vault should be locked after clearing key")
+		}
+
+		// Get the key again and verify it works
+		thirdKey, err := vlt.GetOrDeriveLongTermKey()
+		if err != nil {
+			t.Fatalf("Failed to re-derive long-term key: %v", err)
+		}
+
+		// Verify the public key still matches
+		actualPubKey = thirdKey.Recipient().String()
+		if actualPubKey != expectedPubKey {
+			t.Errorf("Re-derived public key mismatch. Expected %s, got %s", expectedPubKey, actualPubKey)
+		}
+	})
+
+	// Test vault name validation
+	t.Run("VaultNameValidation", func(t *testing.T) {
+		stateDir := filepath.Join(tempDir, "name-validation-test")
+		if err := os.MkdirAll(stateDir, 0700); err != nil {
+			t.Fatalf("Failed to create state dir: %v", err)
+		}
+
+		// Test valid vault names
+		validNames := []string{
+			"default",
+			"test-vault",
+			"production.vault",
+			"vault_123",
+			"a-very-long-vault-name-with-dashes",
+		}
+
+		for _, name := range validNames {
+			_, err := vault.CreateVault(fs, stateDir, name)
+			if err != nil {
+				t.Errorf("Failed to create vault with valid name %q: %v", name, err)
+			}
+		}
+
+		// Test invalid vault names
+		invalidNames := []string{
+			"",             // Empty
+			"UPPERCASE",    // Uppercase not allowed
+			"invalid/name", // Slashes not allowed in vault names
+			"invalid name", // Spaces not allowed
+			"invalid@name", // Special chars not allowed
+		}
+
+		for _, name := range invalidNames {
+			_, err := vault.CreateVault(fs, stateDir, name)
+			if err == nil {
+				t.Errorf("Expected error creating vault with invalid name %q, but got none", name)
+			}
+		}
+	})
+
+	// Test multiple vaults and switching between them
+	t.Run("MultipleVaults", func(t *testing.T) {
+		stateDir := filepath.Join(tempDir, "multi-vault-test")
+		if err := os.MkdirAll(stateDir, 0700); err != nil {
+			t.Fatalf("Failed to create state dir: %v", err)
+		}
+
+		// Create three vaults
+		vaultNames := []string{"vault1", "vault2", "vault3"}
+		for _, name := range vaultNames {
+			_, err := vault.CreateVault(fs, stateDir, name)
+			if err != nil {
+				t.Fatalf("Failed to create vault %s: %v", name, err)
+			}
+		}
+
+		// List vaults and verify all three are there
+		vaults, err := vault.ListVaults(fs, stateDir)
+		if err != nil {
+			t.Fatalf("Failed to list vaults: %v", err)
+		}
+
+		if len(vaults) != 3 {
+			t.Errorf("Expected 3 vaults, got %d", len(vaults))
+		}
+
+		// Test switching between vaults
+		for _, name := range vaultNames {
+			// Select the vault
+			if err := vault.SelectVault(fs, stateDir, name); err != nil {
+				t.Fatalf("Failed to select vault %s: %v", name, err)
+			}
+
+			// Get current vault and verify it's the one we selected
+			currentVault, err := vault.GetCurrentVault(fs, stateDir)
+			if err != nil {
+				t.Fatalf("Failed to get current vault after selecting %s: %v", name, err)
+			}
+
+			if currentVault.GetName() != name {
+				t.Errorf("Expected current vault to be %s, got %s", name, currentVault.GetName())
+			}
+		}
+	})
+
+	// Test adding a secret in one vault and verifying it's not visible in another
+	t.Run("VaultIsolation", func(t *testing.T) {
+		stateDir := filepath.Join(tempDir, "isolation-test")
+		if err := os.MkdirAll(stateDir, 0700); err != nil {
+			t.Fatalf("Failed to create state dir: %v", err)
+		}
+
+		// Create two vaults
+		vault1, err := vault.CreateVault(fs, stateDir, "vault1")
+		if err != nil {
+			t.Fatalf("Failed to create vault1: %v", err)
+		}
+
+		vault2, err := vault.CreateVault(fs, stateDir, "vault2")
+		if err != nil {
+			t.Fatalf("Failed to create vault2: %v", err)
+		}
+
+		// Derive long-term key from mnemonic
+		ltIdentity, err := agehd.DeriveIdentity(testMnemonic, 0)
+		if err != nil {
+			t.Fatalf("Failed to derive long-term key: %v", err)
+		}
+
+		// Setup both vaults with the same long-term key
+		for _, vlt := range []*vault.Vault{vault1, vault2} {
+			vaultDir, err := vlt.GetDirectory()
+			if err != nil {
+				t.Fatalf("Failed to get vault directory: %v", err)
+			}
+
+			ltPubKeyPath := filepath.Join(vaultDir, "pub.age")
+			pubKey := ltIdentity.Recipient().String()
+			if err := afero.WriteFile(fs, ltPubKeyPath, []byte(pubKey), secret.FilePerms); err != nil {
+				t.Fatalf("Failed to write long-term public key: %v", err)
+			}
+
+			vlt.Unlock(ltIdentity)
+		}
+
+		// Add a secret to vault1
+		secretName := "test-secret"
+		secretValue := []byte("secret in vault1")
+		if err := vault1.AddSecret(secretName, secretValue, false); err != nil {
+			t.Fatalf("Failed to add secret to vault1: %v", err)
+		}
+
+		// Verify the secret exists in vault1
+		vault1Secrets, err := vault1.ListSecrets()
+		if err != nil {
+			t.Fatalf("Failed to list secrets in vault1: %v", err)
+		}
+
+		found := false
+		for _, s := range vault1Secrets {
+			if s == secretName {
+				found = true
+				break
+			}
+		}
+
+		if !found {
+			t.Errorf("Secret not found in vault1")
+		}
+
+		// Verify the secret does NOT exist in vault2
+		vault2Secrets, err := vault2.ListSecrets()
+		if err != nil {
+			t.Fatalf("Failed to list secrets in vault2: %v", err)
+		}
+
+		found = false
+		for _, s := range vault2Secrets {
+			if s == secretName {
+				found = true
+				break
+			}
+		}
+
+		if found {
+			t.Errorf("Secret from vault1 should not be visible in vault2")
+		}
+	})
+}
diff --git a/internal/vault/management.go b/internal/vault/management.go
index 8b9efb6..e5204ce 100644
--- a/internal/vault/management.go
+++ b/internal/vault/management.go
@@ -27,9 +27,9 @@ func isValidVaultName(name string) bool {
 	return matched
 }
 
-// resolveVaultSymlink resolves the currentvault symlink by reading either the symlink target or file contents
+// ResolveVaultSymlink resolves the currentvault symlink by reading either the symlink target or file contents
 // This function is designed to work on both Unix and Windows systems, as well as with in-memory filesystems
-func resolveVaultSymlink(fs afero.Fs, symlinkPath string) (string, error) {
+func ResolveVaultSymlink(fs afero.Fs, symlinkPath string) (string, error) {
 	secret.Debug("resolveVaultSymlink starting", "symlink_path", symlinkPath)
 
 	// First try to handle the path as a real symlink (works on Unix systems)
@@ -121,7 +121,7 @@ func GetCurrentVault(fs afero.Fs, stateDir string) (*Vault, error) {
 
 	// Resolve the symlink to get the actual vault directory
 	secret.Debug("Resolving vault symlink")
-	targetPath, err := resolveVaultSymlink(fs, currentVaultPath)
+	targetPath, err := ResolveVaultSymlink(fs, currentVaultPath)
 	if err != nil {
 		return nil, err
 	}
@@ -240,10 +240,9 @@ func SelectVault(fs afero.Fs, stateDir string, name string) error {
 	// First try to remove existing symlink if it exists
 	if _, err := fs.Stat(currentVaultPath); err == nil {
 		secret.Debug("Removing existing current vault symlink", "path", currentVaultPath)
-		if err := fs.Remove(currentVaultPath); err != nil {
-			// On some systems, removing a symlink may fail
-			// Just ignore and try to create/update it anyway
-		}
+		// Ignore errors from Remove as we'll try to create/update it anyway.
+		// On some systems, removing a symlink may fail but the subsequent create may still succeed.
+		_ = fs.Remove(currentVaultPath)
 	}
 
 	// Try to create a real symlink first (works on Unix systems)
diff --git a/internal/vault/unlock_keys.go b/internal/vault/unlock_keys.go
index b84f16a..6a44d77 100644
--- a/internal/vault/unlock_keys.go
+++ b/internal/vault/unlock_keys.go
@@ -37,7 +37,7 @@ func (v *Vault) GetCurrentUnlockKey() (secret.UnlockKey, error) {
 	if _, ok := v.fs.(*afero.OsFs); ok {
 		secret.Debug("Resolving unlock key symlink (real filesystem)")
 		// For real filesystems, resolve the symlink properly
-		unlockKeyDir, err = resolveVaultSymlink(v.fs, currentUnlockKeyPath)
+		unlockKeyDir, err = ResolveVaultSymlink(v.fs, currentUnlockKeyPath)
 		if err != nil {
 			secret.Debug("Failed to resolve unlock key symlink", "error", err, "symlink_path", currentUnlockKeyPath)
 			return nil, fmt.Errorf("failed to resolve current unlock key symlink: %w", err)
diff --git a/internal/vault/vault.go b/internal/vault/vault.go
index aa11699..36d255a 100644
--- a/internal/vault/vault.go
+++ b/internal/vault/vault.go
@@ -21,13 +21,16 @@ type Vault struct {
 }
 
 // NewVault creates a new Vault instance
-func NewVault(fs afero.Fs, name string, stateDir string) *Vault {
-	return &Vault{
+func NewVault(fs afero.Fs, stateDir string, name string) *Vault {
+	secret.Debug("Creating NewVault instance")
+	v := &Vault{
 		Name:        name,
 		fs:          fs,
 		stateDir:    stateDir,
 		longTermKey: nil,
 	}
+	secret.Debug("Created NewVault instance successfully")
+	return v
 }
 
 // Locked returns true if the vault doesn't have a long-term key in memory