use axum::extract::State;
use axum::response::{IntoResponse, Response};
use serde_json::Value;
use trust_tasks_rs::TrustTask;
use crate::auth::AuthClaims;
use crate::error::AppError;
use crate::server::AppState;
mod acl;
mod audit;
mod auth;
mod backup;
mod config;
mod contexts;
mod credential_exchange;
mod device;
mod did_templates;
mod discovery;
mod helpers;
mod keys;
mod management;
#[cfg(all(feature = "webvh", feature = "didcomm"))]
mod passkey_vms;
#[cfg(feature = "webvh")]
mod provision_integration;
mod seeds;
pub(crate) mod step_up;
mod step_up_policy;
pub(crate) use step_up::{
AclChangeRoleOp, AclGrantOp, AclRevokeOp, AclSwapKeyOp, ContextDeleteOp, RequireStepUp,
};
mod vault;
#[cfg(feature = "webvh")]
mod webvh;
mod wire_v0_2;
pub(crate) use helpers::TrustTaskOutcome;
use helpers::{body_parse_error_response, method_not_found, reject_with};
#[cfg(feature = "didcomm")]
use trust_tasks_rs::RejectReason;
#[allow(dead_code)] const REST_ROUTED: &[&str] = vta_sdk::trust_tasks::REST_ROUTED_URIS;
#[allow(dead_code)] const KNOWN_FEATURE_GATED_URIS: &[&str] = &[
vta_sdk::trust_tasks::TASK_PASSKEY_VMS_ENROLL_CHALLENGE_0_1,
vta_sdk::trust_tasks::TASK_PASSKEY_VMS_ENROLL_SUBMIT_0_1,
vta_sdk::trust_tasks::TASK_PASSKEY_VMS_LIST_0_1,
vta_sdk::trust_tasks::TASK_PASSKEY_VMS_REVOKE_0_1,
vta_sdk::trust_tasks::TASK_PROVISION_INTEGRATION_REQUEST_1_0,
vta_sdk::trust_tasks::TASK_WEBVH_SERVERS_LIST_1_0,
vta_sdk::trust_tasks::TASK_WEBVH_SERVERS_ADD_1_0,
vta_sdk::trust_tasks::TASK_WEBVH_SERVERS_UPDATE_1_0,
vta_sdk::trust_tasks::TASK_WEBVH_SERVERS_REMOVE_1_0,
vta_sdk::trust_tasks::TASK_WEBVH_DIDS_LIST_1_0,
vta_sdk::trust_tasks::TASK_WEBVH_DIDS_CREATE_1_0,
vta_sdk::trust_tasks::TASK_WEBVH_DIDS_GET_1_0,
vta_sdk::trust_tasks::TASK_WEBVH_DIDS_GET_LOG_1_0,
vta_sdk::trust_tasks::TASK_WEBVH_DIDS_DELETE_1_0,
vta_sdk::trust_tasks::TASK_WEBVH_DIDS_UPDATE_1_0,
vta_sdk::trust_tasks::TASK_WEBVH_DIDS_ROTATE_KEYS_1_0,
vta_sdk::trust_tasks::TASK_WEBVH_DIDS_REGISTER_WITH_SERVER_1_0,
vta_sdk::trust_tasks::TASK_DID_MANAGEMENT_DID_REGISTER_0_1,
vta_sdk::trust_tasks::TASK_DID_MANAGEMENT_DID_PUBLISH_0_1,
vta_sdk::trust_tasks::TASK_DID_MANAGEMENT_DID_DELETE_0_1,
vta_sdk::trust_tasks::TASK_DID_MANAGEMENT_DID_ENABLE_0_1,
vta_sdk::trust_tasks::TASK_DID_MANAGEMENT_DID_DISABLE_0_1,
vta_sdk::trust_tasks::TASK_DID_MANAGEMENT_DID_LIST_0_1,
vta_sdk::trust_tasks::TASK_DID_MANAGEMENT_DID_INFO_0_1,
vta_sdk::trust_tasks::TASK_DID_MANAGEMENT_DID_CHECK_NAME_0_1,
vta_sdk::trust_tasks::TASK_DID_MANAGEMENT_DID_CHANGE_OWNER_0_1,
vta_sdk::trust_tasks::TASK_DID_MANAGEMENT_DID_ROLLBACK_0_1,
vta_sdk::trust_tasks::TASK_DID_MANAGEMENT_DID_PROBLEM_REPORT_0_1,
vta_sdk::trust_tasks::TASK_DID_MANAGEMENT_DOMAIN_CREATE_0_1,
vta_sdk::trust_tasks::TASK_DID_MANAGEMENT_DOMAIN_UPDATE_0_1,
vta_sdk::trust_tasks::TASK_DID_MANAGEMENT_DOMAIN_DISABLE_0_1,
vta_sdk::trust_tasks::TASK_DID_MANAGEMENT_DOMAIN_PURGE_0_1,
vta_sdk::trust_tasks::TASK_DID_MANAGEMENT_DOMAIN_SET_DEFAULT_0_1,
vta_sdk::trust_tasks::TASK_DID_MANAGEMENT_DOMAIN_ASSIGN_0_1,
vta_sdk::trust_tasks::TASK_DID_MANAGEMENT_DOMAIN_UNASSIGN_0_1,
vta_sdk::trust_tasks::TASK_DID_MANAGEMENT_SERVER_REGISTER_0_1,
vta_sdk::trust_tasks::TASK_DID_MANAGEMENT_SERVER_HEALTH_0_1,
vta_sdk::trust_tasks::TASK_DID_MANAGEMENT_SERVER_STATS_SYNC_0_1,
vta_sdk::trust_tasks::TASK_DID_MANAGEMENT_REGISTRY_ADMIN_REGISTER_0_1,
vta_sdk::trust_tasks::TASK_DID_MANAGEMENT_REGISTRY_DEREGISTER_0_1,
];
macro_rules! dispatch_table {
(
$(
$(#[$meta:meta])*
$($uri:path)|+ => $handler:path
),+ $(,)?
) => {
#[allow(deprecated)]
async fn dispatch_typed(
state: &AppState,
auth: &AuthClaims,
doc: TrustTask<Value>,
) -> TrustTaskOutcome {
let type_uri = doc.type_uri.to_string();
match type_uri.as_str() {
$(
$(#[$meta])*
$($uri)|+ => $handler(state, auth, doc).await,
)+
_ => method_not_found(doc, &type_uri),
}
}
#[cfg(test)]
#[allow(deprecated)]
fn dispatched_uris() -> Vec<&'static str> {
let mut v: Vec<&'static str> = Vec::new();
$(
$(#[$meta])*
v.extend([$($uri),+]);
)+
v
}
};
}
pub async fn dispatch_trust_task(
auth: AuthClaims,
State(state): State<AppState>,
body: axum::body::Bytes,
) -> Result<Response, AppError> {
Ok(dispatch_trust_task_core(&state, &auth, &body)
.await
.into_response())
}
pub(crate) async fn dispatch_trust_task_core(
state: &AppState,
auth: &AuthClaims,
body: &[u8],
) -> TrustTaskOutcome {
let doc: TrustTask<Value> = match serde_json::from_slice(body) {
Ok(d) => d,
Err(e) => return body_parse_error_response(&e.to_string()),
};
{
let vta_did = state.config.read().await.vta_did.clone();
if let Some(my_vid) = vta_did.as_deref()
&& let Err(reason) = doc.validate_basic(chrono::Utc::now(), my_vid)
{
return reject_with(&doc, reason);
}
}
let _ = auth;
let type_uri = doc.type_uri.to_string();
if let Some(spec) = wire_v0_2::lookup_0_2(&type_uri) {
let mut doc = doc;
wire_v0_2::downconvert_request(&mut doc.payload, spec);
if let Ok(uri_0_1) = spec.uri_0_1.parse() {
doc.type_uri = uri_0_1;
}
let outcome = dispatch_typed(state, auth, doc).await;
return wire_v0_2::upconvert_response(outcome, spec);
}
dispatch_typed(state, auth, doc).await
}
#[cfg(feature = "didcomm")]
pub(crate) fn reject_trust_task(body: &[u8], reason: RejectReason) -> TrustTaskOutcome {
match serde_json::from_slice::<TrustTask<Value>>(body) {
Ok(doc) => reject_with(&doc, reason),
Err(e) => body_parse_error_response(&e.to_string()),
}
}
dispatch_table! {
vta_sdk::trust_tasks::TASK_AUTH_REVOKE_SESSION_0_1 => auth::handle_revoke_session,
vta_sdk::trust_tasks::TASK_AUTH_WHOAMI_0_1 => auth::handle_whoami,
vta_sdk::trust_tasks::TASK_AUTH_SESSIONS_LIST_0_1 => auth::handle_sessions_list,
vta_sdk::trust_tasks::TASK_AUTH_STEP_UP_APPROVE_RESPONSE_0_1
| vta_sdk::trust_tasks::TASK_AUTH_STEP_UP_APPROVE_RESPONSE_0_2
=> step_up::handle_approve_response,
vta_sdk::trust_tasks::TASK_AUTH_STEP_UP_POLICY_0_2 => step_up_policy::handle_set_step_up_policy,
vta_sdk::trust_tasks::TASK_ACL_LIST_1_0 => acl::handle_list,
vta_sdk::trust_tasks::TASK_ACL_CREATE_1_0 => acl::handle_create,
vta_sdk::trust_tasks::TASK_ACL_GET_1_0 => acl::handle_get,
vta_sdk::trust_tasks::TASK_ACL_UPDATE_1_0 => acl::handle_update,
vta_sdk::trust_tasks::TASK_ACL_DELETE_1_0 => acl::handle_delete,
vta_sdk::trust_tasks::TASK_DEVICE_REGISTER_0_1 => device::handle_register,
vta_sdk::trust_tasks::TASK_DEVICE_HEARTBEAT_0_1 => device::handle_heartbeat,
vta_sdk::trust_tasks::TASK_DEVICE_LIST_0_1 => device::handle_list,
vta_sdk::trust_tasks::TASK_DEVICE_DISABLE_0_1 => device::handle_disable,
vta_sdk::trust_tasks::TASK_DEVICE_WIPE_0_1 => device::handle_wipe,
vta_sdk::trust_tasks::TASK_DEVICE_SET_WAKE_0_1 => device::handle_set_wake,
vta_sdk::trust_tasks::TASK_CONTEXTS_LIST_1_0 => contexts::handle_list,
vta_sdk::trust_tasks::TASK_CONTEXTS_CREATE_1_0 => contexts::handle_create,
vta_sdk::trust_tasks::TASK_CONTEXTS_GET_1_0 => contexts::handle_get,
vta_sdk::trust_tasks::TASK_CONTEXTS_UPDATE_1_0 => contexts::handle_update,
vta_sdk::trust_tasks::TASK_CONTEXTS_UPDATE_DID_1_0 => contexts::handle_update_did,
vta_sdk::trust_tasks::TASK_CONTEXTS_PREVIEW_DELETE_1_0 => contexts::handle_preview_delete,
vta_sdk::trust_tasks::TASK_CONTEXTS_DELETE_1_0 => contexts::handle_delete,
vta_sdk::trust_tasks::TASK_KEYS_LIST_1_0 => keys::handle_list,
vta_sdk::trust_tasks::TASK_KEYS_CREATE_1_0 => keys::handle_create,
vta_sdk::trust_tasks::TASK_KEYS_GET_1_0 => keys::handle_get,
vta_sdk::trust_tasks::TASK_KEYS_RENAME_1_0 => keys::handle_rename,
vta_sdk::trust_tasks::TASK_KEYS_REVOKE_1_0 => keys::handle_revoke,
vta_sdk::trust_tasks::TASK_KEYS_SIGN_1_0 => keys::handle_sign,
vta_sdk::trust_tasks::TASK_SEEDS_LIST_1_0 => seeds::handle_list,
vta_sdk::trust_tasks::TASK_SEEDS_ROTATE_1_0 => seeds::handle_rotate,
vta_sdk::trust_tasks::TASK_SEEDS_EXPORT_MNEMONIC_1_0 => seeds::handle_export_mnemonic,
vta_sdk::trust_tasks::TASK_AUDIT_LIST_LOGS_1_0 => audit::handle_list_logs,
vta_sdk::trust_tasks::TASK_AUDIT_GET_RETENTION_1_0 => audit::handle_get_retention,
vta_sdk::trust_tasks::TASK_AUDIT_UPDATE_RETENTION_1_0 => audit::handle_update_retention,
vta_sdk::trust_tasks::TASK_DISCOVERY_CAPABILITIES_1_0 => discovery::handle_capabilities,
vta_sdk::protocols::credential_exchange::PENDING_LIST
=> credential_exchange::handle_pending_list,
vta_sdk::protocols::credential_exchange::PENDING_APPROVE
=> credential_exchange::handle_pending_approve,
vta_sdk::protocols::credential_exchange::PENDING_DENY
=> credential_exchange::handle_pending_deny,
vta_sdk::trust_tasks::TASK_VAULT_LIST_0_1 => vault::handle_list,
vta_sdk::trust_tasks::TASK_VAULT_GET_0_1 => vault::handle_get,
vta_sdk::trust_tasks::TASK_VAULT_UPSERT_0_1 => vault::handle_upsert,
vta_sdk::trust_tasks::TASK_VAULT_DELETE_0_1 => vault::handle_delete,
vta_sdk::trust_tasks::TASK_VAULT_RELEASE_0_1 => vault::handle_release,
vta_sdk::trust_tasks::TASK_VAULT_PROXY_LOGIN_0_1 => vault::handle_proxy_login,
vta_sdk::trust_tasks::TASK_VAULT_SIGN_TRUST_TASK_0_1 => vault::handle_sign_trust_task,
vta_sdk::trust_tasks::TASK_CONFIG_GET_1_0 => config::handle_get,
vta_sdk::trust_tasks::TASK_CONFIG_UPDATE_1_0 => config::handle_update,
vta_sdk::trust_tasks::TASK_MANAGEMENT_RELOAD_SERVICES_1_0 => management::handle_reload_services,
vta_sdk::trust_tasks::TASK_BACKUP_INITIATE_EXPORT_1_0 => backup::handle_initiate_export,
vta_sdk::trust_tasks::TASK_BACKUP_COMPLETE_EXPORT_1_0 => backup::handle_complete_export,
vta_sdk::trust_tasks::TASK_BACKUP_INITIATE_IMPORT_1_0 => backup::handle_initiate_import,
vta_sdk::trust_tasks::TASK_BACKUP_FINALIZE_IMPORT_1_0 => backup::handle_finalize_import,
vta_sdk::trust_tasks::TASK_BACKUP_ABORT_1_0 => backup::handle_abort,
vta_sdk::trust_tasks::TASK_DID_TEMPLATES_LIST_1_0 => did_templates::handle_list,
vta_sdk::trust_tasks::TASK_DID_TEMPLATES_CREATE_1_0 => did_templates::handle_create,
vta_sdk::trust_tasks::TASK_DID_TEMPLATES_GET_1_0 => did_templates::handle_get,
vta_sdk::trust_tasks::TASK_DID_TEMPLATES_UPDATE_1_0 => did_templates::handle_update,
vta_sdk::trust_tasks::TASK_DID_TEMPLATES_DELETE_1_0 => did_templates::handle_delete,
vta_sdk::trust_tasks::TASK_DID_TEMPLATES_RENDER_1_0 => did_templates::handle_render,
vta_sdk::trust_tasks::TASK_CONTEXTS_DID_TEMPLATES_LIST_1_0 => did_templates::handle_context_list,
vta_sdk::trust_tasks::TASK_CONTEXTS_DID_TEMPLATES_CREATE_1_0
=> did_templates::handle_context_create,
vta_sdk::trust_tasks::TASK_CONTEXTS_DID_TEMPLATES_GET_1_0 => did_templates::handle_context_get,
vta_sdk::trust_tasks::TASK_CONTEXTS_DID_TEMPLATES_UPDATE_1_0
=> did_templates::handle_context_update,
vta_sdk::trust_tasks::TASK_CONTEXTS_DID_TEMPLATES_DELETE_1_0
=> did_templates::handle_context_delete,
vta_sdk::trust_tasks::TASK_CONTEXTS_DID_TEMPLATES_RENDER_1_0
=> did_templates::handle_context_render,
#[cfg(all(feature = "webvh", feature = "didcomm"))]
vta_sdk::trust_tasks::TASK_PASSKEY_VMS_ENROLL_CHALLENGE_0_1
=> passkey_vms::handle_enroll_challenge,
#[cfg(all(feature = "webvh", feature = "didcomm"))]
vta_sdk::trust_tasks::TASK_PASSKEY_VMS_ENROLL_SUBMIT_0_1 => passkey_vms::handle_enroll_submit,
#[cfg(all(feature = "webvh", feature = "didcomm"))]
vta_sdk::trust_tasks::TASK_PASSKEY_VMS_LIST_0_1 => passkey_vms::handle_list,
#[cfg(all(feature = "webvh", feature = "didcomm"))]
vta_sdk::trust_tasks::TASK_PASSKEY_VMS_REVOKE_0_1 => passkey_vms::handle_revoke,
#[cfg(feature = "webvh")]
vta_sdk::trust_tasks::TASK_PROVISION_INTEGRATION_REQUEST_1_0
=> provision_integration::handle_request,
#[cfg(feature = "webvh")]
vta_sdk::trust_tasks::TASK_WEBVH_SERVERS_LIST_1_0 => webvh::handle_servers_list,
#[cfg(feature = "webvh")]
vta_sdk::trust_tasks::TASK_WEBVH_SERVERS_ADD_1_0 => webvh::handle_servers_add,
#[cfg(feature = "webvh")]
vta_sdk::trust_tasks::TASK_WEBVH_SERVERS_UPDATE_1_0 => webvh::handle_servers_update,
#[cfg(feature = "webvh")]
vta_sdk::trust_tasks::TASK_WEBVH_SERVERS_REMOVE_1_0 => webvh::handle_servers_remove,
#[cfg(feature = "webvh")]
vta_sdk::trust_tasks::TASK_WEBVH_DIDS_LIST_1_0 => webvh::handle_dids_list,
#[cfg(feature = "webvh")]
vta_sdk::trust_tasks::TASK_WEBVH_DIDS_CREATE_1_0 => webvh::handle_dids_create,
#[cfg(feature = "webvh")]
vta_sdk::trust_tasks::TASK_WEBVH_DIDS_GET_1_0 => webvh::handle_dids_get,
#[cfg(feature = "webvh")]
vta_sdk::trust_tasks::TASK_WEBVH_DIDS_GET_LOG_1_0 => webvh::handle_dids_get_log,
#[cfg(feature = "webvh")]
vta_sdk::trust_tasks::TASK_WEBVH_DIDS_DELETE_1_0 => webvh::handle_dids_delete,
#[cfg(feature = "webvh")]
vta_sdk::trust_tasks::TASK_WEBVH_DIDS_UPDATE_1_0 => webvh::handle_dids_update,
#[cfg(feature = "webvh")]
vta_sdk::trust_tasks::TASK_WEBVH_DIDS_ROTATE_KEYS_1_0 => webvh::handle_dids_rotate_keys,
#[cfg(feature = "webvh")]
vta_sdk::trust_tasks::TASK_WEBVH_DIDS_REGISTER_WITH_SERVER_1_0
=> webvh::handle_dids_register_with_server,
}
#[cfg(test)]
mod tests {
use trust_tasks_rs::TrustTask;
use super::*;
#[test]
fn body_parse_error_wire_shape() {
let resp = body_parse_error_response("expected `,`");
let _ = resp;
}
#[test]
fn framework_requires_canonical_uri_in_wire_type_field() {
let canonical = serde_json::json!({
"id": "urn:uuid:00000000-0000-0000-0000-000000000001",
"type": "https://trusttasks.org/spec/auth/revoke-session/0.1",
"issuer": "did:example:alice",
"recipient": "did:example:vta",
"issuedAt": "2026-05-20T00:00:00Z",
"payload": { "session_id": "sess-1" }
});
let bytes = serde_json::to_vec(&canonical).unwrap();
let parsed: Result<TrustTask<Value>, _> = serde_json::from_slice(&bytes);
assert!(
parsed.is_ok(),
"canonical URI must parse: {:?}",
parsed.err()
);
let flat = serde_json::json!({
"id": "urn:uuid:00000000-0000-0000-0000-000000000001",
"type": "https://trusttasks.org/vta/auth/revoke-session/1.0",
"issuer": "did:example:alice",
"recipient": "did:example:vta",
"issuedAt": "2026-05-20T00:00:00Z",
"payload": { "session_id": "sess-1" }
});
let bytes = serde_json::to_vec(&flat).unwrap();
let parsed: Result<TrustTask<Value>, _> = serde_json::from_slice(&bytes);
assert!(
parsed.is_err(),
"flat URI must NOT parse — if this changes, the framework \
relaxed its parser and Phase 3 design can simplify"
);
}
#[test]
#[allow(deprecated)] fn phase_2_uri_registry_present() {
let _ = vta_sdk::trust_tasks::TASK_AUTH_CHALLENGE_0_1;
let _ = vta_sdk::trust_tasks::TASK_AUTH_AUTHENTICATE_0_1;
let _ = vta_sdk::trust_tasks::TASK_AUTH_REFRESH_0_1;
let _ = vta_sdk::trust_tasks::TASK_AUTH_REVOKE_SESSION_0_1;
let _ = vta_sdk::trust_tasks::TASK_AUTH_WHOAMI_0_1;
let _ = vta_sdk::trust_tasks::TASK_AUTH_SESSIONS_LIST_0_1;
let _ = vta_sdk::trust_tasks::TASK_AUTH_PASSKEY_LOGIN_START_0_1;
let _ = vta_sdk::trust_tasks::TASK_AUTH_PASSKEY_LOGIN_FINISH_0_1;
}
#[test]
fn dispatcher_handles_every_vta_sdk_uri() {
let dispatched = dispatched_uris();
for declared in vta_sdk::trust_tasks::ALL_URIS {
let in_dispatched = dispatched.contains(declared);
let in_rest_routed = REST_ROUTED.contains(declared);
let in_feature_gated = KNOWN_FEATURE_GATED_URIS.contains(declared);
let in_wire_v0_2 = wire_v0_2::WIRE_V0_2_URIS.contains(declared);
assert!(
in_dispatched || in_rest_routed || in_feature_gated || in_wire_v0_2,
"vta-sdk declares URI `{declared}` but it is not tracked in this dispatcher — \
either (a) add a `dispatch_table!` entry (`URI => slice::handler`), \
(b) add it to `REST_ROUTED` if it lives on a dedicated REST route, \
(c) add it to `KNOWN_FEATURE_GATED_URIS` with a comment explaining the gating, or \
(d) register it in `wire_v0_2::WIRE_V0_2_URIS` if it's an edge-transformed 0.2 URI"
);
}
}
#[test]
fn passkey_vms_0_1_dispatched() {
let dispatched = dispatched_uris();
let tracked = |u: &&str| dispatched.contains(u) || KNOWN_FEATURE_GATED_URIS.contains(u);
for v0_1 in [
vta_sdk::trust_tasks::TASK_PASSKEY_VMS_ENROLL_CHALLENGE_0_1,
vta_sdk::trust_tasks::TASK_PASSKEY_VMS_ENROLL_SUBMIT_0_1,
vta_sdk::trust_tasks::TASK_PASSKEY_VMS_LIST_0_1,
vta_sdk::trust_tasks::TASK_PASSKEY_VMS_REVOKE_0_1,
] {
assert!(tracked(&v0_1), "canonical 0.1 URI not dispatched: {v0_1}");
assert!(v0_1.ends_with("/0.1"), "version-label mismatch for {v0_1}");
}
}
#[test]
fn no_uri_is_both_dispatched_and_rest_routed() {
let dispatched = dispatched_uris();
for uri in REST_ROUTED {
assert!(
!dispatched.contains(uri),
"URI `{uri}` is in REST_ROUTED but also in a `dispatch_table!` entry — \
a URI must live on exactly one transport"
);
}
}
}