use remora_domain::ports::{ActiveCredentialGrant, CredentialResolver}; use crate::error::CredentialError; use crate::registry::CredentialRegistry; use crate::types::SecretMaterial; use crate::vault::VaultClient; /// Grant-bound credential resolver. /// /// Every resolve reloads durable execution-grant pin state. Unknown, revoked, /// or destroyed pins fail closed. Callers cannot resolve by forging bare /// credential/version IDs. #[derive(Clone, Debug)] pub struct GrantBoundResolver { registry: CredentialRegistry, vault: VaultClient, } impl GrantBoundResolver { #[must_use] pub fn new(registry: CredentialRegistry, vault: VaultClient) -> Self { Self { registry, vault } } pub async fn resolve_access( &self, access: &ActiveCredentialGrant, ) -> Result { let pin = self .registry .load_active_grant_pin(access.grant_id()) .await? .ok_or(CredentialError::GrantInactive)?; let coord = self .registry .load_version_coord(&pin.credential_id, &pin.version_id) .await? .ok_or(CredentialError::Unresolvable)?; if coord.destroyed { return Err(CredentialError::Unresolvable); } self.vault .get_version( &coord.secret_mount, &coord.secret_path, coord.vault_kv_version, ) .await } } impl CredentialResolver for GrantBoundResolver { type Error = CredentialError; type Secret = SecretMaterial; fn resolve( &self, access: &ActiveCredentialGrant, ) -> impl std::future::Future> + Send { self.resolve_access(access) } }