Skip to content

API Guide

The Rust, Python, and Go APIs intentionally share names and behavior. This page summarizes the user-facing surface and points out the few language-specific differences. Go methods are synchronous and return error; a missing miner is asic_go.ErrNotFound. See go/README.md for cgo build notes.

Discovery

MinerFactory owns the scan range and discovery tuning. Connectivity retries are additional attempts after the initial probe. Setting retries to zero still performs one probe; later attempts use bounded exponential backoff and remain under the same scan concurrency limit. The default remains three retries; callers that want one pass can explicitly set zero.

Common miner ports are probed concurrently, returning after the first success. The scan concurrency limit also bounds the total number of active TCP probes, including probes made by connectivity retries.

The identification timeout is an end-to-end deadline covering discovery commands and firmware-specific miner construction. Discovery HTTP clients also apply explicit connection and total-request deadlines.

let factory = MinerFactory::from_subnet("192.168.1.0/24")?
    .with_concurrent_limit(2500)
    .with_connectivity_timeout_secs(1)
    .with_connectivity_retries(0)
    .with_identification_timeout_secs(10);
factory = (
    MinerFactory.from_subnet("192.168.1.0/24")
    .with_concurrent_limit(2500)
    .with_connectivity_timeout_secs(1)
    .with_connectivity_retries(0)
    .with_identification_timeout_secs(10)
)
factory, err := asic_go.NewMinerFactoryFromSubnet("192.168.1.0/24")
if err != nil {
    log.Fatal(err)
}
factory.WithConcurrentLimit(2500).
    WithConnectivityTimeoutSecs(1).
    WithConnectivityRetries(0).
    WithIdentificationTimeoutSecs(10)
Operation Rust Python Go
Known IP get_miner(ip).await? await get_miner(ip) GetMiner(ip)
Full scan scan().await? await scan() Scan()
Stream found miners scan_stream() scan_stream() not wrapped yet
Stream every IP scan_stream_with_ip() scan_stream_with_ip() not wrapped yet

Miner Identity

Miner identity is available without awaiting because it is known when the miner handle is constructed.

In Go, retrieve the identity and handle its error before reading fields:

info, err := miner.GetDeviceInfo()
if err != nil {
    log.Fatal(err)
}
Value Rust Python Go
IP address miner.get_ip() miner.ip miner.GetIP()
Make miner.get_device_info().make miner.make info.Make
Model miner.get_device_info().model miner.model info.Model
Firmware miner.get_device_info().firmware miner.firmware info.Firmware
Algorithm miner.get_device_info().algo miner.algo info.Algo
Hardware shape miner.get_device_info().hardware miner.hardware info.Hardware

Data Collection

The full telemetry snapshot is MinerData. Individual getters return focused fields when a caller does not need the whole snapshot.

let data = miner.get_data().await;
let hashrate = miner.get_hashrate().await;
let fans = miner.get_fans().await;
data = await miner.get_data()
hashrate = await miner.get_hashrate()
fans = await miner.get_fans()
data, err := miner.GetData()
if err != nil {
    log.Fatal(err)
}
hashrate, err := miner.GetHashrate()
fans, err := miner.GetFans()

Common telemetry methods:

Field Method
MAC address get_mac
Serial number get_serial_number
Hostname get_hostname
Firmware version get_firmware_version
Hashboards get_hashboards
Hashrate get_hashrate
Fans get_fans
Power supplies get_psus
Wattage get_wattage
Best share difficulty get_best_share
Session best share difficulty get_session_best_share
Messages get_messages
Pools get_pools
Mining state get_is_mining

MinerData.psus is Option<Vec<PsuData>>: None when the firmware does not report PSU details, otherwise one entry per PSU with model, serial number, firmware version, input/output voltage (V), output current (A), input/output power (W), efficiency (0–1), and temperature (°C). Custom Rust backends implement GetPsus; an empty implementation reports None. SealMiner Stock reads these from RPC stats (PSU Input Voltage, PSU Output Current, …) and also reports its non-zero PSU Error bitmask as a PowerSupply message.

Controls And Capability Checks

Not every miner supports every control. Rust exposes supports_*() methods; Python exposes matching supports_* properties; Go returns them from Supports().

Capability Control
supports_restart restart()
supports_restart_mining restart_mining()
supports_pause pause()
supports_resume resume()
supports_set_fault_light set_fault_light(...)
supports_set_power_limit set_power_limit(...)
supports_change_password change_password(...)
supports_read_logs read_logs()
supports_factory_reset factory_reset()
supports_restore_stock_os restore_stock_os()
supports_prepare_firmware prepare_firmware(...)
supports_upgrade_firmware upgrade_firmware(...)
supports_check_firmware_update check_firmware_update()

All miner handles also expose revalidate(), which re-runs that backend's firmware discovery checks against the same IP and returns whether the device is still valid for the existing miner handle.

prepare_firmware(...) is read-only. It validates backend-specific containers and may query miner metadata to select a compatible payload, but it never starts an upload. The returned FirmwareImage contains the filename and bytes that an upgrade would use.

check_firmware_update() is an on-demand, read-only check returning installed and latest versions and, when resolved, a firmware download source. Braiins v26.04 queries the miner's update service. ePIC queries GitHub's latest release API, using epicblockchain/powerplay-antminer for STM32 UMC boards and epicblockchain/umcos-antminer for recognized stock control boards. Unknown ePIC board identities and legacy VolcMiner Xilinx boards return an error. ePIC currently reports versions only; the download source is absent because board/model asset selection is not yet implemented. GitHub checks require internet access and are subject to its public API rate limits. An unknown installed version makes update_available() return false, even if the latest version is known. ePIC shares concurrent GitHub lookups per repository and caches valid release metadata for five minutes. Failed lookups are cached for one minute before a retry, avoiding repeated requests during outages or rate limiting. Installed versions are always read from each miner and are never shared through this cache.

