aldabra/Cargo.toml
Cobb f17479ab92 audit fixes: all 9 findings resolved + wallet generation tooling
HIGH:
- HIGH-1 enforce_value_cap helper applied to wallet.send,
  wallet.mint, wallet.mint.cip68_nft, wallet.script.spend. each
  gained a `force` arg; cap also covers the user_lovelace+ref_lovelace
  sum on cip68_nft. wallet.stake.delegate skipped (2 ada deposit is
  protocol-fixed, not a transfer to a non-wallet destination).
- HIGH-2 wallet.tx_summary mcp tool — read-only decode of a conway
  tx cbor → typed TxSummary (inputs, outputs+assets, fee, certs,
  mint, witness count, aux-data presence). new aldabra-core::inspect
  module. callers MUST run this before wallet.sign_partial /
  wallet.submit_signed_tx on any cbor they didn't build themselves.

MEDIUM:
- M-1 zeroize stack-resident extended_bytes after SecretKeyExtended
  consumes them. tx.rs::payment_key_to_private + sign.rs::add_witness.
- M-2 atomic 0o600 mnemonic file create via OpenOptions+
  OpenOptionsExt. removes the prior toctou window between fs::write
  (default umask) and chmod 600.
- M-3 prompt_or_env_passphrase + unlock_passphrase helpers wrap the
  passphrase in Zeroizing<String>. ALDABRA_PASSPHRASE env still
  unzeroizable in the env block itself (documented headless tradeoff).
- M-4 is_hex_64 validator on submit_tx response — koios error wrapped
  in quotes can no longer round-trip as a fake tx_hash.

LOW + cleanup:
- L-1 checked_add for inner sums of checked_sub patterns in tx.rs.
  remaining sites (mint.rs, stake.rs, plutus.rs) deferred — same
  pattern, can't overflow with realistic cardano amounts but
  defensive. picked up next.
- L-2 root key scoped to a block in main.rs — XPrv drops + wipes
  after deriving payment_key + stake_key + address. saves ~96 bytes
  of secret material lifetime.
- L-3 TxStatus gained a Pending variant for the mempool-but-not-yet-
  confirmed case. previously rendered as Confirmed{block_height: None}
  which was misleading.
- L-4 .expect("we built this key") → typed ? propagation in
  tx.rs::prepare_payment.
- L-5 removed dead fns (build_and_sign, decode_hex) + unused imports.

WALLET GENERATION (audit prompted gap-find):
aldabra had only an import path. no "generate fresh wallet" tool.
- Mnemonic::generate() — bip39::Mnemonic::generate_in(English, 24)
  with the rand feature. returns (Mnemonic, Zeroizing<String>) so
  the caller can display the phrase once for cold backup.
- aldabra --generate-mnemonic — print fresh phrase, exit. no disk.
- aldabra --bootstrap-new — generate + display + encrypt one-shot.
- bip39 dep gains the rand feature for OsRng-backed generation.
- standard 24-word BIP-39, recoverable from any cardano wallet.

mcp tools: 16 → 17 (added wallet.tx_summary).
unit tests: 88 → 93. cargo audit clean (0 cves), cargo build clean
(0 warnings). all four cli flags smoke-tested:
--generate-mnemonic prints + exits; --bootstrap-new generates +
encrypts + derives a real preprod address; mnemonic.age has 0o600
perms confirmed atomic.

audit doc memory/spec-aldabra-audit-2026-05-04.md updated with
status markers.
2026-05-04 14:52:08 -07:00

108 lines
4.7 KiB
TOML

