Skip to main content

auths_cli/commands/
demo.rs

1use std::time::Instant;
2
3use anyhow::{Context, Result, anyhow};
4use chrono::Utc;
5use serde_json::Value;
6
7use auths_sdk::domains::signing::service::{EphemeralSignRequest, sign_artifact_ephemeral};
8
9use crate::commands::executable::ExecutableCommand;
10use crate::config::CliConfig;
11use crate::ux::format::Output;
12
13/// Synthetic commit SHA stamped into the demo attestation.
14///
15/// `sign_artifact_ephemeral` binds provenance to a 40/64-hex commit SHA, but the
16/// demo signs ad-hoc bytes rather than a real commit — this recognizable placeholder
17/// keeps the call valid without pretending to reference a real commit.
18const DEMO_COMMIT_SHA: &str = "0000000000000000000000000000000000000000";
19
20#[derive(Debug, clap::Args)]
21#[command(about = "Sign and verify a demo artifact — works offline, no setup or registry needed")]
22pub struct DemoCommand {}
23
24impl ExecutableCommand for DemoCommand {
25    fn execute(&self, _ctx: &CliConfig) -> Result<()> {
26        let out = Output::new();
27        let data = b"Hello, Auths!\n";
28
29        // Sign with an ephemeral in-process key: no identity, no keychain, no Secure
30        // Enclave — so the "aha" works the instant the binary is installed and never
31        // blocks on a Touch ID prompt (even on a TTY-less CI shell).
32        let t_sign = Instant::now();
33        let sign_result = sign_artifact_ephemeral(
34            Utc::now(),
35            EphemeralSignRequest {
36                data,
37                artifact_name: Some("demo.txt".into()),
38                commit_sha: DEMO_COMMIT_SHA.into(),
39                curve: auths_crypto::CurveType::default(),
40                expires_in: None,
41                note: Some("auths demo — local only".into()),
42                ci_env: None,
43                oidc_binding: None,
44            },
45        )
46        .map_err(|e| anyhow!("{}", e))?;
47        let sign_ms = t_sign.elapsed().as_millis();
48
49        // Verify locally: the digest the attestation commits to must match what we signed.
50        let t_verify = Instant::now();
51        let attestation: Value = serde_json::from_str(&sign_result.attestation_json)
52            .context("failed to parse attestation")?;
53        let stored_digest = attestation
54            .pointer("/payload/digest/hex")
55            .and_then(|v| v.as_str())
56            .context("attestation missing payload digest")?;
57        if stored_digest != sign_result.digest {
58            anyhow::bail!(
59                "demo verification failed: digest mismatch\n  expected: {}\n  got:      {}",
60                sign_result.digest,
61                stored_digest
62            );
63        }
64        let verify_ms = t_verify.elapsed().as_millis();
65
66        let issuer = attestation
67            .pointer("/issuer")
68            .and_then(|v| v.as_str())
69            .unwrap_or("(unknown)");
70
71        out.print_heading("Auths Demo");
72        out.println("");
73        out.println(&out.key_value("Demo identity", issuer));
74        out.println(&out.key_value("Signed in", &format!("{sign_ms}ms")));
75        out.println(&out.key_value("Verified in", &format!("{verify_ms}ms")));
76        out.println("");
77        out.print_success("Signed + verified locally — no network, no setup required.");
78        out.println("");
79        out.println(
80            "This used a throwaway demo key. Run `auths init` to sign with your real identity.",
81        );
82
83        Ok(())
84    }
85}