use tracing::info;
use crate::acl::{AclEntry, Role, store_acl_entry};
use crate::auth::credentials::generate_did_key;
use crate::auth::session::now_epoch;
use crate::config::AppConfig;
use crate::contexts;
use crate::error::AppError;
use crate::store::{KeyspaceHandle, Store};
use vta_sdk::credentials::CredentialBundle;
const ADMIN_CREDENTIAL_STORE_KEY: &str = "tee:admin_credential";
pub async fn maybe_bootstrap_admin(
config: &AppConfig,
store: &Store,
storage_encryption_key: Option<[u8; 32]>,
) -> Result<(), AppError> {
let kms_config = match &config.tee.kms {
Some(kms) => kms,
None => return Ok(()),
};
let apply_enc = |ks: KeyspaceHandle| -> KeyspaceHandle {
if let Some(key) = storage_encryption_key {
ks.with_encryption(key)
} else {
ks
}
};
let keys_ks = apply_enc(store.keyspace("keys")?);
let contexts_ks = apply_enc(store.keyspace("contexts")?);
let acl_ks = apply_enc(store.keyspace("acl")?);
if keys_ks.get_raw(ADMIN_CREDENTIAL_STORE_KEY).await?.is_some() {
info!("admin credential already bootstrapped — skipping");
return Ok(());
}
let context_id = &kms_config.admin_context_id;
let _ctx = match contexts::get_context(&contexts_ks, context_id).await? {
Some(ctx) => ctx,
None => contexts::create_context(&contexts_ks, context_id, "Default Admin Context")
.await
.map_err(|e| AppError::Internal(format!("failed to create admin context: {e}")))?,
};
if let Some(ref admin_did) = kms_config.admin_did {
info!(did = %admin_did, context_id, "bootstrapping super-admin from config admin_did");
let entry = AclEntry {
did: admin_did.clone(),
role: Role::Admin,
label: Some("TEE bootstrap admin".to_string()),
allowed_contexts: vec![],
created_at: now_epoch(),
created_by: "tee:bootstrap".to_string(),
};
store_acl_entry(&acl_ks, &entry).await?;
keys_ks
.insert_raw(ADMIN_CREDENTIAL_STORE_KEY, admin_did.as_bytes().to_vec())
.await?;
store.persist().await?;
info!(
did = %admin_did,
context_id,
"super-admin ACL created — connect using the private key for this DID"
);
} else {
info!(context_id, "no admin_did configured — generating random admin credential");
let (did, private_key_multibase) = generate_did_key();
let entry = AclEntry {
did: did.clone(),
role: Role::Admin,
label: Some("TEE bootstrap admin".to_string()),
allowed_contexts: vec![],
created_at: now_epoch(),
created_by: "tee:bootstrap".to_string(),
};
store_acl_entry(&acl_ks, &entry).await?;
let vta_did = config.vta_did.clone().unwrap_or_default();
let bundle = CredentialBundle {
did: did.clone(),
private_key_multibase,
vta_did,
vta_url: config.public_url.clone(),
};
let credential = bundle
.encode()
.map_err(|e| AppError::Internal(format!("failed to encode credential bundle: {e}")))?;
keys_ks
.insert_raw(ADMIN_CREDENTIAL_STORE_KEY, credential.as_bytes().to_vec())
.await?;
let bootstrap_ks = store.keyspace("bootstrap")?;
bootstrap_ks
.insert_raw(ADMIN_CREDENTIAL_STORE_KEY, credential.as_bytes().to_vec())
.await?;
store.persist().await?;
info!(
did = %did,
context_id,
"super-admin credential generated — retrieve via GET /attestation/admin-credential"
);
}
Ok(())
}