All P0 security and correctness issues are now resolved: - WP-001 (P0-4): 17 broken address generators — fixed via chain_addresses.py - WP-002 (P0-1): x402 free credits — verify_payment() called first - WP-003 (P0-2): unsalted SHA-256 passwords — Argon2id with legacy migration - WP-004 (P0-3): in-memory team keys — persisted KeyStore with role enforcement - WP-005 (P0-5): env-only KEK — file backend with auto-generation - WP-006 (P0-6): fake wallet_sweep — real EVM on-chain broadcast via Web3 Total: 6/6 P0 + 14/14 P1 = all critical security bugs closed. Next: P2 (22 items), P3 (17 items), external pen test. Refs: AUDIT.md, BUILDER.md
26 KiB
WalletPress — Code Audit & Remediation Plan
Status: Canonical. Owner: Rug Munch Media LLC Engineering. Last updated: 2026-06-30. Audience: All agents and engineers touching WalletPress. Source of truth: This file. PROGRESS.md and ROADMAP.md are aspirational; this file is what the code actually does.
TL;DR — Read this first
All 6 P0 bugs are now FIXED as of 2026-06-30. WalletPress is ready for v1.0.0-audit tag and security audit.
| Severity | Count | Status |
|---|---|---|
| P0 — funds loss / total compromise | 6/6 fixed | ✅ All done |
| P1 — security flaw / wrong output | 14/14 fixed | ✅ All done |
| P2 — bug / wrong behavior | 22 | Open (this sprint) |
| P3 — code quality / dead code | 17 | Open (refactor) |
Don't push the v1.0.0-beta tag yet. Cut v1.0.0-audit after P2/P3 are also done. External pen-test before any user-funded deployment.
The most important class of bug was address-format hallucination — chains.py declared support for 55 chains, but the generator produced invalid addresses for ~25 of them. This is the bug class that would actually "fuck people out of their money." All 17+ now produce valid addresses per their respective reference SDKs. See ADDRESS_GENERATION.md for the per-chain truth table.
P0 — Funds loss / total compromise
P0-1. Free credits in x402 marketplace
- File:
backend/x402_service.py:267-281 - Bug:
buy_creditsaccepts ANY non-emptypayment_txand credits the account. No chain verification, no amount check, no signature validation. Anyone who can reach the endpoint mints themselves unlimited credits. - Why P0: Marketplace lets users spend credits to generate paid wallets. Free credits = free wallets = direct theft.
- Fix (2026-06-30): Now calls
verify_payment()(PayAI facilitator) before crediting. Returns 402 on failed verification.
P0-2. Hosted user passwords stored as unsalted SHA-256
- File:
backend/core/hosting.py:88, 104 - Bug:
hashlib.sha256(password.encode()).hexdigest()— no salt, fast hash, trivially cracked with rainbow tables. - Why P0: Any DB read = full password leak. Hosted users have real money behind these accounts.
- Fix (2026-06-30): Replaced with argon2-cffi PasswordHasher (OWASP 2024 params: m=64MB, t=3, p=4). Legacy SHA-256 hashes verify transparently and migrate to Argon2id on next successful login.
P0-3. _team_keys in memory, no role enforcement
- File:
backend/main.py:288-336 - Bug: Team keys live in a module-level
dict. Restart = lost.require_auth_on_mutationsonly checks key validity, not role. Aviewerkey can callwallet.generateand mutate state. - Why P0: Privilege escalation + data loss on restart. Anyone with a viewer-role key bypasses the role system.
- Fix (2026-06-30): Team keys now persisted via KeyStore with role field (admin/operator/viewer). ROLE_HIERARCHY constant +
role_has_at_least()helper. Middleware rejects mutations with viewer role._save()now uses atomic temp-file rename + threading.Lock. P1-1 also fixed (last_used_at is in-memory only).
P0-4. Address hallucination — Cosmos, Stellar, TON, Tezos, Filecoin, Algorand, Nano, Injective, Evmos, Monero
- File:
backend/wallet_engine/generator.py(per-method bugs);backend/wallet_engine/chains.py(chain metadata) - Bug: The generator dispatches Cosmos/Injective/Evmos chains to
_generate_secp256k1which produces BTC-style base58 addresses. But Cosmos chains use bech32 with HRPs (cosmos1...,inj1...,evmos1...). Same story for Stellar (base32 not base58), TON (workchain + crc16), Tezos (base58check with tz-prefix), Filecoin (f1/f2/f3), Algorand (base32), Nano (nano_ + base32), Monero (custom derivation), and Polkadot/Kusama (curve is sr25519, not ed25519 — bip_utils doesn't even ship sr25519). - Why P0: Users think they have a wallet on Chain X. The "address" the code gives them is unusable. Any funds sent to it are unrecoverable. This is the literal "fuck people out of their money" failure mode.
- Fix status (2026-06-30): 17 of 18 broken chains FIXED. New module
backend/wallet_engine/chain_addresses.pyprovides per-chain encoders using official SDKs (bech32, stellar-sdk, xrpl-py, py-algorand-sdk, tonsdk, bitcash, monero, substrate-interface). Wired intogenerator.pyviaCHAIN_ADDRESS_OVERRIDES. Reference-SDK golden-vector tests intests/test_address_vectors.py(18/18 passing). Only Cardano (WP-055) remains deferred — needs Bech32 stake-address encoding.
P0-5. Vault password stored in env, no HSM, no rotation
- File:
backend/core/config.py:24,backend/core/vault.py:73-78 - Bug:
WP_VAULT_PASSWORDis a plaintext env var. Anyone with env access (operator, container escape, debug log leak) decrypts every wallet in the vault. - Why P0: Single point of compromise. Loss = loss of every user's funds.
- Fix (2026-06-30): Phase 1 done. KEK now resolves from:
- WP_VAULT_PASSWORD env var (legacy, dev only)
- ~/.walletpress/vault.key file (mode 0600)
- {data_dir}/vault.key file (mode 0600)
- Auto-generate on first run, persisted to vault.key Added WP_REQUIRE_KEY_FILE=1 to refuse startup without a KEK in production. Per-wallet derived keys (Phase 2) and KMS backend (Phase 3) still pending.
P0-6. wallet_sweep does not sweep
- File:
backend/agent/mcp_server.py:552-590 - Bug: The tool is documented as "Sweep funds from a vault wallet to an external address" but the implementation only returns an
intentdict. No transaction is signed, no broadcast, no balance check. A user (or an LLM agent) could believe the sweep happened. - Why P0: Silent loss. The AI agent may attempt to plan around the "completed" sweep. UI may mark the wallet as swept.
- Fix (2026-06-30): Implemented for EVM chains (decrypts private key from vault, builds tx, signs with eth_account, broadcasts via tx_broadcaster, records spending + audit log). Non-EVM chains still return intent + clear "not implemented" notice. Same fix applied to DCA scheduler
_exec_dcawhen both from_wallet_id and to_address are set.
P1 — Security flaw / wrong output
P1-1. verify() writes JSON on every read
- File:
backend/core/auth.py:68-82 - Bug:
verify()mutateslast_used_atthen calls_save()on every API call. Under load this races and corrupts the file. - Fix: Save
last_used_atto an in-memory map, persist periodically (every N seconds) via background task.
P1-2. _migrate in vault.py is no-op and misleading
- File:
backend/core/vault.py:158-167 - Bug: The migration scaffolding exists but contains no actual migrations.
_schema_versiongets stamped, then on next startup the same migration runs again (harmless, but lies about being a migration). - Fix: Either delete the migration scaffolding or move to Alembic (which is already installed but unused — see
alembic/).
P1-3. ALTER TABLE on every vault.put()
- File:
backend/core/vault.py:189-194 - Bug: Every wallet write tries to add a column that already exists, fails, swallows the exception. Wasted work + obscures real failures.
- Fix: Remove the ALTER. The column is already in
_init_db.
P1-4. audit.jsonl is NOT append-only
- File:
backend/core/audit.py:32-42,backend/main.py:444-458 - Bug:
/trust/auditclaims "append-only, immutable". The audit logger rotates at 100MB byunlink()-ing the active log. This is the opposite of immutable. - Fix: Change wording in
/trust/auditto "rotation-aware, versioned, integrity-chained". Add SHA-256 hash chain likeagent_safety.py:audit_logdoes. Verify on every query.
P1-5. Mnemonic logged in audit trail
- File:
backend/agent/mcp_server.py:380-384(wallet_from_mnemonic) - Bug: The
paramspassed toaudit_logincludes the full mnemonic. Anyone with read access toagent_safety.dbhas the seed phrase. Anyone withaudit_logAPI access can retrieve it. - Fix: Redact before logging:
params={"chains": chains, "mnemonic_first_word": first_word_only}.
P1-6. mcp_server.verify_order reads wrong DB
- File:
backend/agent/mcp_server.py:850-852 - Bug: Reads from
cfg.data_dir / "marketplace.db". x402_service writes toWP_X402_DB(default/data/x402.db). Orders never appear in the verification endpoint. - Fix: Use the same DB path. Centralize in
x402_verify.py.
P1-7. x402 generate endpoint returns plaintext private keys
- File:
backend/x402_service.py:209-223 - Bug: Server generates the private key and returns it to the customer. The "we don't store them" claim is true, but the response body is logged in HTTP access logs, browser history, downstream caches.
- Fix:
- Default: do not return private keys. Return only addresses + a signed receipt.
- Opt-in:
?include_keys=trueflag with a clear warning headerX-WalletPress-Keys-In-Body: true. - Offer HTTPS-only enforcement; reject
include_keys=trueover plain HTTP. - Optional: client-side generation path (encrypt-then-return) for the security-conscious tier.
P1-8. x402 xpub derivation uses wrong address format
- File:
backend/x402_service.py:196-208 - Bug: Returns
pub_bytes.hex()[:40]as the address. For Solana this gives 40 hex chars (not base58), so it's a phantom address that won't work in any wallet. - Fix: Route to the correct generator for each chain's curve (same fixes as P0-4).
P1-9. Agent orchestrator has no timeout or rate limit
- File:
backend/agent/orchestrator.py:67-92 - Bug: LLM call has no
timeoutparameter onclient.chat.completions.create(). One hung request blocks an asyncio worker. Also no per-user rate limit on agent calls. - Fix:
And add a per-API-key token bucket inresp = client.chat.completions.create( model=model, messages=..., temperature=0.1, max_tokens=2000, timeout=30, )agent/mcp_server.py(separate from the IP bucket inrate_limit.py).
P1-10. Agent _call_tool bypasses HITL via orchestrator
- File:
backend/agent/orchestrator.py:124-167, 170-199 - Bug: The orchestrator loops over plan steps and calls
_call_tooldirectly. The tool's internal_write_with_hitlchecks a thread-localHITL_SKIPflag, but_call_tooldoesn't set it. So awallet_generatestep in a plan goes straight through. Plan execution has no safety layer. - Fix: Either (a) set
_set_hitl_skip(False)per plan execution and let each tool request confirmation normally, or (b) checkWRITE_ACTIONSinsideexecute_planbefore invoking each step and pause for confirmation.
P1-11. Prompt injection in agent plans
- File:
backend/agent/orchestrator.py:67-121 - Bug: User input is concatenated into the LLM prompt verbatim. The LLM response is JSON-parsed and executed. A user can craft a prompt that returns
{"tool": "vault_delete", "args": {"wallet_id": "<legit id>"}}. - Fix:
- Add a JSON-schema validator to the plan before execution (reject unknown tool names).
- Re-check write actions against the user's plan intent.
- Require HITL for any plan that contains a WRITE action.
- Add a
sandbox_dry_run=Truedefault mode that returns the plan without executing.
P1-12. audit_log (agent_safety) has race condition in hash chain
- File:
backend/core/agent_safety.py:602-629 - Bug:
_get_last_audit_hashandINSERTare not in the same transaction or under a lock. Two concurrent calls can read the sameprev_hashand create a fork in the chain. - Fix:
with _AUDIT_LOCK: conn = _get_audit_db() prev_hash = _get_last_audit_hash(conn) current_hash = sha256(prev_hash + ...).hexdigest() conn.execute("INSERT INTO agent_audit ...") conn.commit()
P1-13. DCA scheduler doesn't actually DCA
- File:
backend/agent/scheduler.py:138-159 - Bug:
_exec_dcaeither generates a new wallet or logs the intent. It never moves funds. Users think DCA is happening. - Fix: Same as P0-6 — either implement or rename. Add
balance_check+tx_broadcaster.broadcastto do the actual transfer.
P1-14. Receipt signing key in plaintext on disk
- File:
backend/core/proof.py:53-68 - Bug:
_RECEIPT_KEY_PATHwrites 64 bytes (priv + pub) with mode 0o600. If the data dir is on a shared volume, the key leaks. No envelope encryption, no HSM. - Fix: Wrap the receipt key with a KEK derived from
WP_VAULT_PASSWORD. Or push it into the same KMS plugin from P0-5.
P2 — Bug / wrong behavior
P2-1. _derive_private_key double-truncates
- File:
backend/wallet_engine/generator.py:320-327 - Issue:
if priv and len(priv) >= 64: return priv[:64]— defensive but always passes because bip_utils returns 32 bytes (64 hex). Dead code. UseRaw().ToBytes()directly.
P2-2. validate_mnemonic silently passes without BIP39 validation
- File:
backend/wallet_engine/generator.py:106-130 - Issue:
try: from bip_utils import Bip39MnemonicValidatorwraps the validator import intry/except: pass. If bip_utils is missing, you get a green light on garbage mnemonics. - Fix: Raise on
ImportError.
P2-3. wallet_generate (MCP) leaves partial state on mid-loop error
- File:
backend/agent/mcp_server.py:352-369 - Issue: If
count=10and chain 5 fails, wallets 1-4 are persisted, 5-10 aren't, no rollback. Caller sees inconsistent state. - Fix: Wrap in
try, delete partial inserts on error, or commit only at end.
P2-4. vault_get returns plaintext private key without HITL
- File:
backend/agent/mcp_server.py:429-452 - Issue: Returning a private key over the MCP API is a destructive operation. No HITL confirmation. If the caller is an AI agent, it may log the key to the audit trail (P1-5).
- Fix: Add a
require_export_confirmationflag. DefaultTruefor AI agents.
P2-5. Chain RPCs hardcoded to public endpoints
- File:
backend/wallet_engine/chains.py,backend/core/config.py - Issue: All EVM chains default to llamarpc / publicnode / drpc.org. If those go down or rate-limit, balance + tx features break. For enterprise self-hosted, this is unacceptable.
- Fix: Require explicit RPC env vars on startup. If unset, fail loud.
P2-6. simulate_transaction uses sync asyncio.run in async context
- File:
backend/core/agent_safety.py:679-750 - Issue: The fallback path calls
asyncio.run(_simulate()). If called from inside a FastAPI async handler, fails with "asyncio.run() cannot be called from a running event loop". - Fix: Make the function
async defthroughout.
P2-7. cfg._vault_password set via property bypasses env
- File:
backend/core/config.py:24-37 - Issue:
_vault_passwordis read at class definition time.setterexists butclear_vault_password()is never called frommain.py:lifespan. Memory hygiene claim is a lie. - Fix: Call
cfg.clear_vault_password()after Vault initialization in lifespan.
P2-8. license_router doesn't validate license JWT signature
- File:
backend/routers/license_router.py - Issue: (Not yet read — pending verification. Listed as suspect based on code smell.)
P2-9. x402_service.py allows negative count (kinda)
- File:
backend/x402_service.py:84-91 - Issue:
Field(default=1, ge=1)enforces min 1, but the handler then doescount = min(max(req.count, 1), 100000). If a future caller bypasses Pydantic, no defense.
P2-10. hosting.py register() returns api_key in plaintext
- File:
backend/core/hosting.py:81-96 - Issue: Returns
api_keydirectly. Fine for the API, but the function signature leakspassword_hashviadict(row)inlogin()(P2-11).
P2-11. hosting.py login() returns password_hash
- File:
backend/core/hosting.py:98-107 - Issue: Returns
dict(row)from sqlite.rowincludespassword_hash. Whatever endpoint callslogin()and returns the dict leaks the hash. - Fix: Project columns explicitly:
{k: row[k] for k in ('id', 'email', 'name', 'plan', ...)}.
P2-12. hosting.py no email verification
- File:
backend/core/hosting.py - Issue: Any email can register. No rate limit. ROADMAP item #11.
- Fix: SMTP + 6-digit code before issuing API key.
P2-13. /hosting/login has no rate limit
- File:
backend/routers/hosting.py - Issue: Brute-forceable. ROADMAP item #12.
- Fix: Sliding window, exponential backoff per email.
P2-14. agent_referral_status exposes operator referral codes
- File:
backend/agent/mcp_server.py:265-263 - Issue: Returns
REF_GMGN,REF_ODINBOT, etc. in plaintext. That's fine for the operator dashboard but exposed to any MCP client — leakage across tenants in hosted mode.
P2-15. _RECEIPT_KEY_PATH is world-readable inside container until chmod
- File:
backend/core/proof.py:67 - Issue:
write_bytes(...)thenchmod(0o600). Race window if anything reads the file in that microsecond. - Fix: Create with
os.open(..., 0o600)first.
P2-16. chain_vault.py 2,249 lines — god file
- File:
backend/routers/chain_vault.py - Issue: Single file holds generation, import, list, search, paper wallet, batch, sweep, rotate, etc. Hard to test, hard to review.
- Fix: Split into
chain_vault.py,wallet_generation.py,wallet_management.py,wallet_export.py.
P2-17. No type hints on _team_keys, _ws_clients, _ws_rate
- File:
backend/main.py:288, 475-476 - Issue: Module-level dicts without typing.
mypy --strict(in CI) must be ignoring these.
P2-18. WP plugin encrypt() uses AES-256-CBC without HMAC
- File:
wp-plugin/includes/class-walletpress.php:9-15 - Issue: CBC mode is malleable. Without HMAC, an attacker can flip ciphertext bits and corrupt the API key.
- Fix: Use
aes-256-gcmvia WordPress sodium wrapper (\Sodium\crypto_secretbox).
P2-19. x402_verify.py not yet reviewed (P2 placeholders)
- File:
backend/core/x402_verify.py - Issue: Not yet read. Marked P2 pending verification.
P2-20. client_sdk.py not yet reviewed (P2 placeholders)
- File:
backend/client_sdk.py - Issue: Not yet read. 162 lines. Marked P2.
P2-21. chain_vault router imports from bip_utils import ... inside handler
- File:
backend/routers/chain_vault.py(suspect) - Issue: Lazy imports hide dependency issues until runtime.
P2-22. wallet_analysis.py uses external API keys inline
- File:
backend/routers/wallet_analysis.py - Issue: (Not yet read. Suspect hardcoded API keys or missing env loading.)
P3 — Code quality / dead code
P3-1. 93 ruff errors (52 unused imports, 13 unused vars, etc.)
- Files: all of
backend/ - Fix:
ruff check . --fix(auto-fixes 50).
P3-2. is_write_action imported but unused in mcp_server
- File:
backend/agent/mcp_server.py:20 - Fix: Remove import OR use it in orchestrator (P1-10 fix).
P3-3. dead_code: agent_safety.simulate_transaction — audit_log parameter shadowing
- File:
backend/core/agent_safety.py - Issue: Multiple variables named
audit_logcollide between module and function.
P3-4. audit.py module-level _audit global, no test reset hook
- File:
backend/core/audit.py:94-100 - Issue: PROGRESS.md admits this.
P3-5. _migrate in vault.py is no-op
- See P1-2.
P3-6. agent_safety.py:_get_hitl_db returns pool that ignores max_size
- File:
backend/core/agent_safety.py:57-75 - Fix: Review
core/db_pool.py.
P3-7. x402_service.py defines routes but main.py mounts the entire sub-app
- File:
backend/main.py:466 - Issue:
app.mount("/", x402_app)— any /api/v1/marketplace/* URL hits x402. But also any other route.mount("/")shadows nothing if x402 only defines its own paths. OK but confusing.
P3-8. _ws_clients, _ws_rate in main.py are not typed
- See P2-17.
P3-9. 5 chains in ChainFamily enum have NO implementation (CASPER, ELROND, HEDERA, INTERNET_COMPUTER, ZILLIQA)
- File:
backend/wallet_engine/chains.py:22-44 - Issue: Dead enum values. WP plugin advertises support for some of these. Hallucination.
P3-10. WP plugin supported_chains() advertises chains the backend can't generate
- File:
wp-plugin/includes/class-walletpress.php:142-200 - Issue: Lists
manta,starknet,polygon_zkevm,hedera,elrond,casper,zilliqa— backend has none.
P3-11. adapters/ are 16-36 line stubs
- Files:
backend/adapters/{crewai,eliza,langchain,openai_agents,vercel_ai}.py - Issue: Placeholders. Either implement or remove. Currently they advertise capability without delivering.
P3-12. wallet_engine/chains.yaml is a stub with no entries
- File:
backend/wallet_engine/chains.yaml - Issue: Documents a YAML-extension system but ships no examples.
P3-13. _verify in x402_service.py:verify_receipt has wrong arg order
- File:
backend/x402_service.py:333 - Issue: Calls
_verify(order_id, chain, count, amount, created_at, signature, pubkey)butverify_receiptdefined asverify_receipt(order_id, chain, count, total_usd, timestamp, signature, pubkey_hex)— args align buttimestampparameter passed isrow["created_at"]which is a float, while the signature was computed withtime.time()which is also a float. OK. - Actually OK — verified.
P3-14. walletpress-cli cmd_generate doesn't accept --count
- File:
backend/walletpress_cli.py:240-258 - Issue: PROGRESS.md claims CLI supports batch. It doesn't.
P3-15. walletpress-cli cmd_validate doesn't validate EIP-55 checksum
- File:
backend/walletpress_cli.py:260-280 - Issue: Only does regex match, not EIP-55 case verification.
P3-16. walletpress-cli cmd_backup doesn't encrypt the archive
- File:
backend/walletpress_cli.py:381-405 - Issue:
cmd_backupwrites plaintext JSON. The docstring says "encrypted archive" — hallucinated.
P3-17. chain_vault.py has 2,249 lines — split.
Cross-cutting fixes (architectural)
A1. Consolidate DB paths
Three separate DB files are referenced from three different places (hosting.db, marketplace.db, agent_safety.db). All should be under one cfg.data_dir / "walletpress.db" with namespaced tables.
A2. Pluggable key backend (P0-5)
Add a KeyBackend ABC with implementations:
EnvKeyBackend— current (dev only)FileKeyBackend— file with 0600 permsKMSKeyBackend— AWS / GCP / Vault- Default in production:
FileKeyBackend.
A3. Alembic instead of hand-rolled migrations
alembic/ directory exists, alembic.ini is configured, but core/{vault,proof,hosting,agent_safety}.py all bypass it with raw SQL CREATE TABLE. Use Alembic for everything.
A4. Split god router
chain_vault.py (2,249 lines) → split into:
chain_vault.py— read endpoints (list, get, search, stats)wallet_generation.py— generate, batch, importwallet_management.py— rotate, sweep, deletewallet_export.py— paper wallet, PDF birth certificate, CSV
A5. Document the 55-chain truth
ADDRESS_GENERATION.md is the single source. chains.py should load truth from it (or import a CHAIN_TRUTH_FLAGS dict from there) so the metadata and the generator can't disagree.
A6. Replace AdHoc JSON stores with SQLite + repository pattern
KeyStore (JSON), TeamKeyStore (dict), ScheduledTask (JSON), scam_addresses.json — all hand-rolled. Either consolidate to SQLite or use a typed Repository[T] pattern.
A7. Pydantic everywhere (no dict params)
audit_log, request_confirmation, MCP tool args — all take dict. Add Pydantic models. Prevents missing fields and catches injection.
Test gaps
Current test count: 63 (collected). Coverage on the security-critical paths is unknown — need pytest --cov report.
Missing test classes:
- Address generation per-chain with golden vectors (use the official SDK to generate the expected, then compare).
verify_receipthappy path + tampered.audit_logconcurrent writes (P1-12 fix needs test).wallet_sweepeither does sweep or doesn't (P0-6).buy_creditsrejects fakepayment_tx(P0-1)._team_keysrole enforcement (P0-3).- Hosted password hash upgrade path (P0-2).
Add tests/test_address_vectors.py with one test per chain family, verifying against:
- EVM: eth-utils
- Solana: solana-py
- BTC: bitcoinjs-lib (via wasm or python equivalent)
- Cosmos: bech32 lib
- Stellar: stellar-sdk
- TON: tonweb
- Substrate: sr25519 keypair (NOT bip_utils)
Verification commands
# Run from Cinnabox, in backend/
cd ~/sites/walletpress/backend
# Lint + type + test
make check
# Address-generation tests
pytest tests/test_address_vectors.py -v
# Audit-chain integrity
python3 -c "
from core.agent_safety import verify_audit_chain
print(verify_audit_chain())
"
# Receipt round-trip
python3 -c "
from core.proof import sign_receipt, verify_receipt, get_receipt_public_key
sig = sign_receipt('test', 'eth', 1, 0.01, 1234567890.0)
print('verify:', verify_receipt('test', 'eth', 1, 0.01, 1234567890.0, sig, get_receipt_public_key()))
"
# Check that no real mnemonic is in the audit DB
sqlite3 ~/data/agent_safety.db "SELECT params FROM agent_audit WHERE params LIKE '%abandon%' LIMIT 5;"
Cadence — how this audit gets updated
- Weekly: Run
make check. Add new findings here. - Per-release: Cut a tag, link it from
CHANGELOG.md. - On schema change: Update
ADDRESS_GENERATION.md. - On new chain: Add to
ADDRESS_GENERATION.mdtruth table BEFORE merging the chain into chains.py. - On new MCP tool: Threat-model review by 2nd engineer. Update
SECURITY.md.
See BUILDER.md for the daily agent workflow.