@wlfi-agent/cli 1.4.13 → 1.4.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.lock +3968 -0
- package/Cargo.toml +50 -0
- package/README.md +426 -6
- package/crates/vault-cli-admin/Cargo.toml +26 -0
- package/crates/vault-cli-admin/src/io_utils.rs +500 -0
- package/crates/vault-cli-admin/src/main.rs +3990 -0
- package/crates/vault-cli-admin/src/shared_config.rs +624 -0
- package/crates/vault-cli-admin/src/tui/amounts.rs +180 -0
- package/crates/vault-cli-admin/src/tui/token_rpc.rs +250 -0
- package/crates/vault-cli-admin/src/tui/utils.rs +82 -0
- package/crates/vault-cli-admin/src/tui.rs +3410 -0
- package/crates/vault-cli-agent/Cargo.toml +24 -0
- package/crates/vault-cli-agent/src/io_utils.rs +576 -0
- package/crates/vault-cli-agent/src/main.rs +833 -0
- package/crates/vault-cli-daemon/Cargo.toml +28 -0
- package/crates/vault-cli-daemon/src/bin/wlfi-agent-system-keychain.rs +216 -0
- package/crates/vault-cli-daemon/src/main.rs +644 -0
- package/crates/vault-cli-daemon/src/relay_sync.rs +894 -0
- package/crates/vault-cli-daemon/tests/system_keychain_helper_acl.rs +167 -0
- package/crates/vault-daemon/Cargo.toml +32 -0
- package/crates/vault-daemon/src/daemon_parts/api_impl_and_utils.rs +1041 -0
- package/crates/vault-daemon/src/daemon_parts/core_helpers.rs +1256 -0
- package/crates/vault-daemon/src/daemon_parts/types_api_rpc.rs +622 -0
- package/crates/vault-daemon/src/lib.rs +54 -0
- package/crates/vault-daemon/src/persistence.rs +441 -0
- package/crates/vault-daemon/src/tests.rs +237 -0
- package/crates/vault-daemon/src/tests_parts/part1.rs +1224 -0
- package/crates/vault-daemon/src/tests_parts/part2.rs +1021 -0
- package/crates/vault-daemon/src/tests_parts/part3.rs +835 -0
- package/crates/vault-daemon/src/tests_parts/part4.rs +604 -0
- package/crates/vault-domain/Cargo.toml +20 -0
- package/crates/vault-domain/src/action.rs +849 -0
- package/crates/vault-domain/src/address.rs +51 -0
- package/crates/vault-domain/src/approval.rs +90 -0
- package/crates/vault-domain/src/constants.rs +4 -0
- package/crates/vault-domain/src/error.rs +54 -0
- package/crates/vault-domain/src/keys.rs +71 -0
- package/crates/vault-domain/src/lib.rs +42 -0
- package/crates/vault-domain/src/nonce.rs +102 -0
- package/crates/vault-domain/src/policy.rs +172 -0
- package/crates/vault-domain/src/request.rs +53 -0
- package/crates/vault-domain/src/scope.rs +24 -0
- package/crates/vault-domain/src/session.rs +50 -0
- package/crates/vault-domain/src/signature.rs +34 -0
- package/crates/vault-domain/src/tests.rs +651 -0
- package/crates/vault-domain/src/u128_as_decimal_string.rs +44 -0
- package/crates/vault-policy/Cargo.toml +17 -0
- package/crates/vault-policy/src/engine.rs +301 -0
- package/crates/vault-policy/src/error.rs +81 -0
- package/crates/vault-policy/src/lib.rs +17 -0
- package/crates/vault-policy/src/report.rs +34 -0
- package/crates/vault-policy/src/tests.rs +891 -0
- package/crates/vault-policy/src/tests_explain.rs +78 -0
- package/crates/vault-sdk-agent/Cargo.toml +21 -0
- package/crates/vault-sdk-agent/src/lib.rs +711 -0
- package/crates/vault-signer/Cargo.toml +25 -0
- package/crates/vault-signer/src/lib.rs +731 -0
- package/crates/vault-signer/tests/secure_enclave_acl.rs +54 -0
- package/crates/vault-transport-unix/Cargo.toml +24 -0
- package/crates/vault-transport-unix/src/lib.rs +1640 -0
- package/crates/vault-transport-xpc/Cargo.toml +25 -0
- package/crates/vault-transport-xpc/src/client_codec_api.rs +635 -0
- package/crates/vault-transport-xpc/src/lib.rs +680 -0
- package/crates/vault-transport-xpc/src/tests.rs +818 -0
- package/crates/vault-transport-xpc/tests/e2e_flow.rs +773 -0
- package/dist/cli.cjs +35088 -0
- package/dist/cli.cjs.map +1 -0
- package/package.json +49 -43
- package/packages/cache/.turbo/turbo-build.log +52 -0
- package/packages/cache/dist/chunk-2QFWMUXT.cjs +43 -0
- package/packages/cache/dist/chunk-2QFWMUXT.cjs.map +1 -0
- package/packages/cache/dist/chunk-4U63TZTQ.js +43 -0
- package/packages/cache/dist/chunk-4U63TZTQ.js.map +1 -0
- package/packages/cache/dist/chunk-ALQ6H7KG.cjs +404 -0
- package/packages/cache/dist/chunk-ALQ6H7KG.cjs.map +1 -0
- package/packages/cache/dist/chunk-FGJEEF5N.js +404 -0
- package/packages/cache/dist/chunk-FGJEEF5N.js.map +1 -0
- package/packages/cache/dist/chunk-UYNEHZHB.cjs +45 -0
- package/packages/cache/dist/chunk-UYNEHZHB.cjs.map +1 -0
- package/packages/cache/dist/chunk-VXVMPG3W.js +45 -0
- package/packages/cache/dist/chunk-VXVMPG3W.js.map +1 -0
- package/packages/cache/dist/client/index.cjs +11 -0
- package/packages/cache/dist/client/index.cjs.map +1 -0
- package/packages/cache/dist/client/index.d.cts +15 -0
- package/packages/cache/dist/client/index.d.ts +15 -0
- package/packages/cache/dist/client/index.js +11 -0
- package/packages/cache/dist/client/index.js.map +1 -0
- package/packages/cache/dist/errors/index.cjs +11 -0
- package/packages/cache/dist/errors/index.cjs.map +1 -0
- package/packages/cache/dist/errors/index.d.cts +26 -0
- package/packages/cache/dist/errors/index.d.ts +26 -0
- package/packages/cache/dist/errors/index.js +11 -0
- package/packages/cache/dist/errors/index.js.map +1 -0
- package/packages/cache/dist/index.cjs +29 -0
- package/packages/cache/dist/index.cjs.map +1 -0
- package/packages/cache/dist/index.d.cts +4 -0
- package/packages/cache/dist/index.d.ts +4 -0
- package/packages/cache/dist/index.js +29 -0
- package/packages/cache/dist/index.js.map +1 -0
- package/packages/cache/dist/service/index.cjs +15 -0
- package/packages/cache/dist/service/index.cjs.map +1 -0
- package/packages/cache/dist/service/index.d.cts +184 -0
- package/packages/cache/dist/service/index.d.ts +184 -0
- package/packages/cache/dist/service/index.js +15 -0
- package/packages/cache/dist/service/index.js.map +1 -0
- package/packages/cache/node_modules/.bin/jiti +17 -0
- package/packages/cache/node_modules/.bin/tsc +17 -0
- package/packages/cache/node_modules/.bin/tsserver +17 -0
- package/packages/cache/node_modules/.bin/tsup +17 -0
- package/packages/cache/node_modules/.bin/tsup-node +17 -0
- package/packages/cache/node_modules/.bin/tsx +17 -0
- package/packages/cache/node_modules/.bin/vitest +17 -0
- package/packages/cache/package.json +48 -0
- package/packages/cache/src/client/index.ts +56 -0
- package/packages/cache/src/errors/index.ts +53 -0
- package/packages/cache/src/index.ts +3 -0
- package/packages/cache/src/service/index.test.ts +263 -0
- package/packages/cache/src/service/index.ts +678 -0
- package/packages/cache/tsconfig.json +13 -0
- package/packages/cache/tsup.config.ts +13 -0
- package/packages/cache/vitest.config.ts +16 -0
- package/packages/config/.turbo/turbo-build.log +18 -0
- package/packages/config/dist/index.cjs +1037 -0
- package/packages/config/dist/index.cjs.map +1 -0
- package/packages/config/dist/index.d.ts +131 -0
- package/packages/config/node_modules/.bin/jiti +17 -0
- package/packages/config/node_modules/.bin/tsc +17 -0
- package/packages/config/node_modules/.bin/tsserver +17 -0
- package/packages/config/node_modules/.bin/tsup +17 -0
- package/packages/config/node_modules/.bin/tsup-node +17 -0
- package/packages/config/node_modules/.bin/tsx +17 -0
- package/packages/config/package.json +21 -0
- package/packages/config/src/index.js +1 -0
- package/packages/config/src/index.ts +1282 -0
- package/packages/config/tsconfig.json +4 -0
- package/packages/rpc/.turbo/turbo-build.log +32 -0
- package/packages/rpc/dist/_esm-BCLXDO2R.cjs +3660 -0
- package/packages/rpc/dist/_esm-BCLXDO2R.cjs.map +1 -0
- package/packages/rpc/dist/ccip-OWJLAW55.cjs +16 -0
- package/packages/rpc/dist/ccip-OWJLAW55.cjs.map +1 -0
- package/packages/rpc/dist/chunk-APQIFZ3B.cjs +6247 -0
- package/packages/rpc/dist/chunk-APQIFZ3B.cjs.map +1 -0
- package/packages/rpc/dist/chunk-CDO2GWRD.cjs +410 -0
- package/packages/rpc/dist/chunk-CDO2GWRD.cjs.map +1 -0
- package/packages/rpc/dist/chunk-QGTNTFJ7.cjs +2249 -0
- package/packages/rpc/dist/chunk-QGTNTFJ7.cjs.map +1 -0
- package/packages/rpc/dist/chunk-TZDTAHWR.cjs +44 -0
- package/packages/rpc/dist/chunk-TZDTAHWR.cjs.map +1 -0
- package/packages/rpc/dist/index.cjs +7342 -0
- package/packages/rpc/dist/index.cjs.map +1 -0
- package/packages/rpc/dist/index.d.ts +3857 -0
- package/packages/rpc/dist/secp256k1-WCNM675D.cjs +18 -0
- package/packages/rpc/dist/secp256k1-WCNM675D.cjs.map +1 -0
- package/packages/rpc/node_modules/.bin/jiti +17 -0
- package/packages/rpc/node_modules/.bin/tsc +17 -0
- package/packages/rpc/node_modules/.bin/tsserver +17 -0
- package/packages/rpc/node_modules/.bin/tsup +17 -0
- package/packages/rpc/node_modules/.bin/tsup-node +17 -0
- package/packages/rpc/node_modules/.bin/tsx +17 -0
- package/packages/rpc/package.json +25 -0
- package/packages/rpc/src/index.ts +206 -0
- package/packages/rpc/tsconfig.json +4 -0
- package/packages/typescript/base.json +36 -0
- package/packages/typescript/nextjs.json +17 -0
- package/packages/typescript/package.json +10 -0
- package/packages/ui/.turbo/turbo-build.log +44 -0
- package/packages/ui/dist/chunk-MOAFBKSA.js +11 -0
- package/packages/ui/dist/chunk-MOAFBKSA.js.map +1 -0
- package/packages/ui/dist/components/badge.d.ts +12 -0
- package/packages/ui/dist/components/badge.js +31 -0
- package/packages/ui/dist/components/badge.js.map +1 -0
- package/packages/ui/dist/components/button.d.ts +13 -0
- package/packages/ui/dist/components/button.js +40 -0
- package/packages/ui/dist/components/button.js.map +1 -0
- package/packages/ui/dist/components/card.d.ts +10 -0
- package/packages/ui/dist/components/card.js +39 -0
- package/packages/ui/dist/components/card.js.map +1 -0
- package/packages/ui/dist/components/input.d.ts +5 -0
- package/packages/ui/dist/components/input.js +28 -0
- package/packages/ui/dist/components/input.js.map +1 -0
- package/packages/ui/dist/components/label.d.ts +5 -0
- package/packages/ui/dist/components/label.js +13 -0
- package/packages/ui/dist/components/label.js.map +1 -0
- package/packages/ui/dist/components/separator.d.ts +5 -0
- package/packages/ui/dist/components/separator.js +13 -0
- package/packages/ui/dist/components/separator.js.map +1 -0
- package/packages/ui/dist/components/textarea.d.ts +5 -0
- package/packages/ui/dist/components/textarea.js +27 -0
- package/packages/ui/dist/components/textarea.js.map +1 -0
- package/packages/ui/dist/tailwind.d.ts +56 -0
- package/packages/ui/dist/tailwind.js +60 -0
- package/packages/ui/dist/tailwind.js.map +1 -0
- package/packages/ui/dist/utils/cn.d.ts +5 -0
- package/packages/ui/dist/utils/cn.js +7 -0
- package/packages/ui/dist/utils/cn.js.map +1 -0
- package/packages/ui/node_modules/.bin/jiti +17 -0
- package/packages/ui/node_modules/.bin/tsc +17 -0
- package/packages/ui/node_modules/.bin/tsserver +17 -0
- package/packages/ui/node_modules/.bin/tsup +17 -0
- package/packages/ui/node_modules/.bin/tsup-node +17 -0
- package/packages/ui/node_modules/.bin/tsx +17 -0
- package/packages/ui/package.json +69 -0
- package/packages/ui/src/components/badge.tsx +27 -0
- package/packages/ui/src/components/button.tsx +40 -0
- package/packages/ui/src/components/card.tsx +31 -0
- package/packages/ui/src/components/input.tsx +21 -0
- package/packages/ui/src/components/label.tsx +6 -0
- package/packages/ui/src/components/separator.tsx +6 -0
- package/packages/ui/src/components/textarea.tsx +20 -0
- package/packages/ui/src/globals.css +70 -0
- package/packages/ui/src/tailwind.ts +56 -0
- package/packages/ui/src/utils/cn.ts +6 -0
- package/packages/ui/tsconfig.json +20 -0
- package/packages/ui/tsup.config.ts +20 -0
- package/pnpm-workspace.yaml +4 -0
- package/scripts/install-rust-binaries.mjs +84 -0
- package/scripts/launchd/install-user-daemon.sh +358 -0
- package/scripts/launchd/run-vault-daemon.sh +5 -0
- package/scripts/launchd/run-wlfi-agent-daemon.sh +73 -0
- package/scripts/launchd/uninstall-user-daemon.sh +103 -0
- package/src/cli.ts +2121 -0
- package/src/lib/admin-guard.js +1 -0
- package/src/lib/admin-guard.ts +185 -0
- package/src/lib/admin-passthrough.ts +33 -0
- package/src/lib/admin-reset.ts +751 -0
- package/src/lib/admin-setup.ts +1612 -0
- package/src/lib/agent-auth-clear.js +1 -0
- package/src/lib/agent-auth-clear.ts +58 -0
- package/src/lib/agent-auth-forwarding.js +1 -0
- package/src/lib/agent-auth-forwarding.ts +149 -0
- package/src/lib/agent-auth-migrate.js +1 -0
- package/src/lib/agent-auth-migrate.ts +150 -0
- package/src/lib/agent-auth-revoke.ts +103 -0
- package/src/lib/agent-auth-rotate.ts +107 -0
- package/src/lib/agent-auth-token.js +1 -0
- package/src/lib/agent-auth-token.ts +25 -0
- package/src/lib/agent-auth.ts +89 -0
- package/src/lib/asset-broadcast.js +1 -0
- package/src/lib/asset-broadcast.ts +285 -0
- package/src/lib/bootstrap-artifacts.js +1 -0
- package/src/lib/bootstrap-artifacts.ts +205 -0
- package/src/lib/bootstrap-credentials.js +1 -0
- package/src/lib/bootstrap-credentials.ts +832 -0
- package/src/lib/config-amounts.js +1 -0
- package/src/lib/config-amounts.ts +189 -0
- package/src/lib/config-mutation.ts +27 -0
- package/src/lib/fs-trust.js +1 -0
- package/src/lib/fs-trust.ts +537 -0
- package/src/lib/keychain.js +1 -0
- package/src/lib/keychain.ts +225 -0
- package/src/lib/local-admin-access.ts +106 -0
- package/src/lib/network-selection.js +1 -0
- package/src/lib/network-selection.ts +71 -0
- package/src/lib/passthrough-security.js +1 -0
- package/src/lib/passthrough-security.ts +114 -0
- package/src/lib/rpc-guard.js +1 -0
- package/src/lib/rpc-guard.ts +7 -0
- package/src/lib/rust-spawn-options.js +1 -0
- package/src/lib/rust-spawn-options.ts +98 -0
- package/src/lib/rust.js +1 -0
- package/src/lib/rust.ts +143 -0
- package/src/lib/signed-tx.js +1 -0
- package/src/lib/signed-tx.ts +116 -0
- package/src/lib/status-repair-cli.ts +116 -0
- package/src/lib/sudo.js +1 -0
- package/src/lib/sudo.ts +172 -0
- package/src/lib/vault-password-forwarding.js +1 -0
- package/src/lib/vault-password-forwarding.ts +155 -0
- package/src/lib/wallet-profile.js +1 -0
- package/src/lib/wallet-profile.ts +332 -0
- package/src/lib/wallet-repair.js +1 -0
- package/src/lib/wallet-repair.ts +304 -0
- package/src/lib/wallet-setup.js +1 -0
- package/src/lib/wallet-setup.ts +1466 -0
- package/src/lib/wallet-status.js +1 -0
- package/src/lib/wallet-status.ts +640 -0
- package/tsconfig.base.json +17 -0
- package/tsconfig.json +10 -0
- package/tsup.config.ts +25 -0
- package/turbo.json +41 -0
- package/LICENSE.md +0 -1
- package/dist/wlfa/index.cjs +0 -250
- package/dist/wlfa/index.d.cts +0 -1
- package/dist/wlfa/index.d.ts +0 -1
- package/dist/wlfa/index.js +0 -250
- package/dist/wlfc/index.cjs +0 -1839
- package/dist/wlfc/index.d.cts +0 -1
- package/dist/wlfc/index.d.ts +0 -1
- package/dist/wlfc/index.js +0 -1839
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
use std::fmt::{Display, Formatter};
|
|
2
|
+
use std::str::FromStr;
|
|
3
|
+
|
|
4
|
+
use serde::{Deserialize, Deserializer, Serialize};
|
|
5
|
+
|
|
6
|
+
use crate::DomainError;
|
|
7
|
+
|
|
8
|
+
/// Canonical lower-case EVM address (`0x` + 40 hex chars).
|
|
9
|
+
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
|
|
10
|
+
pub struct EvmAddress(String);
|
|
11
|
+
|
|
12
|
+
impl EvmAddress {
|
|
13
|
+
/// Returns the normalized address string.
|
|
14
|
+
#[must_use]
|
|
15
|
+
pub fn as_str(&self) -> &str {
|
|
16
|
+
&self.0
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
impl Display for EvmAddress {
|
|
21
|
+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
22
|
+
f.write_str(&self.0)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
impl FromStr for EvmAddress {
|
|
27
|
+
type Err = DomainError;
|
|
28
|
+
|
|
29
|
+
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
30
|
+
let normalized = s.to_ascii_lowercase();
|
|
31
|
+
let payload = normalized
|
|
32
|
+
.strip_prefix("0x")
|
|
33
|
+
.ok_or(DomainError::InvalidAddress)?;
|
|
34
|
+
|
|
35
|
+
if payload.len() != 40 || !payload.chars().all(|c| c.is_ascii_hexdigit()) {
|
|
36
|
+
return Err(DomainError::InvalidAddress);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
Ok(Self(normalized))
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
impl<'de> Deserialize<'de> for EvmAddress {
|
|
44
|
+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
45
|
+
where
|
|
46
|
+
D: Deserializer<'de>,
|
|
47
|
+
{
|
|
48
|
+
let value = String::deserialize(deserializer)?;
|
|
49
|
+
Self::from_str(&value).map_err(serde::de::Error::custom)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
use serde::{Deserialize, Serialize};
|
|
2
|
+
use sha2::{Digest, Sha256};
|
|
3
|
+
use time::OffsetDateTime;
|
|
4
|
+
use uuid::Uuid;
|
|
5
|
+
|
|
6
|
+
use crate::{AgentAction, AssetId, DomainError, EvmAddress};
|
|
7
|
+
|
|
8
|
+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
|
9
|
+
#[serde(rename_all = "snake_case")]
|
|
10
|
+
pub enum ManualApprovalDecision {
|
|
11
|
+
Approve,
|
|
12
|
+
Reject,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
|
16
|
+
#[serde(rename_all = "snake_case")]
|
|
17
|
+
pub enum ManualApprovalStatus {
|
|
18
|
+
Pending,
|
|
19
|
+
Approved,
|
|
20
|
+
Rejected,
|
|
21
|
+
Completed,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
25
|
+
pub struct ManualApprovalRequest {
|
|
26
|
+
pub id: Uuid,
|
|
27
|
+
pub agent_key_id: Uuid,
|
|
28
|
+
pub vault_key_id: Uuid,
|
|
29
|
+
pub request_payload_hash_hex: String,
|
|
30
|
+
pub action: AgentAction,
|
|
31
|
+
pub chain_id: u64,
|
|
32
|
+
pub asset: AssetId,
|
|
33
|
+
pub recipient: EvmAddress,
|
|
34
|
+
#[serde(with = "crate::u128_as_decimal_string")]
|
|
35
|
+
pub amount_wei: u128,
|
|
36
|
+
pub created_at: OffsetDateTime,
|
|
37
|
+
pub updated_at: OffsetDateTime,
|
|
38
|
+
pub status: ManualApprovalStatus,
|
|
39
|
+
pub triggered_by_policy_ids: Vec<Uuid>,
|
|
40
|
+
pub completed_at: Option<OffsetDateTime>,
|
|
41
|
+
pub rejection_reason: Option<String>,
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
|
|
45
|
+
pub struct RelayConfig {
|
|
46
|
+
pub relay_url: Option<String>,
|
|
47
|
+
pub frontend_url: Option<String>,
|
|
48
|
+
pub daemon_id_hex: String,
|
|
49
|
+
pub daemon_public_key_hex: String,
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
53
|
+
pub struct RelayFeedbackStatus {
|
|
54
|
+
pub update_id: String,
|
|
55
|
+
pub status: String,
|
|
56
|
+
pub detail: Option<String>,
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
pub fn manual_approval_capability_token(
|
|
60
|
+
relay_private_key_hex: &str,
|
|
61
|
+
approval_request_id: Uuid,
|
|
62
|
+
) -> Result<String, DomainError> {
|
|
63
|
+
let normalized = relay_private_key_hex.trim().trim_start_matches("0x");
|
|
64
|
+
if normalized.is_empty() {
|
|
65
|
+
return Err(DomainError::InvalidRelayCapabilitySecret);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
let secret_bytes =
|
|
69
|
+
hex::decode(normalized).map_err(|_| DomainError::InvalidRelayCapabilitySecret)?;
|
|
70
|
+
if secret_bytes.len() != 32 {
|
|
71
|
+
return Err(DomainError::InvalidRelayCapabilitySecret);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
let mut hasher = Sha256::new();
|
|
75
|
+
hasher.update(b"wlfi:manual-approval-capability:v1");
|
|
76
|
+
hasher.update(secret_bytes);
|
|
77
|
+
hasher.update(approval_request_id.as_bytes());
|
|
78
|
+
Ok(hex::encode(hasher.finalize()))
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
pub fn manual_approval_capability_hash(token: &str) -> Result<String, DomainError> {
|
|
82
|
+
let normalized = token.trim();
|
|
83
|
+
if normalized.is_empty() {
|
|
84
|
+
return Err(DomainError::InvalidRelayCapabilityToken);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
let mut hasher = Sha256::new();
|
|
88
|
+
hasher.update(normalized.as_bytes());
|
|
89
|
+
Ok(hex::encode(hasher.finalize()))
|
|
90
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
use thiserror::Error;
|
|
2
|
+
|
|
3
|
+
/// Errors returned by domain constructors and parsers.
|
|
4
|
+
#[derive(Debug, Error)]
|
|
5
|
+
pub enum DomainError {
|
|
6
|
+
/// Provided address was malformed.
|
|
7
|
+
#[error("address must start with 0x and contain exactly 40 hex characters")]
|
|
8
|
+
InvalidAddress,
|
|
9
|
+
/// Spending amount was zero.
|
|
10
|
+
#[error("amount must be greater than zero")]
|
|
11
|
+
InvalidAmount,
|
|
12
|
+
/// Numeric value could not fit target type.
|
|
13
|
+
#[error("numeric value exceeds supported range")]
|
|
14
|
+
AmountOutOfRange,
|
|
15
|
+
/// Chain ID must be greater than zero.
|
|
16
|
+
#[error("chain_id must be greater than zero")]
|
|
17
|
+
InvalidChainId,
|
|
18
|
+
/// ERC-20 calldata was invalid or unsupported.
|
|
19
|
+
#[error("invalid erc20 calldata: {0}")]
|
|
20
|
+
InvalidErc20Calldata(String),
|
|
21
|
+
/// Transaction data hex was malformed.
|
|
22
|
+
#[error("invalid transaction data hex")]
|
|
23
|
+
InvalidTransactionDataHex,
|
|
24
|
+
/// Gas fields were invalid.
|
|
25
|
+
#[error("invalid gas configuration")]
|
|
26
|
+
InvalidGasConfiguration,
|
|
27
|
+
/// Delegation is not permitted for broadcast transactions.
|
|
28
|
+
#[error("delegation is not allowed for broadcast transactions")]
|
|
29
|
+
DelegationNotAllowed,
|
|
30
|
+
/// ERC-20 approve/transfer calldata must not include native value.
|
|
31
|
+
#[error("erc20 approve/transfer transactions must set value_wei to 0")]
|
|
32
|
+
Erc20CallWithNativeValue,
|
|
33
|
+
/// A policy attachment set was empty.
|
|
34
|
+
#[error("policy set cannot be empty")]
|
|
35
|
+
EmptyPolicySet,
|
|
36
|
+
/// Transaction type is unsupported for a requested operation.
|
|
37
|
+
#[error("unsupported transaction type for operation: 0x{0:02x}")]
|
|
38
|
+
UnsupportedTransactionType(u8),
|
|
39
|
+
/// Invalid ECDSA recovery parity.
|
|
40
|
+
#[error("invalid signature parity; must be 0 or 1")]
|
|
41
|
+
InvalidSignatureParity,
|
|
42
|
+
/// Authorization time window is malformed.
|
|
43
|
+
#[error("authorization window is invalid")]
|
|
44
|
+
InvalidAuthorizationWindow,
|
|
45
|
+
/// Typed-data domain or nonce field is malformed.
|
|
46
|
+
#[error("invalid typed-data domain: {0}")]
|
|
47
|
+
InvalidTypedDataDomain(String),
|
|
48
|
+
/// Relay capability secret could not derive a secure approval token.
|
|
49
|
+
#[error("invalid relay approval capability secret")]
|
|
50
|
+
InvalidRelayCapabilitySecret,
|
|
51
|
+
/// Relay capability token was missing or malformed.
|
|
52
|
+
#[error("invalid relay approval capability token")]
|
|
53
|
+
InvalidRelayCapabilityToken,
|
|
54
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
use std::fmt;
|
|
2
|
+
|
|
3
|
+
use serde::{Deserialize, Serialize};
|
|
4
|
+
use time::OffsetDateTime;
|
|
5
|
+
use uuid::Uuid;
|
|
6
|
+
use zeroize::Zeroize;
|
|
7
|
+
|
|
8
|
+
use crate::PolicyAttachment;
|
|
9
|
+
|
|
10
|
+
/// Source of private key material.
|
|
11
|
+
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
12
|
+
pub enum KeySource {
|
|
13
|
+
/// Key generated by backend.
|
|
14
|
+
Generated,
|
|
15
|
+
/// Key imported by backend.
|
|
16
|
+
Imported,
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/// Public metadata for a vault signing key.
|
|
20
|
+
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
21
|
+
pub struct VaultKey {
|
|
22
|
+
/// Stable key identifier.
|
|
23
|
+
pub id: Uuid,
|
|
24
|
+
/// How key was created.
|
|
25
|
+
pub source: KeySource,
|
|
26
|
+
/// Uncompressed SEC1 public key bytes encoded as lowercase hex.
|
|
27
|
+
pub public_key_hex: String,
|
|
28
|
+
/// Creation timestamp.
|
|
29
|
+
pub created_at: OffsetDateTime,
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/// Agent key metadata.
|
|
33
|
+
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
34
|
+
pub struct AgentKey {
|
|
35
|
+
/// Stable agent key identifier.
|
|
36
|
+
pub id: Uuid,
|
|
37
|
+
/// Linked vault key identifier.
|
|
38
|
+
pub vault_key_id: Uuid,
|
|
39
|
+
/// Policy attachment.
|
|
40
|
+
pub policies: PolicyAttachment,
|
|
41
|
+
/// Creation timestamp.
|
|
42
|
+
pub created_at: OffsetDateTime,
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/// Agent authentication material returned at provisioning time.
|
|
46
|
+
///
|
|
47
|
+
/// The `auth_token` is a bearer credential and must be treated like a secret:
|
|
48
|
+
/// it should be stored securely and never logged.
|
|
49
|
+
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
50
|
+
pub struct AgentCredentials {
|
|
51
|
+
/// Public metadata for the provisioned agent key.
|
|
52
|
+
pub agent_key: AgentKey,
|
|
53
|
+
/// Secret bearer token required for signing requests.
|
|
54
|
+
pub auth_token: String,
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
impl fmt::Debug for AgentCredentials {
|
|
58
|
+
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
59
|
+
formatter
|
|
60
|
+
.debug_struct("AgentCredentials")
|
|
61
|
+
.field("agent_key", &self.agent_key)
|
|
62
|
+
.field("auth_token", &"<redacted>")
|
|
63
|
+
.finish()
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
impl AgentCredentials {
|
|
68
|
+
pub fn zeroize_secrets(&mut self) {
|
|
69
|
+
self.auth_token.zeroize();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
//! Domain model for the vault daemon, policy engine, and agent SDK.
|
|
2
|
+
//!
|
|
3
|
+
//! This crate is intentionally dependency-light and transport-agnostic so that
|
|
4
|
+
//! all higher-level crates share a single, strongly typed model.
|
|
5
|
+
|
|
6
|
+
#![forbid(unsafe_code)]
|
|
7
|
+
|
|
8
|
+
mod action;
|
|
9
|
+
mod address;
|
|
10
|
+
mod approval;
|
|
11
|
+
mod constants;
|
|
12
|
+
mod error;
|
|
13
|
+
mod keys;
|
|
14
|
+
mod nonce;
|
|
15
|
+
mod policy;
|
|
16
|
+
mod request;
|
|
17
|
+
mod scope;
|
|
18
|
+
mod session;
|
|
19
|
+
mod signature;
|
|
20
|
+
mod u128_as_decimal_string;
|
|
21
|
+
|
|
22
|
+
pub use action::{
|
|
23
|
+
action_from_erc20_calldata, parse_erc20_call, AgentAction, BroadcastTx, Eip3009Transfer,
|
|
24
|
+
Erc20Call, Permit2Permit,
|
|
25
|
+
};
|
|
26
|
+
pub use address::EvmAddress;
|
|
27
|
+
pub use approval::{
|
|
28
|
+
manual_approval_capability_hash, manual_approval_capability_token, ManualApprovalDecision,
|
|
29
|
+
ManualApprovalRequest, ManualApprovalStatus, RelayConfig, RelayFeedbackStatus,
|
|
30
|
+
};
|
|
31
|
+
pub use constants::{DEFAULT_MAX_GAS_SPEND_PER_CHAIN_WEI, EIP7702_TX_TYPE};
|
|
32
|
+
pub use error::DomainError;
|
|
33
|
+
pub use keys::{AgentCredentials, AgentKey, KeySource, VaultKey};
|
|
34
|
+
pub use nonce::{NonceReleaseRequest, NonceReservation, NonceReservationRequest};
|
|
35
|
+
pub use policy::{AssetId, PolicyAttachment, PolicyType, SpendEvent, SpendingPolicy};
|
|
36
|
+
pub use request::SignRequest;
|
|
37
|
+
pub use scope::EntityScope;
|
|
38
|
+
pub use session::{AdminSession, Lease};
|
|
39
|
+
pub use signature::Signature;
|
|
40
|
+
|
|
41
|
+
#[cfg(test)]
|
|
42
|
+
mod tests;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
use std::fmt;
|
|
2
|
+
|
|
3
|
+
use serde::{Deserialize, Serialize};
|
|
4
|
+
use time::OffsetDateTime;
|
|
5
|
+
use uuid::Uuid;
|
|
6
|
+
use zeroize::Zeroize;
|
|
7
|
+
|
|
8
|
+
/// Agent request for reserving a unique broadcast nonce.
|
|
9
|
+
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
10
|
+
pub struct NonceReservationRequest {
|
|
11
|
+
/// Client-generated unique request identifier for replay protection.
|
|
12
|
+
pub request_id: Uuid,
|
|
13
|
+
/// Agent key used for authorization.
|
|
14
|
+
pub agent_key_id: Uuid,
|
|
15
|
+
/// Bearer token bound to `agent_key_id`.
|
|
16
|
+
pub agent_auth_token: String,
|
|
17
|
+
/// EVM chain id for nonce scope.
|
|
18
|
+
pub chain_id: u64,
|
|
19
|
+
/// Minimum nonce the caller is willing to reserve.
|
|
20
|
+
pub min_nonce: u64,
|
|
21
|
+
/// Request timestamp.
|
|
22
|
+
pub requested_at: OffsetDateTime,
|
|
23
|
+
/// Request expiry timestamp.
|
|
24
|
+
pub expires_at: OffsetDateTime,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
impl fmt::Debug for NonceReservationRequest {
|
|
28
|
+
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
29
|
+
formatter
|
|
30
|
+
.debug_struct("NonceReservationRequest")
|
|
31
|
+
.field("request_id", &self.request_id)
|
|
32
|
+
.field("agent_key_id", &self.agent_key_id)
|
|
33
|
+
.field("agent_auth_token", &"<redacted>")
|
|
34
|
+
.field("chain_id", &self.chain_id)
|
|
35
|
+
.field("min_nonce", &self.min_nonce)
|
|
36
|
+
.field("requested_at", &self.requested_at)
|
|
37
|
+
.field("expires_at", &self.expires_at)
|
|
38
|
+
.finish()
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
impl NonceReservationRequest {
|
|
43
|
+
pub fn zeroize_secrets(&mut self) {
|
|
44
|
+
self.agent_auth_token.zeroize();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/// Reserved nonce lease for a specific agent and chain.
|
|
49
|
+
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
50
|
+
pub struct NonceReservation {
|
|
51
|
+
/// Unique reservation identifier.
|
|
52
|
+
pub reservation_id: Uuid,
|
|
53
|
+
/// Agent key that owns this reservation.
|
|
54
|
+
pub agent_key_id: Uuid,
|
|
55
|
+
/// Backing vault key used for signing scope.
|
|
56
|
+
pub vault_key_id: Uuid,
|
|
57
|
+
/// EVM chain id for nonce scope.
|
|
58
|
+
pub chain_id: u64,
|
|
59
|
+
/// Reserved nonce value.
|
|
60
|
+
pub nonce: u64,
|
|
61
|
+
/// Lease issuance timestamp.
|
|
62
|
+
pub issued_at: OffsetDateTime,
|
|
63
|
+
/// Lease expiry timestamp.
|
|
64
|
+
pub expires_at: OffsetDateTime,
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/// Agent request for explicitly releasing a nonce reservation.
|
|
68
|
+
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
69
|
+
pub struct NonceReleaseRequest {
|
|
70
|
+
/// Client-generated unique request identifier for replay protection.
|
|
71
|
+
pub request_id: Uuid,
|
|
72
|
+
/// Agent key used for authorization.
|
|
73
|
+
pub agent_key_id: Uuid,
|
|
74
|
+
/// Bearer token bound to `agent_key_id`.
|
|
75
|
+
pub agent_auth_token: String,
|
|
76
|
+
/// Reservation id to release.
|
|
77
|
+
pub reservation_id: Uuid,
|
|
78
|
+
/// Request timestamp.
|
|
79
|
+
pub requested_at: OffsetDateTime,
|
|
80
|
+
/// Request expiry timestamp.
|
|
81
|
+
pub expires_at: OffsetDateTime,
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
impl fmt::Debug for NonceReleaseRequest {
|
|
85
|
+
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
86
|
+
formatter
|
|
87
|
+
.debug_struct("NonceReleaseRequest")
|
|
88
|
+
.field("request_id", &self.request_id)
|
|
89
|
+
.field("agent_key_id", &self.agent_key_id)
|
|
90
|
+
.field("agent_auth_token", &"<redacted>")
|
|
91
|
+
.field("reservation_id", &self.reservation_id)
|
|
92
|
+
.field("requested_at", &self.requested_at)
|
|
93
|
+
.field("expires_at", &self.expires_at)
|
|
94
|
+
.finish()
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
impl NonceReleaseRequest {
|
|
99
|
+
pub fn zeroize_secrets(&mut self) {
|
|
100
|
+
self.agent_auth_token.zeroize();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
use std::collections::BTreeSet;
|
|
2
|
+
use std::fmt::{Display, Formatter};
|
|
3
|
+
|
|
4
|
+
use serde::{Deserialize, Serialize};
|
|
5
|
+
use time::OffsetDateTime;
|
|
6
|
+
use uuid::Uuid;
|
|
7
|
+
|
|
8
|
+
use crate::u128_as_decimal_string;
|
|
9
|
+
use crate::{DomainError, EntityScope, EvmAddress};
|
|
10
|
+
|
|
11
|
+
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
|
12
|
+
#[serde(tag = "kind", content = "value", rename_all = "snake_case")]
|
|
13
|
+
pub enum AssetId {
|
|
14
|
+
NativeEth,
|
|
15
|
+
Erc20(EvmAddress),
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
impl Display for AssetId {
|
|
19
|
+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
20
|
+
match self {
|
|
21
|
+
Self::NativeEth => f.write_str("native_eth"),
|
|
22
|
+
Self::Erc20(token) => write!(f, "erc20:{token}"),
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
|
28
|
+
pub enum PolicyType {
|
|
29
|
+
DailyMaxSpending,
|
|
30
|
+
DailyMaxTxCount,
|
|
31
|
+
WeeklyMaxSpending,
|
|
32
|
+
PerTxMaxSpending,
|
|
33
|
+
PerTxMaxFeePerGas,
|
|
34
|
+
PerTxMaxPriorityFeePerGas,
|
|
35
|
+
PerTxMaxCalldataBytes,
|
|
36
|
+
PerChainMaxGasSpend,
|
|
37
|
+
ManualApproval,
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
41
|
+
pub struct SpendingPolicy {
|
|
42
|
+
pub id: Uuid,
|
|
43
|
+
pub priority: u32,
|
|
44
|
+
pub policy_type: PolicyType,
|
|
45
|
+
#[serde(
|
|
46
|
+
default,
|
|
47
|
+
skip_serializing_if = "Option::is_none",
|
|
48
|
+
with = "crate::u128_as_decimal_string::option"
|
|
49
|
+
)]
|
|
50
|
+
pub min_amount_wei: Option<u128>,
|
|
51
|
+
#[serde(with = "u128_as_decimal_string")]
|
|
52
|
+
pub max_amount_wei: u128,
|
|
53
|
+
pub recipients: EntityScope<EvmAddress>,
|
|
54
|
+
pub assets: EntityScope<AssetId>,
|
|
55
|
+
pub networks: EntityScope<u64>,
|
|
56
|
+
pub enabled: bool,
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
impl SpendingPolicy {
|
|
60
|
+
pub fn new(
|
|
61
|
+
priority: u32,
|
|
62
|
+
policy_type: PolicyType,
|
|
63
|
+
max_amount_wei: u128,
|
|
64
|
+
recipients: EntityScope<EvmAddress>,
|
|
65
|
+
assets: EntityScope<AssetId>,
|
|
66
|
+
networks: EntityScope<u64>,
|
|
67
|
+
) -> Result<Self, DomainError> {
|
|
68
|
+
Self::new_with_range(
|
|
69
|
+
priority,
|
|
70
|
+
policy_type,
|
|
71
|
+
None,
|
|
72
|
+
max_amount_wei,
|
|
73
|
+
recipients,
|
|
74
|
+
assets,
|
|
75
|
+
networks,
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
pub fn new_manual_approval(
|
|
80
|
+
priority: u32,
|
|
81
|
+
min_amount_wei: u128,
|
|
82
|
+
max_amount_wei: u128,
|
|
83
|
+
recipients: EntityScope<EvmAddress>,
|
|
84
|
+
assets: EntityScope<AssetId>,
|
|
85
|
+
networks: EntityScope<u64>,
|
|
86
|
+
) -> Result<Self, DomainError> {
|
|
87
|
+
Self::new_with_range(
|
|
88
|
+
priority,
|
|
89
|
+
PolicyType::ManualApproval,
|
|
90
|
+
Some(min_amount_wei),
|
|
91
|
+
max_amount_wei,
|
|
92
|
+
recipients,
|
|
93
|
+
assets,
|
|
94
|
+
networks,
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
pub fn new_with_range(
|
|
99
|
+
priority: u32,
|
|
100
|
+
policy_type: PolicyType,
|
|
101
|
+
min_amount_wei: Option<u128>,
|
|
102
|
+
max_amount_wei: u128,
|
|
103
|
+
recipients: EntityScope<EvmAddress>,
|
|
104
|
+
assets: EntityScope<AssetId>,
|
|
105
|
+
networks: EntityScope<u64>,
|
|
106
|
+
) -> Result<Self, DomainError> {
|
|
107
|
+
if max_amount_wei == 0 {
|
|
108
|
+
return Err(DomainError::InvalidAmount);
|
|
109
|
+
}
|
|
110
|
+
if matches!(min_amount_wei, Some(0)) {
|
|
111
|
+
return Err(DomainError::InvalidAmount);
|
|
112
|
+
}
|
|
113
|
+
if let Some(min_amount_wei) = min_amount_wei {
|
|
114
|
+
if min_amount_wei > max_amount_wei {
|
|
115
|
+
return Err(DomainError::InvalidAmount);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if let EntityScope::Set(values) = &networks {
|
|
120
|
+
if values.is_empty() || values.contains(&0) {
|
|
121
|
+
return Err(DomainError::InvalidChainId);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
Ok(Self {
|
|
126
|
+
id: Uuid::new_v4(),
|
|
127
|
+
priority,
|
|
128
|
+
policy_type,
|
|
129
|
+
min_amount_wei,
|
|
130
|
+
max_amount_wei,
|
|
131
|
+
recipients,
|
|
132
|
+
assets,
|
|
133
|
+
networks,
|
|
134
|
+
enabled: true,
|
|
135
|
+
})
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
140
|
+
pub enum PolicyAttachment {
|
|
141
|
+
AllPolicies,
|
|
142
|
+
PolicySet(BTreeSet<Uuid>),
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
impl PolicyAttachment {
|
|
146
|
+
pub fn policy_set(policies: BTreeSet<Uuid>) -> Result<Self, DomainError> {
|
|
147
|
+
if policies.is_empty() {
|
|
148
|
+
return Err(DomainError::EmptyPolicySet);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
Ok(Self::PolicySet(policies))
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
#[must_use]
|
|
155
|
+
pub fn applies_to(&self, policy_id: Uuid) -> bool {
|
|
156
|
+
match self {
|
|
157
|
+
Self::AllPolicies => true,
|
|
158
|
+
Self::PolicySet(ids) => ids.contains(&policy_id),
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
164
|
+
pub struct SpendEvent {
|
|
165
|
+
pub agent_key_id: Uuid,
|
|
166
|
+
pub chain_id: u64,
|
|
167
|
+
pub asset: AssetId,
|
|
168
|
+
pub recipient: EvmAddress,
|
|
169
|
+
#[serde(with = "u128_as_decimal_string")]
|
|
170
|
+
pub amount_wei: u128,
|
|
171
|
+
pub at: OffsetDateTime,
|
|
172
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
use std::fmt;
|
|
2
|
+
|
|
3
|
+
use serde::{Deserialize, Serialize};
|
|
4
|
+
use time::OffsetDateTime;
|
|
5
|
+
use uuid::Uuid;
|
|
6
|
+
use zeroize::Zeroize;
|
|
7
|
+
|
|
8
|
+
use crate::AgentAction;
|
|
9
|
+
|
|
10
|
+
/// Request sent by an agent to receive signature approval.
|
|
11
|
+
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
12
|
+
pub struct SignRequest {
|
|
13
|
+
/// Client-generated unique request identifier for replay protection.
|
|
14
|
+
pub request_id: Uuid,
|
|
15
|
+
/// Agent key used for authorization.
|
|
16
|
+
pub agent_key_id: Uuid,
|
|
17
|
+
/// Bearer token bound to `agent_key_id`.
|
|
18
|
+
pub agent_auth_token: String,
|
|
19
|
+
/// Payload to sign.
|
|
20
|
+
///
|
|
21
|
+
/// Security contract: payload must be the canonical
|
|
22
|
+
/// `serde_json::to_vec(&action)` encoding of [`AgentAction`]; daemon rejects
|
|
23
|
+
/// semantic or byte-level mismatches.
|
|
24
|
+
pub payload: Vec<u8>,
|
|
25
|
+
/// Semantic action details used by policy checks.
|
|
26
|
+
pub action: AgentAction,
|
|
27
|
+
/// Request timestamp.
|
|
28
|
+
pub requested_at: OffsetDateTime,
|
|
29
|
+
/// Request expiry timestamp.
|
|
30
|
+
pub expires_at: OffsetDateTime,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
impl fmt::Debug for SignRequest {
|
|
34
|
+
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
35
|
+
formatter
|
|
36
|
+
.debug_struct("SignRequest")
|
|
37
|
+
.field("request_id", &self.request_id)
|
|
38
|
+
.field("agent_key_id", &self.agent_key_id)
|
|
39
|
+
.field("agent_auth_token", &"<redacted>")
|
|
40
|
+
.field("payload", &self.payload)
|
|
41
|
+
.field("action", &self.action)
|
|
42
|
+
.field("requested_at", &self.requested_at)
|
|
43
|
+
.field("expires_at", &self.expires_at)
|
|
44
|
+
.finish()
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
impl SignRequest {
|
|
49
|
+
pub fn zeroize_secrets(&mut self) {
|
|
50
|
+
self.agent_auth_token.zeroize();
|
|
51
|
+
self.payload.zeroize();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
use std::collections::BTreeSet;
|
|
2
|
+
|
|
3
|
+
use serde::{Deserialize, Serialize};
|
|
4
|
+
|
|
5
|
+
/// Scope matcher for recipients, assets, and networks.
|
|
6
|
+
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
7
|
+
#[serde(tag = "mode", content = "values")]
|
|
8
|
+
pub enum EntityScope<T: Ord> {
|
|
9
|
+
/// Match all values.
|
|
10
|
+
All,
|
|
11
|
+
/// Match a specific set of values.
|
|
12
|
+
Set(BTreeSet<T>),
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
impl<T: Ord> EntityScope<T> {
|
|
16
|
+
/// Returns whether `value` is allowed by this scope.
|
|
17
|
+
#[must_use]
|
|
18
|
+
pub fn allows(&self, value: &T) -> bool {
|
|
19
|
+
match self {
|
|
20
|
+
Self::All => true,
|
|
21
|
+
Self::Set(set) => set.contains(value),
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|