What you will learn
- Why trust domain isolation is necessary for AI agent crypto operations
- What each domain can and cannot do
- How boundaries are enforced at package, network, process, and test levels
- The full threat model: 14 threat scenarios across all domains with mitigations and test references
- The security invariants that every deployment must satisfy
The core problem
AI agents run potentially untrusted code: community-authored skills, LLM-generated actions, third-party plugins. If this code has access to private keys, a single compromise drains the wallet. Clavion solves this by dividing the system into three trust domains with strict, enforced boundaries. Every line of code belongs to exactly one domain. No component straddles a boundary.Domain A — Untrusted
Packages:adapter-openclaw, adapter-mcp, plugin-eliza, adapter-telegram
Domain A contains all agent-facing code. Code in this domain is considered potentially compromisable at all times.
What Domain A can do:
- Construct declarative TxIntents (JSON describing desired operations)
- Communicate with Domain B via localhost HTTP API
- Read balance and transaction status data through Domain B
- Access private keys or any key material
- Sign transactions directly
- Contact blockchain RPC endpoints
- Control approval summaries shown to users
- Access policy configuration or audit data
Domain B — Trusted
Packages:core, signer, audit, policy, preflight, registry, types
Domain B is the only domain that holds key material and can sign transactions. Every operation follows a mandatory pipeline:
signRaw API, no backdoor for “trusted” skills, no way to skip policy evaluation.
Security guarantees:
- Keys encrypted at rest (scrypt + AES-256-GCM), decrypted only in memory for signing
- Policy engine evaluates every intent against configurable rules
- Preflight simulation detects anomalies before signing
- Approval tokens are single-use with 300s TTL
- Every step is written to an append-only audit trail
Domain C — Limited Trust
Packages:sandbox
Domain C runs untrusted skill code in Docker containers with aggressive restrictions:
| Restriction | Flag | Effect |
|---|---|---|
| No network | --network none | Cannot reach internet or other containers |
| Read-only filesystem | --read-only | Cannot write to disk (except /tmp) |
| No capabilities | --cap-drop ALL | All Linux capabilities removed |
| No privilege escalation | --security-opt no-new-privileges | Cannot gain new privileges |
| No process spawning | seccomp profile | Blocks clone, fork, exec syscalls |
| Resource limits | --memory, --cpus | Memory (128MB) and CPU (0.5 cores) caps |
Boundary enforcement
The three-domain boundary is enforced at multiple levels simultaneously:| Level | Mechanism |
|---|---|
| Package | Domain B packages are never imported by Domain A code. TypeScript project references enforce this at build time |
| Network | Keys never traverse HTTP. Only approval tokens and signed transaction hashes cross the API boundary |
| Process | Sandbox containers have no access to the host filesystem where keys are stored |
| Runtime | Strict JSON Schema validation (additionalProperties: false) on all API inputs |
| Test | Security test suite (tests/security/) verifies domain boundaries: key exfiltration attempts, import graph integrity, cross-domain access |
What compromise looks like
| Scenario | Impact |
|---|---|
| Malicious skill in Domain A | Can submit TxIntents, but each must pass policy, simulation, and approval. Cannot access keys. |
| Compromised sandbox in Domain C | No network access, no keys, no filesystem. Cannot exfiltrate data or sign transactions. |
| Compromised RPC endpoint | Can return false simulation results, but policy engine and approval flow still protect against unauthorized signing. |
| Prompt injection in agent | Agent can only call the ISCL API. Cannot bypass policy or approval. Worst case: submits many TxIntents (rate limited). |
Security blueprint: threat model
The following threat model captures every identified threat to the system and maps each one to specific mitigations, responsible components, and test references. The goal of v0.1 is not “absolute security” but provable invariants: keys are isolated, signatures are controlled by policies, and malicious skills cannot silently exfiltrate data or send arbitrary transactions.Trust boundaries and invariants (v0.1)
Every deployment must satisfy these five invariants. If any invariant is violated, the system is considered compromised.- The private key is never available in Domain A or C. It exists only in Domain B.
- Any transaction signature passes through the PolicyEngine and Preflight; if confirmation mode is enabled, it requires human approval.
- OpenClaw skills do not have direct RPC access to the blockchain; only ISCL Core can access RPC via an allowlist.
- Any operation that can lead to a loss of funds is expressed as a TxIntent v1 and validated against a schema; arbitrary calldata is not signed in v0.1.
- All critical steps are written to the AuditTrace with correlation:
intentId→ preflight → approval → signature →txHash→ receipt.
Domain A threats (OpenClaw / skills)
These threats target the untrusted agent layer — the code most likely to be compromised via prompt injection, supply chain attacks, or malicious skill packages.T1: Malicious skill attempts to steal the private key
T1: Malicious skill attempts to steal the private key
~/.ssh, ~/.config), process memory, or calls guessed API endpoints to extract key material.Mitigation: Keys are physically not present in Domain A. OpenClaw skills do not receive secrets. All signing occurs in the WalletService of Domain B. There is no API endpoint in Domain A to extract the key — only a request to “sign txRequest” which requires passing policy checks and approval.Components: WalletService, OpenClaw Adapter (thin clients), Config Manager.Test: SecurityTest_A1 "SkillKeyExfiltration" — Launches a test “evil-skill” in the OpenClaw environment that attempts to read standard paths and environment variables and calls guessed APIs. Expectation: keys are missing, access error, attempts are recorded in the audit trace. Separately verifies that signing is impossible without policy + approval.T2: Malicious skill makes arbitrary network requests (exfiltration, C2 communication)
T2: Malicious skill makes arbitrary network requests (exfiltration, C2 communication)
SecurityTest_A2 "NoExternalNetworkFromSkill" — Skill attempts to make a request to an arbitrary domain. Expectation: blocked, logged, execution denied.T3: Skill attempts to deceive the user via UI (semantic spoofing)
T3: Skill attempts to deceive the user via UI (semantic spoofing)
BuildPlan and Preflight results. The skill does not control the confirmation text. The ApprovalRequest is formed from canonical data: to, contract, method signature, token amounts, slippage, and expected balance diffs.Components: TxEngine, PreflightService, ApprovalComposer.Test: SecurityTest_A3 "ApprovalSummarySourceOfTruth" — Skill sends a TxIntent “swap 1 USDC” but attempts to spoof the summary. Expectation: in the ApprovalRequest, the text and details correspond to build/preflight output, not the skill input; spoofing has no effect.T4: Skill generates TxIntent with harmful parameters
T4: Skill generates TxIntent with harmful parameters
approve amount (MaxUint256), a spoofed recipient address, or a swap targeting an unknown/malicious contract.Mitigation: Strict TxIntent schema validation + PolicyEngine. Policy blocks unknown contracts, amounts exceeding maxApprovalAmount, values exceeding maxValueWei, and mismatches against tokenAllowlist / contractAllowlist. Preflight adds warnings and elevates risk for anomalies.Components: TxIntent validator, PolicyEngine, PreflightService.Test: SecurityTest_A4 "PolicyDeniesBadIntents" — A set of cases: approve for MaxUint without permission, transfer to a disallowed address, swap via an unknown router. Expectation: deny with reason code; policy_denied event recorded in audit trace.Domain C threats (Secure Executor / Sandbox)
These threats target the container-isolated execution environment where untrusted skill code runs.T5: Malicious crypto-skill in Domain C attempts to access keys
T5: Malicious crypto-skill in Domain C attempts to access keys
WalletService (B), Sandbox FS isolation (C), IPC boundary.Test: SecurityTest_C1 "ExecutorCannotReadKeys" — Runs a malicious package in the sandbox that attempts to find keys on the filesystem and in environment variables. Expectation: absent; attempts recorded; signing impossible without approval.T6: Skill in Domain C attempts to access the internet
T6: Skill in Domain C attempts to access the internet
--network none by default). Only specific RPC endpoints are allowed if necessary, but generally network access is blocked entirely for skills, as RPC access belongs to ISCL Core. Any external domains are blocked.Components: Sandbox Runner network policy.Test: SecurityTest_C2 "NetworkAllowlist" — Attempts requests to domains outside the allowlist. Expectation: blocked, written to audit trace.T7: Skill attempts to execute arbitrary processes / mining / DoS
T7: Skill attempts to execute arbitrary processes / mining / DoS
no_spawn or whitelisted-spawn policy via seccomp profiles. cgroups resource limits enforce memory (128MB) and CPU (0.5 cores) caps. Execution timeouts kill long-running processes. Crypto miners and any binaries outside the allowlist are prohibited.Components: Sandbox Runner process policy, resource limits.Test: SecurityTest_C3 "NoSpawnNoMine" — Attempts to spawn “bash”, “curl”, “node -e download” with no_spawn enabled. Expectation: denial. Attempts CPU burn. Expectation: stopped by limits and recorded in trace.T8: Supply chain attack -- substitution of skill package or dependency
T8: Supply chain attack -- substitution of skill package or dependency
manifestHash / sourceHash verification on installation. Lockfile is mandatory. If the signature is absent or the hash mismatches, installation is blocked or requires an explicit override from the operator.Components: SkillRegistryService, Installer, Scanner.Test: SecurityTest_C4 "TamperedPackageRejected" — Changes one file in the package. Expectation: SHA-256 mismatch, installation forbidden. Changes manifest after signing. Expectation: signature invalid, registration rejected.Domain B threats (ISCL Core)
These threats target the trusted core — the most critical components that hold keys and sign transactions.T9: RPC endpoint compromised, returns false data
T9: RPC endpoint compromised, returns false data
source=rpcX. On discrepancies between sources, the system elevates risk and may demand additional confirmation.Components: RpcClient, PreflightService.Test: SecurityTest_B1 "RpcMismatchElevatesRisk" — Mocks two RPCs where one returns a different result. Expectation: risk elevated, approval requires confirmation, trace records mismatch.T10: Policy bypass via undocumented signing path
T10: Policy bypass via undocumented signing path
WalletService.sign() without passing through the PolicyEngine or requiring an approval token.Mitigation: WalletService signs only via a single method requiring both a valid PolicyDecision and a single-use ApprovalToken. No signRaw API exists. Internal APIs are closed. All signatures are logged to the audit trail.Components: WalletService, PolicyEngine, ApprovalTokenManager.Test: SecurityTest_B2 "NoBypassSigning" — Attempts to call the internal signing endpoint without an approval token. Expectation: 403/deny. Verifies that all signatures are accompanied by policy_allowed + approval_used trace events.T11: Replay attack -- reuse of approvalToken or intentId
T11: Replay attack -- reuse of approvalToken or intentId
intentId to trigger duplicate execution.Mitigation: approvalToken is single-use, bound to a specific intentId and txRequestHash, and has a TTL (300 seconds). intentId is idempotent: repeated build returns the same plan, or reports “intent already consumed” after sending. Replayed tokens are rejected immediately.Components: IdempotencyStore, ApprovalTokenManager, TxEngine.Test: SecurityTest_B3 "ApprovalTokenSingleUse" — Uses an approval token a second time. Expectation: refusal. Repeats sign-and-send with the same intentId after sending. Expectation: refusal or “already sent” returning the existing txHash.T12: Excessively broad approvals (MaxUint allowances)
T12: Excessively broad approvals (MaxUint allowances)
approve action with an unlimited amount (MaxUint256), granting a spender permanent access to the user’s entire token balance.Mitigation: Policy maxApprovalAmount + preference for “exact approval” or “bounded approval”. Preflight shows the allowance change and warns the user. By default, maxApprovalAmount is restricted to prevent unlimited approvals.Components: TxEngine (approve builder), PolicyEngine, ApprovalComposer.Test: SecurityTest_B4 "ApprovalBounded" — Submits a TxIntent approve with amount exceeding the policy limit. Expectation: deny. Swap workflow requiring approve builds approve for the bounded amount only.User threats and social engineering
These threats target the human operator rather than the software.T13: User approves a harmful transaction (social engineering)
T13: User approves a harmful transaction (social engineering)
T14: User OS compromise / rootkit
T14: User OS compromise / rootkit
Test matrix: “definition of secure enough” for pilot
For the v0.1 pilot, the system is considered “secure enough” if all four criteria are met:| # | Criterion | Verification |
|---|---|---|
| 1 | All security tests pass | SecurityTest_A1..A4, C1..C4, B1..B4 pass in CI |
| 2 | Full audit chain for every signature | Every signature in logs has the linkage: intentId → policyAllowed → preflight → approval → sign → send |
| 3 | No signing without human confirmation | With human approval enabled, it is impossible to send a transaction without manual confirmation |
| 4 | Sandbox isolation verified | Access to the network outside the allowlist and filesystem outside allowedPaths is impossible within the sandbox |
Mandatory audit events
TheAuditTrace must capture these nine event types for every transaction lifecycle. These events are necessary for incident investigations and future reputation/attestation systems.
| Event | Key Fields |
|---|---|
intent_received | intentId, skillId, openclawVersion, createdAt |
policy_evaluated | decision, reasons, policyVersion |
build_completed | txRequestHash, to, value, methodSig |
preflight_completed | riskScore, balanceDiffsSummary, warnings |
approval_issued | approvalTokenId, ttl |
signature_created | txRequestHash, signerId |
tx_sent | txHash, chainId |
tx_receipt | status, gasUsed |
security_violation | type, details (for sandbox events) |
Security roadmap (post-pilot)
v0.2 will add: session keys/allowances, smart accounts, multi-RPC consensus, stricter sandbox (WASM / Firecracker / Nanoclaw executor), package signing with attestation, and optional private relay integration.Architectural decision
This architecture is formalized in ADR-001: Trust Domain Isolation, which documents the problem analysis, alternatives considered, and trade-offs accepted.Next steps
- Transaction Lifecycle — How intents flow through the pipeline
- Policy Engine — How policy rules enforce security
- Sandbox Security — Hands-on guide to Domain C isolation