fix(wallet): harden keystore fallback, persist policy/history, wire gateway injection

Addresses review findings:
- Remove insecure automatic encrypted-file credential-store fallback.
   now only uses real OS/keyctl-backed stores,
  or remains unavailable. Headless users must use explicit
  HERMES_KEYSTORE_PASSPHRASE if desired.
- Add shared wallet runtime so tools/CLI/approval use the same configured
  providers and persisted policy state.
- Inject keystore-backed secrets into gateway/headless startup too, so
  migrated .env stubs don't break messaging deployments.
- Persist wallet policy state (freeze, daily totals, rate-limit timestamps,
  cooldown timestamps) across invocations.
- Persist transaction history to disk across invocations.
- Make owner-approved sends execute through the same runtime/policy path and
  record policy state after successful approved sends.
- Fix wallet export by allowing explicit CLI export reads of sealed keys via
  dedicated requester path () instead of generic CLI reads.
- Make CLI wallet sends evaluate policy before execution and honor freeze.
- Align docs with actual crypto primitive (XSalsa20-Poly1305 via SecretBox)
  and current policy-config scope.

Validation:
- 129 tests passing
- freeze persistence verified manually
- wallet export verified manually
This commit is contained in:
Shannon Sands
2026-03-26 17:41:22 +10:00
parent 3fef2fd3ee
commit 253c7abbe9
14 changed files with 477 additions and 143 deletions

View File

@@ -33,11 +33,16 @@ hermes keystore init
You'll be prompted to create a passphrase. This is needed each time Hermes starts. To avoid typing it every time:
```bash
# Save to your OS credential store (macOS Keychain, GNOME Keyring, etc.)
# Save to your OS credential store (macOS Keychain, Windows Credential Locker,
# GNOME/KDE Secret Service, or Linux keyctl when available)
hermes keystore remember
# Or set an env var (for Docker / systemd / headless)
export HERMES_KEYSTORE_PASSPHRASE="your-passphrase"
Hermes intentionally does **not** fall back to a machine-derived encrypted file
for remembered passphrases. In the current same-user execution model, that would
be derivable by the local agent process and would weaken the keystore boundary.
```
### 2. Create a wallet
@@ -135,7 +140,7 @@ hermes keystore status Show keystore status
### Encryption
- Master key derived from your passphrase via **Argon2id** (memory-hard KDF, 64MB)
- Each secret encrypted with **XChaCha20-Poly1305** (AEAD, random nonce per write)
- Each secret encrypted with **XSalsa20-Poly1305** via libsodium SecretBox (AEAD, random nonce per write)
- Master key held in memory only — never written to disk
- Keystore DB file permissions: `0600`, directory: `0700`
@@ -244,11 +249,20 @@ wallet:
solana: "https://api.mainnet-beta.solana.com"
ethereum: "https://eth.llamarpc.com"
# Agent wallet policy overrides (tightens defaults, cannot loosen)
# Minimal policy overrides currently supported at runtime
# (global/shared state, not per-wallet yet)
agent_wallet:
enabled: true
auto_approve_below_native: "0.5"
daily_limit_native: "5.0"
max_per_tx_native: "1.0"
rate_limit: "5/hour"
auto_approve_below_native: "0.5" # maps to require_approval.above_native
daily_limit_native: "5.0" # maps to daily_limit.max_native
max_per_tx_native: "1.0" # maps to spending_limit.max_native
```
:::note
Per-wallet policy management and richer policy configuration are not fully surfaced yet. Today Hermes supports:
- runtime RPC endpoint overrides via `wallet.rpc_endpoints`
- a minimal set of global agent-wallet policy overrides via `wallet.agent_wallet`
- durable freeze/rate-limit/daily-limit state across CLI invocations
More granular per-wallet policy editing is planned follow-up work.
:::