# Cargo workspace root for aldabra.
#
# Three crates:
# aldabra-core — key derivation, signing, types, mnemonic handling
# aldabra-chain — pluggable chain backends (Koios, Ogmios). Trait-first.
# aldabra-mcp — binary; the MCP server, glues core+chain together.
#
# Named for the Aldabra giant tortoise (Aldabrachelys gigantea) — endemic
# to the Aldabra atoll in the Seychelles, up to 250 kg, 150-year lifespan.
# Long-lived, defended, slow but unstoppable. Fitting metaphor for a
# wallet that holds your money.
#
# Workspace deps are pinned here so all three crates use the same versions.
# Add a dep here, then reference it in each crate's Cargo.toml as
# foo = { workspace = true }
[workspace]
resolver = "2"
members = [
"crates/aldabra-core",
"crates/aldabra-chain",
"crates/aldabra-mcp",
]
[workspace.package]
version = "0.0.1"
edition = "2021"
license-file = "LICENSE"
repository = "http://192.168.0.5:3001/Sulkta-Coop/aldabra"
authors = ["Cobb <cobb@sulkta.com>", "Kayos <kayos@sulkta.com>"]
[workspace.dependencies]
# Async runtime — almost everything we do is I/O bound (chain queries, MCP stdio)
tokio = { version = "1", features = ["full"] }
# Cardano stack — pallas is the rust-native primitives library by txpipe.
# We pull individual crates rather than the meta-crate so we control feature flags.
pallas-primitives = "0.32"
pallas-codec = "0.32"
pallas-crypto = "0.32"
pallas-addresses = "0.32"
pallas-txbuilder = "0.32"
pallas-wallet = "0.32"
pallas-traverse = "0.32"
pallas-network = "0.32"
# Mnemonic + key derivation.
# bip39 — 24-word wordlist parsing + BIP-39 entropy extraction.
# ed25519-bip32 — Cardano's variant of BIP-32-Ed25519 HD derivation
# (XPrv + DerivationScheme::V2 hard/soft children).
# pallas-crypto only ships raw ed25519, not HD derivation.
# cryptoxide — PBKDF2-HMAC-SHA512 for Icarus master-key generation
# (CIP-3). Already pulled in transitively by
# ed25519-bip32; declared here so we can use pbkdf2 + Sha512
# directly in aldabra-core.
# `rand` feature pulls in OsRng-backed Mnemonic::generate_in for new-wallet flows.
bip39 = { version = "2", features = ["rand"] }
ed25519-bip32 = "0.4"
cryptoxide = "0.4"
# At-rest encryption for the mnemonic + derived keys on disk. age is
# what the cauldron Fernet pattern would have been if we'd had it back
# then — modern, audited, FOSS, and the secret never has to round-trip
# through a daemon password prompt.
age = "0.10"
# Memory hygiene — wipe key material from RAM when keys go out of scope.
zeroize = { version = "1", features = ["derive"] }
# Errors — anyhow at the boundaries (binary), thiserror for crate-internal types
anyhow = "1"
thiserror = "1"
# Serde for everything JSON
serde = { version = "1", features = ["derive"] }
serde_json = "1"
# HTTP client for Koios + future Ogmios HTTP endpoints
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] }
# MCP SDK for Rust. Note: the official Rust SDK has been moving fast
# (modelcontextprotocol/rust-sdk on github). Pin a version once we
# verify the API shape we actually use.
rmcp = { version = "0.1", features = ["server", "transport-io"] }
# Logging
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
# Config file parsing — TOML at $ALDABRA_DATA/config.toml.
toml = "0.9"
# Hidden-input passphrase prompts for the mnemonic bootstrap CLI.
# rpassword is the standard "tty echo off" prompt crate.
rpassword = "7"
# Vendored fork of txpipe/pallas with auxiliary_data support added to
# pallas-txbuilder (upstream had TODO markers we filled in). Patches
# all pallas-* crates so the version graph resolves consistently
# against the same commit. PR upstream pending; switch back to
# crates.io once merged.
[patch.crates-io]
pallas-codec = { git = "http://kayos:***REDACTED***@192.168.0.5:3001/Sulkta-Coop/pallas.git", branch = "feat-aux-data" }
pallas-crypto = { git = "http://kayos:***REDACTED***@192.168.0.5:3001/Sulkta-Coop/pallas.git", branch = "feat-aux-data" }
pallas-primitives = { git = "http://kayos:***REDACTED***@192.168.0.5:3001/Sulkta-Coop/pallas.git", branch = "feat-aux-data" }
pallas-traverse = { git = "http://kayos:***REDACTED***@192.168.0.5:3001/Sulkta-Coop/pallas.git", branch = "feat-aux-data" }
pallas-addresses = { git = "http://kayos:***REDACTED***@192.168.0.5:3001/Sulkta-Coop/pallas.git", branch = "feat-aux-data" }
pallas-wallet = { git = "http://kayos:***REDACTED***@192.168.0.5:3001/Sulkta-Coop/pallas.git", branch = "feat-aux-data" }
pallas-txbuilder = { git = "http://kayos:***REDACTED***@192.168.0.5:3001/Sulkta-Coop/pallas.git", branch = "feat-aux-data" }