Skip to main content

auths_sdk/ports/
agent.rs

1//! Agent-based signing port for delegating cryptographic operations to a running agent process.
2//!
3//! The agent signing port abstracts the IPC-based signing protocol so that
4//! the SDK workflow layer remains platform-independent. On Unix, the CLI
5//! wires a concrete adapter that speaks the auths-agent wire protocol over
6//! a Unix domain socket. On Windows, WASM, and other targets the
7//! [`NoopAgentProvider`] is used instead.
8
9/// Errors from agent signing operations.
10#[derive(Debug, thiserror::Error)]
11#[non_exhaustive]
12pub enum AgentSigningError {
13    /// The agent is not available on this platform or is not installed.
14    #[error("agent unavailable: {0}")]
15    Unavailable(String),
16
17    /// The agent socket exists but the connection failed.
18    #[error("agent connection failed: {0}")]
19    ConnectionFailed(String),
20
21    /// The agent accepted the request but signing failed.
22    #[error("agent signing failed: {0}")]
23    SigningFailed(String),
24
25    /// The agent could not be started.
26    #[error("agent startup failed: {0}")]
27    StartupFailed(String),
28}
29
30/// Port for delegating signing operations to a running agent process.
31///
32/// Implementations must convert the agent's native response format into an
33/// SSHSIG PEM string (`-----BEGIN SSH SIGNATURE----- … -----END SSH SIGNATURE-----`)
34/// so that callers receive the same format as [`crate::signing::sign_with_seed`].
35///
36/// Args:
37/// * Trait methods accept namespace identifiers, public key bytes, and raw data to sign.
38///
39/// Usage:
40/// ```ignore
41/// let pem = agent.try_sign("git", &pubkey_bytes, &commit_data)?;
42/// agent.ensure_running()?;
43/// agent.add_identity("git", &pkcs8_der_bytes)?;
44/// ```
45pub trait AgentSigningPort: Send + Sync + 'static {
46    /// Attempt to sign `data` via the running agent.
47    ///
48    /// Returns an SSHSIG PEM string on success. The adapter is responsible for
49    /// converting raw Ed25519 bytes from the agent wire protocol into PEM.
50    ///
51    /// Args:
52    /// * `namespace`: The SSH namespace for the signature (e.g. `"git"`).
53    /// * `pubkey`: The Ed25519 public key bytes to identify the signing key.
54    /// * `data`: The raw bytes to sign.
55    fn try_sign(
56        &self,
57        namespace: &str,
58        pubkey: &[u8],
59        data: &[u8],
60    ) -> Result<String, AgentSigningError>;
61
62    /// Start the agent daemon if it is not already running.
63    ///
64    /// Implementations should suppress all stdout/stderr output (quiet mode).
65    fn ensure_running(&self) -> Result<(), AgentSigningError>;
66
67    /// Load a decrypted PKCS#8 DER-encoded keypair into the running agent.
68    ///
69    /// Args:
70    /// * `namespace`: The namespace to associate with the loaded key.
71    /// * `pkcs8_der`: Decrypted PKCS#8 DER bytes — the output of keychain
72    ///   decryption (i.e. `decrypt_keypair()` result), not a raw seed or
73    ///   encrypted blob.
74    fn add_identity(&self, namespace: &str, pkcs8_der: &[u8]) -> Result<(), AgentSigningError>;
75}
76
77/// No-op agent provider for platforms without agent support.
78///
79/// All methods return [`AgentSigningError::Unavailable`].
80pub struct NoopAgentProvider;
81
82impl AgentSigningPort for NoopAgentProvider {
83    fn try_sign(
84        &self,
85        _namespace: &str,
86        _pubkey: &[u8],
87        _data: &[u8],
88    ) -> Result<String, AgentSigningError> {
89        Err(AgentSigningError::Unavailable(
90            "agent not supported on this platform".into(),
91        ))
92    }
93
94    fn ensure_running(&self) -> Result<(), AgentSigningError> {
95        Err(AgentSigningError::Unavailable(
96            "agent not supported on this platform".into(),
97        ))
98    }
99
100    fn add_identity(&self, _namespace: &str, _pkcs8_der: &[u8]) -> Result<(), AgentSigningError> {
101        Err(AgentSigningError::Unavailable(
102            "agent not supported on this platform".into(),
103        ))
104    }
105}