Skip to main content

auths_cli/commands/
whoami.rs

1use anyhow::{Result, anyhow};
2use auths_sdk::ports::IdentityStorage;
3use auths_sdk::storage::RegistryIdentityStorage;
4use auths_sdk::storage_layout as layout;
5use clap::Parser;
6use serde::Serialize;
7
8use crate::config::CliConfig;
9use crate::ux::format::{JsonResponse, Output, is_json_mode};
10
11/// Show the current identity on this machine.
12#[derive(Parser, Debug, Clone)]
13#[command(
14    name = "whoami",
15    about = "Show the current identity on this machine",
16    after_help = "Examples:
17  auths whoami              # Show the current identity
18  auths whoami --json       # JSON output
19
20Related:
21  auths status  — Show full identity and device status
22  auths init    — Initialize a new identity"
23)]
24pub struct WhoamiCommand {}
25
26#[derive(Debug, Serialize)]
27struct WhoamiResponse {
28    identity_did: String,
29    #[serde(skip_serializing_if = "Option::is_none")]
30    label: Option<String>,
31    #[serde(skip_serializing_if = "Option::is_none")]
32    device_did: Option<String>,
33    /// The identity's *current* signing public key (post-rotation), hex-encoded —
34    /// the value `trust pin --key` and `verify --signer-key` accept.
35    #[serde(skip_serializing_if = "Option::is_none")]
36    public_key_hex: Option<String>,
37    #[serde(skip_serializing_if = "Option::is_none")]
38    curve: Option<String>,
39}
40
41/// Resolve the identity's current public key (hex) and curve from its local KEL.
42/// Best-effort: `None` never blocks `whoami` from reporting the DID.
43fn current_key_hex(repo_path: &std::path::Path, did: &str) -> Option<(String, String)> {
44    let registry = auths_sdk::storage::GitRegistryBackend::from_config_unchecked(
45        auths_sdk::storage::RegistryConfig::single_tenant(repo_path),
46    );
47    let (pk, curve) = auths_sdk::keri::resolve_current_public_key(&registry, did).ok()?;
48    Some((hex::encode(pk), format!("{curve:?}").to_lowercase()))
49}
50
51/// Resolve this machine's device DID. Best-effort.
52fn local_device_did(repo_path: &std::path::Path) -> Option<String> {
53    let env_config = auths_sdk::core_config::EnvironmentConfig::from_env();
54    let ctx = crate::factories::storage::build_auths_context(repo_path, &env_config, None).ok()?;
55    auths_sdk::domains::identity::local::resolve_local_signer(&ctx)
56        .ok()
57        .map(|signer| signer.signer_did.to_string())
58}
59
60pub fn handle_whoami(_cmd: WhoamiCommand, repo: Option<std::path::PathBuf>) -> Result<()> {
61    let repo_path = layout::resolve_repo_path(repo).map_err(|e| anyhow!(e))?;
62
63    if crate::factories::storage::open_git_repo(&repo_path).is_err() {
64        if is_json_mode() {
65            JsonResponse::<()>::error(
66                "whoami",
67                "No identity found. Run `auths init` to get started.",
68            )
69            .print()?;
70        } else {
71            let out = Output::new();
72            out.print_error("No identity found. Run `auths init` to get started.");
73        }
74        return Ok(());
75    }
76
77    let storage = RegistryIdentityStorage::new(&repo_path);
78    match storage.load_identity() {
79        Ok(identity) => {
80            let identity_did = identity.controller_did.to_string();
81            let key = current_key_hex(&repo_path, &identity_did);
82            let response = WhoamiResponse {
83                identity_did,
84                label: None,
85                device_did: local_device_did(&repo_path),
86                public_key_hex: key.as_ref().map(|(hex, _)| hex.clone()),
87                curve: key.as_ref().map(|(_, curve)| curve.clone()),
88            };
89
90            if is_json_mode() {
91                JsonResponse::success("whoami", &response).print()?;
92            } else {
93                let out = Output::new();
94                out.println(&format!("Identity: {}", out.info(&response.identity_did)));
95                if let Some(ref device) = response.device_did {
96                    out.println(&format!("Device:   {}", out.dim(device)));
97                }
98            }
99        }
100        Err(_) => {
101            if is_json_mode() {
102                JsonResponse::<()>::error(
103                    "whoami",
104                    "No identity found. Run `auths init` to get started.",
105                )
106                .print()?;
107            } else {
108                let out = Output::new();
109                out.print_error("No identity found. Run `auths init` to get started.");
110            }
111        }
112    }
113
114    Ok(())
115}
116
117impl crate::commands::executable::ExecutableCommand for WhoamiCommand {
118    fn execute(&self, ctx: &CliConfig) -> Result<()> {
119        handle_whoami(self.clone(), ctx.repo_path.clone())
120    }
121}