pub struct PasswordStore<S: Store> { /* private fields */ }Expand description
Password-encrypted store wrapper.
Wraps any Store type with transparent AES-256-GCM encryption using
password-derived keys (Argon2id). The type parameter S specifies the
wrapped store type, and PasswordStore<S> delegates Store::Data to
S::Data — encryption is a transport-level concern invisible at the type level.
§Type Parameter
S- The wrapped store type (e.g.,DocStore,Table<T>)
§State Machine
PasswordStore has three states (derived from internal fields):
- Uninitialized - Created via
get_store(), no encryption configured - Locked - Has encryption config, not yet decrypted
- Unlocked - Decrypted and ready to use
State transitions:
get_store()→ Uninitialized (new) or Locked (existing)initialize()→ Unlocked (from Uninitialized only)open()→ Unlocked (from Locked only)
§Security
- Encryption: AES-256-GCM authenticated encryption
- Key Derivation: Argon2id memory-hard password hashing
- Nonces: Unique random nonce per encryption operation
- Zeroization: The converted password, temporary and cached keys, and local plaintext JSON buffers live in zeroizing owners that clear their current allocation on drop, including on errors, cancellation and unwinding
- Diagnostics: Errors about malformed decrypted metadata or rows do not include decrypted content
§Limitations
- Password Loss: Losing the password means permanent data loss
- Performance: Encryption/decryption overhead on every operation
- Metadata: Backends can observe record counts, ciphertext sizes, and access patterns
- Memory: Cleanup is best-effort for the owners above. Caller copies, plaintext
returned to the application, parsed
Doc/CRDT values, parser, KDF, cipher and allocator copies are not erased, and the registered encryptor (with its password and key) lives until the last clone of the transaction drops
§Examples
Creating a new encrypted store:
let tx = db.new_transaction().await?;
let mut encrypted = tx.get_store::<PasswordStore<DocStore>>("secrets").await?;
encrypted.initialize("my_password", Doc::new()).await?;
let docstore = encrypted.inner().await?;
docstore.set("key", "secret value").await?;
tx.commit().await?;Opening an existing encrypted store:
let tx = db.new_transaction().await?;
let mut store = tx.get_store::<PasswordStore<DocStore>>("secrets").await?;
store.open("my_password")?;
let docstore = store.inner().await?;
let value = docstore.get("key").await?;Implementations§
Source§impl<S: Store> PasswordStore<S>
impl<S: Store> PasswordStore<S>
Sourcepub async fn initialize(
&mut self,
password: impl Into<String>,
wrapped_config: Doc,
) -> Result<()>
pub async fn initialize( &mut self, password: impl Into<String>, wrapped_config: Doc, ) -> Result<()>
Initialize encryption on an uninitialized store
This configures encryption for a PasswordStore that was obtained via
get_store(). The wrapped store’s type (derived from S) and config
are encrypted and stored in the PasswordStore’s configuration in _index.
After calling this method, the store transitions to the Unlocked state and is ready to use.
§Arguments
password- Password for encryption. After the state check, it is converted and moved into a zeroizing owner before any fallible work. An argument rejected by the state check, and copies the caller retains, are not zeroized by this method.wrapped_config- Configuration for wrapped store
§Returns
Ok(()) on success, the store is now unlocked
§Errors
- Returns error if store is not in Uninitialized state
- Returns error if encryption fails
§Examples
let tx = db.new_transaction().await?;
let mut encrypted = tx.get_store::<PasswordStore<DocStore>>("secrets").await?;
encrypted.initialize("my_password", Doc::new()).await?;
let docstore = encrypted.inner().await?;
docstore.set("key", "secret value").await?;
tx.commit().await?;Sourcepub fn open(&mut self, password: impl Into<String>) -> Result<()>
pub fn open(&mut self, password: impl Into<String>) -> Result<()>
Open (unlock) the encrypted store with a password
This decrypts the wrapped store configuration and caches the password for subsequent encrypt/decrypt operations.
§Arguments
password- Password to decrypt the store
§Returns
Ok(()) if password is correct, Err otherwise
§Errors
- Returns error if store is Uninitialized (use
initialize()first) - Returns error if store is already Unlocked
- Returns error if password is incorrect
§Security
The password is cached in memory (with zeroization on drop) for convenience. After the state check, it is converted and moved into a zeroizing owner before any fallible work; an argument rejected by the state check, and copies the caller retains, are not zeroized by this method. Errors about malformed decrypted metadata or a mismatched wrapped type do not include decrypted content.
Sourcepub fn is_initialized(&self) -> bool
pub fn is_initialized(&self) -> bool
Check if the store is initialized (has encryption configuration)
Sourcepub async fn inner(&self) -> Result<S>
pub async fn inner(&self) -> Result<S>
Get the wrapped store, providing transparent encryption.
Returns the inner S store instance that transparently encrypts data
on write and decrypts on read. The wrapped store is unaware of
encryption — all crypto operations are handled by an encryptor
registered with the transaction during open() or initialize().
§Errors
- Returns error if store is not opened (call
open()first)
§Examples
let mut encrypted = tx2.get_store::<PasswordStore<DocStore>>("test").await?;
encrypted.open("pass")?;
let docstore = encrypted.inner().await?;
docstore.set("key", "value").await?; // Automatically encryptedTrait Implementations§
Source§impl<S: Store> Registered for PasswordStore<S>
impl<S: Store> Registered for PasswordStore<S>
Source§impl<S: Store> Store for PasswordStore<S>
impl<S: Store> Store for PasswordStore<S>
Source§type Data = <S as Store>::Data
type Data = <S as Store>::Data
Source§fn state_model() -> StoreStateModel<Self::Data>
fn state_model() -> StoreStateModel<Self::Data>
Source§fn load<'life0, 'async_trait>(
txn: &'life0 Transaction,
subtree_name: String,
) -> Pin<Box<dyn Future<Output = Result<Self>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn load<'life0, 'async_trait>(
txn: &'life0 Transaction,
subtree_name: String,
) -> Pin<Box<dyn Future<Output = Result<Self>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Store handle associated with a specific transaction. Read moreSource§fn register<'life0, 'async_trait>(
txn: &'life0 Transaction,
subtree_name: String,
) -> Pin<Box<dyn Future<Output = Result<Self>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn register<'life0, 'async_trait>(
txn: &'life0 Transaction,
subtree_name: String,
) -> Pin<Box<dyn Future<Output = Result<Self>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
_index. Read moreSource§fn transaction(&self) -> &Transaction
fn transaction(&self) -> &Transaction
Source§fn default_config() -> Doc
fn default_config() -> Doc
Source§fn open<'life0, 'async_trait>(
txn: &'life0 Transaction,
name: impl 'async_trait + Into<String> + Send,
) -> Pin<Box<dyn Future<Output = Result<Self>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn open<'life0, 'async_trait>(
txn: &'life0 Transaction,
name: impl 'async_trait + Into<String> + Send,
) -> Pin<Box<dyn Future<Output = Result<Self>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Source§fn get_config<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<Doc>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn get_config<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<Doc>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
_index subtree. Read moreSource§fn set_config<'life0, 'async_trait>(
&'life0 self,
config: Doc,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn set_config<'life0, 'async_trait>(
&'life0 self,
config: Doc,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
_index subtree. Read moreSource§fn get_height_strategy<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<Option<HeightStrategy>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn get_height_strategy<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<Option<HeightStrategy>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
_index subtree. Read moreAuto Trait Implementations§
impl<S> Freeze for PasswordStore<S>
impl<S> !RefUnwindSafe for PasswordStore<S>
impl<S> Send for PasswordStore<S>
impl<S> Sync for PasswordStore<S>
impl<S> Unpin for PasswordStore<S>where
S: Unpin,
impl<S> UnsafeUnpin for PasswordStore<S>
impl<S> !UnwindSafe for PasswordStore<S>
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more