For any backend with supports_check_firmware_update() == true, use the returned FirmwareStats::update_available() to compare versions:

let stats = miner.check_firmware_update().await?;
let update_available = stats.update_available();

factory_reset() restores the miner's settings to their factory defaults; it does not replace the installed operating system. restore_stock_os() is a separate, disruptive operation that uninstalls an aftermarket OS and restores the manufacturer's stock OS. A successful request may reboot the miner and does not mean the restore has already completed.

if miner.supports_set_power_limit() {
    miner.set_power_limit(measurements::Power::from_watts(3200.0)).await?;
}
if miner.supports_set_power_limit:
    await miner.set_power_limit(3200.0)
caps, err := miner.Supports()
if err != nil {
    log.Fatal(err)
}
if caps.SetPowerLimit {
    _, err := miner.SetPowerLimit(3200.0)
    if err != nil {
        log.Fatal(err)
    }
}

Configuration Models

Configuration objects are shared concepts across Rust and Python. Python models are backed by Rust structs and expose Pydantic-style helpers.

use asic_rs::core::config::{
    fan::FanConfig,
    pools::{PoolConfig, PoolGroupConfig},
    tuning::TuningConfig,
};
use asic_rs::core::data::{miner::TuningTarget, pool::PoolURL};

let fan = FanConfig::manual(80);
let tuning = TuningConfig::new(TuningTarget::from_watts(3200.0));
let pool = PoolGroupConfig {
    name: "default".to_string(),
    quota: 1,
    pools: vec![PoolConfig {
        url: PoolURL::from("stratum+tcp://pool.example.com:3333".to_string()),
        username: "worker.1".to_string(),
        password: "x".to_string(),
    }],
};

Rust pool configs can add an exact worker suffix without storing it in the config model:

let with_suffix = pool.use_worker_suffix(".device-1");
let account_only = with_suffix.clear_worker_suffix();

The suffix passed to use_worker_suffix includes the separator. Clearing keeps the account name before the first dot, even when the worker name contains more dots.

from pyasic_rs.config import FanConfig, Pool, PoolGroup, TuningConfig

fan = FanConfig.manual(80)
tuning = TuningConfig.power(3200.0)
pool = PoolGroup(
    name="default",
    quota=1,
    pools=[
        Pool(
            url="stratum+tcp://pool.example.com:3333",
            username="worker.1",
            password="x",
        )
    ],
)

Use the matching config support property before reading or writing config:

Capability Get Set
supports_pools_config get_pools_config() set_pools_config(...)
supports_fan_config get_fan_config() set_fan_config(...)
supports_tuning_config get_tuning_config() set_tuning_config(...)
supports_scaling_config get_scaling_config() set_scaling_config(...)

Python Pydantic Interop

Python data/config classes can be used inside Pydantic models and support model_validate, model_dump, and model_json_schema where applicable.

from pydantic import BaseModel
from pyasic_rs.data import HashRate


class Snapshot(BaseModel):
    hashrate: HashRate


snapshot = Snapshot.model_validate(
    {"hashrate": {"value": 100.0, "unit": "TH/s", "algo": "SHA256"}}
)

print(snapshot.model_dump())

Binding Names And Wire Formats

Shared type names are retained in Go: MinerFactory, DeviceInfo, MinerHardware, HashAlgorithm, MiningMode, MinerControlBoard, MinerComponent, and the tuning-capability models. Go exports names in PascalCase, retaining initialisms (IP, MAC, API, PSU). Identity and telemetry getters consistently use Get…; constructors use New… followed by the type and, where needed, the variant.

Python additionally exports PoolConfig and PoolGroupConfig as aliases for its existing Pool and PoolGroup classes. MinerHardware.total_chips is the shared count name; Python's chips property remains an alias. Go exposes TotalChips, BoardCount, and ChipsForBoard, returning a value and a boolean that distinguishes missing counts from zero.

Go tuning targets expose Variant, Watts, TargetHashrate, TargetMode, PresetName, and Boards. These correspond to Python's variant, watts, target_hashrate, target_mode, preset_name, and boards. ManualTuningValues uses numeric board IDs in Go, Rust, and Python. The complete Go constructor set is NewTuningTargetManual, NewTuningTargetPower, NewTuningTargetHashrate, NewTuningTargetMiningMode, and NewTuningTargetPreset.

Some established Python representations differ from Rust's Serde schema. The C bridge and Go JSON output retain the Rust schema, also described by the Rust TypeScript derives; they do not rename existing wire fields.

Value Rust / C / Go JSON output Existing Python representation
Hashrate unit "TeraHash" "TH/s"
Pool scheme "StratumV1" "stratum+tcp"
Pool URL Object containing scheme/host/port/pubkey URL string
Fan mode "Auto" / "Manual" "auto" / "manual"
Power target {"Power":{"watts":3200}} {"type":"power","value":3200}
Mining-mode target {"MiningMode":"High"} {"type":"mode","value":"High"}

Go accepts these Python input forms for hash units, pool schemes/URLs, fan modes, and tuning targets, then emits canonical Rust JSON. Invalid hash units and algorithms return errors instead of silently becoming hashes per second or SHA-256. HashRate.DefaultUnit and IntoDefaultUnit use the algorithm-specific unit, matching Rust/Python.

Supports() and Expected() remain Go conveniences for grouped results; individual GetExpectedHashboards, GetExpectedChips, and GetExpectedFans methods also match the shared API. Go's GetControlBoardVersion and the snapshot both preserve {known, name}; Python's established individual getter continues to return a display string. Go firmware checks and revalidation propagate backend errors, whereas the existing Python methods return None for those failures.