Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ dev: install
cd $(PROJECT_DIR) && bun run dev

dev-hmr: install
-lsof -ti :5173 | xargs kill -9 2>/dev/null || true
-lsof -ti :5177 | xargs kill -9 2>/dev/null || true
-pkill -f "electrobun dev" 2>/dev/null || true
cd $(PROJECT_DIR) && bun run dev:hmr

Expand Down
153 changes: 98 additions & 55 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,101 +1,144 @@
# KeepKey Vault v11

Desktop hardware wallet management UI built with **Electrobun** + **React 18** + **Chakra UI 3.0**.
Desktop hardware wallet application built with **Electrobun** (Bun main process + system WebView) + **React 18** + **Chakra UI 3.0**.

## Architecture

```
Electrobun Desktop App
├── Main Process (Bun) ──── HTTP REST ──── keepkey-desktop (port 1646) ──── KeepKey Device
└── WebView (React + Chakra UI + Vite)
├── Main Process (Bun) ──── USB (HID + WebUSB) ──── KeepKey Device
│ ├── Engine Controller (device lifecycle, USB events)
│ ├── SQLite persistence (bun:sqlite)
│ ├── Pioneer API integration (balance/portfolio)
│ └── REST API on port 1646 (opt-in, KEEPKEY_REST_API=true)
└── WebView (React 18 + Chakra UI 3.0 + Vite)
└── Tab-based state machine (dashboard | addresses | settings)
```

