cratefield_adapter_turnkey/lib.rs
1//! `cratefield-adapter-turnkey` — the Turnkey provider for the
2//! [`KeySigner`](cratefield_signer::KeySigner) port (issue #761).
3//!
4//! Every end user gets a Turnkey **sub-organization**: their passkey is
5//! the root user, a wallet is minted inside it, and our backend is only
6//! a **delegated access** user — a second root user whose API key (the
7//! P-256 pair this crate stamps with, held in the Secrets port) is
8//! scoped by `EFFECT_ALLOW` policies to exactly what the venture
9//! allows, then dropped from the root quorum so it can never widen its
10//! own permissions. [`set_kill_switch`](TurnkeySigner::set_kill_switch)
11//! freezes the backend's signing with one `EFFECT_DENY` policy;
12//! [`clear_kill_switch`](TurnkeySigner::clear_kill_switch) lifts it.
13//!
14//! Signing goes **by key reference** through the port: EVM transactions
15//! as `ACTIVITY_TYPE_SIGN_TRANSACTION_V2` (the one activity Turnkey's
16//! policy engine parses, so `eth.tx.to`, `eth.tx.chain_id`,
17//! `eth.tx.value` and `eth.tx.data[0..10]` conditions bind), digests —
18//! `userOpHash`, EIP-712, Solana messages — as
19//! `ACTIVITY_TYPE_SIGN_RAW_PAYLOAD_V2`. No export activity is ever
20//! wrapped.
21//!
22//! ```
23//! use cratefield_adapter_turnkey::{AllowRule, key_ref};
24//! use cratefield_signer::Scheme;
25//!
26//! # fn main() -> Result<(), cratefield_signer::SignerError> {
27//! // The allow rules a sub-organization's backend is scoped to, and the
28//! // reference naming one of its keys — both built without any I/O.
29//! let rule = AllowRule::evm(
30//! 1,
31//! "0x000000000000000000000000000000000000aaaa",
32//! Some("0xa9059cbb"), // ERC-20 transfer
33//! Some(1_000_000_000_000_000_000),
34//! )?;
35//! assert_eq!(
36//! rule.condition(),
37//! "eth.tx.to == '0x000000000000000000000000000000000000aaaa' \
38//! && eth.tx.chain_id == 1 \
39//! && eth.tx.data[0..10] == '0xa9059cbb' \
40//! && eth.tx.value <= 1000000000000000000"
41//! );
42//! let reference = key_ref(
43//! "acme",
44//! "11111111-2222-3333-4444-555555555555",
45//! Scheme::Secp256k1,
46//! "0x000000000000000000000000000000000000aaaa",
47//! )?;
48//! assert!(reference.as_str().starts_with("turnkey/acme/"));
49//! # Ok(())
50//! # }
51//! ```
52//!
53//! ## Reading order
54//!
55//! - [`api`]: [`TurnkeySigner`] — construction, the setup flow, kill
56//! switches, and the typed activity calls underneath the port.
57//! - [`policy`]: the pure builders — allow rules, the delegated-access
58//! consensus, and the validators that keep the policy language
59//! injection-proof.
60//! - [`stamp`]: the `X-Stamp`, signed inside the call that unseals the
61//! API key.
62//! - [`evm`]: the EIP-1559 wire forms — the unsigned transaction
63//! `sign_transaction` asks for, and the signed one back.
64//! - [`signer`]: the [`KeySigner`](cratefield_signer::KeySigner)
65//! implementation and the key-reference format, with every answer
66//! verified against the reference's identity before it is returned.
67//!
68//! ## Why the port's conformance suite does not run here
69//!
70//! [`cratefield_signer::key_signer_conformance`] starts at
71//! `create_key`, and this provider's keys are born with their owner's
72//! passkey at sub-organization creation — the port's signature cannot
73//! carry an attestation. Its determinism check (RFC 6979 / ed25519,
74//! "a provider that disagrees is doing something exotic") also excludes
75//! a provider whose signatures are minted by distributed signing
76//! infrastructure. The adapter's tests cover what transfers: signing
77//! verified against the published identity, scheme mismatches, malformed
78//! payloads, and the status-to-error mapping.
79
80#![forbid(unsafe_code)]
81
82pub mod api;
83pub mod evm;
84pub mod policy;
85pub mod signer;
86pub mod stamp;
87
88pub use api::{PasskeyAttestation, SubOrgSetup, SubOrganization, TurnkeySigner};
89pub use policy::{AllowRule, EvmAddress, ProgramKey, Selector, da_consensus};
90pub use signer::key_ref;