<div align="center">
<h1>matroskin</h1>
<img src="https://raw.githubusercontent.com/TOwInOK/matroskin/refs/heads/main/.github/.content/logo.webp" alt="matroskin logo" width="220"/>
<p><i>Asynchronous Rust client library for WhatsMiner ASIC mining hardware.</i></p>
<p>
<a href="https://crates.io/crates/matroskin"><img src="https://img.shields.io/crates/v/matroskin.svg?style=flat-square" alt="Crates.io"/></a>
<a href="https://docs.rs/matroskin"><img src="https://img.shields.io/docsrs/matroskin?style=flat-square" alt="Docs.rs"/></a>
<a href="https://github.com/TOwInOK/matroskin/actions/workflows/push.yml"><img src="https://img.shields.io/github/actions/workflow/status/TOwInOK/matroskin/push.yml?branch=main&style=flat-square" alt="CI"/></a>
<a href="https://apidoc.whatsminer.com/"><img src="https://img.shields.io/badge/API-v3.0.3_(upstream_v3.0.5)-blue?style=flat-square" alt="API v3.0.3"/></a>
<a href="LICENSE-MIT.md"><img src="https://img.shields.io/badge/license-MIT%2FApache--2.0-blue?style=flat-square" alt="License"/></a>
<a href="https://www.rust-lang.org"><img src="https://img.shields.io/badge/rustc-1.85%2B-orange?style=flat-square" alt="MSRV 1.85+"/></a>
</p>
</div>
---
`matroskin` is an asynchronous Rust client for WhatsMiner ASIC hardware. An internal actor state machine encapsulates the WhatsMiner v3 TCP protocol—handling framing, encrypted authentication, and automatic reconnects behind a lock-free, strongly-typed `actor.send(cmd)` interface.
> [!WARNING]
> **Operational & API Version Status:**
>
> - **API Version:** The library implements **WhatsMiner API v3.0.3**. The upstream API has since released **v3.0.5**; features introduced in 3.0.4/3.0.5 are not yet supported.
> - **Hardware Validation:** Read-only queries (`get.*`) and verified control commands (`set.miner.fastboot`, `set.system.reboot`, `set.user.permission`) have been tested on physical hardware (marked `●`). All other state-modifying operations (marked `○`) are verified against the mock harness and must be evaluated with caution prior to production use.
## Highlights
- **Actor-based FSM:** Single-owner Tokio task manages the connection lifecycle without mutexes or runtime locks.
- **Complete API Coverage:** All 39 WhatsMiner v3 commands implemented as typed, owned structures.
- **Simple Command Dispatch:** Uniform `actor.send(cmd).await?` interface with strongly-typed request and response payloads.
- **Resilient Reconnection:** Transparent backoff and reconnect queuing that preserves pending commands during connection drops.
- **Secure by Default:** Zeroizing credentials and session salts on drop, AES-256-ECB parameter encryption, and automatic secret redaction in debug logs.
## Quickstart
Add `matroskin` and `tokio` to your `Cargo.toml` (requires **Rust 1.85+** / Edition 2024):
```toml
[dependencies]
matroskin = "0.1.0"
tokio = { version = "1", features = ["full"] }
```
### Basic Usage
> **Note:** Initializing an `Actor` via `Actor::new` requires an active Tokio runtime context. `Actor::clone()` creates an independent actor instance; wrap in `Arc<Actor>` to share one connected actor across tasks.
```rust
use matroskin::{
account::Account,
actor::Actor,
command::get_device_info::GetDeviceInfo,
password::Password,
};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// 1. Initialize actor in `Created` state (synchronous in memory, no network I/O yet).
let actor = Actor::new("10.10.10.10:4433", Account::Super, Password::Super);
// 2. Queue non-blocking connect intent.
actor.connect()?;
// 3. Send an owned command by value.
// The command mailbox waits until the actor establishes a Connected session.
let response = actor.send(GetDeviceInfo::all()).await?;
println!("Device info: {:#?}", response.msg);
// 4. Graceful non-blocking shutdown intent.
actor.shutdown()?;
Ok(())
}
```
### Flat Commands & Typed Parameters
All commands use flat structures with named fields and `.new(...)` constructors. Executing `actor.send(cmd)` returns `Response<C::Response>`, where `C::Response` is the typed payload in `.msg`:
```rust
use matroskin::{
account::{Account, Users},
actor::Actor,
command::{
get_device_info::GetDeviceInfo,
set_miner_pools::{PoolConfig, SetMinerPools},
set_user_permission::SetUserPermission,
},
password::Password,
};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let actor = Actor::new("10.10.10.10:4433", Account::Super, Password::Super);
actor.connect()?;
// Query device info: response payload is DeviceInfo (optional salt is redacted)
let info = actor.send(GetDeviceInfo::all()).await?;
println!("Salt present: {}", info.msg.salt.is_some());
// Set user permissions: requires All permission level
let perm = SetUserPermission::new(Users::User1, "set.system.led,set.miner.pools");
actor.send(perm).await?;
// Configure pools: 3 fixed entries with protected credentials (AES-256-ECB encrypted)
let pools = [
PoolConfig::new("stratum+tcp://pool1.com:3333", "worker1", "pass1"),
PoolConfig::new("stratum+tcp://pool2.com:3333", "worker2", "pass2"),
PoolConfig::new("stratum+tcp://pool3.com:3333", "worker3", "pass3"),
];
actor.send(SetMinerPools::new(pools)).await?;
actor.shutdown()?;
Ok(())
}
```
### Direct Streaming Operations
Streaming operations take exclusive ownership of the network transport while active:
```rust
use matroskin::{account::Account, actor::Actor, password::Password};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let actor = Actor::new("10.10.10.10:4433", Account::Super, Password::Super);
actor.connect()?;
// Download system logs directly into an owned AsyncWrite destination
let log_file = tokio::fs::File::create("miner.log").await?;
actor.download_log(log_file).await?;
// Firmware upload accepts an owned AsyncRead source and exact byte length
// (Use with caution on production devices)
// let fw_file = tokio::fs::File::open("firmware.bin").await?;
// let len = fw_file.metadata().await?.len();
// actor.upload_firmware(fw_file, len).await?;
actor.shutdown()?;
Ok(())
}
```
## Command Inventory
Status of implemented commands against the [WhatsMiner API v3](https://apidoc.whatsminer.com/) (target specification: **v3.0.3**, current upstream: **v3.0.5**):
- `●` **Hardware verified:** Tested and confirmed on physical WhatsMiner hardware.
- `○` **Mock verified:** Verified against the in-process mock test suite; use on physical hardware at your own risk.
| `get.device.custom_data` | ● | Public | Read-only | Retrieve device custom metadata fields |
| `get.device.info` | ● | Public | Read-only | Retrieve system, network, miner, power, and error status |
| `get.fan.setting` | ● | Public | Read-only | Get fan speed and temperature offset summary |
| `get.log.download` | ● | Public | Read-only | Stream packaged `.tgz` log archive (`download_log`) |
| `get.miner.history` | ● | Public | Read-only | Get miner history within a time window (≤ 24h) |
| `get.miner.setting` | ● | Public | Read-only | Get power mode, limit, and fastboot settings |
| `get.miner.status` | ● | Public | Read-only | Get detailed status: summary, pools, and edevs |
| `get.system.setting` | ● | Public | Read-only | Get system settings (hostname, NTP, timezone) |
| `set.device.custom_data` | ○ | Secured | Read-Write | Set custom metadata key-value pair |
| `set.fan.poweroff_cool` | ○ | Secured | Read-Write | Toggle power-off cooling |
| `set.fan.temp_offset` | ○ | Secured | Read-Write | Set fan temperature target offset (-10..10°C) |
| `set.fan.zero_speed` | ○ | Secured | Read-Write | Toggle zero fan speed mode |
| `set.log.upload` | ○ | Secured | Read-Write | Configure remote syslog upload server |
| `set.miner.cointype` | ○ | Secured | Read-Write | Set coin mining algorithm (e.g. BTC) |
| `set.miner.fast_hash` | ○ | Secured | Read-Write | Toggle fast hash mode |
| `set.miner.fastboot` | ● | Secured | Read-Write | Toggle fastboot mode |
| `set.miner.heat_mode` | ○ | Secured | Read-Write | Configure heat mode (e.g. normal, heating) |
| `set.miner.pools` | ○ | Encrypted | Read-Write | Set 3 stratum mining pools (AES-256-ECB) |
| `set.miner.power` | ○ | Secured | Read-Write | Set target power consumption in Watts |
| `set.miner.power_limit` | ○ | Secured | Read-Write | Set maximum power limit threshold in Watts |
| `set.miner.power_mode` | ○ | Secured | Read-Write | Set power mode (low, normal, high) |
| `set.miner.power_percent` | ○ | Secured | Read-Write | Set target power percentage and mode |
| `set.miner.report` | ○ | Secured | Read-Write | Configure periodic status report interval |
| `set.miner.restore_setting` | ○ | Secured | Read-Write | Restore miner settings to factory defaults |
| `set.miner.service` | ○ | Secured | Read-Write | Restart or stop miner hashing service |
| `set.miner.target_freq` | ○ | Secured | Read-Write | Adjust target frequency percentage |
| `set.miner.upfreq_speed` | ○ | Secured | Read-Write | Configure frequency ramp-up speed |
| `set.system.factory_reset` | ○ | Secured | All (Super) | Factory reset control board settings |
| `set.system.hostname` | ○ | Secured | Read-Write | Set device network hostname |
| `set.system.led` | ○ | Secured | Read-Write | Set LED status mode (auto, manual, off) |
| `set.system.net_config` | ○ | Secured | All (Super) | Configure network interface (DHCP or static) |
| `set.system.ntp_server` | ○ | Secured | Read-Write | Configure NTP time synchronization servers |
| `set.system.reboot` | ● | Secured | Read-Write | Immediately restart the ASIC controller |
| `set.system.time_randomized` | ○ | Encrypted | Read-Write | Configure randomized start/stop time jitter |
| `set.system.timezone` | ○ | Encrypted | Read-Write | Configure system timezone name and offset |
| `set.system.update_firmware` | ○ | Secured | All (Super) | Stream binary firmware update (`upload_firmware`) |
| `set.system.webpools` | ○ | Secured | Read-Write | Toggle web pool modification lock |
| `set.user.change_passwd` | ○ | Encrypted | Super/User | Change account password (AES-256-ECB) |
| `set.user.permission` | ● | Secured | All (Super) | Set comma-separated allowed commands for user |
## Development & Testing
The repository provides built-in Cargo aliases in `.cargo/config.toml`:
```sh
# Run all unit tests, mock device tests, and doc tests
cargo test
# Run in-process mock device tests (thread-isolated)
cargo test-mock
# Strict linter check across all targets and features
cargo lint
# Check code formatting
cargo fmt-check
# Verify documentation builds without warnings
cargo doc-check
# List hardware integration tests without connecting to an ASIC
cargo test-real-list
# Run physical hardware tests (requires ASIC reachable at MINER_ADDR)
MINER_ADDR=10.20.1.2:4433 cargo test --test real_device --features real-device
```
- See [mock device testing](docs/MOCK.md) for in-process mock device testing.
- See [physical hardware testing](docs/E2E.md) for real-device testing guidelines.
- See [architecture reference](docs/architecture.md) for the actor, session, and protocol model.
- See [AGENTS.md](AGENTS.md) for repository terminology and contribution constraints.
## License
Dual-licensed under either of:
- [MIT license](LICENSE-MIT.md)
- [Apache License, Version 2.0](LICENSE-APACHE.md)
at your option.