Skip to main content

auths_cli/commands/
did_webs.rs

1//! `auths did-webs` — emit a `did:webs` DID document for an AID.
2//!
3//! `did:webs` anchors a KERI AID into a **web-resolvable** DID document so a
4//! standard DID resolver verifies the identifier without speaking KERI. The
5//! document is derived by replaying the AID's KEL into its current key-state, so
6//! the verification material is exactly the AID's current signing keys — the KEL
7//! stays the source of truth. The document auths emits is byte-compatible with
8//! the ToIP did:webs reference resolver's `didDocument`
9//! (`{id, verificationMethod, service, alsoKnownAs}`). The crypto/wire definition
10//! lives in `auths-keri::did_webs`; this is a thin CLI adapter over it.
11
12use std::path::{Path, PathBuf};
13
14use anyhow::{Result, anyhow};
15use auths_keri::{DidWebsDocument, TrustedKel, parse_kel_json};
16use auths_utils::path::expand_tilde;
17use clap::Parser;
18
19use crate::config::CliConfig;
20
21/// Emit a `did:webs` DID document for an AID (KEL-anchored).
22#[derive(Parser, Debug, Clone)]
23#[command(
24    about = "Emit a did:webs DID document for an AID — resolvable under a standard did:webs resolver",
25    after_help = "Examples:
26  auths did-webs --from-kel kel.json --domain example.com
27  auths did-webs --from-kel kel.json --domain 'example.com%3A3901:dids'"
28)]
29pub struct DidWebsCommand {
30    /// Replay this KEL file and project its current key-state into a `did:webs`
31    /// DID document (the shape a did:webs/DID-core resolver reads).
32    #[clap(long, value_name = "KEL.json")]
33    pub from_kel: PathBuf,
34
35    /// The web domain the `did:webs` is anchored at — the host (optionally
36    /// `host%3Aport` and path segments) before the AID in `did:webs:<domain>:<aid>`.
37    #[clap(long, value_name = "DOMAIN")]
38    pub domain: String,
39}
40
41impl DidWebsCommand {
42    /// Run the command: replay the KEL and print the `did:webs` DID document.
43    pub fn execute(&self, _ctx: &CliConfig) -> Result<()> {
44        self.emit(&self.from_kel)
45    }
46
47    /// Replay a KEL file and print the projected `did:webs` DID document.
48    fn emit(&self, kel_path: &Path) -> Result<()> {
49        let path = expand_tilde(kel_path)?;
50        let json = std::fs::read_to_string(&path)
51            .map_err(|e| anyhow!("read KEL {}: {e}", path.display()))?;
52        let events = parse_kel_json(&json).map_err(|e| anyhow!("parse KEL: {e}"))?;
53        // A KEL file the operator hands us is a local, self-owned artifact — the
54        // reviewable trust assertion that structural replay requires.
55        let state = TrustedKel::from_trusted_source(&events)
56            .replay()
57            .map_err(|e| anyhow!("replay KEL: {e}"))?;
58        let doc = DidWebsDocument::from_key_state(&state, &self.domain)
59            .map_err(|e| anyhow!("project key-state into a did:webs document: {e}"))?;
60        println!("{}", serde_json::to_string_pretty(&doc)?);
61        Ok(())
62    }
63}