- **Main process** (`src/bun/index.ts`): Bun runtime, window management, RPC bridge
- **WebView** (`src/mainview/`): React 18 + Chakra UI 3.0, client-side routing
- **API layer**: Direct REST client to keepkey-desktop on port 1646 (no Pioneer SDK)
- **Theme**: Black/gold (#000/#111/#FFD700) matching KeepKey branding
- The vault talks directly to the KeepKey device via USB -- no external desktop app dependency.
- All device operations go through Electrobun RPC (no REST required for normal use).
- The REST API on port 1646 is opt-in (`KEEPKEY_REST_API=true`), compatible with the `kkapi://` protocol.
- No React Router -- the UI uses a simple tab-based state machine.
- Theme: black/gold (#000/#111/#FFD700) matching KeepKey branding.

## Prerequisites

- [Bun](https://bun.sh) >= 1.0
- [keepkey-desktop](https://github.com/keepkey/keepkey-desktop) running on port 1646
- [Bun](https://bun.sh) >= 1.3.5
- [Yarn](https://yarnpkg.com) (for hdwallet monorepo build)
- KeepKey hardware wallet connected via USB
- For signing/notarization: Apple Developer ID certificate + Xcode CLI tools

## Quick Start

```bash
make install # Install dependencies
make dev # Build and launch app
make dev-hmr # Dev mode with Vite hot reload
make vault # Build modules + install deps + run dev mode
make dev # Build and run in dev mode
make dev-hmr # Dev mode with Vite HMR
```

## Make Targets

| Target | Description |
|--------|-------------|
| `make install` | Install dependencies |
| `make vault` | Build modules + install deps + run dev mode |
| `make dev` | Build and run in dev mode |
| `make dev-hmr` | Dev mode with Vite HMR on port 5173 |
| `make build` | Production build |
| `make build-prod` | Production build (prod channel) |
| `make clean` | Remove build artifacts and node_modules |
| `make dev-hmr` | Dev mode with Vite HMR |
| `make build` | Development build (no signing) |
| `make build-signed` | Full pipeline: build, prune, DMG, sign, notarize |
| `make prune-bundle` | Prune app bundle (version-aware dedup + re-sign) |
| `make dmg` | Create DMG from existing build |
| `make upload-dmg` | Upload signed DMG to CI draft release |
| `make release` | Build, sign, and create new GitHub release |
| `make modules-build` | Build hdwallet + proto-tx-builder from source |
| `make audit` | Generate dependency manifest + SBOM |
| `make clean` | Remove all build artifacts |

## Project Structure

```
keepkey-vault-v11/
├── Makefile # Top-level make targets
├── hdwallet/ # Git submodule: keepkey/hdwallet
├── Makefile
├── modules/
│ ├── hdwallet/ # Git submodule: keepkey/hdwallet (yarn+lerna)
│ ├── proto-tx-builder/ # Git submodule: @keepkey/proto-tx-builder
│ ├── keepkey-firmware/ # Git submodule: device firmware (C, CMake)
│ └── device-protocol/ # Git submodule: protobuf definitions
├── docs/
│ ├── ARCHITECTURE.md
│ ├── COIN-ADDITION-GUIDE.md
│ ├── coins/
│ └── firmware/README.md
├── firmware/ # Firmware manifest + binaries
└── projects/
└── keepkey-vault/ # Electrobun app
├── electrobun.config.ts # App identity & build config
├── vite.config.ts # Vite build config
├── package.json
└── keepkey-vault/ # Electrobun app
├── electrobun.config.ts
├── vite.config.ts
├── entitlements.plist
├── scripts/ # Build scripts (collect-externals, prune, etc.)
├── docs/ # BUILD.md, API.md, ELECTROBUN.md
└── src/
├── bun/index.ts # Main process (window, RPC)
├── shared/types.ts # RPC type definitions
└── mainview/
├── main.tsx # React entry + Chakra Provider
├── App.tsx # Router + layout shell
├── theme.ts # Chakra 3.0 black/gold theme
├── bun/ # Main process
│ ├── index.ts # Electrobun RPC + engine controller + REST
│ ├── engine-controller.ts # USB event-driven device lifecycle
│ ├── rest-api.ts # Bun.serve() REST API (opt-in)
│ ├── evm-rpc.ts # EVM chain RPC calls
│ └── ... # DB, Pioneer, TX builder modules
├── shared/
│ ├── rpc-schema.ts # Electrobun RPC type definitions
│ ├── types.ts # DeviceStateInfo, FirmwareProgress, etc.
│ └── chains.ts # Chain definitions
└── mainview/ # React frontend
├── main.tsx # React entry + ChakraProvider
├── App.tsx # Tab-based state machine (no router)
├── components/
│ ├── layout/ # Header, Sidebar, StatusBar
│ ├── dashboard/ # Dashboard overview
│ ├── device/ # DeviceStatus, PinEntry, Settings
│ ├── addresses/ # Multi-chain address derivation
│ └── signing/ # Transaction signing
├── hooks/ # useKeepKey, useApi
├── services/ # keepkey-api.ts (REST client)
└── types/ # Frontend type definitions
│ ├── Dashboard.tsx
│ ├── Addresses.tsx
│ ├── TopNav.tsx
│ ├── SplashScreen.tsx
│ ├── OobSetupWizard.tsx
│ ├── DeviceSettings.tsx
│ └── device/ # PinEntry, PassphraseEntry, RecoveryWordEntry
├── hooks/ # useDeviceState, useFirmwareUpdate, etc.
└── lib/ # rpc.ts (browser-side RPC transport)
```

## keepkey-desktop API
## Supported Chains

The app communicates with keepkey-desktop's REST API on port 1646:
- **Bitcoin**: Multi-account, SegWit (p2wpkh, p2sh-p2wpkh, p2pkh)
- **Ethereum + 6 EVM L2s**: Polygon, Arbitrum, Optimism, Avalanche, BSC, Base
- **Cosmos ecosystem**: Cosmos, THORChain, Osmosis, Mayachain
- **Other**: Ripple (XRP), Binance (BNB), Solana
- **Custom EVM chains**: User-defined via Add Chain dialog

- **Auth**: `POST /auth/pair` (get Bearer token)
- **Addresses**: `/addresses/eth`, `/addresses/utxo`, `/addresses/cosmos`, etc.
- **Signing**: `/eth/sign-transaction`, `/utxo/sign-transaction`, `/cosmos/sign-amino`, etc.
- **System**: `/system/info/get-features`, `/system/apply-settings`, `/system/wipe-device`, etc.
## Tech Stack

See [docs/API.md](docs/API.md) for the full endpoint reference.
- **Runtime**: [Electrobun](https://electrobun.dev) (Bun + system WebView)
- **UI**: React 18 + Chakra UI 3.0
- **Build**: Vite 6
- **Device communication**: @keepkey/hdwallet-* (HID + WebUSB dual transport with automatic fallback)
- **Persistence**: SQLite (bun:sqlite)
- **Signing**: Apple codesign + notarize + staple

## Supported Chains
## Documentation

Bitcoin, Ethereum, Cosmos, THORChain, Osmosis, Litecoin, Dogecoin, Bitcoin Cash, Dash, Ripple, Mayachain, Binance
- [Build and Signing Guide](projects/keepkey-vault/docs/BUILD.md)
- [REST API Reference](projects/keepkey-vault/docs/API.md)
- [Electrobun Integration](projects/keepkey-vault/docs/ELECTROBUN.md)
- [Architecture](docs/ARCHITECTURE.md)
- [Coin Addition Guide](docs/COIN-ADDITION-GUIDE.md)

## Tech Stack
## Submodules

- **Runtime**: [Electrobun](https://electrobun.dev) (Bun + system WebView, ~14MB bundle)
- **UI**: React 18 + Chakra UI 3.0 + Emotion
- **Build**: Vite 6 with HMR support
- **Routing**: React Router 7
- **API**: Direct fetch to keepkey-desktop REST API
After cloning, initialize the git submodules:

## hdwallet Submodule
```bash
git submodule update --init --recursive
```

The `hdwallet/` directory is a git submodule pointing to [keepkey/hdwallet](https://github.com/keepkey/hdwallet). To initialize after cloning:
The `modules/hdwallet/` submodule must be on the `master` branch (which has lodash/rxjs removed from source). Build with:

```bash
git submodule update --init --recursive
make modules-build
```

This builds proto-tx-builder (bun + tsc) then hdwallet (yarn install + yarn build).
41 changes: 21 additions & 20 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
│ │ Main Process (Bun) │◄──────────►│ WebView (React) │ │
│ │ │ │ Chakra UI 3.0 │ │
│ │ EngineController │ │ Vite 6 + HMR │ │
│ │ REST API Server │ │ React Router 7 │ │
│ │ REST API Server │ │ Tab state machine │ │
│ │ SQLite Cache │ └────────────────────┘ │
│ │ Auth Store │ │
│ └───────────┬───────────┘ │
Expand Down Expand Up @@ -38,24 +38,26 @@
|------|---------|
| `index.ts` | App entry: window creation, RPC bridge, event wiring |
| `engine-controller.ts` | Device lifecycle: connect, pair, PIN/passphrase, firmware update |
| `rest-api.ts` | HTTP REST API (port 1646, opt-in, keepkey-desktop compatible) |
| `rest-api.ts` | HTTP REST API (port 1646, opt-in, kkapi:// compatible) |
| `auth.ts` | Bearer token auth, pairing requests, signing approval |
| `db.ts` | SQLite persistence (balances cache, pubkeys, settings, custom tokens) |
| `db.ts` | SQLite persistence (balances, pubkeys, settings, custom tokens/chains) |
| `pioneer.ts` | Pioneer API client (portfolio balances, tx building, market data) |
| `txbuilder.ts` | Transaction construction (UTXO, EVM, Cosmos) |
| `schemas.ts` | Zod schemas for REST API request/response validation |
| `txbuilder/` | Transaction construction (UTXO, EVM, Cosmos) |
| `schemas.ts` | Zod schemas for REST API validation |
| `evm-rpc.ts` | Direct EVM chain RPC calls (balance, nonce, gas, broadcast) |
| `evm-addresses.ts` | EVM multi-address manager |
| `btc-accounts.ts` | BTC multi-account manager |
| `camera.ts` | QR code scanning via system camera |

### Frontend (`src/mainview/`)

| Directory | Purpose |
|-----------|---------|
| `components/layout/` | Header, Sidebar, StatusBar |
| `components/dashboard/` | Portfolio overview, chain balances |
| `components/device/` | DeviceStatus, PinEntry, Settings |
| `components/addresses/` | Multi-chain address derivation |
| `components/signing/` | Transaction review & signing |
| `hooks/` | useKeepKey (RPC wrapper), useApi (REST client) |
| `services/` | keepkey-api.ts REST client |
| Directory/File | Purpose |
|----------------|---------|
| `App.tsx` | Tab-based state machine (dashboard, addresses, settings) |
| `components/` | TopNav, Dashboard, SendForm, OobSetupWizard, SplashScreen, etc. |
| `components/device/` | PinEntry, PassphraseEntry, RecoveryWordEntry |
| `hooks/` | useDeviceState, useBtcAccounts, useEvmAddresses, useFirmwareUpdate |
| `lib/rpc.ts` | Browser-side Electrobun RPC transport |

### CLI (`projects/keepkey-cli/`)

Expand All @@ -74,12 +76,11 @@ Standalone Bun/TypeScript CLI. Same `@keepkey/hdwallet-*` packages as vault, but
| `keepkey-firmware` | C (CMake) | Device firmware — protobuf handlers, crypto, OLED UI |
| `device-protocol` | protobuf | `.proto` message definitions shared by firmware + hdwallet |

> **Note on hdwallet lodash/rxjs dependencies**: `hdwallet-core` imports `lodash` and `rxjs`;
> `hdwallet-keepkey` imports `lodash`. These are declared dependencies in each package's
> `package.json` and are required at compile time. They are **stripped at bundle time** by
> `collect-externals.ts` (pruning step) so they do not ship in the final app. A future cleanup
> should inline the ~6 usages (`isObject`, `cloneDeep`, `omit`, `takeFirstOfManyEvents`) and
> remove the deps entirely from source. See `keepkey/hdwallet` for details.
> **Note on hdwallet lodash/rxjs dependencies**: The lodash/rxjs imports have been removed from
> `hdwallet-core` and `hdwallet-keepkey` source code on the `master` branch (commit `179c5668`).
> `isObject` was inlined, `cloneDeep` replaced with `structuredClone`, `omit` replaced with a
> local helper, and `takeFirstOfManyEvents` (dead code) was removed. The build scripts
> (`collect-externals.ts`) still strip lodash/rxjs as a safety measure.

## Transport Layer

Expand Down
1 change: 0 additions & 1 deletion firmware/.gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
# Firmware binaries — download via `make firmware-download`
signed/*.bin
unsigned/*.bin
# Keep directory structure
!.gitkeep
10 changes: 10 additions & 0 deletions firmware/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,17 @@
"e6685ab14844d0a381d658d77e13d6145fe7ae80469e5a5360210ae9c3447a77": "2.1.3",
"fe98454e7ebd4aef4a6db5bd4c60f52cf3f58b974283a7c1e1fcc5fea02cf3eb": "2.1.4"
},
"unsigned_firmware": {
"solana": {
"version": "7.10.0-solana",
"filename": "firmware.keepkey.solana-8acfd898.bin",
"sha256_payload": "8ad2ecd35ad0d9714a3592edfaa6343c0c7c63dc33677907cd8eb9ffb4f8bea7",
"sha256_full": "0906d9343c1a971b069715d84b78b4fce4ff4ba095e2b63ca7de18e6dd60a686",
"note": "Unsigned firmware with Solana (Ed25519) support — requires bootloader policy override"
}
},
"firmware_hashes": {
"8ad2ecd35ad0d9714a3592edfaa6343c0c7c63dc33677907cd8eb9ffb4f8bea7": "7.10.0-solana",
"958764cf3baa53eec0002eab9c54e02ce6f5fdab71e7efbbe723f958e26ff419": "7.10.0",
"24cca93ef5e7907dc6d8405b8ab9800d4e072dd9259138cf7679107985b88137": "7.9.3",
"9e691874bb6966aa0616d36b60489b82fab166d96e5166518eaa3e11468bf6a8": "7.9.1",
Expand Down
Binary file not shown.
2 changes: 1 addition & 1 deletion modules/hdwallet
1 change: 1 addition & 0 deletions projects/keepkey-sdk/lib/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ export declare class KeepKeySdk {
eth: {
ethSignTransaction: (params: EthSignTxParams) => Promise<SignedTx>;
ethSignMessage: (params: EthSignMessageParams) => Promise<any>;
ethSign: (params: EthSignMessageParams) => Promise<any>;
ethSignTypedData: (params: EthSignTypedDataParams) => Promise<any>;
ethVerifyMessage: (params: EthVerifyMessageParams) => Promise<boolean>;
};
Expand Down
Loading
Loading