use crate::trust_tasks;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum RetrySafety {
ReadOnly,
RetrySafe,
Keyed,
KeyedSecret,
}
impl RetrySafety {
pub fn is_blind_retry_safe(self) -> bool {
matches!(self, Self::ReadOnly | Self::RetrySafe)
}
pub fn needs_key(self) -> bool {
matches!(self, Self::Keyed | Self::KeyedSecret)
}
pub fn response_is_replayable(self) -> bool {
!matches!(self, Self::KeyedSecret)
}
}
use RetrySafety::{Keyed, KeyedSecret, ReadOnly, RetrySafe};
#[allow(deprecated)] pub const RETRY_SAFETY: &[(&str, RetrySafety)] = &[
(trust_tasks::TASK_AUTH_CHALLENGE_0_1, RetrySafe),
(trust_tasks::TASK_AUTH_AUTHENTICATE_0_1, RetrySafe),
(trust_tasks::TASK_AUTH_REFRESH_0_1, Keyed),
(trust_tasks::TASK_AUTH_REVOKE_SESSION_0_1, RetrySafe),
(trust_tasks::TASK_AUTH_WHOAMI_0_1, ReadOnly),
(trust_tasks::TASK_AUTH_SESSIONS_LIST_0_1, ReadOnly),
(trust_tasks::TASK_AUTH_PASSKEY_LOGIN_START_0_1, RetrySafe),
(trust_tasks::TASK_AUTH_PASSKEY_LOGIN_START_0_2, RetrySafe),
(trust_tasks::TASK_AUTH_PASSKEY_LOGIN_FINISH_0_1, RetrySafe),
(trust_tasks::TASK_AUTH_PASSKEY_LOGIN_FINISH_0_2, RetrySafe),
(trust_tasks::TASK_AUTH_STEP_UP_APPROVE_RESPONSE_0_1, Keyed),
(trust_tasks::TASK_AUTH_STEP_UP_APPROVE_RESPONSE_0_2, Keyed),
(trust_tasks::TASK_DEVICE_REGISTER_0_1, Keyed),
(trust_tasks::TASK_DEVICE_REGISTER_0_2, Keyed),
(trust_tasks::TASK_DEVICE_HEARTBEAT_0_1, RetrySafe),
(trust_tasks::TASK_DEVICE_HEARTBEAT_0_2, RetrySafe),
(trust_tasks::TASK_DEVICE_LIST_0_1, ReadOnly),
(trust_tasks::TASK_DEVICE_LIST_0_2, ReadOnly),
(trust_tasks::TASK_DEVICE_DISABLE_0_1, RetrySafe),
(trust_tasks::TASK_DEVICE_WIPE_0_1, RetrySafe),
(trust_tasks::TASK_DEVICE_SET_WAKE_0_1, RetrySafe),
(trust_tasks::TASK_DEVICE_SET_WAKE_0_2, RetrySafe),
(trust_tasks::TASK_MESSAGING_PING_0_1, ReadOnly),
(trust_tasks::TASK_ACL_LIST_0_1, ReadOnly),
(trust_tasks::TASK_ACL_GRANT_0_1, RetrySafe),
(trust_tasks::TASK_ACL_SHOW_0_1, ReadOnly),
(trust_tasks::TASK_ACL_UPDATE_0_1, RetrySafe),
(trust_tasks::TASK_ACL_CHANGE_ROLE_0_1, RetrySafe),
(trust_tasks::TASK_ACL_REVOKE_0_1, RetrySafe),
(trust_tasks::TASK_ACL_SWAP_KEY_0_1, Keyed),
(trust_tasks::TASK_CONTEXTS_LIST_1_0, ReadOnly),
(trust_tasks::TASK_CONTEXTS_CREATE_1_0, Keyed),
(trust_tasks::TASK_CONTEXTS_GET_1_0, ReadOnly),
(trust_tasks::TASK_CONTEXTS_UPDATE_1_0, RetrySafe),
(trust_tasks::TASK_CONTEXTS_UPDATE_DID_1_0, RetrySafe),
(trust_tasks::TASK_CONTEXTS_PREVIEW_DELETE_1_0, ReadOnly),
(trust_tasks::TASK_CONTEXTS_DELETE_1_0, RetrySafe),
(trust_tasks::TASK_SERVICES_LIST_1_0, ReadOnly),
(trust_tasks::TASK_SERVICES_GET_1_0, ReadOnly),
(trust_tasks::TASK_SERVICES_ENABLE_1_0, Keyed),
(trust_tasks::TASK_SERVICES_UPDATE_1_0, Keyed),
(trust_tasks::TASK_SERVICES_DISABLE_1_0, Keyed),
(trust_tasks::TASK_SERVICES_ROLLBACK_1_0, Keyed),
(trust_tasks::TASK_SERVICES_DRAIN_LIST_1_0, ReadOnly),
(trust_tasks::TASK_SERVICES_DRAIN_CANCEL_1_0, Keyed),
(trust_tasks::TASK_KEYS_LIST_0_1, ReadOnly),
(trust_tasks::TASK_KEYS_CREATE_0_1, Keyed),
(trust_tasks::TASK_KEYS_IMPORT_0_1, Keyed),
(trust_tasks::TASK_KEYS_SHOW_0_1, ReadOnly),
(trust_tasks::TASK_KEYS_RENAME_0_1, RetrySafe),
(trust_tasks::TASK_KEYS_REVOKE_0_1, RetrySafe),
(trust_tasks::TASK_KEYS_SIGN_0_1, ReadOnly),
(trust_tasks::TASK_KEYS_DERIVE_AND_SIGN_0_1, ReadOnly),
(
trust_tasks::TASK_KEYS_DERIVE_AND_SIGN_DOCUMENT_0_1,
ReadOnly,
),
(trust_tasks::TASK_SEEDS_LIST_1_0, ReadOnly),
(trust_tasks::TASK_SEEDS_ROTATE_1_0, Keyed),
(trust_tasks::TASK_SEEDS_EXPORT_MNEMONIC_1_0, KeyedSecret),
(trust_tasks::TASK_AUDIT_LIST_0_1, ReadOnly),
(trust_tasks::TASK_AUDIT_GET_RETENTION_1_0, ReadOnly),
(trust_tasks::TASK_AUDIT_UPDATE_RETENTION_1_0, RetrySafe),
(trust_tasks::TASK_DISCOVERY_CAPABILITIES_1_0, ReadOnly),
(trust_tasks::TASK_VAULT_LIST_0_1, ReadOnly),
(trust_tasks::TASK_VAULT_LIST_0_2, ReadOnly),
(trust_tasks::TASK_VAULT_GET_0_1, ReadOnly),
(trust_tasks::TASK_VAULT_GET_0_2, ReadOnly),
(trust_tasks::TASK_VAULT_UPSERT_0_1, RetrySafe),
(trust_tasks::TASK_VAULT_UPSERT_0_2, RetrySafe),
(trust_tasks::TASK_VAULT_DELETE_0_1, RetrySafe),
(trust_tasks::TASK_VAULT_ARCHIVE_0_1, RetrySafe),
(trust_tasks::TASK_VAULT_UNARCHIVE_0_1, RetrySafe),
(trust_tasks::TASK_VAULT_RESTORE_0_1, RetrySafe),
(trust_tasks::TASK_VAULT_PURGE_0_1, RetrySafe),
(trust_tasks::TASK_VAULT_RELEASE_0_1, ReadOnly),
(trust_tasks::TASK_VAULT_RELEASE_0_2, ReadOnly),
(trust_tasks::TASK_VAULT_PROXY_LOGIN_0_1, ReadOnly),
(trust_tasks::TASK_VAULT_PROXY_LOGIN_0_2, ReadOnly),
(trust_tasks::TASK_VAULT_SIGN_TRUST_TASK_0_1, ReadOnly),
(trust_tasks::TASK_VAULT_SIGN_TRUST_TASK_0_2, ReadOnly),
(trust_tasks::TASK_DID_MANAGEMENT_DID_REGISTER_0_1, Keyed),
(trust_tasks::TASK_DID_MANAGEMENT_DID_PUBLISH_0_1, Keyed),
(trust_tasks::TASK_DID_MANAGEMENT_DID_DELETE_0_1, RetrySafe),
(trust_tasks::TASK_DID_MANAGEMENT_DID_ENABLE_0_1, RetrySafe),
(trust_tasks::TASK_DID_MANAGEMENT_DID_DISABLE_0_1, RetrySafe),
(trust_tasks::TASK_DID_MANAGEMENT_DID_LIST_0_1, ReadOnly),
(trust_tasks::TASK_DID_MANAGEMENT_DID_INFO_0_1, ReadOnly),
(
trust_tasks::TASK_DID_MANAGEMENT_DID_CHECK_NAME_0_1,
ReadOnly,
),
(
trust_tasks::TASK_DID_MANAGEMENT_DID_CHANGE_OWNER_0_1,
RetrySafe,
),
(trust_tasks::TASK_DID_MANAGEMENT_DID_ROLLBACK_0_1, Keyed),
(
trust_tasks::TASK_DID_MANAGEMENT_DID_PROBLEM_REPORT_0_1,
RetrySafe,
),
(trust_tasks::TASK_DID_MANAGEMENT_DOMAIN_CREATE_0_1, Keyed),
(
trust_tasks::TASK_DID_MANAGEMENT_DOMAIN_UPDATE_0_1,
RetrySafe,
),
(
trust_tasks::TASK_DID_MANAGEMENT_DOMAIN_DISABLE_0_1,
RetrySafe,
),
(trust_tasks::TASK_DID_MANAGEMENT_DOMAIN_PURGE_0_1, RetrySafe),
(
trust_tasks::TASK_DID_MANAGEMENT_DOMAIN_SET_DEFAULT_0_1,
RetrySafe,
),
(
trust_tasks::TASK_DID_MANAGEMENT_DOMAIN_ASSIGN_0_1,
RetrySafe,
),
(
trust_tasks::TASK_DID_MANAGEMENT_DOMAIN_UNASSIGN_0_1,
RetrySafe,
),
(trust_tasks::TASK_DID_MANAGEMENT_SERVER_REGISTER_0_1, Keyed),
(trust_tasks::TASK_DID_MANAGEMENT_SERVER_HEALTH_0_1, ReadOnly),
(
trust_tasks::TASK_DID_MANAGEMENT_SERVER_STATS_SYNC_0_1,
RetrySafe,
),
(
trust_tasks::TASK_DID_MANAGEMENT_REGISTRY_ADMIN_REGISTER_0_1,
Keyed,
),
(
trust_tasks::TASK_DID_MANAGEMENT_REGISTRY_DEREGISTER_0_1,
RetrySafe,
),
(trust_tasks::TASK_CONFIG_SHOW_0_1, ReadOnly),
(trust_tasks::TASK_CONFIG_PATCH_0_1, RetrySafe),
(trust_tasks::TASK_MANAGEMENT_RELOAD_SERVICES_1_0, RetrySafe),
(
trust_tasks::TASK_PASSKEY_VMS_ENROLL_CHALLENGE_0_1,
RetrySafe,
),
(trust_tasks::TASK_PASSKEY_VMS_ENROLL_SUBMIT_0_1, Keyed),
(trust_tasks::TASK_PASSKEY_VMS_LIST_0_1, ReadOnly),
(trust_tasks::TASK_PASSKEY_VMS_REVOKE_0_1, RetrySafe),
(trust_tasks::TASK_PROVISION_INTEGRATION_0_2, KeyedSecret),
(trust_tasks::TASK_WEBVH_SERVERS_LIST_1_0, ReadOnly),
(trust_tasks::TASK_WEBVH_SERVERS_REGISTER_1_0, Keyed),
(trust_tasks::TASK_WEBVH_SERVERS_REMOVE_1_0, RetrySafe),
(trust_tasks::TASK_WEBVH_SERVERS_DOMAINS_0_1, ReadOnly),
(trust_tasks::TASK_WEBVH_SERVERS_RECONCILE_0_1, RetrySafe),
(trust_tasks::TASK_WEBVH_SERVERS_RETIRE_ORPHAN_0_1, RetrySafe),
(trust_tasks::TASK_WEBVH_DIDS_LIST_1_0, ReadOnly),
(trust_tasks::TASK_WEBVH_DIDS_CREATE_1_0, Keyed),
(trust_tasks::TASK_WEBVH_DIDS_GET_1_0, ReadOnly),
(trust_tasks::TASK_WEBVH_DIDS_DELETE_1_0, RetrySafe),
(trust_tasks::TASK_WEBVH_DIDS_UPDATE_1_0, Keyed),
(trust_tasks::TASK_WEBVH_DIDS_ROTATE_KEYS_1_0, Keyed),
(trust_tasks::TASK_WEBVH_DIDS_REGISTER_WITH_SERVER_1_0, Keyed),
(trust_tasks::TASK_WEBVH_AGENT_NAME_LIST_1_0, ReadOnly),
(trust_tasks::TASK_WEBVH_AGENT_NAME_CHECK_1_0, ReadOnly),
(trust_tasks::TASK_WEBVH_AGENT_NAME_SET_1_0, RetrySafe),
(trust_tasks::TASK_WEBVH_AGENT_NAME_REMOVE_1_0, RetrySafe),
(trust_tasks::TASK_WEBVH_AGENT_NAME_DISABLE_1_0, RetrySafe),
(trust_tasks::TASK_WEBVH_AGENT_NAME_ENABLE_1_0, RetrySafe),
(trust_tasks::TASK_DID_TEMPLATES_LIST_2_0, ReadOnly),
(trust_tasks::TASK_DID_TEMPLATES_CREATE_2_0, RetrySafe),
(trust_tasks::TASK_DID_TEMPLATES_GET_2_0, ReadOnly),
(trust_tasks::TASK_DID_TEMPLATES_UPDATE_2_0, RetrySafe),
(trust_tasks::TASK_DID_TEMPLATES_DELETE_2_0, RetrySafe),
(trust_tasks::TASK_DID_TEMPLATES_RENDER_2_0, ReadOnly),
(trust_tasks::TASK_BACKUP_INITIATE_EXPORT_1_0, Keyed),
(trust_tasks::TASK_BACKUP_COMPLETE_EXPORT_1_0, KeyedSecret),
(trust_tasks::TASK_BACKUP_INITIATE_IMPORT_1_0, Keyed),
(trust_tasks::TASK_BACKUP_FINALIZE_IMPORT_1_0, Keyed),
(trust_tasks::TASK_BACKUP_ABORT_1_0, RetrySafe),
(trust_tasks::TASK_ATTESTATION_STATUS_1_0, ReadOnly),
(trust_tasks::TASK_ATTESTATION_REPORT_1_0, ReadOnly),
(trust_tasks::TASK_CONSENT_REQUEST_1_0, RetrySafe),
(trust_tasks::TASK_CONSENT_DECISION_1_0, RetrySafe),
(trust_tasks::TASK_TASK_CONSENT_DECISION_0_1, RetrySafe),
(trust_tasks::TASK_CONSENT_REVOKE_1_0, RetrySafe),
(trust_tasks::TASK_CONSENT_LIST_1_0, ReadOnly),
(trust_tasks::TASK_CONSENT_APPROVER_SET_1_0, RetrySafe),
(trust_tasks::TASK_CONSENT_APPROVER_LIST_1_0, ReadOnly),
(trust_tasks::TASK_VTA_CREDENTIALS_ISSUE_0_1, Keyed),
(trust_tasks::TASK_VTA_CREDENTIALS_REVOKE_0_1, RetrySafe),
(trust_tasks::TASK_VTA_MEMORY_PUT_0_1, RetrySafe),
(trust_tasks::TASK_VTA_MEMORY_LIST_0_1, ReadOnly),
(trust_tasks::TASK_VTA_MEMORY_DELETE_0_1, RetrySafe),
(trust_tasks::TASK_POLICY_LIST_0_2, ReadOnly),
(trust_tasks::TASK_POLICY_GET_0_1, ReadOnly),
(trust_tasks::TASK_POLICY_UPSERT_0_2, RetrySafe),
(trust_tasks::TASK_POLICY_DELETE_0_1, RetrySafe),
];
pub fn retry_safety(uri: &str) -> Option<RetrySafety> {
RETRY_SAFETY
.iter()
.find(|(u, _)| *u == uri)
.map(|(_, s)| *s)
}
pub fn keyed_uris() -> Vec<&'static str> {
RETRY_SAFETY
.iter()
.filter(|(_, s)| s.needs_key())
.map(|(u, _)| *u)
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashSet;
#[test]
fn every_uri_is_classified() {
let classified: HashSet<&str> = RETRY_SAFETY.iter().map(|(u, _)| *u).collect();
let missing: Vec<_> = trust_tasks::ALL_URIS
.iter()
.filter(|u| !classified.contains(*u))
.collect();
assert!(
missing.is_empty(),
"these tasks have no retry-safety classification — add them to \
RETRY_SAFETY (when unsure, `Keyed` is the conservative answer): {missing:#?}"
);
}
#[test]
fn no_classification_without_a_task() {
let catalog: HashSet<&str> = trust_tasks::ALL_URIS.iter().copied().collect();
let orphans: Vec<_> = RETRY_SAFETY
.iter()
.map(|(u, _)| *u)
.filter(|u| !catalog.contains(u))
.collect();
assert!(
orphans.is_empty(),
"classified URIs that are no longer in ALL_URIS: {orphans:#?}"
);
}
#[test]
fn no_duplicate_entries() {
let mut seen = HashSet::new();
for (u, _) in RETRY_SAFETY {
assert!(seen.insert(*u), "duplicate classification for {u}");
}
}
#[test]
fn needs_key_and_blind_retry_safe_partition_the_space() {
for class in [
RetrySafety::ReadOnly,
RetrySafety::RetrySafe,
RetrySafety::Keyed,
RetrySafety::KeyedSecret,
] {
assert_ne!(
class.is_blind_retry_safe(),
class.needs_key(),
"{class:?} is both or neither"
);
}
}
#[test]
fn secret_bearing_responses_are_never_replayable() {
assert!(!RetrySafety::KeyedSecret.response_is_replayable());
assert!(RetrySafety::Keyed.response_is_replayable());
}
#[test]
fn the_operations_that_prompted_this_are_keyed() {
for uri in [
trust_tasks::TASK_WEBVH_DIDS_CREATE_1_0,
trust_tasks::TASK_KEYS_CREATE_0_1,
trust_tasks::TASK_CONTEXTS_CREATE_1_0,
trust_tasks::TASK_ACL_SWAP_KEY_0_1,
] {
assert_eq!(
retry_safety(uri).map(|s| s.needs_key()),
Some(true),
"{uri} must stay keyed"
);
}
assert_eq!(
retry_safety(trust_tasks::TASK_PROVISION_INTEGRATION_0_2),
Some(RetrySafety::KeyedSecret)
);
}
#[test]
fn unknown_uris_are_none() {
assert_eq!(retry_safety("https://example.invalid/not-a-task/0.1"), None);
}
#[test]
fn keyed_uris_are_a_subset_of_the_catalog() {
let catalog: HashSet<&str> = trust_tasks::ALL_URIS.iter().copied().collect();
let keyed = keyed_uris();
assert!(!keyed.is_empty());
for u in keyed {
assert!(catalog.contains(u), "{u} not in ALL_URIS");
}
}
}