cratefield_signer/lib.rs
1//! `cratefield-signer` — the key-reference signing port for wallet
2//! automation (issue #761).
3//!
4//! A venture asks to sign an EVM transaction, an ERC-4337
5//! `UserOperation`, EIP-712 data or a Solana message **by key
6//! reference**. It never handles raw keys: keys are minted here and
7//! referenced everywhere else, and no API in this crate — port,
8//! reference implementations, or fakes — exports private key material.
9//! That guarantee is the trait's shape ([`KeySigner`] has no method
10//! that can answer the question) as much as any implementation's
11//! discipline.
12//!
13//! Every signature is audit-chained: subject, key reference, payload
14//! hash, decoded intent, policy decision, outcome. Every sign call
15//! passes through the guardrails module before the provider is called,
16//! and [`GuardedSigner`] is the composition that makes skipping it
17//! impossible. Audit failure fails closed, as does a guardrail that
18//! cannot evaluate: an unrecorded or undecided signature is one that
19//! never happened.
20//!
21//! The default composition is [`SecretsSigner`] — a secp256k1 or
22//! ed25519 key held in the Secrets port, unsealed only inside `sign` —
23//! wrapped in a [`GuardedSigner`]. It mints **session keys only**,
24//! never owner keys: there is no import API, and a key's whole life is
25//! under a secret a venture can delete.
26//!
27//! ```
28//! use cratefield_signer::{
29//! EvmTransaction, FakeSigner, GuardedSigner, MemorySignAudit, Payload, Scheme,
30//! SignerError, StaticGuardrails, Subject,
31//! };
32//!
33//! # fn main() -> Result<(), SignerError> {
34//! let guardrails = StaticGuardrails::new().allow_evm(
35//! 1,
36//! "0x000000000000000000000000000000000000aaaa",
37//! [0xa9, 0x05, 0x9c, 0xbb], // ERC-20 transfer
38//! )?;
39//! let signer = GuardedSigner::new(FakeSigner::new(), guardrails, MemorySignAudit::new());
40//! let subject = Subject::new("acme", None)?;
41//! let key = pollster::block_on(async {
42//! signer.create_key(&subject, Scheme::Secp256k1, "session-1").await
43//! })?;
44//!
45//! let tx = EvmTransaction {
46//! chain_id: 1,
47//! nonce: 0,
48//! max_priority_fee_per_gas: 1_000_000_000,
49//! max_fee_per_gas: 2_000_000_000,
50//! gas_limit: 21_000,
51//! to: Some("0x000000000000000000000000000000000000aaaa".into()),
52//! value: 1_000_000_000_000_000_000,
53//! data: vec![0xa9, 0x05, 0x9c, 0xbb], // the allowed selector
54//! };
55//! let signature = pollster::block_on(async {
56//! signer
57//! .sign(&subject, key.key_ref(), &Payload::EvmTransaction(tx))
58//! .await
59//! })?;
60//! assert!(matches!(signature, cratefield_signer::Signature::Secp256k1 { .. }));
61//! # Ok(())
62//! # }
63//! ```
64//!
65//! ## Reading order
66//!
67//! - [`payload`]: what can be signed, how each signing hash is
68//! computed, and the [`Intent`] the guardrails see.
69//! - [`port`]: the [`KeySigner`] trait and its error vocabulary.
70//! - [`guardrails`]: the [`Guardrails`] seam and the
71//! [`StaticGuardrails`] reference allowlist.
72//! - [`audit`]: the [`SignAudit`] seam and the hash-chained
73//! [`MemorySignAudit`].
74//! - [`guarded`]: [`GuardedSigner`], the order enforcement lives in.
75//! - [`secrets_signer`]: the Secrets-port provider.
76//! - [`fakes`] and [`conformance`]: the deterministic fake and the
77//! suite every provider runs.
78
79#![forbid(unsafe_code)]
80
81pub mod audit;
82pub mod conformance;
83mod crypto;
84pub mod fakes;
85pub mod guarded;
86pub mod guardrails;
87pub mod keys;
88pub mod payload;
89pub mod port;
90mod rlp;
91pub mod secrets_signer;
92
93pub use audit::{
94 AuditAnchor, MemorySignAudit, PolicyDecisionRecord, SignAudit, SignAuditRecord, SignOutcome,
95};
96pub use conformance::{guardrails_conformance, key_signer_conformance, sign_audit_conformance};
97pub use fakes::{FakeGuardrails, FakeSigner, RefusingAudit};
98pub use guarded::GuardedSigner;
99pub use guardrails::{Guardrails, PolicyDecision, SignContext, StaticGuardrails};
100pub use keys::{KeyInfo, KeyRef, KeyRole, Scheme, Signature, Subject};
101pub use payload::{
102 Chain, Eip712, EvmTransaction, Intent, Payload, SELECTOR_EXECUTE, SolanaMessage, UserOperation,
103};
104pub use port::{KeySigner, SignerError};
105pub use secrets_signer::SecretsSigner;