pub mod backup_bundle_store;
pub mod backup_bundle_sweeper;
pub mod ops;
pub mod restore;
#[cfg(test)]
mod restore_tests;
#[cfg(test)]
mod test_support;
use vta_sdk::protocols::backup_management::types::BackupEnvironment;
use vta_support::restore_stage::AnchorBinding;
use vti_common::error::AppError;
use vti_common::store::{KeyspaceHandle, Store};
#[cfg(test)]
pub(crate) const BACKUP_BUNDLES_TEST: &str = "backup_bundles_test";
#[cfg(test)]
pub(crate) const BACKUP_BUNDLES_SWEEPER_TEST: &str = "backup_bundles_sweeper_test";
#[derive(Clone, Copy)]
pub struct BackupTarget<'a> {
pub store: &'a Store,
pub storage_key: Option<[u8; 32]>,
pub environment: BackupEnvironment,
}
impl BackupTarget<'_> {
pub fn keyspace(&self, name: &str) -> Result<KeyspaceHandle, AppError> {
let ks = self.store.keyspace(name)?;
Ok(match self.storage_key {
Some(key) => ks.with_encryption(key),
None => ks,
})
}
pub fn bootstrap(&self) -> Result<KeyspaceHandle, AppError> {
self.store.keyspace(vta_keyspaces::BOOTSTRAP)
}
}
pub struct RestoredSecrets<'a> {
pub seed: &'a [u8],
pub jwt_key: Option<[u8; 32]>,
pub vta_did: Option<&'a str>,
}
#[derive(Default)]
pub struct PreparedCommit {
pub tee_secrets_row: Option<Vec<u8>>,
pub anchor: Option<AnchorBinding>,
}
#[async_trait::async_trait]
pub trait RestoreCommitter: Sync {
async fn prepare(&self, secrets: &RestoredSecrets<'_>) -> Result<PreparedCommit, AppError>;
async fn commit(
&self,
secrets: &RestoredSecrets<'_>,
prepared: PreparedCommit,
) -> Result<(), AppError>;
async fn abort(&self) {}
}
pub struct SeedStoreCommitter<'a> {
pub seed_store: &'a dyn vta_keys::seed_store::SeedStore,
}
#[async_trait::async_trait]
impl RestoreCommitter for SeedStoreCommitter<'_> {
async fn prepare(&self, _secrets: &RestoredSecrets<'_>) -> Result<PreparedCommit, AppError> {
if !self.seed_store.set_persists_across_restart() {
return Err(AppError::Validation(
"this VTA's seed store cannot persist a new seed across a restart, so a \
restore could never take effect. Configure a persistent secret-store \
backend (keyring, aws, gcp, azure, vault, k8s) before restoring."
.into(),
));
}
Ok(PreparedCommit::default())
}
async fn commit(
&self,
secrets: &RestoredSecrets<'_>,
_prepared: PreparedCommit,
) -> Result<(), AppError> {
self.seed_store
.set(secrets.seed)
.await
.map_err(|e| AppError::Internal(format!("seed store: {e}")))
}
}