Skip to main content

acdp_types/
capabilities.rs

1use serde::{Deserialize, Serialize};
2
3/// Registry capabilities document served at `GET /.well-known/acdp.json`.
4///
5/// `additionalProperties` is `true` in the schema so future versions can add
6/// capability flags without a schema bump. Unknown fields are preserved in
7/// [`Self::extensions`] for forward-compatible inspection.
8#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct CapabilitiesDocument {
10    /// Protocol version this registry implements.
11    pub acdp_version: String,
12
13    /// Registry's Decentralized Identifier (`did:web:…`).
14    pub registry_did: String,
15
16    /// Signature algorithms accepted on publish.  MUST contain `"ed25519"`.
17    pub supported_signature_algorithms: Vec<String>,
18
19    /// DID methods the registry can resolve.  MUST contain `"did:web"`.
20    pub supported_did_methods: Vec<String>,
21
22    /// Profile(s) this registry claims.  MUST contain `"acdp-registry-core"`.
23    pub profiles: Vec<String>,
24
25    /// Resource limits.
26    pub limits: Limits,
27
28    /// Read-authentication methods supported for non-public contexts.
29    #[serde(default, skip_serializing_if = "Vec::is_empty")]
30    pub read_authentication_methods: Vec<String>,
31
32    /// Whether anonymous reads of public contexts are permitted.
33    #[serde(default)]
34    pub anonymous_public_reads: bool,
35
36    /// Whether `Idempotency-Key` is honoured on `POST /contexts`.
37    #[serde(default)]
38    pub supports_idempotency_key: bool,
39
40    /// Forward-compatible extensions: any unknown top-level field appears
41    /// here verbatim.
42    #[serde(flatten)]
43    pub extensions: serde_json::Map<String, serde_json::Value>,
44}
45
46impl CapabilitiesDocument {
47    /// Returns `true` if this registry supports keyword search.
48    pub fn supports_discovery(&self) -> bool {
49        self.profiles.iter().any(|p| p == "acdp-registry-discovery")
50    }
51
52    /// Returns `true` if this registry supports cross-registry resolution.
53    pub fn supports_federation(&self) -> bool {
54        self.profiles.iter().any(|p| p == "acdp-registry-federated")
55    }
56
57    /// Returns `true` if this registry advertises support for the given
58    /// signature algorithm (case-sensitive match against
59    /// `supported_signature_algorithms`).
60    pub fn supports_algorithm(&self, algorithm: &str) -> bool {
61        self.supported_signature_algorithms
62            .iter()
63            .any(|a| a == algorithm)
64    }
65
66    /// Returns `true` if this registry can resolve the given DID method
67    /// (e.g. `"did:web"`).
68    pub fn supports_did_method(&self, method: &str) -> bool {
69        self.supported_did_methods.iter().any(|m| m == method)
70    }
71
72    /// Idempotency-key TTL as a [`std::time::Duration`], if the registry
73    /// advertises one. Returns `None` when `supports_idempotency_key` is
74    /// `false` or the TTL field is absent.
75    pub fn idempotency_ttl(&self) -> Option<std::time::Duration> {
76        self.limits
77            .idempotency_key_ttl_seconds
78            .map(|s| std::time::Duration::from_secs(u64::from(s)))
79    }
80
81    /// Returns `true` when the registry requires authenticated requests
82    /// even for `visibility: public` reads (i.e. `anonymous_public_reads`
83    /// is `false`).
84    pub fn requires_anonymous_auth(&self) -> bool {
85        !self.anonymous_public_reads
86    }
87}
88
89/// Resource limits declared by the registry.
90///
91/// The capabilities document is OPEN at the top level, but `limits` is a
92/// CLOSED sub-object (`additionalProperties: false`): new limit keys are
93/// added by a spec version bump, not by registries inventing fields, so
94/// `deny_unknown_fields` rejects an unknown key (RFC-ACDP-0007 §3.3.1,
95/// conformance fixture schema-010).
96#[derive(Debug, Clone, Serialize, Deserialize)]
97#[serde(deny_unknown_fields)]
98pub struct Limits {
99    /// Maximum total publish request size in bytes.
100    pub max_payload_bytes: u64,
101
102    /// Maximum size of any single embedded data reference in bytes (≤ 65536).
103    pub max_embedded_bytes: u64,
104
105    /// How long idempotency-key mappings are retained, in seconds.
106    /// MUST be present when `supports_idempotency_key` is true.
107    ///
108    /// Optional and absent-or-integer in the schema — not nullable.
109    /// `de_present` rejects an explicit `"idempotency_key_ttl_seconds": null`.
110    #[serde(
111        default,
112        skip_serializing_if = "Option::is_none",
113        deserialize_with = "crate::serde_helpers::de_present"
114    )]
115    pub idempotency_key_ttl_seconds: Option<u32>,
116}