matroskin 0.1.0

whatsminer api client lib
Documentation
<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.

| Command                      | Status | Level     | Access      | Description                                              |
| ---------------------------- | :----: | --------- | ----------- | -------------------------------------------------------- |
| `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.