rename: sulkta-wallet → aldabra (per Cobb 2026-05-04)

Aldabra giant tortoise (Aldabrachelys gigantea) — endemic to the
Aldabra atoll, up to 250 kg, 150-year lifespan. Long-lived,
defended, slow but unstoppable. Better metaphor for the wallet
than 'sulkta-wallet' which was on-the-tin descriptive.

All renames in one pass:
- repo: Sulkta-Coop/sulkta-wallet → Sulkta-Coop/aldabra (via gitea API)
- workspace dir: sulkta-wallet → aldabra
- crate dirs: wallet-{core,chain,mcp} → aldabra-{core,chain,mcp}
- crate names + path imports in Cargo.toml workspace + each crate
- binary name: sulkta-wallet → aldabra
- README, ROADMAP, docs/architecture: all references swept
This commit is contained in:
Kayos 2026-05-04 10:11:23 -07:00
parent 489b58cc1e
commit 1f1993ed97
10 changed files with 63 additions and 58 deletions

View file

@ -1,9 +1,14 @@
# Cargo workspace root for sulkta-wallet.
# Cargo workspace root for aldabra.
#
# Three crates:
# wallet-core — key derivation, signing, types, mnemonic handling
# wallet-chain — pluggable chain backends (Koios, Ogmios). Trait-first.
# wallet-mcp — binary; the MCP server, glues core+chain together.
# 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
@ -11,16 +16,16 @@
[workspace]
resolver = "2"
members = [
"crates/wallet-core",
"crates/wallet-chain",
"crates/wallet-mcp",
"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/sulkta-wallet"
repository = "http://192.168.0.5:3001/Sulkta-Coop/aldabra"
authors = ["Cobb <cobb@sulkta.com>", "Kayos <kayos@sulkta.com>"]
[workspace.dependencies]

View file

@ -1,4 +1,4 @@
# sulkta-wallet
# aldabra
Rust-native Cardano lite wallet with an MCP-server interface — built
for LLM-first usage (send, receive, mint, Plutus interaction).
@ -25,20 +25,20 @@ Three crates in a Cargo workspace:
| Crate | Responsibility |
|---|---|
| `wallet-core` | Pure crypto + types. Mnemonic → root key (CIP-3), root → payment + stake key (CIP-1852), address construction, signing. **No I/O, no network.** This is the security boundary. |
| `wallet-chain` | Pluggable backends for chain queries. `ChainBackend` trait, with Koios as the phase-1 implementation. Ogmios + submission paths in phase 2. |
| `wallet-mcp` | Binary. MCP server speaking stdio. Glues core + chain together, exposes tools to the LLM client. |
| `aldabra-core` | Pure crypto + types. Mnemonic → root key (CIP-3), root → payment + stake key (CIP-1852), address construction, signing. **No I/O, no network.** This is the security boundary. |
| `aldabra-chain` | Pluggable backends for chain queries. `ChainBackend` trait, with Koios as the phase-1 implementation. Ogmios + submission paths in phase 2. |
| `aldabra-mcp` | Binary. MCP server speaking stdio. Glues core + chain together, exposes tools to the LLM client. |
```
┌─────────────────────────────┐
LLM client │ wallet-mcp (bin) │ stdio
LLM client │ aldabra-mcp (bin) │ stdio
─────────► │ tool handlers, lifecycle │ ────►
└──────────┬──────────────────┘
┌────────┴────────┐
▼ ▼
┌──────────────┐ ┌──────────────┐
wallet-core │ │ wallet-chain │
aldabra-core │ │ aldabra-chain │
│ keys, sign │ │ Koios/Ogmios │
└──────────────┘ └──────────────┘
```
@ -70,21 +70,21 @@ Phase 4:
cargo build --release
# Through crafting-table (preferred — validates the toolchain there)
crafting-table build sulkta-wallet
crafting-table build aldabra
```
## Run
```bash
# Direct invocation (smoke test only — does nothing useful in phase 1)
./target/release/sulkta-wallet
./target/release/aldabra
# As an MCP server registered with Claude Code:
# add to ~/.claude.json:
# "sulkta-wallet": {
# "command": "/path/to/sulkta-wallet",
# "aldabra": {
# "command": "/path/to/aldabra",
# "env": {
# "SULKTA_WALLET_DATA": "/mnt/cache/appdata/sulkta-wallet"
# "ALDABRA_DATA": "/mnt/cache/appdata/aldabra"
# }
# }
```

View file

@ -1,4 +1,4 @@
# sulkta-wallet roadmap
# aldabra roadmap
Phased buildout. Each phase ships a usable increment + leaves the
codebase in a state where Phase N+1 picks up cleanly.
@ -9,12 +9,12 @@ codebase in a state where Phase N+1 picks up cleanly.
end-to-end through the MCP transport.
- [x] Cargo workspace
- [x] Crate skeletons: `wallet-core`, `wallet-chain`, `wallet-mcp`
- [x] Crate skeletons: `aldabra-core`, `aldabra-chain`, `aldabra-mcp`
- [x] Type stubs + ZeroizeOnDrop scaffolding for keys
- [ ] `wallet-core::Mnemonic::into_root_key` — real CIP-3 derivation via `pallas-crypto`
- [ ] `wallet-core::derive_base_address` — real CIP-1852 + bech32
- [ ] `wallet-chain::KoiosClient::get_utxos` — real `reqwest` to `/address_utxos`
- [ ] `wallet-chain::KoiosClient::get_balance`
- [ ] `aldabra-core::Mnemonic::into_root_key` — real CIP-3 derivation via `pallas-crypto`
- [ ] `aldabra-core::derive_base_address` — real CIP-1852 + bech32
- [ ] `aldabra-chain::KoiosClient::get_utxos` — real `reqwest` to `/address_utxos`
- [ ] `aldabra-chain::KoiosClient::get_balance`
- [ ] Interactive mnemonic bootstrap CLI: paste once, age-encrypt to disk
- [ ] On-startup decryption — single passphrase prompt, derived key in
RAM only
@ -30,8 +30,8 @@ a Koios query.**
**Goal:** the wallet can spend.
- [ ] `wallet-chain::ChainBackend::submit_tx` — POST CBOR to Koios `/submittx`
- [ ] `wallet-chain::tx_status` — poll `/tx_info`
- [ ] `aldabra-chain::ChainBackend::submit_tx` — POST CBOR to Koios `/submittx`
- [ ] `aldabra-chain::tx_status` — poll `/tx_info`
- [ ] Build + sign ADA-only payment via `pallas-txbuilder`
- [ ] MCP tool `wallet.send` with `to_address`, `lovelace` args
- [ ] MCP tool `wallet.tx_status` with `tx_hash` arg

View file

@ -1,4 +1,4 @@
# wallet-chain — pluggable backends for chain queries.
# aldabra-chain — pluggable backends for chain queries.
#
# A `ChainBackend` trait abstracts over Koios (HTTPS), Ogmios (WS/HTTP),
# or any future option. Phase 1 ships with a Koios implementation since
@ -7,7 +7,7 @@
# Submission paths land in phase 2. Read-only queries first.
[package]
name = "wallet-chain"
name = "aldabra-chain"
version.workspace = true
edition.workspace = true
license-file.workspace = true

View file

@ -1,4 +1,4 @@
//! sulkta-wallet chain backends — Koios first, Ogmios next.
//! aldabra chain backends — Koios first, Ogmios next.
//!
//! Trait-first design: the MCP server depends on `ChainBackend`, not on
//! a specific implementation. Swapping Koios → Ogmios is a config change.

View file

@ -1,4 +1,4 @@
# wallet-core — pure crypto + types. No I/O, no network. Deterministic
# aldabra-core — pure crypto + types. No I/O, no network. Deterministic
# given the same mnemonic + derivation path.
#
# This crate is intentionally narrow:
@ -6,18 +6,18 @@
# - Root key → payment / stake key (CIP-1852 derivation)
# - Address construction (mainnet, testnet)
# - Transaction signing (given an unsigned TX builder output from
# wallet-chain or pallas-txbuilder)
# aldabra-chain or pallas-txbuilder)
#
# It deliberately does NOT:
# - Do any chain queries (that's wallet-chain's job)
# - Talk to MCP (that's wallet-mcp's job)
# - Do any chain queries (that's aldabra-chain's job)
# - Talk to MCP (that's aldabra-mcp's job)
# - Touch files (the daemon owns disk I/O; we get keys handed in)
#
# Rationale: this is the most security-sensitive crate. Keeping it
# narrow + I/O-free makes it auditable.
[package]
name = "wallet-core"
name = "aldabra-core"
version.workspace = true
edition.workspace = true
license-file.workspace = true

View file

@ -1,4 +1,4 @@
//! sulkta-wallet core — keys, addresses, signing.
//! aldabra core — keys, addresses, signing.
//!
//! This crate is the security boundary. Everything that touches private
//! key material lives here, and only here. No I/O, no network, no MCP.

View file

@ -1,4 +1,4 @@
# wallet-mcp — the binary. MCP server speaking stdio that exposes
# aldabra-mcp — the binary. MCP server speaking stdio that exposes
# the wallet's tools to an LLM. Spawned as a subprocess from any MCP
# client (Claude Code, OpenClaw, etc.).
#
@ -6,7 +6,7 @@
# between core + chain crates.
[package]
name = "wallet-mcp"
name = "aldabra-mcp"
version.workspace = true
edition.workspace = true
license-file.workspace = true
@ -14,12 +14,12 @@ repository.workspace = true
authors.workspace = true
[[bin]]
name = "sulkta-wallet"
name = "aldabra"
path = "src/main.rs"
[dependencies]
wallet-core = { path = "../wallet-core" }
wallet-chain = { path = "../wallet-chain" }
aldabra-core = { path = "../aldabra-core" }
aldabra-chain = { path = "../aldabra-chain" }
tokio = { workspace = true }
anyhow = { workspace = true }

View file

@ -1,4 +1,4 @@
//! sulkta-wallet — MCP server entry point.
//! aldabra — MCP server entry point.
//!
//! Speaks MCP over stdio. Any MCP client (Claude Code, OpenClaw, etc.)
//! launches this as a subprocess and gets a wallet's worth of tools.
@ -6,7 +6,7 @@
//! ## Phase 1 tools
//!
//! - `wallet.address` — return the derived base address (placeholder
//! until wallet-core's CIP-1852 derivation lands)
//! until aldabra-core's CIP-1852 derivation lands)
//! - `wallet.balance` — query balance via the configured chain backend
//!
//! ## Phase 2-4 tools (TODO)
@ -34,7 +34,7 @@ async fn main() -> Result<()> {
.with_env_filter(EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into()))
.init();
tracing::info!("sulkta-wallet starting (phase 1 scaffold)");
tracing::info!("aldabra starting (phase 1 scaffold)");
// TODO(phase 1):
// 1. Load config (network, koios url, mnemonic path)
@ -47,9 +47,9 @@ async fn main() -> Result<()> {
// For now: a smoke-test print so the binary actually does something
// when invoked manually (not through MCP).
tracing::info!(
target_address = %wallet_core::derive_base_address(
target_address = %aldabra_core::derive_base_address(
&dummy_root_key()?,
wallet_core::Network::Mainnet,
aldabra_core::Network::Mainnet,
0,
0,
)?,
@ -62,13 +62,13 @@ async fn main() -> Result<()> {
/// Phase 1 only — produces a zero-bytes RootKey so the placeholder
/// address derivation runs. Will be deleted once real mnemonic loading
/// lands.
fn dummy_root_key() -> Result<wallet_core::RootKey> {
fn dummy_root_key() -> Result<aldabra_core::RootKey> {
// Need a way to construct one from this crate without exposing
// private fields. Phase 1: temporary public constructor on
// wallet-core, gated behind a #[cfg(test)] or feature flag and
// aldabra-core, gated behind a #[cfg(test)] or feature flag and
// removed before phase 2.
//
// For tonight: this fn is a TODO marker — the smoke test won't
// actually run until we finish wallet-core::Mnemonic::into_root_key.
// actually run until we finish aldabra-core::Mnemonic::into_root_key.
anyhow::bail!("phase 1 scaffold: real mnemonic loading not yet implemented")
}

View file

@ -1,4 +1,4 @@
# sulkta-wallet architecture notes
# aldabra architecture notes
Deeper design notes than the README. Read this before extending.
@ -7,15 +7,15 @@ Deeper design notes than the README. Read this before extending.
The three-crate split exists to keep the security-sensitive code
auditable in isolation.
- `wallet-core` — **no I/O.** Given a mnemonic + a derivation path,
- `aldabra-core` — **no I/O.** Given a mnemonic + a derivation path,
produces keys + addresses + signatures. Deterministic, no
dependencies on tokio, reqwest, MCP, or anything that could
introduce side channels. Easy to audit because it's narrow.
- `wallet-chain` — **all the I/O lives here.** Trait-first so the MCP
- `aldabra-chain` — **all the I/O lives here.** Trait-first so the MCP
layer never knows whether it's talking to Koios, Ogmios, or a future
backend. Future contributors swap implementations without touching
the security-sensitive crate.
- `wallet-mcp` — **the binary glue.** Owns process lifecycle, config
- `aldabra-mcp` — **the binary glue.** Owns process lifecycle, config
loading, MCP transport, tool registration, error mapping. The
thinnest layer.
@ -36,7 +36,7 @@ roughly in order:
`--dry-run` flag for any state-changing tool.
2. **Daemon process compromise.** If the wallet binary is exploited
(e.g. via a malformed Koios response triggering memory corruption),
the keys are at risk. Mitigations: keep `wallet-core` narrow
the keys are at risk. Mitigations: keep `aldabra-core` narrow
(smaller attack surface), zeroize on drop, future: drop
privileges + seccomp the daemon.
3. **Disk read.** The encrypted mnemonic on disk could be exfiltrated.
@ -56,18 +56,18 @@ roughly in order:
First run:
user pastes mnemonic at interactive prompt
wallet-mcp asks for an encryption passphrase
aldabra-mcp asks for an encryption passphrase
age-encrypt the mnemonic phrase
write to $SULKTA_WALLET_DATA/mnemonic.age
write to $ALDABRA_DATA/mnemonic.age
derive RootKey, hold in RAM, zeroize the source phrase
daemon ready
Subsequent runs:
read $SULKTA_WALLET_DATA/mnemonic.age
read $ALDABRA_DATA/mnemonic.age
prompt for passphrase
@ -114,7 +114,7 @@ phase-2 design is:
2. Returns the unsigned CBOR + a one-line human summary ("send 100
ADA to addr1xyz, fee 0.17 ADA, expected balance after: …").
3. LLM relays the summary to Cobb, gets approval.
4. Cobb runs a separate `sulkta-wallet-cold-sign` CLI on a different
4. Cobb runs a separate `aldabra-cold-sign` CLI on a different
box (offline laptop, cardano-signer, whatever) — paste the CBOR,
approve, paste back the signed CBOR.
5. `wallet.submit_signed_tx` takes the signed CBOR + submits.