Skip to main content

vta_sdk/
context_provision.rs

1use serde::{Deserialize, Serialize};
2
3use crate::credentials::CredentialBundle;
4use crate::did_secrets::SecretEntry;
5
6/// A self-contained bundle for provisioning an application context.
7///
8/// Contains everything an independent application needs to connect to the VTA,
9/// authenticate, and self-administer its context. Optionally includes DID
10/// material (document, log entry, keys) when a DID was created during
11/// provisioning.
12///
13/// Post-Phase-5 the canonical transport is [`crate::sealed_transfer`]
14/// (`SealedPayloadV1::ContextProvision`) — HPKE-sealed, armored, with a
15/// producer assertion anchoring trust. This struct is now only what rides
16/// inside that envelope.
17#[derive(Debug, Clone, Serialize, Deserialize)]
18pub struct ContextProvisionBundle {
19    /// Context identifier.
20    pub context_id: String,
21    /// Human-readable context name.
22    pub context_name: String,
23    /// VTA service public URL.
24    #[serde(default, skip_serializing_if = "Option::is_none")]
25    pub vta_url: Option<String>,
26    /// VTA service DID.
27    #[serde(default, skip_serializing_if = "Option::is_none")]
28    pub vta_did: Option<String>,
29    /// Admin credential bundle for the provisioned context.
30    pub credential: CredentialBundle,
31    /// DID of the admin identity created for this context.
32    pub admin_did: String,
33    /// DID material, present when a DID was created during provisioning.
34    #[serde(default, skip_serializing_if = "Option::is_none")]
35    pub did: Option<ProvisionedDid>,
36}
37
38/// DID material included when a DID is created during context provisioning.
39#[derive(Debug, Clone, Serialize, Deserialize)]
40pub struct ProvisionedDid {
41    /// The DID identifier (e.g. `did:webvh:...`).
42    pub id: String,
43    /// DID document (JSON).
44    #[serde(default, skip_serializing_if = "Option::is_none")]
45    pub did_document: Option<serde_json::Value>,
46    /// Serialized DID log entry for `did.jsonl`.
47    #[serde(default, skip_serializing_if = "Option::is_none")]
48    pub log_entry: Option<String>,
49    /// Private keys associated with the DID.
50    pub secrets: Vec<SecretEntry>,
51}
52
53#[cfg(test)]
54mod tests {
55    use super::*;
56
57    fn sample_credential() -> CredentialBundle {
58        CredentialBundle::new("did:key:z6Mk123", "z1234567890", "did:key:z6MkVTA")
59    }
60
61    #[test]
62    fn test_serde_json_roundtrip_without_did() {
63        let bundle = ContextProvisionBundle {
64            context_id: "my-app".to_string(),
65            context_name: "My Application".to_string(),
66            vta_url: Some("https://vta.example.com".to_string()),
67            vta_did: Some("did:webvh:abc:example.com".to_string()),
68            credential: sample_credential(),
69            admin_did: "did:key:z6Mk123".to_string(),
70            did: None,
71        };
72        let json = serde_json::to_string(&bundle).unwrap();
73        let decoded: ContextProvisionBundle = serde_json::from_str(&json).unwrap();
74        assert_eq!(decoded.context_id, "my-app");
75        assert_eq!(decoded.context_name, "My Application");
76        assert_eq!(decoded.admin_did, "did:key:z6Mk123");
77        assert_eq!(decoded.credential.did, "did:key:z6Mk123");
78        assert!(decoded.did.is_none());
79    }
80
81    #[test]
82    fn test_serde_json_roundtrip_with_did() {
83        let bundle = ContextProvisionBundle {
84            context_id: "my-app".to_string(),
85            context_name: "My Application".to_string(),
86            vta_url: None,
87            vta_did: None,
88            credential: sample_credential(),
89            admin_did: "did:key:z6Mk123".to_string(),
90            did: Some(ProvisionedDid {
91                id: "did:webvh:abc:example.com".to_string(),
92                did_document: Some(serde_json::json!({"id": "did:webvh:abc:example.com"})),
93                log_entry: Some("{\"log\": \"entry\"}".to_string()),
94                secrets: vec![SecretEntry {
95                    key_id: "did:webvh:abc:example.com#key-0".to_string(),
96                    key_type: crate::keys::KeyType::Ed25519,
97                    private_key_multibase: "z6Mk...signing".to_string(),
98                }],
99            }),
100        };
101        let json = serde_json::to_string(&bundle).unwrap();
102        let decoded: ContextProvisionBundle = serde_json::from_str(&json).unwrap();
103        assert_eq!(decoded.context_id, "my-app");
104        let did = decoded.did.unwrap();
105        assert_eq!(did.id, "did:webvh:abc:example.com");
106        assert!(did.did_document.is_some());
107        assert!(did.log_entry.is_some());
108        assert_eq!(did.secrets.len(), 1);
109    }
110}