Skip to main content
The Vault is Palyra’s central abstraction for the secure storage and retrieval of sensitive information, such as API keys, session tokens, and encryption seeds. It provides a platform-agnostic API while leveraging native OS secure enclaves (macOS Keychain, Linux Secret Service, Windows DPAPI) or local encrypted files to protect data at rest.

Core Abstraction: The Vault

The Vault struct crates/palyra-vault/src/api.rs#12-12 acts as the primary interface for secret management. It coordinates between the BlobBackend for raw data storage and the metadata layer for indexing and integrity.

Data Flow: Putting a Secret

When a secret is stored via put_secret crates/palyra-vault/src/tests.rs#138-138, the following sequence occurs:
  1. Key Validation: The key is checked for length and character constraints crates/palyra-vault/src/crypto.rs#93-113.
  2. Object ID Generation: A unique storage ID is derived using SHA-256 of the scope and key crates/palyra-vault/src/crypto.rs#115-122.
  3. Encryption (Sealing): The payload is encrypted using ChaCha20-Poly1305. The Key Encryption Key (KEK) is derived from the device identity crates/palyra-vault/src/crypto.rs#20-33.
  4. Blob Storage: The encrypted blob is passed to the active BlobBackend crates/palyra-vault/src/backend.rs#90-90.
  5. Metadata Update: An entry is added to objects.store.json containing the key, timestamps, and size crates/palyra-vault/src/backend.rs#26-26.

Secret Storage Architecture

The diagram below illustrates how the Vault bridges the high-level API to platform-specific storage entities. Vault System Entities Sources: crates/palyra-vault/src/api.rs#12-12, crates/palyra-vault/src/backend.rs#88-93, crates/palyra-vault/src/crypto.rs#89-91

Blob Backends

The BlobBackend trait crates/palyra-vault/src/backend.rs#88-93 abstracts the physical storage of encrypted blobs. The system automatically selects the most secure backend available for the current platform unless overridden in the configuration.
Backend KindPlatformImplementation Details
MacosKeychainmacOSUses security CLI to interface with the macOS Keychain crates/palyra-vault/src/backend.rs#28-29.
LinuxSecretServiceLinuxUses secret-tool to interface with the Freedesktop Secret Service API crates/palyra-vault/src/backend.rs#30-35.
WindowsDpapiWindowsUses DPAPI (CryptProtectData) to bind secrets to the current user’s credentials crates/palyra-vault/src/backend.rs#36-37.
EncryptedFileAllStores blobs in an objects/ directory with filesystem-level owner-only permissions crates/palyra-vault/src/backend.rs#194-205.

Backend Selection Logic

The select_backend function crates/palyra-vault/src/backend.rs#95-133 determines the storage mechanism. If a backend.kind marker file exists in the vault root, it enforces that backend. Otherwise, it follows the BackendPreference (Auto or EncryptedFile) crates/palyra-vault/src/backend.rs#83-86. Sources: crates/palyra-vault/src/backend.rs#39-49, crates/palyra-vault/src/backend.rs#135-158

Encryption and Key Derivation

Palyra uses an Envelope Encryption model. Secrets are sealed using a device-specific Key Encryption Key (KEK).

KEK Derivation Process

The KEK is never stored directly. It is derived at runtime from the device’s private identity material:
  1. Seed Extraction: The private_key_pem is extracted from the identity store crates/palyra-vault/src/crypto.rs#50-78.
  2. HKDF Expansion: The seed is processed through HMAC-based Key Derivation Function (HKDF) using the salt palyra.vault.kek.v1 crates/palyra-vault/src/crypto.rs#13-14.
  3. Output: A 32-byte KEK is generated for use in ChaCha20-Poly1305 crates/palyra-vault/src/crypto.rs#80-87.

Integrity with AAD

Each secret is bound to its scope and key via Additional Authenticated Data (AAD). The AAD string palyra.vault.v1|<scope>|<key> is passed to the AEAD cipher crates/palyra-vault/src/crypto.rs#89-91. This prevents “cipher-text substitution” attacks where an attacker might try to move an encrypted blob from one key to another. Sources: crates/palyra-vault/src/crypto.rs#13-33, crates/palyra-vault/src/crypto.rs#156-174

Vault Configuration and References

In the palyra.toml configuration, sensitive fields are not stored as plaintext. Instead, they use a VaultRef.

VaultRef Structure

A VaultRef is a string in the format scope/key (e.g., global/openai_api_key) crates/palyra-vault/src/api.rs#12-12.

Configuration Injection

When the daemon loads the configuration, it identifies paths marked in SECRET_CONFIG_PATHS. If a value at one of these paths matches a VaultRef pattern, the daemon:
  1. Opens the Vault.
  2. Resolves the VaultRef to its decrypted bytes.
  3. Injects the secret into the runtime configuration while keeping the file on disk clean.
Secret Configuration Lifecycle Sources: crates/palyra-cli/src/commands/secrets.rs#144-179, crates/palyra-vault/src/api.rs#12-12

Security Guarantees

Atomic Writes and Persistence

The EncryptedFileBackend ensures data integrity during writes using a temporary file and atomic rename strategy crates/palyra-vault/src/backend.rs#120-130. This prevents partial writes from corrupting the vault if the process is interrupted.

Memory Safety

The SensitiveBytes wrapper crates/palyra-vault/src/crypto.rs#156-174 is used for decrypted secrets. It implements the Drop trait to zero-fill the underlying memory buffer when the secret is no longer in use, minimizing the window for memory forensics.

Filesystem Hardening

The Vault enforces strict filesystem permissions. Both the vault root and individual object files are checked to ensure they are accessible only by the owner (0700 for directories, 0600 for files) crates/palyra-vault/src/filesystem.rs#15-15. Sources: crates/palyra-vault/src/backend.rs#120-132, crates/palyra-vault/src/crypto.rs#170-174, crates/palyra-vault/src/backend.rs#199-205