use vta_cli_common::render::{CYAN, DIM, GREEN, RED, RESET, print_section};
use vta_sdk::client::VtaClient;
use crate::auth;
pub(crate) async fn run(
url_override: Option<&str>,
keyring_key: &str,
fresh_tsp_probe: bool,
) -> Result<(), Box<dyn std::error::Error>> {
use affinidi_did_resolver_cache_sdk::DIDCacheClient;
let session = auth::loaded_session(keyring_key);
let did_resolver = DIDCacheClient::new(vta_sdk::resolver::build_did_cache_config_from_env())
.await
.ok();
print_section("VTA");
if let Some(ref info) = session {
match info.vta_did.as_deref() {
Some(vta_did) => {
println!(" {CYAN}{:<13}{RESET} {vta_did}", "DID");
if let Some(ref resolver) = did_resolver {
match resolver.resolve(vta_did).await {
Ok(_) => {
let method = vta_did
.strip_prefix("did:")
.and_then(|s| s.split(':').next())
.unwrap_or("?");
println!(" {GREEN}✓{RESET} resolves ({method})");
}
Err(e) => {
println!(" {RED}✗{RESET} resolution failed: {e}")
}
}
}
}
None => {
println!(
" {CYAN}{:<13}{RESET} {DIM}(pending — run `pnm setup continue <slug>`){RESET}",
"DID"
);
}
}
}
let caps = match (
session.as_ref().and_then(|s| s.vta_did.as_deref()),
did_resolver.as_ref(),
) {
(Some(vta_did), Some(resolver)) => resolver
.resolve(vta_did)
.await
.ok()
.and_then(|r| serde_json::to_value(&r.doc).ok())
.map(|doc| vta_sdk::protocol::matching::ServiceCapabilities::from_did_document(&doc)),
_ => None,
};
let has_vta_did = session
.as_ref()
.and_then(|s| s.vta_did.as_deref())
.is_some();
let (mode_label, advertised_rest_url, advertises_messaging) = match &caps {
Some(caps) => {
use vta_sdk::protocol::matching::Protocol;
let label = if caps.advertised().is_empty() {
"unknown (no advertised services)".to_string()
} else {
caps.advertised()
.iter()
.map(|p| match p {
Protocol::Tsp => "TSP",
Protocol::Didcomm => "DIDComm",
Protocol::Rest => "REST",
})
.collect::<Vec<_>>()
.join(" + ")
};
let rest_url = caps
.rest
.as_deref()
.map(|u| u.trim_matches('"').trim_end_matches('/').to_string());
(
label,
rest_url,
caps.tsp.is_some() || caps.didcomm.is_some(),
)
}
None if has_vta_did => (
"unknown (could not enumerate services)".to_string(),
None,
false,
),
None => ("(pending DID setup)".to_string(), None, false),
};
println!(" {CYAN}{:<13}{RESET} {mode_label}", "Mode");
let override_url = url_override
.map(str::trim)
.filter(|s| !s.is_empty())
.map(str::to_string);
let url_overridden =
override_url.is_some() && advertised_rest_url.as_deref() != override_url.as_deref();
let effective_rest_url = override_url.clone().or_else(|| advertised_rest_url.clone());
if let Some(ref url) = effective_rest_url {
let suffix = if url_overridden {
format!(" {DIM}(--url override){RESET}")
} else {
format!(" {DIM}(from DID){RESET}")
};
println!(" {CYAN}{:<13}{RESET} {url}{suffix}", "URL");
let probe_client = VtaClient::new(url);
match probe_client.health().await {
Ok(resp) => {
let ver = resp
.version
.as_deref()
.map(|v| format!(" (v{v})"))
.unwrap_or_default();
println!(" {CYAN}{:<13}{RESET} {GREEN}✓{RESET} ok{ver}", "Service");
}
Err(e) => {
println!(
" {CYAN}{:<13}{RESET} {RED}✗{RESET} unreachable ({e})",
"Service"
);
}
}
}
print_section("Authentication");
if let Some(ref url) = effective_rest_url {
if let Some(ref info) = session {
println!(" {CYAN}{:<13}{RESET} {}", "Client DID", info.client_did);
match auth::ensure_authenticated(url, keyring_key).await {
Ok(_token) => {
if let Some(status) = auth::session_status(keyring_key) {
match status.token_status {
vta_sdk::session::TokenStatus::Valid { expires_in_secs } => {
println!(
" {CYAN}{:<13}{RESET} {GREEN}✓{RESET} valid (expires in {expires_in_secs}s)",
"Token"
);
}
_ => {
println!(" {CYAN}{:<13}{RESET} {GREEN}✓{RESET} valid", "Token");
}
}
}
}
Err(e) => {
println!(" {CYAN}{:<13}{RESET} {RED}✗{RESET} {e}", "Token");
}
}
} else {
println!(" {DIM}Not authenticated{RESET}");
}
} else if advertises_messaging {
println!(" {DIM}Messaging-only VTA (TSP/DIDComm) — no REST auth{RESET}");
} else {
println!(" {DIM}No transport advertised{RESET}");
}
print_section("Mediator");
if let Some(ref info) = session
&& let Some(vta_did) = info.vta_did.as_deref()
{
let mediator_result = if let Some(ref resolver) = did_resolver {
vta_sdk::session::resolve_mediator_did_with_resolver(vta_did, resolver).await
} else {
vta_sdk::session::resolve_mediator_did(vta_did).await
};
let mediator_result = match mediator_result {
Ok(Some(mediator_did)) => Ok(Some((mediator_did, false))),
Ok(None) => {
if let Some(url) = effective_rest_url.as_deref() {
match auth::ensure_authenticated(url, keyring_key).await {
Ok(token) => {
let client = VtaClient::new(url);
client.set_token_async(token).await;
match client.didcomm_status().await {
Ok(status) if status.enabled => {
Ok(status.mediator_did.map(|did| (did, true)))
}
Ok(_) => Ok(None),
Err(e) => {
println!(" {DIM}(status check failed: {e}){RESET}");
Ok(None)
}
}
}
Err(e) => {
println!(" {DIM}(auth for status check failed: {e}){RESET}");
Ok(None)
}
}
} else {
Ok(None)
}
}
Err(e) => Err(e),
};
match mediator_result {
Ok(Some((mediator_did, via_status_endpoint))) => {
println!(" {CYAN}{:<13}{RESET} {mediator_did}", "DID");
if via_status_endpoint {
println!(" {DIM}discovered via /services/didcomm{RESET}");
}
if let Some(ref resolver) = did_resolver {
match resolver.resolve(&mediator_did).await {
Ok(_) => {
let method = mediator_did
.strip_prefix("did:")
.and_then(|s| s.split(':').next())
.unwrap_or("?");
println!(" {GREEN}✓{RESET} resolves ({method})");
}
Err(e) => {
println!(" {RED}✗{RESET} resolution failed: {e}");
}
}
}
match tokio::time::timeout(
std::time::Duration::from_secs(15),
vta_sdk::session::TrustPingSession::new(
&info.client_did,
&info.private_key_multibase,
&mediator_did,
),
)
.await
{
Ok(Ok(session)) => {
match tokio::time::timeout(
std::time::Duration::from_secs(20),
steady_ping(&session, None),
)
.await
{
Ok(Ok(latency)) => {
println!(" {GREEN}✓{RESET} pong ({latency}ms)");
}
Ok(Err(e)) => {
println!(" {RED}✗{RESET} trust-ping failed: {e}");
}
Err(_) => {
println!(" {RED}✗{RESET} trust-ping timed out");
}
}
print_section("VTA DIDComm");
match tokio::time::timeout(
std::time::Duration::from_secs(30),
steady_ping(&session, Some(vta_did)),
)
.await
{
Ok(Ok(latency)) => {
println!(
" {CYAN}{:<13}{RESET} {GREEN}✓{RESET} pong ({latency}ms)",
"Trust-ping"
);
}
Ok(Err(e)) => {
println!(
" {CYAN}{:<13}{RESET} {RED}✗{RESET} trust-ping failed: {e}",
"Trust-ping"
);
}
Err(_) => {
println!(
" {CYAN}{:<13}{RESET} {RED}✗{RESET} trust-ping timed out",
"Trust-ping"
);
}
}
session.shutdown().await;
}
Ok(Err(e)) => {
println!(" {RED}✗{RESET} DIDComm setup failed: {e}");
}
Err(_) => {
println!(" {RED}✗{RESET} DIDComm setup timed out");
}
}
}
Ok(None) => {
println!(" {DIM}(not configured){RESET}");
}
Err(e) => {
println!(
" {CYAN}{:<13}{RESET} {RED}✗{RESET} could not resolve VTA DID: {e}",
"DID"
);
}
}
} else {
println!(" {DIM}(no session){RESET}");
}
let tsp_mediator = caps.as_ref().and_then(|c| c.tsp.as_deref());
if let (Some(tsp_mediator), Some(info)) = (tsp_mediator, session.as_ref())
&& let Some(vta_did) = info.vta_did.as_deref()
{
print_section("VTA TSP");
if fresh_tsp_probe {
let (fresh_did, fresh_key) =
vta_cli_common::local_keygen::generate_unbound_admin_did_key();
println!(
" {DIM}cold send probe — fresh throwaway DID (no prior relationship possible):{RESET}"
);
println!(" {CYAN}{:<13}{RESET} {fresh_did}", "Probe DID");
tsp_probe(&fresh_did, &fresh_key, tsp_mediator, vta_did, true).await;
} else {
tsp_probe(
&info.client_did,
&info.private_key_multibase,
tsp_mediator,
vta_did,
false,
)
.await;
}
}
Ok(())
}
async fn steady_ping(
session: &vta_sdk::session::TrustPingSession,
target: Option<&str>,
) -> Result<u128, Box<dyn std::error::Error>> {
session.ping(target).await?; session.ping(target).await }
#[cfg(feature = "tsp")]
async fn tsp_probe(
client_did: &str,
private_key_multibase: &str,
mediator_did: &str,
vta_did: &str,
cold: bool,
) {
match tokio::time::timeout(
std::time::Duration::from_secs(15),
vta_sdk::session::TspPingSession::new(client_did, private_key_multibase, mediator_did),
)
.await
{
Ok(Ok(mut session)) => {
if cold {
match session.probe_send(vta_did).await {
Ok(()) => {
println!(
" {CYAN}{:<13}{RESET} {GREEN}✓{RESET} cold send accepted — relationship-free routed send (§3 = 3c)",
"Cold-send"
);
}
Err(e) => {
println!(
" {CYAN}{:<13}{RESET} {RED}✗{RESET} cold send failed: {e}",
"Cold-send"
);
}
}
} else {
let ping_timeout = std::time::Duration::from_secs(10);
let measured = match session.ping(vta_did, ping_timeout).await {
Ok(_) => session.ping(vta_did, ping_timeout).await,
Err(e) => Err(e),
};
match measured {
Ok(latency) => {
println!(
" {CYAN}{:<13}{RESET} {GREEN}✓{RESET} pong ({latency}ms)",
"Trust-ping"
);
}
Err(e) => {
println!(
" {CYAN}{:<13}{RESET} {RED}✗{RESET} TSP ping failed: {e}",
"Trust-ping"
);
}
}
}
session.shutdown().await;
}
Ok(Err(e)) => {
println!(
" {CYAN}{:<13}{RESET} {RED}✗{RESET} TSP setup failed: {e}",
"Trust-ping"
);
}
Err(_) => {
println!(
" {CYAN}{:<13}{RESET} {RED}✗{RESET} TSP setup timed out",
"Trust-ping"
);
}
}
}
#[cfg(not(feature = "tsp"))]
async fn tsp_probe(
_client_did: &str,
_private_key_multibase: &str,
_mediator_did: &str,
_vta_did: &str,
_cold: bool,
) {
println!(" {DIM}advertised — rebuild pnm with `--features tsp` to probe over TSP{RESET}");
}