# MDK Docs (/v0-0-2)
## I'm building with MDK
}
title={Learn what MDK is}
href="/v0-0-2/overview/about"
description={
Product overview, architecture, and the MDK packages
}
/>
}
title={Ship a dashboard fast}
href="/v0-0-2/ui/react/quickstart"
description={
React Quickstart: MdkProvider, adapter hooks, foundation components
}
/>
}
title={Use UI Core without a dashboard}
href="/v0-0-2/how-to/ui/core/use-ui-core-headlessly"
description={
@tetherto/mdk-ui-core is headless: Zustand stores and a QueryClient factory you can wire into any runtime
}
/>
}
title={Get started path picker}
href="/v0-0-2/get-started"
description={
Discover your path, including backend tools and the raw client
}
/>
## I'm an AI agent (or building one)
}
title={Full docs in one file}
href="/v0-0-2/llms-full.txt"
description={
Every page of these docs concatenated for LLM ingestion
}
/>
mdk-ui CLI}
href="/v0-0-2/resources/roadmap"
description={
<>
@tetherto/mdk-ui-cli — agent-first registry, scaffold, and docs
Coming soon →
>
}
/>
# Code of Conduct (/v0-0-2/community/code-of-conduct)
## Our Commitment
MDK is committed to fostering an open, professional, and respectful community.
We welcome contributors of all backgrounds and experience levels. Participation in the MDK community should be harassment-free and inclusive for everyone.
---
## Expected Behavior
All participants in the MDK community are expected to:
- Be respectful and constructive in communication
- Provide helpful and professional feedback
- Assume good intent
- Focus on what is best for the project
- Accept constructive criticism gracefully
---
## Unacceptable Behavior
The following behaviors are not tolerated:
- Harassment, discrimination, or hateful conduct
- Personal attacks or insulting language
- Public or private harassment
- Trolling, intimidation, or deliberate disruption
- Publishing private information without consent
- Any conduct that would be considered unprofessional in a workplace setting
---
## Scope
This Code of Conduct applies to:
- GitHub repositories
- Issues and pull requests
- Discussions and community channels
- Official MDK communication platforms
- Any other space officially associated with the MDK project
---
## Enforcement
The Community Manager is responsible for enforcing this Code of Conduct.
**Community Manager:** Gio\
**Lead Maintainer:** Hemant T
If you experience or witness unacceptable behavior, report it privately to the Community Manager.
Reports will be handled confidentially and reviewed in coordination with the MDK core team.
---
## Enforcement Guidelines
The MDK core team may take any action deemed appropriate, including:
- Warning the participant
- Temporarily restricting access
- Permanently banning a participant from the community
- Removing content that violates this Code of Conduct
Decisions regarding enforcement are final.
---
## Amendments
This Code of Conduct may be updated from time to time by the MDK core team to reflect evolving community needs.
# Contributing (/v0-0-2/community/contributing)
Thank you for your interest in contributing to **MDK**!
This document outlines the contribution workflow for all MDK repositories, from setting up your development environment to submitting pull requests and participating in releases.
## Getting started
### Prerequisites
Before contributing, make sure you have the following installed:
- **Node.js** (version 20.0 or higher)
- **Git** (latest stable version)
- **npm** (included with Node.js)
### Licensing
MDK is released under the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0.html).
By contributing, you agree that:
- You retain copyright over your contributions
- You grant a perpetual, worldwide, royalty-free license for their use
- Contributions are provided **"AS IS"**, without warranty
## Development environment setup
### Fork and clone
1. Fork the repository on GitHub.
2. Clone your fork locally and navigate into the project directory:
```bash
git clone https://github.com/YOUR_USERNAME/REPOSITORY_NAME.git
cd REPOSITORY_NAME
```
3. Add the upstream remote to keep your fork in sync with the main repository:
```bash
git remote add upstream https://github.com/tetherto/REPOSITORY_NAME.git
```
> Replace `REPOSITORY_NAME` with the actual repository name.
## Pull request workflow
### Branch naming convention
Create branches using the following pattern:
```text
{type}/{short-description}
```
Supported types:
- `feature/`: new features or workers
- `fix/`: bug fixes
- `docs/`: documentation changes
- `refactor/`: code refactoring
- `test/`: test additions or changes
#### Examples
```bash
# New feature
git checkout -b feature/mdk-new-device
# Bug fix
git checkout -b fix/timeout-handling
```
### Keeping your fork updated
Regularly sync your fork with the upstream repository:
```bash
git fetch upstream
git checkout main
git merge upstream/main
git push origin main
```
### Pull request steps
1. Create a branch from `main`
2. Make your code changes
3. Write or update tests
4. Run linting and tests locally
5. Commit changes with meaningful messages
6. Push your branch and open a Pull Request targeting `main`
### PR checklist
Before submitting your PR, ensure that:
- Code builds locally
- Tests pass (`npm test`)
- Linting passes (`npm run lint` or `npm run lint:fix`)
- New features include tests
- Documentation is updated if applicable
### PR title format
Use the following convention:
```text
{type}({scope}): {description}
```
Types:
- `feat`
- `fix`
- `docs`
- `refactor`
- `test`
- `chore`
Examples:
- `feat(miner): add Antminer S21 support`
- `fix(timeout): resolve action timeout handling`
- `docs(api): update stats documentation`
## Release process
### PR review
All pull requests go through the following review steps:
1. **Automated checks**: linting and tests must pass
2. **Code review**: at least one maintainer approval is required
3. **Feedback resolution**: all requested changes must be addressed
4. **Squash and merge**: maintainers squash commits to keep history clean
### Release workflow
Releases are created by merging approved branches into `main` and tagging a version when applicable.
**Workflow:**
1. Contributor creates a feature or fix branch from `main`
2. Contributor runs tests locally
3. If tests fail, fixes are applied and tests re-run
4. Contributor opens a PR targeting `main`
5. Reviewer performs code review
6. Contributor addresses feedback (if requested)
7. Reviewer approves or rejects the PR
8. Approved PRs are merged into `main`
9. Reviewer tags a release version (if applicable)
10. Reviewer deploys to production
11. On failure, rollback to the previous tag
### Workflow diagram
```mermaid
flowchart TB
subgraph contributor [Contributor]
start((Start)) --> createBranch[Create branch from main]
createBranch --> test[Run tests]
test --> testGw{Tests pass?}
testGw -->|No| fix[Fix issues]
fix --> test
testGw -->|Yes| createPR[Create PR]
createPR --> review
address[Address feedback] --> pushFixes[Push fixes]
pushFixes --> test
end
subgraph reviewer [Reviewer / Maintainer]
review[Code review]
reviewGw{Approved?}
review --> reviewGw
reviewGw -->|Request changes| address
reviewGw -->|Rejected| cancel[Close PR]
reviewGw -->|Approved| merge[Merge to main]
merge --> tagGw{Tag release?}
tagGw -->|No| endNoTag((End))
tagGw -->|Yes| tag[Tag version]
tag --> deploy[Deploy]
deploy --> deployGw{Deploy success?}
deployGw -->|Yes| endSuccess((End))
deployGw -->|No| rollback[Rollback]
rollback --> fix
end
```
## Versioning and tagging
### Version tagging
```bash
git checkout main
git pull origin main
git tag -a v1.2.0 -m "Release v1.2.0: Add RTD support"
git push origin main
git push origin v1.2.0
```
### Versioning scheme
MDK follows **Semantic Versioning**:
- **MAJOR** (`1.x.x`): breaking changes
- **MINOR** (`x.1.x`): new backward-compatible features
- **PATCH** (`x.x.1`): backward-compatible bug fixes
---
## Code standards
### JavaScript style
MDK uses **StandardJS** style to keep the codebase consistent and easy to review across repositories.
Key rules:
- 2-space indentation
- No semicolons
- Single quotes for strings
- Space after keywords (`if`, `for`, `while`)
- No unused variables
## Quick reference
### Common Git commands
```bash
# Start a new feature
git checkout main && git pull && git checkout -b feature/my-feature
# Sync with upstream
git fetch upstream && git merge upstream/main
# Prepare for PR
npm run lint:fix && npm test
# Squash commits
git rebase -i HEAD~N
# Amend last commit
git commit --amend
# Force push after rebase (feature branches only)
git push --force-with-lease origin feature/my-feature
```
# Governance (/v0-0-2/community/governance)
This document describes how the MDK project is governed and how decisions are made.
MDK is an open-source project. While the code is publicly available and community contributions are welcome, final decision-making authority rests with the MDK core team.
---
## Project Roles
### Users
Anyone who uses MDK and provides feedback, bug reports, or feature requests.
### Contributors
Community members who contribute code, documentation, tests, or other improvements via pull requests or issues.
Contributors do not have merge access.
---
### Maintainers
Maintainers are responsible for reviewing pull requests, maintaining code quality, and ensuring alignment with the project roadmap.
Maintainers may be appointed by the Lead Maintainer.
The following GitHub IDs currently hold maintainer status for MDK:
- arif-dewi
- eugeneglova
- robdll
- eskawl
- boris91
- habrahamyanbf
- efr-nox
- rob-aslanian
- mukama
- paragmore
- tekwani
Only the GitHub accounts listed above have maintainer privileges within the MDK repositories. Maintainer status is granted based on demonstrated technical expertise, sustained contribution, and alignment with project goals.
---
### MDK core team
The MDK Core Team consists of all active developers, the Project Manager, and the Community Manager.
The Core Team is responsible for the overall health, sustainability, and long-term success of the project. While specific authorities are defined for the Lead Maintainer (technical) and the Community Manager (strategic), the Core Team operates as the primary collaborative decision-making body.
### Lead Maintainer
**Hemant T** is the Lead Maintainer and Technical Lead for MDK.
The Lead Maintainer has final authority over:
- Pull request approval and merging
- Architecture and design decisions
- Release planning and versioning
- Accepting or rejecting features
- Appointing or removing maintainers
If consensus cannot be reached among maintainers, the Lead Maintainer makes the final decision.
---
### Community Manager
**Gio** is the Community Manager for MDK.
The Community Manager is responsible for:
- Managing community communication channels
- Moderating discussions and enforcing the Code of Conduct
- Supporting contributors during onboarding
- Acting as a bridge between the community and the MDK core team
- Defining, maintaining, and communicating the project vision and long-term direction
- Leading roadmap prioritization and strategic planning
---
## Decision-Making Process
- Community members may propose changes via issues or pull requests
- Maintainers review contributions for quality, security, and alignment with MDK goals
- The Lead Maintainer has final approval authority on all technical decisions
- Strategic, roadmap, or breaking changes are determined by the MDK core team
- The Community Manager has final decision-making authority over all strategic and directional matters, including project vision, roadmap prioritization, major feature introductions, partnerships, and significant pivots
- In cases where strategic direction and technical considerations intersect, the Community Manager determines the final strategic outcome, while the Lead Maintainer determines the final technical implementation approach.
---
## Contribution Review Process
- All contributions must follow `CONTRIBUTING.md`
- Pull requests require review by a maintainer
- Final approval and merge is performed by the Lead Maintainer
- The MDK team reserves the right to decline contributions that do not align with the project direction
---
## Inactivity and Removal
Maintainers who become inactive for an extended period or violate project policies may be removed by the Lead Maintainer.
---
## Code of Conduct
All participants are expected to follow the project's Code of Conduct.
Violations are handled by the Community Manager in coordination with the MDK core team.
See [Code of Conduct](/v0-0-2/community/code-of-conduct) for details.
---
## Changes to Governance
This governance model may evolve over time.
Any changes will be proposed and approved by the MDK core team.
# Get started with MDK (/v0-0-2/get-started)
## Build with MDK
}
title={Add MDK data to an existing app}
href="#framework-quickstarts"
description={
Use the frontend tools to bring MDK telemetry and controls into a React, Vue, or Svelte app
}
/>
Build a full MDK app from scratch}
href="/v0-0-2/resources/roadmap"
description={
<>
Coming soon: Adopt the App Toolkit shell. Frontend and backend wired together, extended via plugins
Learn about the release schedule β
>
}
/>
Align the MDK backend with your business logic}
href="/v0-0-2/resources/roadmap"
description={
<>
Coming soon: Use the backend tools to plug a library of custom routes and aggregations into the MDK App Node
Learn about the release schedule β
>
}
/>
Connect directly to ORK from any client}
href="/v0-0-2/resources/roadmap"
description={
<>
Coming soon: Use @tetherto/mdk-client to talk to @tetherto/mdk-ork from a CLI, agent, or backend service
Learn about the release schedule β
>
}
/>
## Framework quickstarts
}
title={React}
href="/v0-0-2/ui/react/get-started"
description={
Pick a path: lean Quickstart, full Tutorial, or browse the component packages
}
/>
Vue}
href="/v0-0-2/resources/roadmap"
description={
<>
Reactive hooks for Vue
Learn about the release schedule β
>
}
/>
Svelte}
href="/v0-0-2/resources/roadmap"
description={
<>
Reactive hooks for Svelte
Learn about the release schedule β
>
}
/>
Web Components}
href="/v0-0-2/resources/roadmap"
description={
<>
Framework-agnostic Web Components
Learn about the release schedule β
>
}
/>
## Next steps
- Learn more about the high-level [architecture](/v0-0-2/overview/architecture): runtime stack and deployment modes
- Access the [MDK repositories](/v0-0-2/resources/repositories)
- [Contribute](/v0-0-2/community/contributing)
# Use UI Core headlessly (/v0-0-2/how-to/ui/core/use-ui-core-headlessly)
@tetherto/mdk-ui-core
[`@tetherto/mdk-ui-core`](/v0-0-2/reference/app-toolkit/ui-core) is the framework-agnostic headless layer of the MDK App Toolkit. This how-to walks through installing it on its own and driving its Zustand stores from a non-React runtime — a Node script, a Vue or Svelte adapter you're authoring, a CLI tool, or a test helper.
## When to reach for this
Use headless UI Core when:
- You're authoring a framework adapter (Vue, Svelte, Web Components) and need raw access to the Zustand stores.
- You're building a Node CLI or backend service that has to read MDK telemetry and act on it.
- You're writing test helpers or fixtures that need to seed and inspect store state without a React renderer.
- You need to subscribe to store changes from non-UI code — logging, websocket bridges, metrics.
For a React app, the [React adapter](/v0-0-2/ui/react/get-started) wraps UI Core with `` and adapter hooks. Use that path instead so most React code never touches `@tetherto/mdk-ui-core` directly.
## Install
`@tetherto/mdk-ui-core` has no peer dependencies on React or any UI framework.
```bash
npm install @tetherto/mdk-ui-core
```
## Subpath imports
Pull only the pieces you need from the relevant subpath. Subpath imports give tree-shakers a smaller surface than the top-level barrel:
```ts
```
The [subpath exports table](/v0-0-2/reference/app-toolkit/ui-core#subpath-exports) on the reference lists every supported entry.
## Create a QueryClient
`createMdkQueryClient` returns a TanStack Query Core client wired to your App Node. Pass an explicit `baseUrl`, or let the factory resolve one from environment variables:
```ts
const queryClient = createMdkQueryClient({
baseUrl: 'https://app-node.example.com',
})
```
Without an explicit `baseUrl`, the factory checks `VITE_MDK_API_URL` then `MDK_API_URL` before falling back to `http://localhost:3000`. The [resolution order](/v0-0-2/reference/app-toolkit/ui-core#queryclient-factory) on the reference covers every case.
## Read store state
Each store is a Zustand vanilla singleton. `getState()` returns the current snapshot:
```ts
const { token, permissions } = authStore.getState()
console.log('current token', token)
```
## Write store state
`setState()` accepts either a partial object or a function that receives the previous state:
```ts
devicesStore.setState({ selectedDeviceId: 'wm-002' })
devicesStore.setState((prev) => ({
devices: [...prev.devices, newDevice],
}))
```
## Subscribe to changes
`subscribe()` runs a callback on every state change and returns an unsubscribe function:
```ts
const unsubscribe = notificationStore.subscribe((state) => {
console.log('unread notifications:', state.count)
})
unsubscribe()
```
## A complete Node example
A small Node script that authenticates against the App Node, fetches the device list once, and then tails unread notification count changes:
```ts
authStore,
devicesStore,
notificationStore,
} from '@tetherto/mdk-ui-core/store'
async function main() {
const queryClient = createMdkQueryClient({
baseUrl: process.env.MDK_API_URL ?? 'http://localhost:3000',
})
authStore.setState({ token: process.env.MDK_TOKEN ?? '' })
const devices = await queryClient.fetchQuery({
queryKey: ['devices', 'list'],
queryFn: async () => {
const res = await fetch(`${process.env.MDK_API_URL}/api/devices`, {
headers: { Authorization: `Bearer ${authStore.getState().token}` },
})
return res.json()
},
})
devicesStore.setState({ devices })
console.log(`Found ${devices.length} devices`)
const unsubscribe = notificationStore.subscribe((state) => {
console.log(`unread notifications: ${state.count}`)
})
process.on('SIGINT', () => {
unsubscribe()
process.exit(0)
})
}
main().catch((err) => {
console.error(err)
process.exit(1)
})
```
Run it with:
```bash
MDK_TOKEN=ey... MDK_API_URL=https://app-node.example.com node script.ts
```
For the prebuilt query and mutation factories (`authQuery`, `devicesQuery`, `deviceQuery`, `telemetryQuery`), check the [QueryClient factory section](/v0-0-2/reference/app-toolkit/ui-core#queryclient-factory) on the reference.
## Next steps
- [UI Core reference](/v0-0-2/reference/app-toolkit/ui-core): full store list, query helpers, and the `createMdkQueryClient` resolution order.
- [MDK App Toolkit architecture](/v0-0-2/overview/architecture/app-toolkit): where UI Core fits in the frontend stack.
- [React adapter](/v0-0-2/ui/react/get-started): if you decide to layer React on top.
# Upgrade npm for the MDK UI monorepo (/v0-0-2/how-to/ui/react/upgrade-npm)
The MDK UI monorepo (`mdk-ui`) declares `engines.npm` `>=11.0.0` and pins `packageManager` to `npm@11.12.0`. **Node 22 LTS** often bundles **npm 10.9.x**, so `npm install` at the repo root can fail with an engine warning before you clone, build, or run the demo.
## Check your npm version
```bash
npm -v
```
If the major version is **10** (for example `10.9.4`), upgrade before running `npm install` at the monorepo root.
## Upgrade npm
Pick one approach.
### Option A β global npm 11 (simplest)
```bash
npm install -g npm@11
npm -v
```
### Option B β Corepack (match upstream `packageManager`)
```bash
corepack enable
corepack prepare npm@11.12.0 --activate
npm -v
```
You should see `11.x` (Option B targets `11.12.0`).
## Retry at the monorepo root
```bash
cd /path/to/mdk-ui
npm install
npm run build
```
## Next steps
- [Explore the demo](/v0-0-2/ui/react/explore-the-demo) (`npm run dev:demo`)
- Follow the [quickstart](/v0-0-2/ui/react/quickstart) for app integration
- Follow [Wire a React app](/v0-0-2/tutorials/react/tutorial) (`npm -w my-app run dev`)
# About MDK (/v0-0-2/overview/about)
The current version of MDK is v0.0.1. These develop pages are informational only; do not rely on the code samples,
architectural descriptions, etc. until the official release of v0.0.2.
## Introducing MDK
MDK, the Mining Development Kit, is an [open-source platform](/v0-0-2/community/contributing#licensing) that delivers a modern, transparent, and modular
infrastructure for Bitcoin mining operations. MDK enables Bitcoin mining operations to start small, scale smoothly, and remain in full control, without
lock-in, rewrites, or hidden complexity.
## The problem
The Bitcoin mining industry has long been constrained by closed systems, proprietary tooling, and vendor lock-in. MDK changes that.
## The solution
MDK delivers a modular mining stack that empowers operators and developers to build, monitor, control, and scale mining operations with full ownership:
from a single device to gigawatt-scale facilities — without architectural rewrites.
MDK ships three packages:
1. [Orchestration kernel (ORK)](#the-orchestration-kernel).
1. [Universal SDK](#the-universal-sdk).
1. [MDK App Toolkit](#mdk-app-toolkit).
All three communicate through the **MDK Protocol**. Clients — browsers and
[AI agents](#ai-ready-with-unified-intelligence) alike — reach the kernel exclusively through
the App Node, the secure gateway your team builds with the SDK. Tying everything together is a
**single contract per device type**: the same
[`mdk-contract.json`](/v0-0-2/overview/architecture#responsibilities-and-the-capability-contract) serves the UI
(data labels), the orchestrator (validation rules), and AI agents (reasoning context). One file,
three audiences, no drift.
### The orchestration kernel
ORK, the Orchestration Kernel, is distributed as [`@tetherto/mdk-ork`](/v0-0-2/overview/architecture/ork). It's the central coordination engine of MDK and serves
as a controller: it knows which devices are online, routes commands to the right place, monitors health, and collects performance data.
`@tetherto/mdk-ork` communicates with devices through a standardized language called the **MDK Protocol**, a common set of messages that every device in the system
understands, regardless of manufacturer or model. Adding a new device type never impacts `@tetherto/mdk-ork` thanks to the Worker, a device-specific translator that
sits between the kernel and your hardware: it speaks the MDK Protocol upward, and the device's native API downward.
The kernel is **pull-only**, **device-agnostic**, and **self-healing**.
Learn more about the [internal modules, recovery flows, and protocol specs](/v0-0-2/overview/architecture/ork) that back those
guarantees.
### The universal SDK
`@tetherto/mdk-client` is the universal SDK, a connection library that applications use to talk to `@tetherto/mdk-ork`. It serves as a universal adapter: handling all the
connection details so developers can focus on building their application.
- **Multi-language support**: available for Node.js, Python, Go, and more; use whatever language your team prefers
- **Automatic connection handling**: manages reconnection, retries, and transport selection behind the scenes
- **No lock-in**: developers bring their own stack and connect via the SDK. No framework requirements.
### MDK App Toolkit
For teams that want to ship fast, the [**MDK App Toolkit**](/v0-0-2/overview/architecture/app-toolkit) is the optional, batteries-included application layer
that sits on top of `@tetherto/mdk-ork`. It ships in three parts:
- **Frontend tools**: a headless state brain ([`@tetherto/mdk-ui-core`](/v0-0-2/reference/app-toolkit/ui-core)), framework adapters ([`@tetherto/mdk-react-adapter`](/v0-0-2/ui/react/get-started) for React today), and a production-tested React UI Kit ([`@tetherto/mdk-react-devkit`](/v0-0-2/ui/react/get-started)) for dashboards.
- **Backend tools**: a plug-and-play library that drops into Fastify or Express to handle JWT auth, RBAC, and command
proxying, with hooks for custom routes and aggregations.
- **Plugins**: drop-in modules that pair a frontend tools widget with a backend tools route, so third parties can ship
whole features without forking the App Node.
The toolkit is fully optional: teams can write directly against `@tetherto/mdk-client` instead. See
[MDK App Toolkit](/v0-0-2/overview/architecture/app-toolkit) for the full breakdown.
## Who MDK is for
MDK is built for everyone involved in mining Bitcoin:
- **Mining operators**: monitor and control fleets with real-time dashboards. Get fleet-wide summaries (total hashrate, power usage, temperature alerts)
across all your sites.
- **Hardware manufacturers**: integrate new devices by building a Worker and writing one
[`mdk-contract.json`](/v0-0-2/overview/architecture#responsibilities-and-the-capability-contract). No involvement from MDK maintainers needed.
- **Software developers**: build custom mining applications in any language, or leverage the [MDK App Toolkit](/v0-0-2/overview/architecture/app-toolkit)'s frontend and backend tools for rapid development.
- **AI/Automation teams**: [connect intelligent agents](#ai-ready-with-unified-intelligence) that can monitor, diagnose, and act on device issues autonomously
## Architecture overview
`@tetherto/mdk-ork` is [the kernel](/v0-0-2/overview/architecture/ork). Above it, you build dashboards and
business logic; AI agents drive the fleet through an **MCP endpoint** MDK provides on the App
Node. **The App Node** is your secure gateway above the kernel: all
[user authentication](/v0-0-2/overview/architecture#authentication),
[role-based access control (RBAC)](/v0-0-2/overview/architecture#responsibilities), and fleet-wide aggregation
happen there, keeping `@tetherto/mdk-ork` secure and focused on orchestration. Within the App Node you have two build paths: write custom business
logic directly using `@tetherto/mdk-client`, or adopt the [MDK App Toolkit](/v0-0-2/overview/architecture/app-toolkit), which ships
**frontend tools** for the dashboard and **backend tools** for custom routes.
Below the kernel, **devices are the source of truth**. The actual hardware state is reported by
the Worker to `@tetherto/mdk-ork`, which orchestrates a synchronized view across the fleet.
For the full layer-by-layer view with transports and discovery flows, see the [MDK stack](/v0-0-2/overview/architecture#mdk-stack) on the Architecture page.
## AI-ready with unified intelligence
MDK is designed from the ground up for [AI-driven operations](/v0-0-2/overview/architecture#ai-agents-and-the-mcp-server). Rather than bolting AI on as an afterthought, intelligence is woven directly into the
device definition itself.
In addition to the technical schemas, every device's contract file ([`mdk-contract.json`](/v0-0-2/overview/architecture#responsibilities-and-the-capability-contract)) contains:
- **Safety rules**: for example, "Outlet temperature > 85Β°C requires immediate intervention"
- **Operational constraints**: limits on command frequency, power thresholds, cooling requirements
- **Troubleshooting guides**: if/then recovery steps that AI agents can follow autonomously
This means an AI agent connecting to MDK doesn't need a separate knowledge base or custom prompts per device. The intelligence travels with the device;
the same contract that validates commands and generates dashboards also determines how AI reasons about that hardware safely.
## What you can build
- Operational dashboards (hashrate, power, temperature)
- Multisite fleet management with centralized oversight
- Alerts and notifications for critical device events
- Overheating detection and automated remediation
- AI-driven autonomous monitoring and control
- Custom analytics and reporting pipelines
- White-labeled hosted mining platforms
- Third-party device integrations and plugins
## Scaling
MDK [scales](/v0-0-2/overview/architecture#scaling) naturally without architectural changes:
- **More devices?** Add more Workers. Each Worker owns a specific set of devices, and `@tetherto/mdk-ork` routes commands to the right one automatically.
- **More sites?** Each physical site runs its own `@tetherto/mdk-ork` instance. A single App Node connects to all of them, giving you one view across your entire operation.
- **Site isolation**: `@tetherto/mdk-ork` instances are fully independent. A problem at one site has zero impact on any other.
{/* todo: state/link to licence ?Apache 2.0? this is released under "The entire MDK stack is released under the Apache 2.0 license" */}
## Next steps
- [Get started](/v0-0-2/get-started)
Learn more about:
- [Architecture](/v0-0-2/overview/architecture)
- [MDK App Toolkit](/v0-0-2/overview/architecture/app-toolkit)
- [Connecting intelligent agents](/v0-0-2/overview/architecture#ai-agents-and-the-mcp-server)
# Architecture (/v0-0-2/overview/architecture)
Status: π§ MDK is in active development. This page describes the target architecture and may evolve as real-world implementations land.
## How MDK works
MDK is built around a small kernel with one job: route validated commands to whichever Worker owns a device, and pull telemetry
back. Everything else (authentication, business logic, UI, AI agents) sits outside the kernel as composable layers: keeping the kernel
small and the application surface open.
To prevent unbound flexibility from manifesting as system rigidity, the architecture draws a hard line between what is
standardized and what is delegated. It's:
- **Opinionated where needed**: strict transport envelopes, unified JSON schema, unidirectional flows
- **Flexible where it matters**: isolated Workers handle translation logic, enabling integrations without polluting the core
infrastructure
Five layers compose the stack, with strict, unidirectional flows between them. The kernel itself is **ORK**, the
Orchestration Kernel, distributed as `@tetherto/mdk-ork`.
## MDK stack
```mermaid
graph TB
subgraph consumers ["Layer 1: Consumers"]
UI["UI / Frontend"]
AI["AI Agent"]
end
subgraph appNode ["Layer 2: App Node"]
WebApp["HTTP / API Router"]
MCPServer["MCP Server Endpoint"]
end
subgraph orkKernel ["Layer 3: @tetherto/mdk-ork"]
ORK["ORK β’ Command routing β’ Health monitoring β’ Device registry β’ Telemetry collection"]
end
subgraph workers ["Layer 4: Workers"]
Workers["WORKERS"]
end
subgraph devices ["Layer 5: Physical Devices"]
Devices["Physical devices β’ Miners β’ Containers β’ Sensors"]
end
UI -->|"HTTP / WebSocket"| WebApp
AI -->|"MCP Protocol"| MCPServer
WebApp -->|"MDK Protocol via @tetherto/mdk-client / HRPC"| ORK
MCPServer -->|"MDK Protocol via @tetherto/mdk-client / HRPC"| ORK
Workers -.->|"join known DHT topic"| ORK
ORK -->|"MDK Protocol: pull (identity / schema / telemetry) + command"| Workers
Workers -->|"device libs"| Devices
style consumers fill:#F7931A,stroke:#1A1A1A,color:#1A1A1A
style appNode fill:#F7931A,stroke:#1A1A1A,color:#1A1A1A
style orkKernel fill:#F7931A,stroke:#1A1A1A,color:#1A1A1A
style workers fill:#F7931A,stroke:#1A1A1A,color:#1A1A1A
style devices fill:#F7931A,stroke:#1A1A1A,color:#1A1A1A
```
The MDK components that compose those layers:
| Component | What it does |
|---|---|
| [`@tetherto/mdk-ork`](#the-ork-kernel) | Central coordination: routes commands, collects telemetry, monitors health |
| [`@tetherto/mdk-client`](#the-sdk) | Universal SDK applications use to talk to `@tetherto/mdk-ork` |
| [MDK Protocol](#the-mdk-protocol) | Standardized message envelope every layer speaks |
| [MDK App Toolkit](/v0-0-2/overview/architecture/app-toolkit) | Optional frontend tools, backend tools, and plugins on top of `@tetherto/mdk-ork` |
## Storage
[Hypercore](https://github.com/holepunchto/hypercore)-backed stores (such as
[Hyperbee](https://github.com/holepunchto/hyperbee)) are recommended across the `@tetherto/mdk-ork`, Worker, and App Node layers.
This choice satisfies all storage requirements without the operational baggage of a centralized database.
## The MDK Protocol
The MDK Protocol is the contract that crosses every layer of the stack. Version 0.0.1 is pull-based: Workers passively join a
known DHT topic and `@tetherto/mdk-ork` initiates every RPC call. There are no Worker-to-kernel callbacks, no fan-out events, and no
exceptions to the direction of flow. For the full envelope schema, action catalogue, and base command set, see the
[Protocol reference](/v0-0-2/reference/protocol/messages).
### Design principles
- **Transport-agnostic**: identical messages over in-process calls, Holepunch RPC (HRPC), or API calls
- **Strictly unidirectional**: Workers never initiate RPC calls to `@tetherto/mdk-ork`. They join a known Distributed Hash Table
(DHT) topic; `@tetherto/mdk-ork` discovers their presence passively and initiates all subsequent communication downwards (identity,
capabilities, telemetry, commands).
- **Generic interface**: the accepted interface is defined dynamically at the Worker level via a self-describing capabilities
schema containing both structure and semantic context for AI agents
### Governance
To maintain structural integrity and contract stability across `@tetherto/mdk-ork`, App Node, and Workers, MDK Protocol messages are
governed and strictly validated using [Hyperschema](https://github.com/holepunchto/hyperschema). Hyperschema also aligns
natively with the system's underlying Hyperbee storage.
### Discovery, telemetry, and command flows
```mermaid
sequenceDiagram
participant W as Worker
participant DHT as DHT Topic (Hyperswarm)
participant O as @tetherto/mdk-ork
participant G as Gateway (App Node / MCP)
Note over W,O: Worker discovery and registration
W->>DHT: Joins known topic
O-->>DHT: Detects new peer connection
O->>W: identity.request
W-->>O: identity.response (devices)
O->>O: Save Worker to Registry
O->>W: capability.request
W-->>O: capability.response (schema)
Note over O,W: Telemetry pull loop
O->>W: telemetry.pull
W-->>O: metrics and pending commands
Note over G,W: Command execution
G->>O: MDK Protocol HRPC envelope
O->>W: command.request (routed by deviceId)
W-->>O: command.result
O-->>G: result
```
## The ORK kernel
[`@tetherto/mdk-ork`](/v0-0-2/overview/architecture/ork) is the trusted coordination layer at the heart of MDK. It routes commands, monitors device health, registers
Workers, and pulls telemetry β all on a pull-only model, so the kernel cannot be overwhelmed by upstream pressure.
## Workers
Workers wrap a device library and expose it via the MDK Protocol. They are the integration handlers between physical hardware
and `@tetherto/mdk-ork`, and the unyielding source of truth for that hardware: `@tetherto/mdk-ork` itself operates purely as a synchronised state
machine over Worker-reported state.
### Discovery model
1. **DHT presence**: the Worker joins a known [Hyperswarm](https://github.com/holepunchto/hyperswarm) DHT topic. It does not
send any RPC messages; it becomes a reachable peer.
2. **Peer detection**: `@tetherto/mdk-ork` continuously listens on the same DHT topic and detects the new peer connection automatically.
3. **Identity request**: `@tetherto/mdk-ork` initiates the first RPC call, requesting the Worker's identity and managed devices.
4. **Registration**: after receiving the identity, `@tetherto/mdk-ork` saves the Worker and its RPC keys into its registry.
5. **Capability declaration**: `@tetherto/mdk-ork` then explicitly queries the Worker to declare its full capabilities
(`mdk-contract.json`).
Communication initiation is **strictly unidirectional**: `@tetherto/mdk-ork` initiates every RPC call; Workers only ever respond.
### Responsibilities and the capability contract
`mdk-contract.json` is the canonical source of truth for a Worker's programmatic capabilities **and** its AI context. MDK
deliberately merges formal validation and semantic guidance into a single JSON contract:
- `description` does double duty as the human UI label and AI edge-case rule (for example, *"Outlet temperature > 85C requires
intervention"*)
- `constraints` governs orchestration limits
- `troubleshooting` provides if/then recovery behaviours alongside the payload it evaluates
The exhaustive JSON Schema is `mdk-contract.schema.json`, with a reference instance at `mdk-contract.json`.
{/* todo: when the upstream MDK contract schema stabilizes and lands on mdk-be main, host it in /reference as a first-class
API document and link from the prose above. Until then, the canonical sources are tracked upstream (out-of-tree of this
repo). At the time of this page's import the working sources lived on a feature branch:
- mdk-contract.schema.json: https://github.com/ankit-tether/mdk-be/blob/hld/docs/mdk-contract.schema.json
- mdk-contract.json: https://github.com/ankit-tether/mdk-be/blob/hld/docs/mdk-contract.json
Do NOT surface those branch links in user-facing prose. */}
### Adding new hardware
External integrators add new hardware by building a Worker package that conforms to the strict Device-Lib Contract:
1. Reference `mdk-contract.schema.json` to author the `mdk-contract.json`, validating strict data schemas while injecting
explanations, constraints, and troubleshooting directly into the relevant nodes.
2. Subclass `@tetherto/worker-base` and implement the two translation hooks, `onTelemetryPull` and `onCommand`, in `src/hardware.js`.
All HRPC plumbing is inherited from the base class.
3. Boot the Worker instance, connect to devices, and join the known DHT topic. `@tetherto/mdk-ork` detects the peer and pulls its
identity and capabilities.
## The SDK
The `@tetherto/mdk-client` SDK is the transport abstraction layer used to connect to `@tetherto/mdk-ork` safely and reliably. It is the essential glue
between the kernel and any consumer layer developers choose to build on top.
**Responsibility**: connects the MDK Protocol over native transports (HRPC or IPC) seamlessly, offering:
- **Transport abstraction**: handles MDK Protocol message construction and reconnection logic with exponential backoff.
- **Automatic transport selection**: the SDK picks the transport mechanism based entirely on the URL scheme provided by the
developer.
- `hrpc://` connects over encrypted Hyperswarm streams for remote server-to-server production.
- `ipc://` connects via direct local sockets for extremely low-latency local testing.
- **Major language support**: `@tetherto/mdk-client` will be built for all major languages (Node.js, Python, Go, and others), allowing
developers to dispatch commands, subscribe to live streams, or pull status snapshots from any stack.
## App Node
The App Node is the developer-owned API boundary, the mandatory gateway between the client-facing world and `@tetherto/mdk-ork`.
The UI never connects to `@tetherto/mdk-ork` directly; an App Node must act as the gateway. Developers have two paths:
- **Direct**: write business logic, aggregation routes, and authentication directly in the App Node using `@tetherto/mdk-client` in any
language (Node.js, Go, Python, and others).
- **Toolkit**: adopt the [MDK App Toolkit](/v0-0-2/overview/architecture/app-toolkit), which ships backend tools middleware (drops into Fastify or Express,
handles JWT auth, RBAC, and command proxying), frontend tools, and a plugins shell for plug-and-play extensions.
Both approaches are fully supported; the choice depends on the team's preference for control versus convention.
### Responsibilities
- **Fleet aggregation**: computes site hashrate, average temperature, cross-rack efficiency
- **Auth and RBAC**: guards all access to `@tetherto/mdk-ork` with JWTs and session management
- **API surface**: exposes REST or GraphQL to the UI
### Routing contract
UI and AI agents should only provide `deviceId`; the App Node (via `@tetherto/mdk-client`) passes this down. `@tetherto/mdk-ork` resolves the
owning Worker internally and dispatches the `command.request`.
### Authentication
The App Node validates a JWT (Bearer Token) (signature, expiry, claims) before proxying any traffic into `@tetherto/mdk-ork`.
**Allowlisting at the kernel**: `@tetherto/mdk-ork` does not perform user-level authentication. Instead, it maintains a strict allowlist
of approved App Node and client connections. Once allowlisted securely (for example, via HRPC keys), `@tetherto/mdk-ork` implicitly
trusts the origin of HRPC messages.
## AI agents and the MCP Server
AI agents connect to MDK through an **MCP endpoint** on the App Node, not directly to `@tetherto/mdk-ork`. They are treated as ordinary
authenticated clients and subject to the same JWT validation, rate limits, and RBAC as a human user. This is intentional: the
kernel does not perform user-level [authentication](#authentication), so routing agents through the App Node keeps them inside
the same security envelope as every other consumer.
What makes the integration distinctive is **runtime tool derivation**. The tools exposed to an agent (for example,
`get_device_telemetry` or `reboot_device`) are not hardcoded; they are parsed at runtime from each registered Worker's
[`mdk-contract.json`](#responsibilities-and-the-capability-contract). When a new device type joins the network, the agent gains
the ability to query and control it without any change to the App Node.
## End-to-end data flows
Two scenarios show the full request path from consumer to device and back: a [human user clicking through the UI](#human-ui-scenario), and an [AI
agent executing a multi-step prompt](#ai-agent-scenario).
### AI agent scenario
A user instructs the AI Agent: *"Keep the fleet healthy."* The agent monitors continuously, catches `wm002` overheating, reboots it, and notifies the user.
```mermaid
sequenceDiagram
actor User
participant AI as AI Agent
participant Node as App Node (MCP)
participant ORK as @tetherto/mdk-ork
participant Worker as Generic Worker
User->>AI: "Keep the fleet healthy."
Note over AI,ORK: Step 1: Fleet discovery (read)
AI->>Node: Call MCP tool get_fleet_alerts (token auth)
Node->>Node: Validate agent token and RBAC
Node->>ORK: HRPC query (via @tetherto/mdk-client)
ORK-->>Node: Metrics
Node-->>AI: Tool result (wm002 is overheating)
Note over AI,ORK: Step 2: Execution (write)
AI->>Node: Call MCP tool reboot_device (deviceId wm002)
Node->>Node: Validate token and device:write RBAC
Node->>ORK: dispatch generic protocol message
ORK->>ORK: Resolve deviceId
ORK->>Worker: command.request (HRPC)
Worker-->>ORK: command.result
ORK-->>Node: result OK
Node-->>AI: Tool result (Success)
AI-->>User: "wm002 was overheating and has been rebooted."
```
### Human UI scenario
A user clicks "Reboot" on device `wm001` in the UI.
```mermaid
sequenceDiagram
actor User
participant UI as React UI
participant Node as App Node
participant ORK as @tetherto/mdk-ork
participant Worker as Generic Worker
User->>UI: Click "Reboot" on wm001
UI->>Node: POST { deviceId, action, payload }
Note over Node,ORK: Delegation
Node->>ORK: dispatch generic protocol message
ORK->>ORK: Verify against capabilities
ORK->>ORK: Resolve Worker for deviceId
Note over ORK,Worker: Execution
ORK->>Worker: command.request (HRPC)
Worker-->>ORK: Ack start
Worker->>Worker: Hardware-specific translation
Worker-->>ORK: command.result
ORK-->>Node: result OK
Node-->>UI: HTTP 200
Note over Worker,ORK: State reflection
ORK->>Worker: telemetry.pull (tick)
Worker-->>ORK: Updated status (rebooting)
```
## Scaling
As MDK deployments scale to large mining sites (5,000+ devices), the system must explicitly manage parallel Workers and parallel
`@tetherto/mdk-ork` instances. The kernel is strictly an execution layer; it does not perform application-level aggregation or
cross-regional business logic.
### Parallel Workers
Multiple Workers of the same type (for example, `whatsminer-worker`) can be active concurrently and connected to the same
`@tetherto/mdk-ork` kernel.
```mermaid
flowchart TD
subgraph kernel ["Single @tetherto/mdk-ork kernel"]
ORK["ORK"]
end
W1["Worker 1"]
W2["Worker 2"]
D1["Devices wm001 to wm500"]
D2["Devices wm501 to wm999"]
ORK -->|Routes commands| W1
ORK -->|Routes commands| W2
W1 --- D1
W2 --- D2
style kernel fill:#F7931A,stroke:#1A1A1A,color:#1A1A1A
```
**Device-level routing and ownership**: Workers never share devices. When a Worker connects, its `identity.register` payload
explicitly lists the `deviceId`s it exclusively manages. The Worker Registry maintains this strict mapping and deterministically
routes arriving commands to the designated Worker.
### Multi-site deployments
A deployment may need to manage multiple massive physical boundaries (for example, a Texas Site and an Iceland Site). Each
location runs its own dedicated site-level `@tetherto/mdk-ork` kernel, but all are overseen globally by a single App Node and AI Agent.
```mermaid
flowchart TD
Global["Global App Node / AI Agent"]
subgraph texas ["Texas site"]
ORK_TX["@tetherto/mdk-ork"]
W1_TX["Whatsminer Worker"]
W2_TX["Antminer Worker"]
D1_TX["Whatsminers"]
D2_TX["Antminers"]
ORK_TX -->|Routes| W1_TX
ORK_TX -->|Routes| W2_TX
W1_TX --- D1_TX
W2_TX --- D2_TX
end
subgraph iceland ["Iceland site"]
ORK_IC["@tetherto/mdk-ork"]
W1_IC["Whatsminer Worker"]
W2_IC["Avalon Worker"]
D1_IC["Whatsminers"]
D2_IC["Avalons"]
ORK_IC -->|Routes| W1_IC
ORK_IC -->|Routes| W2_IC
W1_IC --- D1_IC
W2_IC --- D2_IC
end
Global <-->|MDK Protocol via HRPC| ORK_TX
Global <-->|MDK Protocol via HRPC| ORK_IC
style texas fill:#F7931A,stroke:#1A1A1A,color:#1A1A1A
style iceland fill:#F7931A,stroke:#1A1A1A,color:#1A1A1A
```
The single App Node and AI Agent connect globally to all distributed `@tetherto/mdk-ork` kernels via the native HRPC mesh (Hyperswarm).
Parallel `@tetherto/mdk-ork` instances remain entirely isolated from one another: they do not federate registries, share queues, or
synchronize state. A crash at one site has zero impact on any other.
Cross-site aggregation is handled purely at the App Node layer, where routes query multiple Workers via `@tetherto/mdk-ork` and merge
the responses before returning them to the UI or Agent.
## Next steps
Learn more about:
- [About MDK](/v0-0-2/overview/about)
- [MDK App Toolkit](/v0-0-2/overview/architecture/app-toolkit)
- [ORK](/v0-0-2/overview/architecture/ork)
- [Get started](/v0-0-2/get-started)
# MDK App Toolkit (/v0-0-2/overview/architecture/app-toolkit)
Status: π§ The MDK App Toolkit is under active development. The v0.0.2 release ships the [React UI Kit](#react-ui-kit).
## Introducing the MDK App Toolkit
The MDK App Toolkit is an [open-source](/v0-0-2/community/contributing#licensing), reusable package for building applications on top of
[`@tetherto/mdk-ork`](/v0-0-2/overview/architecture/ork).
The App Toolkit ships in three parts:
1. [Frontend tools](#frontend-tools).
2. [Backend tools](#backend-tools).
3. [Plugins](#plugins).
While `@tetherto/mdk-ork` is entirely unopinionated, the App Toolkit provides a batteries-included application layer. It plugs into existing stacks
(leveraging the low-level `@tetherto/mdk-client` for `@tetherto/mdk-ork` connectivity) so teams can ship operator-ready dashboards and custom domain
logic without rebuilding the App Node from scratch.
## The problem
Building an application on top of hardware infrastructure historically forces developers to face four friction points across the full stack:
1. **Frontend repeated logic**: every frontend developer integrating with backend APIs ends up reinventing solutions to the same plumbing challenges:
- Throttling fast-moving telemetry streams
- Managing optimistic UI state transitions
- Detecting silent communication timeouts
2. **Backend repeated logic**: every backend developer ends up writing the same App Node plumbing (JWT auth, RBAC, command proxying, and fleet
aggregation) before they can ship any custom business logic.
3. **The UI rigidity trap**: platforms try to solve the repeated logic problem by shipping a UI component library. UI is inherently subjective; when external
developers are forced to use generic components, they get locked out of customizing the CSS to match their brand, leading to abandoned toolkits and identical-looking dashboards.
4. **The extension bottleneck**: when an external manufacturer builds a brand new miner or device, there is no clear path for injecting a custom dashboard
widget and custom aggregator into an existing deployment.
## The solution
The App Toolkit decouples logic from styling, separates frontend and backend concerns, and provides an explicit plug-and-play extension architecture:
1. It extracts complex API state and caching logic, such as handling server disconnects and buffering data from the App Node gateway, into a purely
headless frontend layer (`@tetherto/mdk-react-devkit/core`).
2. It ships a reusable App Node library that drops into Fastify or Express, handling JWT auth, RBAC, and `@tetherto/mdk-ork` proxying out of the box, while exposing
hooks for developers to plug in their own routes and aggregations.
3. It embraces the *shadcn/ui* pattern by providing reference UI components that developers copy and paste, giving them full control over CSS and layout
while still leveraging the underlying data hooks.
π§ Components will soon be available via npm.
4. It provides the MDK-App plugin architecture: an out-of-the-box, extensible shell framework where third-party frontend widgets and backend routes can
be injected dynamically at runtime as a single drop-in package.
## The scope
While MDK targets Bitcoin mining, the App Toolkit's architecture is deliberately domain-agnostic. Anywhere there is a fleet of devices reporting telemetry,
with operators that need real-time visibility and control, the same patterns apply: IoT sensor networks, industrial telemetry, energy grid monitoring,
autonomous vehicle fleets, and AI-agent control planes are all natural fits.
The portable layers are everything above the protocol boundary: the headless state machine handles buffering, optimistic UI, and stale detection regardless
of what telemetry it receives; the framework adapters translate that state into reactive lifecycles for any React, Vue, or Svelte app; the reference UI
primitives and the plug-and-play shell are generic UI building blocks; the App Node middleware library is generic Fastify/Express; and the plugin pattern,
register a backend route alongside a widget that consumes it, works for any backend with the same shape.
The MDK-specific glue lives below: `@tetherto/mdk-client` speaks the MDK Protocol to `@tetherto/mdk-ork`, the App Node middleware proxies commands over Holepunch RPC,
and `@tetherto/mdk-react-devkit/foundation` adds mining-domain components such as vendor container UIs and the operations centre. Swap that protocol layer
and the App Toolkit pattern carries to any domain with the same shape.
## Frontend tools
Frontend tools are the building blocks for dashboards on top of `@tetherto/mdk-ork`. They decompose into three layers:
**[UI Core](/v0-0-2/reference/app-toolkit/ui-core)** (headless state), **framework adapters** (e.g., React bindings), and **UI Kit**
(styled React components), so business logic is shared while styling stays under developer control.
### UI Core
The UI Core ([`@tetherto/mdk-ui-core`](/v0-0-2/reference/app-toolkit/ui-core)) is the framework-agnostic headless layer. Headless: the same store
logic can back React, Vue, Svelte, or plain JS utilities. Framework adapters bind `@tetherto/mdk-ui-core` into reactive lifecycles for each UI runtime.
Headless primitives [will land in this package](/v0-0-2/reference/app-toolkit/ui-core#whats-not-here-yet).
### Framework adapters
`@tetherto/mdk-ui-core` stores do not trigger UI re-renders on their own. Framework adapters subscribe to those stores and expose reactive hooks.
For React, [`@tetherto/mdk-react-adapter`](/v0-0-2/ui/react/get-started) provides ``, store hooks (`useAuth`, `useDevices`, `useActions`, and others), and TanStack Query re-exports. Future adapters (`@tetherto/mdk-vue`, `@tetherto/mdk-svelte`, `@tetherto/wc`) will follow the same pattern.
### UI Kit
The toolkit optionally ships styled reference components (for example, ``). These follow the *shadcn/ui* pattern:
they are copy-pasted into the developer's source tree. Developers own the styling completely.
#### React UI Kit
For React, [`@tetherto/mdk-react-devkit`](/v0-0-2/ui/react/get-started) is a production-tested UI Kit available as a ready-made implementation
of this layer. Highlights:
- 100+ production-tested components
- Built on React 19 and *shadcn/ui*
- Zero CSS-in-JS runtime overhead
π§ UI Kits for other frameworks (Vue, Svelte, and so on) will follow the same pattern and ship alongside their framework adapters.
### Developer entry points
The toolkit can be adopted at any of the following entry points, from most batteries-included to least.
| Entry point | Package | What ships | What you write | When to choose |
|---|---|---|---|---|
| UI Kit | `@tetherto/mdk-react-devkit` (`/core` + `/foundation` entrypoints) | Pre-built React components, shell layout, ready-made ops dashboard | Data wiring, optional theming | You want a dashboard up fast |
| Framework adapter | `@tetherto/mdk-react-adapter` (React today; Vue/Svelte/WC planned) | ``, store hooks, TanStack Query re-exports | Your own components and layout | You have a design system already |
| UI Core | [`@tetherto/mdk-ui-core`](/v0-0-2/reference/app-toolkit/ui-core) | Zustand vanilla stores, `QueryClient` factory | Framework bindings or headless utilities | You need store access outside React or are building a new adapter |
| Raw SDK | `@tetherto/mdk-client` | MDK Protocol client, connection management, reconnection | Everything above the wire: state, framework, UI | You are building a non-UI consumer (CLI, agent, backend service) |
## Backend tools
Backend tools are a library for implementing custom business logic on top of `@tetherto/mdk-ork`, loosely coupled to the App Node and completely plug-and-play.
Developers do not have to write the App Node gateway from scratch: the library drops directly into Fastify or Express, ships the boilerplate every operator
needs, and exposes hooks for the custom routes and aggregations that make each deployment unique.
### Drop-in App Node middleware
Pre-built middleware that wires up the standard App Node responsibilities so developers do not reinvent them:
- Exposing standard `/auth` endpoints
- Validating incoming user JWTs
- Proxying commands securely down to `@tetherto/mdk-ork` over Holepunch RPC (HRPC) using `@tetherto/mdk-client`
### Route extension API
Hooks allowing developers to bind new REST or WebSocket endpoints (for example, `POST /mining/stats`) that perform complex aggregations using `@tetherto/mdk-ork`
capabilities via `@tetherto/mdk-client`. Custom routes live in a developer-owned package and snap into the middleware without forking it.
## Plugins
A plugin pairs a frontend tools widget with a backend tools route as a single drop-in module. For developers looking for an out-of-the-box solution,
the toolkit pairs the frontend and backend halves into the MDK-App plugin architecture: an extensible, multi-tenant shell.
### The prebuilt shell
Instead of building a custom React layout and a custom Fastify server, developers spin up the MDK UI Shell and MDK Generic App Node. These are ready-to-use
binaries fully wired together.
### Writing a plugin
External developers write plugins consisting of two tightly coupled pieces of code that register dynamically into the shell at runtime:
1. **MDK-App server**: a package of business logic that registers custom backend routes (for example, `/mining/stats`) into the backend tools middleware hooks.
2. **MDK-App widget**: a custom frontend component that mounts into the MDK UI Shell's grid layout and natively queries `/mining/stats`.
**Plug-and-play reusability**: this explicit convention ensures that an external company can build a completely new dashboard widget and backend aggregator
for a new device type, publish it as a single npm package, and allow any user to drop it into their existing MDK deployment without modifying upstream source code.
## Architecture overview
```mermaid
flowchart TD
subgraph frontend ["frontend tools (browser)"]
direction TB
UI_CORE["@tetherto/mdk-ui-core Zustand stores Β· QueryClient factory"]
FRAMEWORKS["Framework adapters (@tetherto/mdk-react-adapter, β¦)"]
UI_COMPS["UI Kit (@tetherto/mdk-react-devkit)"]
UI_COMPS -->|consumes adapter hooks| FRAMEWORKS
FRAMEWORKS -->|binds headless stores| UI_CORE
end
subgraph backend ["backend tools (App Node)"]
direction TB
ROUTER["App Node middleware (Express / Fastify, JWT auth, route extension API)"]
CLIENT["@tetherto/mdk-client isolated native SDK"]
ROUTER -->|translates REST to HRPC via| CLIENT
end
UI_CORE <-->|HTTP| ROUTER
CLIENT -->|MDK Protocol| ORK["@tetherto/mdk-ork"]
style frontend fill:#F7931A,stroke:#1A1A1A,color:#1A1A1A
style backend fill:#F7931A,stroke:#1A1A1A,color:#1A1A1A
```
## Next steps
Learn more:
- [About MDK](/v0-0-2/overview/about)
- [Architecture: the orchestration kernel](..)
- [Connecting intelligent agents](../index#ai-agents-and-the-mcp-server)
- [See the license](/v0-0-2/community/contributing#licensing)
# ORK (/v0-0-2/overview/architecture/ork)
`@tetherto/mdk-ork` is the trusted coordination layer of the stack. It splits internal responsibilities across single-purpose modules with
their own state machines, so domains can evolve independently without coupling to each other.
The design is inspired by Kubernetes: a pull-only model bounds the pace of execution so the kernel cannot be overwhelmed by
upstream pressure.
## Module overview
```mermaid
flowchart TD
MCP["MCP Handler"] -->|Validates and routes| CD["Command Dispatcher"]
CD -->|Enqueues| CSM["Command State Machine"]
CSM <-->|HRPC req / res| W["Worker"]
SCH["Scheduler"] -->|Triggers state pull| CSM
CSM -->|state.pull| W
WR["Worker Registry"] -->|Routing lookup| CD
HM["Health Monitor"] -->|Updates status| WR
SCH -->|Triggers interval| HM
SCH -->|Triggers pull| TC["Telemetry Collector"]
TC -->|Pulls metrics| W
```
## Module catalogue
`@tetherto/mdk-ork`'s coordination splits across single-purpose modules. Each owns its own state machine, persistence boundary,
and scaling characteristics. Six modules ship in v0.0.1; two more are deferred to a later release.
| Module | Role |
|---|---|
| [Command Dispatcher](/v0-0-2/reference/ork/modules#command-dispatcher) | Validates and resolves the destination Worker for incoming commands. |
| [Command State Machine](/v0-0-2/reference/ork/modules#command-state-machine) | Tracks command lifecycle from `QUEUED` to `SUCCESS` or `FAILED`. |
| [Worker Registry](/v0-0-2/reference/ork/modules#worker-registry) | Authoritative lookup of active Workers, their RPC keys, and managed devices. |
| [Telemetry Collector](/v0-0-2/reference/ork/modules#telemetry-collector) | Stateless proxy between callers and Worker-local telemetry stores. |
| [Scheduler](/v0-0-2/reference/ork/modules#scheduler) | System metronome; drives all interval-based pulls (telemetry, state, health). |
| [Health Monitor](/v0-0-2/reference/ork/modules#health-monitor) | Liveness probes against Workers; reports status to the Registry. |
| [Fault Supervisor](/v0-0-2/reference/ork/modules#fault-supervisor) (deferred) | Circuit-breaker patterns to contain cascading failures. |
| [Concurrency Manager](/v0-0-2/reference/ork/modules#concurrency-manager) (deferred) | Per-device locking and queue-depth limits. |
For the full state machines, transition rules, interface signatures, and recovery details, see the
[ORK modules reference](/v0-0-2/reference/ork/modules).
## System recovery
On a full system crash and restart, `@tetherto/mdk-ork` modules orchestrate recovery without user intervention:
1. **Worker Registry** loads last known Worker and device states from Hyperbee.
2. **Command State Machine** sweeps the WAL for stranded `EXECUTING` tasks and forces them to timeout or retry.
3. **Health Monitor** begins firing immediate pings to verify which Workers are still active.
4. **Connections**: the network layer awaits incoming HRPC reconnect storms from persistent Workers.
Recovery is local and predictable. Worker crashes do not bring down the runtime; supervisors (PM2, Docker, Kubernetes) handle
process restarts in multi-process deployments, and Workers rejoin the system after recovery.
## Next steps
- [Architecture](../index) β how ORK fits the broader MDK stack
- [ORK modules reference](/v0-0-2/reference/ork/modules) β per-module state machines, interfaces, and transition rules
# Reference (/v0-0-2/reference)
The Reference section indexes the canonical specs for everything MDK exposes: field semantics, signatures, transition
rules, and contracts. Reach for it when you need exact shapes. For narrative explanations, see [Architecture](/v0-0-2/overview/architecture);
for step-by-step instructions, see [Get started](/v0-0-2/get-started).
## Browse by stack area
### App Toolkit
- **[UI Kit](/v0-0-2/reference/app-toolkit/ui-kit)**: constants, hooks, types, and utilities for the React UI Kit.
- *backend tools*: coming soon.
### ORK
- **[Modules](/v0-0-2/reference/ork/modules)**: kernel module specs, state machines, transition tables, and recovery behavior.
### MDK Protocol
- **[Messages](/v0-0-2/reference/protocol/messages)**: envelope schema, request/response examples, action catalogue, and base command set.
- *Capability contract*: coming soon.
# Hooks (/v0-0-2/reference/app-toolkit/hooks)
Reusable React hooks shipped by the MDK App Toolkit. Grouped by what each hook depends on, not by which package ships it. This matches the
[Developer entry points](/v0-0-2/overview/architecture/app-toolkit#developer-entry-points) model where UI Core, the React adapter, and the
UI Kit are siblings β you should be able to find the hooks you need without adopting layers you do not use.
## At a glance
| Bucket | Page | What it covers | Needs |
|---|---|---|---|
| State | [State hooks](/v0-0-2/reference/app-toolkit/hooks/state) | React-bound views of the headless `@tetherto/mdk-ui-core` Zustand stores | `` from `@tetherto/mdk-react-adapter` |
| Components | [Component hooks](/v0-0-2/reference/app-toolkit/hooks/components) | Hooks coupled to MDK styled components or shell layout (notifications, forms, charts, dashboards, filters, widgets, tables) | `@tetherto/mdk-react-devkit` and (for some) `` |
| Utilities | [Utility hooks](/v0-0-2/reference/app-toolkit/hooks/utilities) | Generic React helpers, mining-domain transforms, permission checks, and TanStack Query re-exports | `@tetherto/mdk-react-adapter` and (for some) `` |
## All hooks
### State
@tetherto/mdk-react-adapter
| Hook | Summary |
|---|---|
| [`useAuth`](/v0-0-2/reference/app-toolkit/hooks/state#useauth) | React view of the headless `authStore` (token, permissions) |
| [`useDevices`](/v0-0-2/reference/app-toolkit/hooks/state#usedevices) | React view of the headless `devicesStore` (device list, selection) |
| [`useTimezone`](/v0-0-2/reference/app-toolkit/hooks/state#usetimezone) | React view of the headless `timezoneStore` (operator timezone) |
| [`useNotifications`](/v0-0-2/reference/app-toolkit/hooks/state#usenotifications) | React view of the headless `notificationStore` (unread counter) |
| [`useActions`](/v0-0-2/reference/app-toolkit/hooks/state#useactions) | React view of the headless `actionsStore` (pending submissions) |
### Components
@tetherto/mdk-react-devkit
| Sub-group | Hooks |
|---|---|
| Notifications | [`useNotification`](/v0-0-2/reference/app-toolkit/hooks/components#usenotification) |
| Shell | [`useHeaderControls`](/v0-0-2/reference/app-toolkit/hooks/components#useheadercontrols), [`useSidebarExpandedState`](/v0-0-2/reference/app-toolkit/hooks/components#usesidebarexpandedstate), [`useSidebarSectionState`](/v0-0-2/reference/app-toolkit/hooks/components#usesidebarsectionstate) |
| Forms | [`useFormField`](/v0-0-2/reference/app-toolkit/hooks/components#useformfield), [`useFormReset`](/v0-0-2/reference/app-toolkit/hooks/components#useformreset) |
| Filters | [`useReportTimeFrameSelectorState`](/v0-0-2/reference/app-toolkit/hooks/components#usereporttimeframeselectorstate), [`useTimeframeControls`](/v0-0-2/reference/app-toolkit/hooks/components#usetimeframecontrols) |
| Widgets | [`useContainerThresholds`](/v0-0-2/reference/app-toolkit/hooks/components#usecontainerthresholds), [`useFinancialDateRange`](/v0-0-2/reference/app-toolkit/hooks/components#usefinancialdaterange) |
| Tables | [`useGetAvailableDevices`](/v0-0-2/reference/app-toolkit/hooks/components#usegetavailabledevices) |
| Charts | [`useChartDataCheck`](/v0-0-2/reference/app-toolkit/hooks/components#usechartdatacheck), [`useEbitda`](/v0-0-2/reference/app-toolkit/hooks/components#useebitda), [`useEnergyBalanceViewModel`](/v0-0-2/reference/app-toolkit/hooks/components#useenergybalanceviewmodel) |
| Dashboards | [`usePoolConfigs`](/v0-0-2/reference/app-toolkit/hooks/components#usepoolconfigs), [`useSiteOverviewDetailsData`](/v0-0-2/reference/app-toolkit/hooks/components#usesiteoverviewdetailsdata) |
### Utilities
@tetherto/mdk-react-adapter + @tetherto/mdk-react-devkit/foundation
| Sub-group | Hooks |
|---|---|
| Permissions | [`useCheckPerm`](/v0-0-2/reference/app-toolkit/hooks/utilities#usecheckperm), [`useHasPerms`](/v0-0-2/reference/app-toolkit/hooks/utilities#usehasperms), [`useIsFeatureEditingEnabled`](/v0-0-2/reference/app-toolkit/hooks/utilities#useisfeatureeditingenabled) |
| Generic React | [`useLocalStorage`](/v0-0-2/reference/app-toolkit/hooks/utilities#uselocalstorage), [`useKeyDown`](/v0-0-2/reference/app-toolkit/hooks/utilities#usekeydown), [`useWindowSize`](/v0-0-2/reference/app-toolkit/hooks/utilities#usewindowsize), [`usePlatform`](/v0-0-2/reference/app-toolkit/hooks/utilities#useplatform), [`useDeviceResolution`](/v0-0-2/reference/app-toolkit/hooks/utilities#usedeviceresolution), [`useBeepSound`](/v0-0-2/reference/app-toolkit/hooks/utilities#usebeepsound), [`usePagination`](/v0-0-2/reference/app-toolkit/hooks/utilities#usepagination), [`useSubtractedTime`](/v0-0-2/reference/app-toolkit/hooks/utilities#usesubtractedtime), [`useTimezoneFormatter`](/v0-0-2/reference/app-toolkit/hooks/utilities#usetimezoneformatter) |
| Device and IP | [`useStaticMinerIpAssignment`](/v0-0-2/reference/app-toolkit/hooks/utilities#usestaticmineripassignment), [`useMinerDuplicateValidation`](/v0-0-2/reference/app-toolkit/hooks/utilities#useminerduplicatevalidation), [`usePduViewer`](/v0-0-2/reference/app-toolkit/hooks/utilities#usepduviewer) |
| Domain transforms | [`useCostSummary`](/v0-0-2/reference/app-toolkit/hooks/utilities#usecostsummary), [`useHashBalance`](/v0-0-2/reference/app-toolkit/hooks/utilities#usehashbalance), [`useSubsidyFees`](/v0-0-2/reference/app-toolkit/hooks/utilities#usesubsidyfees), [`useUpdateExistedActions`](/v0-0-2/reference/app-toolkit/hooks/utilities#useupdateexistedactions) |
| TanStack Query re-exports | [Re-exports table](/v0-0-2/reference/app-toolkit/hooks/utilities#tanstack-query-re-exports) |
## Imports
```tsx
```
# Component hooks (/v0-0-2/reference/app-toolkit/hooks/components)
@tetherto/mdk-react-devkit
Hooks that wrap or compose styled MDK components β notifications, sidebar/header shell, forms, filters, widgets, tables, charts, and dashboards.
Adopt these when you are using `@tetherto/mdk-react-devkit` for your UI.
If you bring your own components, you may not need anything here. Start with [State hooks](/v0-0-2/reference/app-toolkit/hooks/state) or
[Utility hooks](/v0-0-2/reference/app-toolkit/hooks/utilities) instead.
## At a glance
| Sub-group | Hooks |
|---|---|
| [Notifications](#notifications) | `useNotification` |
| [Shell](#shell) | `useHeaderControls`, `useSidebarExpandedState`, `useSidebarSectionState` |
| [Forms](#forms) | `useFormField`, `useFormReset` |
| [Filters](#filters) | `useReportTimeFrameSelectorState`, `useTimeframeControls` |
| [Widgets](#widgets) | `useContainerThresholds`, `useFinancialDateRange` |
| [Tables](#tables) | `useGetAvailableDevices` |
| [Charts](#charts) | `useChartDataCheck`, `useEbitda`, `useEnergyBalanceViewModel` |
| [Dashboards](#dashboards) | `usePoolConfigs`, `useSiteOverviewDetailsData` |
## Prerequisites
- Complete the [@tetherto/mdk-react-devkit installation and add the dependency](/v0-0-2/ui/react/foundation#prerequisites)
- For hooks that read from headless stores (`useNotification`, view-model hooks):
[wrap your app in ``](/v0-0-2/ui/react/quickstart#wrap-your-app-in-mdkprovider)
## Import
```tsx
useChartDataCheck,
useContainerThresholds,
useEbitda,
useEnergyBalanceViewModel,
useFinancialDateRange,
useGetAvailableDevices,
useHeaderControls,
useNotification,
usePoolConfigs,
useReportTimeFrameSelectorState,
useSiteOverviewDetailsData,
useTimeframeControls,
} from '@tetherto/mdk-react-devkit/foundation'
useFormField,
useFormReset,
useSidebarExpandedState,
useSidebarSectionState,
} from '@tetherto/mdk-react-devkit/core'
```
## Notifications
### `useNotification`
@tetherto/mdk-react-devkit/foundation
Show toast notifications backed by the headless `notificationStore`. Supports success, error, info, and warning variants.
```tsx
```
#### Returns
| Member | Type | Description |
|--------|------|-------------|
| `notifySuccess` | `function` | Show success toast |
| `notifyError` | `function` | Show error toast |
| `notifyInfo` | `function` | Show info toast |
| `notifyWarning` | `function` | Show warning toast |
#### Method signature
```tsx
notifySuccess(message: string, description?: string, options?: NotificationOptions)
```
#### Options
Notification methods accept an optional third `options` argument:
| Option | Status | Type | Default | Description |
|--------|--------|------|---------|-------------|
| `duration` | Optional | `number` | `3000` | Duration in milliseconds (`0` = no autoclose) |
| `position` | Optional | `ToastPosition` | `'top-left'` | Toast position on screen |
| `dontClose` | Optional | `boolean` | `false` | When `true`, prevents autoclose |
#### Example
```tsx
function SaveButton() {
const { notifySuccess, notifyError } = useNotification()
const handleSave = async () => {
try {
await saveData()
notifySuccess('Saved', 'Your changes have been saved.')
} catch (error) {
notifyError('Error', 'Failed to save changes.', { dontClose: true })
}
}
return
}
```
## Shell
### `useHeaderControls`
@tetherto/mdk-react-devkit/foundation
Read/write hook for the global header-controls store (toggles, sticky flag, theme).
```tsx
```
#### Returns
| Member | Type | Description |
|--------|------|-------------|
| `preferences` | `HeaderPreferences` | Current visibility state for each header item |
| `isLoading` | `boolean` | Loading state |
| `error` | `Error \| null` | Error state |
| `handleToggle` | `function` | Toggle a header item visibility |
| `handleReset` | `function` | Reset to default preferences |
#### Example
```tsx
function HeaderSettings() {
const { preferences, handleToggle, handleReset } = useHeaderControls()
return (
)
}
```
### `useFinancialDateRange`
@tetherto/mdk-react-devkit/foundation
Resolves the active financial date range (start/end) used by every reporting-section query.
```tsx
```
#### Example
```tsx
function ReportingToolbar({ timezone }: { timezone: string }) {
const { datePicker, dateRange, onDateRangeReset } = useFinancialDateRange({ timezone })
return (
)
}
```
## Tables
### `useGetAvailableDevices`
@tetherto/mdk-react-devkit/foundation
Transforms the host's device list into the available container and miner type sets used by device explorer. Pass `data` from your query result.
```tsx
```
#### Example
```tsx
function DeviceTypeFilter({ devices }) {
const { availableContainerTypes, availableMinerTypes } = useGetAvailableDevices({ data: devices })
return (
)
}
```
## Charts
### `useChartDataCheck`
@tetherto/mdk-react-devkit/foundation
Check if chart data is empty or unavailable. Returns `true` if empty (show empty state), `false` if data exists (show chart).
```tsx
```
Pass chart input in one of two shapes:
1. **`dataset`**: direct dataset for BarChart-style usage.
2. **`data`**: Chart.js-shaped object with `datasets` (LineChart) or a `dataset` property.
Provide at least one of `dataset` or `data` for a meaningful empty check.
#### Options
| Option | Status | Type | Default | Description |
|--------|--------|------|---------|-------------|
| `dataset` | Optional | `object \| array` | none | Direct dataset for BarChart; set `dataset` or `data` (at least one) for a meaningful check |
| `data` | Optional | `object` | none | Chart.js-shaped object with `datasets` (LineChart) or `dataset` property; set `dataset` or `data` (at least one) |
#### Returns
| Type | Description |
|------|-------------|
| `boolean` | `true` if data is empty, `false` if data exists |
#### Example
```tsx
function HashrateChart({ dataset }) {
const isEmpty = useChartDataCheck({ dataset })
if (isEmpty) {
return
}
return
}
```
```tsx
function TemperatureChart({ data }) {
const isEmpty = useChartDataCheck({ data })
return isEmpty ? (
) : (
)
}
```
#### Chart utility integration
`useChartDataCheck` expects **Chart.js-shaped** `data` (`{ labels, datasets }`), not raw `{ labels, series }` from app hooks. Convert hook output with
the [**`buildBarChartData` utility**](/v0-0-2/ui/react/core/components/charts/composition#chart-utilities) from `@tetherto/mdk-react-devkit/core`,
then pass the result to **`useChartDataCheck`**.
```tsx
function RevenueBarChart({ hookOutput }) {
const chartData = buildBarChartData(hookOutput)
const isEmpty = useChartDataCheck({ data: chartData })
return (
)
}
```
For the full `BarChartInput` shape, per-dataset `datalabels` merge, and all-zero empty rules, see
[Hook-shaped bar data (`buildBarChartData`)](/v0-0-2/ui/react/core/components/charts/composition#hook-shaped-bar-data).
### `useEbitda`
@tetherto/mdk-react-devkit/foundation
Transforms an `EbitdaResponse` and date-range options into query params and a chart-ready EBITDA view-model.
```tsx
```
#### Example
```tsx
// Wire your query result in; consume queryParams to drive the fetch
function EbitdaSection({ ebitdaResponse, isLoading, fetchErrors }) {
const { datePicker, dateRange, queryParams, errors } = useEbitda({
ebitda: ebitdaResponse,
isLoading,
fetchErrors,
})
// Pass queryParams to your data-fetching layer whenever the date range changes
// e.g. useGetEbitdaQuery(queryParams, { skip: !queryParams })
return (
{datePicker}
{errors.length > 0 &&
{errors.join(', ')}
}
)
}
```
### `useEnergyBalanceViewModel`
@tetherto/mdk-react-devkit/foundation
Computes the full EnergyBalance view model from raw API data, managing tab selection and display-mode state.
```tsx
```
#### Example
```tsx
function EnergyBalancePanel({ data, isLoading, fetchErrors, dateRange, availablePowerMW }) {
const { viewModel, onTabChange, onRevenueDisplayModeChange } = useEnergyBalanceViewModel({
data,
isLoading,
fetchErrors,
dateRange,
availablePowerMW,
})
return (
{viewModel.isLoading &&
Loadingβ¦
}
{viewModel.errors.length > 0 &&
{viewModel.errors.join(', ')}
}
)
}
```
## Dashboards
### `usePoolConfigs`
@tetherto/mdk-react-devkit/foundation
Transforms raw pool-configuration rows from your API into `PoolSummary` objects for the Pool Manager UI. Fetch with TanStack Query in the host app, then pass `data`, `isLoading`, and `error` into this hook.
Typical usage: fetch with TanStack Query in the host app, then pass `data`, `isLoading`, and `error` into this hook. Foundation components such as [`PoolManagerPools`](/v0-0-2/ui/react/foundation/pool-manager/pools) and [`Miner explorer`](/v0-0-2/ui/react/foundation/pool-manager/miner-explorer) expect data shaped this way.
```tsx
```
#### Options
| Option | Status | Type | Default | Description |
|--------|--------|------|---------|-------------|
| `data` | Optional | `PoolConfigData[]` | none | Raw pool configuration rows from your API |
| `isLoading` | Optional | `boolean` | `false` | When `true`, the host should show a loading state |
| `error` | Optional | `unknown` | none | Error from your query; surfaced to pool-manager components |
#### Returns
| Member | Type | Description |
|--------|------|-------------|
| `pools` | `PoolSummary[]` | Normalized pool list for lists and accordions |
| `poolIdMap` | `Record` | Lookup by pool `id` |
| `isLoading` | `boolean` | Same as the option you passed in |
| `error` | `unknown` | Same as the option you passed in |
#### Example
```tsx
const { data, isLoading, error } = useGetPoolConfigsQuery({})
return usePoolConfigs({ data, isLoading, error })
}
```
```tsx
function PoolsPage({ poolConfig }: { poolConfig: PoolConfigData[] }) {
const { pools, isLoading, error } = usePoolConfigs({ data: poolConfig })
if (isLoading) return
if (error) return Failed to load pools
return (
{pools.map((pool) => (
{pool.name}
))}
)
}
```
### `useSiteOverviewDetailsData`
@tetherto/mdk-react-devkit/foundation
Composes the per-site overview view-model: pools, performance series, and recent activity.
```tsx
```
#### Example
```tsx
function SiteOverviewCard({ unit, pdus, connectedMiners, isLoading }) {
const {
containerHashRate,
isContainerRunning,
minersHashmap,
segregatedPduSections,
} = useSiteOverviewDetailsData(unit, { pdus, connectedMiners, isLoading })
return (
)
}
```
# State hooks (/v0-0-2/reference/app-toolkit/hooks/state)
@tetherto/mdk-react-adapter
State hooks bind the framework-agnostic [`@tetherto/mdk-ui-core`](/v0-0-2/reference/app-toolkit/ui-core) Zustand stores into React. Each hook subscribes the component to its store and re-renders only when the selected slice changes.
Use these when you want headless state with your own components. The [Developer entry points](/v0-0-2/overview/architecture/app-toolkit#developer-entry-points) table compares adoption layers.
## Prerequisites
- [Wrap your app in ``](/v0-0-2/ui/react/quickstart#wrap-your-app-in-mdkprovider)
- Complete the [@tetherto/mdk-react-devkit installation and add the dependency](/v0-0-2/ui/react/foundation#prerequisites)
## Import
```tsx
useActions,
useAuth,
useDevices,
useNotifications,
useTimezone,
} from '@tetherto/mdk-react-adapter'
```
## `useAuth`
@tetherto/mdk-react-adapter
React-bound view of the headless [`authStore`](/v0-0-2/reference/app-toolkit/ui-core). Exposes `token` and `permissions`; equivalent to `useStore(authStore)` with React subscription semantics.
```tsx
```
### Example
```tsx
function SessionStatus() {
const { token } = useAuth()
if (!token) return
Sign in to continue
return
Active session
}
```
## `useDevices`
@tetherto/mdk-react-adapter
React-bound view of the headless [`devicesStore`](/v0-0-2/reference/app-toolkit/ui-core). Exposes the device list, the currently selected devices, and helpers to mutate selection.
```tsx
```
### Example
```tsx
function DeviceToolbar() {
const { selectedDevices, setSelectedDevices } = useDevices()
return (
Selected: {selectedDevices.length}
)
}
```
## `useTimezone`
@tetherto/mdk-react-adapter
React-bound view of the headless [`timezoneStore`](/v0-0-2/reference/app-toolkit/ui-core). Read or update the operator's timezone preference (IANA identifier). Use [`useTimezoneFormatter`](/v0-0-2/reference/app-toolkit/hooks/utilities#usetimezoneformatter) when you also need date-formatting helpers.
```tsx
```
### Example
```tsx
function TimezonePicker() {
const { timezone, setTimezone } = useTimezone()
return (
)
}
```
## `useNotifications`
@tetherto/mdk-react-adapter
React-bound view of the headless [`notificationStore`](/v0-0-2/reference/app-toolkit/ui-core). Exposes the unread counter (`count`) plus `increment`, `decrement`, and `reset`. For the user-facing toast surface use [`useNotification`](/v0-0-2/reference/app-toolkit/hooks/components#usenotification) from foundation.
```tsx
```
### Example
```tsx
function UnreadBadge() {
const { count } = useNotifications()
return
}
```
## `useActions`
@tetherto/mdk-react-adapter
React-bound view of the headless [`actionsStore`](/v0-0-2/reference/app-toolkit/ui-core). Exposes the pending operator submission queue plus helpers like `setAddPendingSubmissionAction` and `removePendingSubmissionAction`.
```tsx
```
### Example
```tsx
function SubmissionTracker() {
const { pendingSubmissions, setAddPendingSubmissionAction } = useActions()
return (
{pendingSubmissions.length} pending submissions
)
}
```
# Utility hooks (/v0-0-2/reference/app-toolkit/hooks/utilities)
@tetherto/mdk-react-adapter + @tetherto/mdk-react-devkit/foundation
Generic React helpers, mining-domain transforms, permission checks, device/IP utilities, and a curated set of TanStack Query re-exports. Use these alongside [State hooks](/v0-0-2/reference/app-toolkit/hooks/state) to wire app concerns (permissions, devices, viewport, formatting) without depending on MDK styled components.
## At a glance
| Sub-group | Hooks |
|---|---|
| [Permissions](#permissions) | `useCheckPerm`, `useHasPerms`, `useIsFeatureEditingEnabled` |
| [Generic React](#generic-react) | `useLocalStorage`, `useKeyDown`, `useWindowSize`, `usePlatform`, `useDeviceResolution`, `useBeepSound`, `usePagination`, `useSubtractedTime`, `useTimezoneFormatter` |
| [Device and IP](#device-and-ip) | `useStaticMinerIpAssignment`, `useMinerDuplicateValidation`, `usePduViewer` |
| [Domain transforms](#domain-transforms) | `useCostSummary`, `useHashBalance`, `useSubsidyFees`, `useUpdateExistedActions` |
| [TanStack Query re-exports](#tanstack-query-re-exports) | `useQuery`, `useMutation`, `useQueries`, `useInfiniteQuery`, `useIsFetching`, `useIsMutating`, `useQueryClient` |
## Prerequisites
- [Wrap your app in ``](/v0-0-2/ui/react/quickstart#wrap-your-app-in-mdkprovider)
- Complete the [@tetherto/mdk-react-devkit installation and add the dependency](/v0-0-2/ui/react/foundation#prerequisites)
## Import
```tsx
useBeepSound,
useCheckPerm,
useDeviceResolution,
useHasPerms,
useIsFeatureEditingEnabled,
useKeyDown,
useLocalStorage,
useMinerDuplicateValidation,
usePagination,
usePduViewer,
usePlatform,
useStaticMinerIpAssignment,
useSubtractedTime,
useTimezoneFormatter,
useWindowSize,
} from '@tetherto/mdk-react-adapter'
useCostSummary,
useHashBalance,
useSubsidyFees,
useUpdateExistedActions,
} from '@tetherto/mdk-react-devkit/foundation'
```
## Permissions
### `useCheckPerm`
@tetherto/mdk-react-adapter
Check if the current user has a specific permission. Reads `permissions` from the adapter [`authStore`](/v0-0-2/reference/app-toolkit/hooks/state#useauth). Prefer this over `useHasPerms` for single-permission gates.
```tsx
```
#### Example
```tsx
function EditDevicesButton() {
const canEdit = useCheckPerm('devices:edit')
return canEdit ? : null
}
```
### `useHasPerms`
@tetherto/mdk-react-adapter
Returns a callable that accepts a permission request β a string, a string array (first match wins), or a check object. Reads from the adapter [`authStore`](/v0-0-2/reference/app-toolkit/hooks/state#useauth).
```tsx
```
#### Example
```tsx
function ContextMenu({ device }) {
const hasPerms = useHasPerms()
return (
)
}
```
### `useIsFeatureEditingEnabled`
@tetherto/mdk-react-adapter
Returns whether the current user has the `features` capability to edit feature flags.
```tsx
```
#### Example
```tsx
function FeatureFlagRow({ flag }) {
const canEdit = useIsFeatureEditingEnabled()
return
}
```
## Generic React
### `useLocalStorage`
@tetherto/mdk-react-adapter
Type-safe `localStorage` access with cross-tab synchronization. Returns `[value, setValue, removeValue]` and stays in sync across browser tabs via the `storage` event.
```tsx
```
#### Example
```tsx
function ThemeToggle() {
const [theme, setTheme] = useLocalStorage<'light' | 'dark'>('app:theme', 'light')
return
}
```
### `useKeyDown`
@tetherto/mdk-react-adapter
Track whether a specific keyboard key is currently pressed. Attaches global `keydown`/`keyup` listeners. Useful for modifier-key interactions like shift-click multi-select.
```tsx
```
#### Example
```tsx
function MinerGrid({ miners }) {
const shiftHeld = useKeyDown('Shift')
return miners.map((m) => (
select(m, { range: shiftHeld })} />
))
}
```
### `useWindowSize`
@tetherto/mdk-react-adapter
Track window width and height, refreshing on `resize`. Returns `{ windowWidth, windowHeight }`. Use [`useDeviceResolution`](#usedeviceresolution) when you only need a device-class branch rather than raw pixels.
```tsx
```
#### Example
```tsx
function ResponsiveChart() {
const { windowWidth } = useWindowSize()
return
}
```
### `usePlatform`
@tetherto/mdk-react-adapter
Detect the host platform (iOS, Android, Mac, Windows, Linux) from the user agent. Returns the value matching the exported `OS_TYPES` constant; pair with `detectPlatform` for one-off checks outside React.
```tsx
```
#### Example
```tsx
function PlatformBadge() {
const platform = usePlatform()
return Running on {platform}
}
```
### `useDeviceResolution`
@tetherto/mdk-react-adapter
Map window width to a device class (`mobile`, `tablet`, `desktop`) using the shared `BREAKPOINTS` constant. Cheaper than re-reading pixels in every render.
```tsx
```
#### Example
```tsx
function Layout({ children }) {
const device = useDeviceResolution()
return device === 'mobile' ? {children} : {children}
}
```
### `useBeepSound`
@tetherto/mdk-react-adapter
Play a repeating beep when `isAllowed` is true. Configurable volume and interval (`delayMs`). The alarm is synthesised via the Web Audio API β no audio asset is bundled or fetched. Designed for audible critical alerts (overheating containers, equipment failure).
```tsx
```
#### Example
```tsx
function CriticalAlertChime({ active }) {
useBeepSound({ isAllowed: active, volume: 0.6, delayMs: 1500 })
return null
}
```
### `usePagination`
@tetherto/mdk-react-adapter
Manage pagination state and produce `{ limit, offset }` query arguments for API calls. Returns state shaped for the devkit `` component plus helpers to change page, page size, and total count.
```tsx
```
#### Options
| Option | Status | Type | Default | Description |
|--------|--------|------|---------|-------------|
| `current` | Optional | `number` | `1` | Initial 1-indexed page |
| `pageSize` | Optional | `number` | `20` | Initial rows per page |
| `total` | Optional | `number` | none | Initial total row count |
| `showSizeChanger` | Optional | `boolean` | `true` | Whether the UI exposes a page-size selector |
#### Returns
| Member | Type | Description |
|--------|------|-------------|
| `pagination` | `PaginationState` | `{ current, pageSize, showSizeChanger, total }` β spread onto `` |
| `queryArgs` | `{ limit, offset }` | Query arguments for API calls |
| `handleChange` | `function` | `(page, pageSize) => void` β pass to `` |
| `setPagination` | `function` | Imperative pagination state update |
| `reset` | `function` | Reset to initial state |
| `setTotal` | `function` | Update total row count |
| `hideNextPage` | `function` | Hide next page when the current page has fewer rows than `pageSize` |
#### Example
```tsx
function MinerList() {
const { pagination, queryArgs, handleChange } = usePagination({ current: 1, pageSize: 25 })
const { data } = useQuery(['miners', queryArgs], () => fetchMiners(queryArgs))
return (
<>
>
)
}
```
### `useSubtractedTime`
@tetherto/mdk-react-adapter
Returns `Date.now() - diff`, refreshing on a fixed interval (default 5s). Useful for "synced N seconds ago" labels without forcing tree-wide re-renders.
```tsx
```
#### Example
```tsx
function LastSyncedLabel({ lastSyncOffsetMs }) {
const now = useSubtractedTime(lastSyncOffsetMs)
return Synced {formatDistanceToNow(now)} ago
}
```
### `useTimezoneFormatter`
@tetherto/mdk-react-adapter
Read the app timezone from the adapter [`timezoneStore`](/v0-0-2/reference/app-toolkit/hooks/state#usetimezone) and format dates for display. Use when foundation components or app code need timestamps in the operator-selected timezone (alerts, pool manager, dashboard widgets).
```tsx
```
#### Example
```tsx
function AlertTimestamp({ raisedAt }) {
const { getFormattedDate } = useTimezoneFormatter()
return
}
```
## Device and IP
### `useStaticMinerIpAssignment`
@tetherto/mdk-react-adapter
Derive the static IP address a miner should receive based on its physical position (container number + socket coordinates). Returns an empty string when the feature is disabled or the socket is incomplete.
```tsx
```
#### Example
```tsx
function MinerIpField({ containerInfo, socket, pdu }) {
const { minerIp } = useStaticMinerIpAssignment({ containerInfo, socket, pdu })
return
}
```
### `useMinerDuplicateValidation`
@tetherto/mdk-react-adapter
Async validation hook that flags duplicate miners against the device inventory (MAC, serial, IP, human-facing code). The public surface is stable while the backing implementation evolves.
```tsx
```
#### Example
```tsx
function MinerForm({ socket, onSubmit }) {
const { checkDuplicate, duplicateError, isDuplicateCheckLoading } = useMinerDuplicateValidation()
const handleSubmit = async (draft) => {
const isDuplicate = await checkDuplicate(socket, draft)
if (!isDuplicate) onSubmit(draft)
}
return (
<>
{duplicateError &&
Duplicate miner detected.
}
>
)
}
```
### `usePduViewer`
@tetherto/mdk-react-adapter
Pan/zoom controller for the PDU floor-plan viewer. Wraps `react-zoom-pan-pinch` with viewport-aware reset logic and a debounced "back to content" indicator that appears when the user pans the layout off-screen.
```tsx
```
#### Example
```tsx
function PduFloorPlan() {
const { onViewerInit, showBackToContent, handleBackToContent } = usePduViewer()
return (
<>
{/* β¦diagramβ¦ */}
{showBackToContent && }
>
)
}
```
## Domain transforms
### `useCostSummary`
@tetherto/mdk-react-devkit/foundation
Base hook for the cost-summary reporting page (single-site mode).
```tsx
```
#### Example
```tsx
// Wire your query result in; consume queryParams to drive the fetch
function CostSummaryPage({ query }) {
const { datePicker, queryParams, isLoading, error } = useCostSummary({ query })
// Pass queryParams to your data-fetching layer whenever the date range changes
// e.g. useGetCostSummaryQuery(queryParams, { skip: !queryParams })
return (
{datePicker}
{isLoading &&
Loadingβ¦
}
{error &&
Failed to load cost data
}
)
}
```
### `useHashBalance`
@tetherto/mdk-react-devkit/foundation
Derives hash-balance metrics and chart datasets from finance log entries for the active date range, currency, and timeframe type. Used by hash balance panels.
```tsx
```
#### Example
```tsx
function HashBalancePanel({ data, log, currency, dateRange }) {
const {
siteHashRevenueChartData,
networkHashpriceChartData,
combinedCostChartData,
} = useHashBalance({ data, log, currency, dateRange })
return (
{/* Pass chart datasets to your BarChart components */}
)
}
```
### `useSubsidyFees`
@tetherto/mdk-react-devkit/foundation
Aggregates raw subsidy-fee log entries into chart-ready datasets keyed by the active period type (day / week / month / year) and surfaces a summary for the matching reporting widgets. Used by `Subsidβ¦
```tsx
```
#### Example
```tsx
function SubsidyFeesPanel({ data, dateRange }) {
const { summary, subsidyFeesChartData, averageFeesChartData, isEmpty } = useSubsidyFees({
data,
dateRange,
})
if (isEmpty) return
No subsidy fee data for this period.
return (
Total fees: {(summary as any)?.total ?? 'β'}
{/* Pass chart datasets to your BarChart components */}
)
}
```
### `useUpdateExistedActions`
@tetherto/mdk-react-devkit/foundation
Mutation hook that updates only the changed fields of an existing action record.
```tsx
```
#### Example
```tsx
function DeviceActionBar({ actionType, pendingSubmissions, selectedDevices }) {
const { updateExistedActions } = useUpdateExistedActions()
const handleApply = () => {
updateExistedActions({ actionType, pendingSubmissions, selectedDevices })
}
return (
)
}
```
## TanStack Query re-exports
The adapter re-exports a curated set of [TanStack Query](https://tanstack.com/query/latest/docs/framework/react/overview) hooks so you can import data-fetching primitives from a single package alongside MDK helpers. The re-exports are unmodified β refer to the upstream TanStack Query documentation for full API details.
| Hook | TanStack docs |
| --- | --- |
| `useQuery` | [TanStack `useQuery`](https://tanstack.com/query/latest/docs/framework/react/reference/useQuery) |
| `useMutation` | [TanStack `useMutation`](https://tanstack.com/query/latest/docs/framework/react/reference/useMutation) |
| `useQueries` | [TanStack `useQueries`](https://tanstack.com/query/latest/docs/framework/react/reference/useQueries) |
| `useInfiniteQuery` | [TanStack `useInfiniteQuery`](https://tanstack.com/query/latest/docs/framework/react/reference/useInfiniteQuery) |
| `useIsFetching` | [TanStack `useIsFetching`](https://tanstack.com/query/latest/docs/framework/react/reference/useIsFetching) |
| `useIsMutating` | [TanStack `useIsMutating`](https://tanstack.com/query/latest/docs/framework/react/reference/useIsMutating) |
| `useQueryClient` | [TanStack `useQueryClient`](https://tanstack.com/query/latest/docs/framework/react/reference/useQueryClient) |
# UI Core (/v0-0-2/reference/app-toolkit/ui-core)
@tetherto/mdk-ui-core
If you are building a React app with the MDK kit, start with the [React adapter](/v0-0-2/ui/react/get-started) instead. `` and
adapter hooks such as `useAuth` and `useDevices` wrap this package so most React code never imports `@tetherto/mdk-ui-core` directly.
Use this reference when you need headless store access outside React (logging, websocket setup, test helpers) or when authoring a future framework adapter.
`@tetherto/mdk-ui-core` is the framework-agnostic headless layer of the [MDK App Toolkit](/v0-0-2/overview/architecture/app-toolkit). It ships
Zustand vanilla stores and a TanStack Query Core `QueryClient` factory. There are **no React imports** in this package.
## Subpath exports
| Subpath | Purpose |
|---------|---------|
| `.` | Top-level barrel |
| `./store` | Zustand vanilla stores |
| `./query` | `QueryClient` factory, query keys, and query/mutation factories |
| `./types` | Shared type contracts |
| `./stores.json` | Machine-readable store manifest (generated at build time) |
## Stores
Each store is a Zustand vanilla singleton. In a React app, read and update state through the matching adapter hook instead of importing stores directly.
| Store | Summary | Adapter hook |
|-------|---------|--------------|
| `authStore` | Session token and permission payload | [`useAuth`](/v0-0-2/reference/app-toolkit/hooks/state#useauth) |
| `devicesStore` | Fleet device list and current selection | [`useDevices`](/v0-0-2/reference/app-toolkit/hooks/state#usedevices) |
| `timezoneStore` | Active operator timezone | [`useTimezone`](/v0-0-2/reference/app-toolkit/hooks/state#usetimezone) |
| `notificationStore` | Unread notification counter (`count`, `increment`, `decrement`, `reset`) | [`useNotifications`](/v0-0-2/reference/app-toolkit/hooks/state#usenotifications) |
| `actionsStore` | Pending device and pool action submissions | [`useActions`](/v0-0-2/reference/app-toolkit/hooks/state#useactions) |
Import from `@tetherto/mdk-ui-core/store` (or the top-level barrel).
## QueryClient factory
`createMdkQueryClient` builds a TanStack Query Core client with environment-aware App Node base URL resolution:
1. Explicit override (typically from ``)
2. Build-time env: `VITE_MDK_API_URL` (Vite) or `MDK_API_URL` (Node)
3. Default: `http://localhost:3000`
The `./query` subpath also exports query key helpers and factories (`authQuery`, `devicesQuery`, `deviceQuery`, `telemetryQuery`).
See [TanStack Query](https://tanstack.com/query/latest) for general usage.
## Headless read outside React
Utility code can subscribe to a store without React:
```ts
const token = authStore.getState().token
const unsubscribe = authStore.subscribe((state) => {
console.log('token changed', state.token)
})
// later: unsubscribe()
```
`` wires these singleton stores into React and creates the shared `QueryClient` for adapter hooks.
## What's not here yet
Headless primitives will include throttled telemetry subscriptions, stale detection, history ring buffers, and an optimistic command state machine.
They ship alongside the consuming code that needs them.
## Next steps
- [React get started](/v0-0-2/ui/react/get-started): three-package install and ``
- [Wire a React app](/v0-0-2/tutorials/react/tutorial): full adapter wiring walkthrough
- [MDK App Toolkit architecture](/v0-0-2/overview/architecture/app-toolkit): where UI Core fits in the frontend stack
# UI Kit (/v0-0-2/reference/app-toolkit/ui-kit)
The UI Kit is the frontend tools half of the [MDK App Toolkit](/v0-0-2/overview/architecture/app-toolkit). This reference documents
its constants, React hooks, TypeScript types, and helper utilities.
## Browse by topic
| Topic | What's there |
|-------|--------------|
| [Constants](/v0-0-2/reference/app-toolkit/ui-kit/constants) | Colors, units, currency, chart configs and permissions, roles, header preferences|
| [Hooks](/v0-0-2/reference/app-toolkit/ui-kit/hooks) | Discover hooks for monitoring and UI patterns; full reference at [Hooks](/v0-0-2/reference/app-toolkit/hooks) |
| [Types](/v0-0-2/reference/app-toolkit/ui-kit/types) | TypeScript type exports. UI primitives and domain models like `Device`, `Alert` |
| [Utilities](/v0-0-2/reference/app-toolkit/ui-kit/utilities) | Helper functions for formatting, validation, conversions and settings persistence |
## Browse by package
If you're working with a specific package, use these per-package shortcuts:
### `@tetherto/mdk-react-devkit/core`
- [Constants](/v0-0-2/reference/app-toolkit/ui-kit/constants#core-constants)
- [Types](/v0-0-2/reference/app-toolkit/ui-kit/types#core-types)
- [Utilities](/v0-0-2/reference/app-toolkit/ui-kit/utilities#core-utilities)
### `@tetherto/mdk-react-devkit/foundation`
- [Constants](/v0-0-2/reference/app-toolkit/ui-kit/constants#foundation-constants)
- [Types](/v0-0-2/reference/app-toolkit/ui-kit/types#foundation-types)
- [Utilities](/v0-0-2/reference/app-toolkit/ui-kit/utilities#foundation-utilities)
- [Hooks](/v0-0-2/reference/app-toolkit/hooks) β every App Toolkit hook (state, components, utilities) lives on a single overview page now.
# Constants (/v0-0-2/reference/app-toolkit/ui-kit/constants)
This page documents constants exported by the MDK packages. Constants live in two distinct domains:
- [Core constants](#core-constants) ships UI primitives: colors, units, currency symbols, chart defaults
- [Foundation constants](#foundation-constants) ships mining-domain values: permissions, roles, header preferences, error codes
## Core constants
UI primitive constants exported by `@tetherto/mdk-react-devkit/core` β colors, units, currency symbols, and chart defaults.
### Prerequisites
- Complete the [@tetherto/mdk-react-devkit installation and add the dependency](/v0-0-2/ui/react/core#prerequisites)
### Import
@tetherto/mdk-react-devkit/core
```tsx
COLOR,
UNITS,
CURRENCY,
CHART_COLORS,
TABLE_COLORS,
HASHRATE_LABEL_DIVISOR,
} from '@tetherto/mdk-react-devkit/core'
```
### Color constants
#### `COLOR`
@tetherto/mdk-react-devkit/core
Comprehensive color palette with 80+ named colors.
```tsx
COLOR.GREEN // '#72F59E'
COLOR.RED // '#EF4444'
COLOR.COLD_ORANGE // '#F7931A'
```
##### Base colors
| Constant | Value | Description |
|----------|-------|-------------|
| `WHITE` | `#FFFFFF` | Pure white |
| `BLACK` | `#17130F` | Standard black |
| `DARK_BACK` | `#1A1815` | Dark background |
| `EBONY` | `#0f0f0f` | Chart background |
| `TRANSPARENT` | `transparent` | Transparent |
##### Status colors
| Constant | Value | Description |
|----------|-------|-------------|
| `GREEN` | `#72F59E` | Success/online |
| `RED` | `#EF4444` | Error/danger |
| `YELLOW` | `#FFC107` | Warning |
| `BRIGHT_YELLOW` | `#EAB308` | Bright warning |
| `LIGHT_BLUE` | `#22AFFF` | Info |
| `SLEEP_BLUE` | `#3B82F6` | Sleep/standby |
##### Brand colors
| Constant | Value | Description |
|----------|-------|-------------|
| `COLD_ORANGE` | `#F7931A` | Bitcoin orange |
| `ORANGE` | `#FF6A00` | Primary orange |
| `EMERALD` | `#009393` | Teal accent |
| `INDIGO` | `#5B5FFB` | Purple accent |
#### `TABLE_COLORS`
@tetherto/mdk-react-devkit/core
Colors for table styling.
```tsx
TABLE_COLORS.HEADER_BG
TABLE_COLORS.ROW_HOVER
```
#### `HEATMAP`
@tetherto/mdk-react-devkit/core
`HEATMAP` color scale for temperature and intensity displays.
```tsx
```
#### `CHART_COLORS`
@tetherto/mdk-react-devkit/core
Default chart color palette.
```tsx
```
#### `PIE_CHART_COLORS`
@tetherto/mdk-react-devkit/core
Color palette for pie and doughnut charts.
```tsx
```
#### `CATEGORICAL_COLORS`
@tetherto/mdk-react-devkit/core
25-color categorical palette for multi-series charts.
```tsx
CATEGORICAL_COLORS[0] // First color
CATEGORICAL_COLORS[24] // Last color
```
#### `TEMPERATURE_COLORS`
@tetherto/mdk-react-devkit/core
Color scale for temperature displays.
```tsx
```
#### `SOCKET_BORDER_COLOR`
@tetherto/mdk-react-devkit/core
Colors for socket status indicators.
```tsx
```
### Unit constants
#### `UNITS`
@tetherto/mdk-react-devkit/core
Physical and measurement units.
```tsx
UNITS.POWER_W // 'W'
UNITS.POWER_KW // 'kW'
UNITS.ENERGY_MWH // 'MWh'
UNITS.TEMPERATURE_C // 'Β°C'
UNITS.HASHRATE_TH_S // 'TH/s'
```
| Constant | Value | Description |
|----------|-------|-------------|
| `POWER_W` | `W` | Watts |
| `POWER_KW` | `kW` | Kilowatts |
| `ENERGY_WH` | `Wh` | Watt-hours |
| `ENERGY_KWH` | `kWh` | Kilowatt-hours |
| `ENERGY_MW` | `MW` | Megawatts |
| `ENERGY_MWH` | `MWh` | Megawatt-hours |
| `ENERGY_GWH` | `GWh` | Gigawatt-hours |
| `TEMPERATURE_C` | `Β°C` | Celsius |
| `VOLTAGE_V` | `V` | Volts |
| `AMPERE` | `A` | Amperes |
| `PERCENT` | `%` | Percentage |
| `PRESSURE_BAR` | `bar` | Pressure (bar) |
| `HASHRATE_MH_S` | `MH/s` | Megahash/second |
| `HASHRATE_TH_S` | `TH/s` | Terahash/second |
| `HASHRATE_PH_S` | `PH/s` | Petahash/second |
| `HASHRATE_EH_S` | `EH/s` | Exahash/second |
| `FREQUENCY_MHZ` | `MHz` | Megahertz |
| `FREQUENCY_HERTZ` | `Hz` | Hertz |
| `HUMIDITY_PERCENT` | `%RH` | Relative humidity |
| `EFFICIENCY_W_PER_TH` | `W/TH` | Watts per terahash |
| `FLOW_M3H` | `m3/h` | Flow rate |
| `SATS` | `Sats` | Satoshis |
| `VBYTE` | `vByte` | Virtual bytes |
#### `CURRENCY`
@tetherto/mdk-react-devkit/core
Currency symbols and labels.
```tsx
CURRENCY.BTC // 'βΏ'
CURRENCY.USD // '$'
CURRENCY.EUR // 'β¬'
CURRENCY.SATS // 'Sats'
CURRENCY.BTC_LABEL // 'BTC'
CURRENCY.USD_LABEL // 'USD'
```
#### `MAX_UNIT_VALUE`
@tetherto/mdk-react-devkit/core
Maximum values for certain units.
```tsx
MAX_UNIT_VALUE.HUMIDITY_PERCENT // 100
MAX_UNIT_VALUE.TEMPERATURE_PERCENT // 100
```
#### `HASHRATE_LABEL_DIVISOR`
@tetherto/mdk-react-devkit/core
Divisors for converting hashrate units.
```tsx
HASHRATE_LABEL_DIVISOR['TH/s'] // 1e6
HASHRATE_LABEL_DIVISOR['PH/s'] // 1e9
HASHRATE_LABEL_DIVISOR['EH/s'] // 1e12
```
### Chart constants
#### `defaultChartColors`
@tetherto/mdk-react-devkit/core
Default color array for chart datasets.
```tsx
```
#### `defaultChartOptions`
@tetherto/mdk-react-devkit/core
Default [Chart.js](https://www.chartjs.org/) options.
```tsx
```
#### `CHART_LEGEND_OPACITY`
@tetherto/mdk-react-devkit/core
Opacity values for chart legends.
```tsx
```
#### `CHART_PERFORMANCE`
@tetherto/mdk-react-devkit/core
Performance threshold constants for charts.
```tsx
```
#### `getChartAnimationConfig`
@tetherto/mdk-react-devkit/core
Get animation configuration based on data count.
```tsx
const animConfig = getChartAnimationConfig(dataPointCount)
```
#### `getDataDecimationConfig`
@tetherto/mdk-react-devkit/core
Get data decimation configuration for large datasets.
```tsx
const decimationConfig = getDataDecimationConfig(dataPointCount)
```
### Type exports
The constants module also exports TypeScript types:
```tsx
UnitKey,
UnitValue,
CurrencyKey,
CurrencyValue,
} from '@tetherto/mdk-react-devkit/core'
```
## Foundation constants
Constants exported by `@tetherto/mdk-react-devkit/foundation`: app identity, dialog flows, header preferences, permissions, roles, and error codes.
### Prerequisites
- Complete the [@tetherto/mdk-react-devkit installation and add the dependency](/v0-0-2/ui/react/foundation#prerequisites)
### Import
@tetherto/mdk-react-devkit/foundation
```tsx
WEBAPP_NAME,
WEBAPP_SHORT_NAME,
WEBAPP_DISPLAY_NAME,
POSITION_CHANGE_DIALOG_FLOWS,
HEADER_ITEMS,
DEFAULT_HEADER_PREFERENCES,
AUTH_PERMISSIONS,
AUTH_LEVELS,
USER_ROLE,
USER_ROLES,
PERM_LEVEL_LABELS,
getRoleBadgeColors,
} from '@tetherto/mdk-react-devkit/foundation'
```
### App identity
Brand strings the foundation UI uses to label the running web app in header rows, chart legends, settings export errors, and confirmation copy. The kit ships placeholder defaults; consumers read them via the named imports.
#### `WEBAPP_NAME`
@tetherto/mdk-react-devkit/foundation
Inline name used in confirmations and the `parseSettingsFile` import-error message.
```tsx
WEBAPP_NAME // 'Appl.'
```
#### `WEBAPP_SHORT_NAME`
@tetherto/mdk-react-devkit/foundation
Compact label embedded in [`HEADER_ITEMS`](#header_items) for the in-app miner and hashrate header rows.
```tsx
WEBAPP_SHORT_NAME // 'APP'
```
#### `WEBAPP_DISPLAY_NAME`
@tetherto/mdk-react-devkit/foundation
Display name used in chart legends, including the hash-rate line chart series label.
```tsx
WEBAPP_DISPLAY_NAME // 'Application'
```
### Dialog flows
#### `POSITION_CHANGE_DIALOG_FLOWS`
@tetherto/mdk-react-devkit/foundation
Flow keys consumed by [`PositionChangeDialog`](/v0-0-2/ui/react/foundation/operations/details-view/fleet-management#positionchangedialog) and its sibling dialogs to route between step-specific surfaces.
```tsx
POSITION_CHANGE_DIALOG_FLOWS.MAINTENANCE // 'maintenance'
POSITION_CHANGE_DIALOG_FLOWS.REPLACE_MINER // 'replaceMiner'
```
| Constant | Value | Description |
|----------|-------|-------------|
| `CONFIRM_REMOVE` | `remove` | Render the remove-miner confirmation surface |
| `CHANGE_INFO` | `changeInfo` | Edit info for an existing miner |
| `MAINTENANCE` | `maintenance` | Move a miner to (or back from) the maintenance container |
| `REPLACE_MINER` | `replaceMiner` | Replace the miner currently occupying a socket |
| `CONFIRM_CHANGE_POSITION` | `confirmChange` | Confirm a position change between sockets |
| `CONTAINER_SELECTION` | `containerSelection` | Pick a destination container and socket |
#### `PositionChangeDialogFlowKey` type
@tetherto/mdk-react-devkit/foundation
```tsx
type PositionChangeDialogFlowKey = keyof typeof POSITION_CHANGE_DIALOG_FLOWS
```
#### `PositionChangeDialogFlowValue` type
@tetherto/mdk-react-devkit/foundation
```tsx
type PositionChangeDialogFlowValue =
(typeof POSITION_CHANGE_DIALOG_FLOWS)[PositionChangeDialogFlowKey]
```
### Header controls
#### `HEADER_ITEMS`
@tetherto/mdk-react-devkit/foundation
Array of header metric options for the [`HeaderControlsSettings`](/v0-0-2/ui/react/foundation/settings/header-controls) component.
```tsx
```
| Key | Label |
|-----|-------|
| `poolMiners` | `Pool Miners` |
| `miners` | `` `${WEBAPP_SHORT_NAME} Miners` `` |
| `poolHashrate` | `Pool Hashrate` |
| `hashrate` | `` `${WEBAPP_SHORT_NAME} Hashrate` `` |
| `consumption` | `Consumption` |
| `efficiency` | `Efficiency` |
The two in-app rows compose their labels from [`WEBAPP_SHORT_NAME`](#webapp_short_name); with the shipped default that renders as `APP Miners` and `APP Hashrate`.
#### `DEFAULT_HEADER_PREFERENCES`
@tetherto/mdk-react-devkit/foundation
Default visibility state for all header items (all `true`).
```tsx
DEFAULT_HEADER_PREFERENCES.poolMiners // true
DEFAULT_HEADER_PREFERENCES.consumption // true
```
#### `HeaderPreferences` type
@tetherto/mdk-react-devkit/foundation
```tsx
type HeaderPreferences = {
poolMiners: boolean
miners: boolean
poolHashrate: boolean
hashrate: boolean
consumption: boolean
efficiency: boolean
}
```
### Permissions
#### `AUTH_PERMISSIONS`
@tetherto/mdk-react-devkit/foundation
Permission resource identifiers.
```tsx
AUTH_PERMISSIONS.USERS // 'users'
AUTH_PERMISSIONS.SETTINGS // 'settings'
AUTH_PERMISSIONS.MINER // 'miner'
```
| Constant | Value | Description |
|----------|-------|-------------|
| `USERS` | `users` | User management |
| `SETTINGS` | `settings` | Application settings |
| `MINER` | `miner` | Miner operations |
| `ALERTS` | `alerts` | Alert management |
| `ACTIONS` | `actions` | Action execution |
| `EXPLORER` | `explorer` | Device explorer |
| `INVENTORY` | `inventory` | Inventory management |
| `CONTAINER` | `container` | Container management |
| `PRODUCTION` | `production` | Production data |
| `REPORTING` | `reporting` | Reports |
#### `AUTH_LEVELS`
@tetherto/mdk-react-devkit/foundation
Permission access levels.
```tsx
AUTH_LEVELS.READ // 'r'
AUTH_LEVELS.WRITE // 'w'
```
#### `USER_ROLE`
@tetherto/mdk-react-devkit/foundation
User role identifiers.
```tsx
USER_ROLE.ADMIN // 'admin'
USER_ROLE.SITE_MANAGER // 'site_manager'
USER_ROLE.READ_ONLY // 'read_only_user'
```
| Constant | Value |
|----------|-------|
| `ADMIN` | `admin` |
| `SITE_MANAGER` | `site_manager` |
| `SITE_OPERATOR` | `site_operator` |
| `FIELD_OPERATOR` | `field_operator` |
| `REPAIR_TECHNICIAN` | `repair_technician` |
| `REPORTING_TOOL_MANAGER` | `reporting_tool_manager` |
| `READ_ONLY` | `read_only_user` |
### Settings
#### `USER_ROLES`
@tetherto/mdk-react-devkit/foundation
Array of role options for select dropdowns.
```tsx
// [{ label: 'Admin', value: 'admin' }, ...]
```
#### `PERM_LEVEL_LABELS`
@tetherto/mdk-react-devkit/foundation
Human-readable labels for permission levels.
```tsx
PERM_LEVEL_LABELS.rw // 'Read & Write'
PERM_LEVEL_LABELS.r // 'Read Only'
PERM_LEVEL_LABELS.none // 'No Access'
```
#### `SETTINGS_ERROR_CODES`
@tetherto/mdk-react-devkit/foundation
Error code to message mapping.
```tsx
SETTINGS_ERROR_CODES.ERR_USER_EXISTS // 'User already exists'
SETTINGS_ERROR_CODES.DEFAULT // 'An error occurred'
```
### Role styling
#### `getRoleBadgeColors`
@tetherto/mdk-react-devkit/foundation
Get badge colors for a role.
```tsx
const { color, bgColor } = getRoleBadgeColors('admin')
// { color: '#e8833a', bgColor: 'rgba(232, 131, 58, 0.1)' }
```
| Role | Color | Background |
|------|-------|------------|
| `admin` | `#e8833a` | Orange tint |
| `site_manager` | `#52c41a` | Green tint |
| `site_operator` | `#faad14` | Yellow tint |
| `read_only_user` | `#8c8c8c` | Gray tint |
# Hooks (/v0-0-2/reference/app-toolkit/ui-kit/hooks)
React hooks from `@tetherto/mdk-react-devkit`. The [Hooks reference](/v0-0-2/reference/app-toolkit/hooks) has the full catalog (including state and adapter hooks). Use the groups below to browse hooks by **Monitoring** and **UI** concern; each hook name links to its entry in that reference.
## Available hooks
| Group | Hooks |
|---|---|
| [Monitoring](/v0-0-2/reference/app-toolkit/hooks) | [`useChartDataCheck`](/v0-0-2/reference/app-toolkit/hooks/components#usechartdatacheck), [`useBeepSound`](/v0-0-2/reference/app-toolkit/hooks/utilities#usebeepsound) |
| [UI](/v0-0-2/reference/app-toolkit/hooks) | [`useNotification`](/v0-0-2/reference/app-toolkit/hooks/components#usenotification), [`useHeaderControls`](/v0-0-2/reference/app-toolkit/hooks/components#useheadercontrols), [`useSidebarExpandedState`](/v0-0-2/reference/app-toolkit/hooks/components#usesidebarexpandedstate), [`useSidebarSectionState`](/v0-0-2/reference/app-toolkit/hooks/components#usesidebarsectionstate), [`useHasPerms`](/v0-0-2/reference/app-toolkit/hooks/utilities#usehasperms), [`useCheckPerm`](/v0-0-2/reference/app-toolkit/hooks/utilities#usecheckperm), [`useLocalStorage`](/v0-0-2/reference/app-toolkit/hooks/utilities#uselocalstorage), [`usePagination`](/v0-0-2/reference/app-toolkit/hooks/utilities#usepagination), [`useIsFeatureEditingEnabled`](/v0-0-2/reference/app-toolkit/hooks/utilities#useisfeatureeditingenabled) |
# Types (/v0-0-2/reference/app-toolkit/ui-kit/types)
This page documents the TypeScript types exported by the MDK packages. The two packages cover different territory:
- [Core types](#core-types) ships **UI primitive types**: sizes, variants, colors, status, common API and chart shapes. These are the building blocks
consumed by core components and re-used in your own component prop types.
- [Foundation types](#foundation-types) ships **mining-domain models**: `Device`, `Container`, `Alert`, `MinerStats`, settings shapes. These describe the
data flowing through foundation components and the API responses they consume.
## Core types
UI primitive types exported by `@tetherto/mdk-react-devkit/core` β sizes, variants, colors, status, and common API and chart shapes. These are the
building blocks consumed by core components and re-used in your own component prop types.
### Prerequisites
- Complete the [@tetherto/mdk-react-devkit installation and add the dependency](/v0-0-2/ui/react/core#prerequisites)
### Import
@tetherto/mdk-react-devkit/core
```tsx
ComponentSize,
ButtonVariant,
ColorVariant,
Status,
ApiResponse,
} from '@tetherto/mdk-react-devkit/core'
```
### Common types
#### `ComponentSize`
@tetherto/mdk-react-devkit/core
Standard size variants used across multiple components.
```tsx
type ComponentSize = 'sm' | 'md' | 'lg'
```
Used by: `Button`, `Badge`, `Checkbox`, `Switch`, `Radio`, `Spinner`, `Indicator`, `EmptyState`, `Pagination`.
#### `ButtonSize`
@tetherto/mdk-react-devkit/core
Extends `ComponentSize` with an icon-only variant.
```tsx
type ButtonSize = ComponentSize | 'icon'
```
#### `BorderRadius`
@tetherto/mdk-react-devkit/core
Border radius variants for form components.
```tsx
type BorderRadius = 'none' | 'small' | 'medium' | 'large' | 'full'
```
Used by: `Checkbox`, `Switch`, `Radio`.
#### `ColorVariant`
@tetherto/mdk-react-devkit/core
Comprehensive color variants for components.
```tsx
type ColorVariant =
| 'default'
| 'primary'
| 'secondary'
| 'success'
| 'warning'
| 'error'
| 'info'
```
#### `StatusVariant`
@tetherto/mdk-react-devkit/core
Status variants for state indication.
```tsx
type StatusVariant = 'success' | 'processing' | 'error' | 'warning' | 'default' | 'idle'
```
Used by: badges, notifications, status indicators.
#### `ComponentColor`
@tetherto/mdk-react-devkit/core
Color options for form components.
```tsx
type ComponentColor = 'default' | 'primary' | 'success' | 'warning' | 'error'
```
Used by: `Checkbox`, `Switch`, `Radio`, `Typography`.
#### `Position`
@tetherto/mdk-react-devkit/core
Position/side options for UI elements.
```tsx
type Position = 'top' | 'right' | 'bottom' | 'left'
```
Used by: `Tooltip`, `Popover`, chart legends.
#### `TextAlign`
@tetherto/mdk-react-devkit/core
Text alignment options.
```tsx
type TextAlign = 'left' | 'center' | 'right' | 'justify'
```
#### `FlexAlign`
@tetherto/mdk-react-devkit/core
Flex/grid alignment options.
```tsx
type FlexAlign = 'start' | 'center' | 'end'
```
### Component types
#### `ButtonVariant`
@tetherto/mdk-react-devkit/core
Button visual variants.
```tsx
type ButtonVariant =
| 'primary'
| 'secondary'
| 'danger'
| 'tertiary'
| 'link'
| 'icon'
| 'outline'
| 'ghost'
```
#### `ButtonIconPosition`
@tetherto/mdk-react-devkit/core
Where icons appear in buttons.
```tsx
type ButtonIconPosition = 'left' | 'right'
```
#### `NotificationVariant`
@tetherto/mdk-react-devkit/core
Toast/notification variants.
```tsx
type NotificationVariant = 'success' | 'error' | 'warning' | 'info'
```
#### `BadgeStatus`
@tetherto/mdk-react-devkit/core
Badge status options.
```tsx
type BadgeStatus = 'success' | 'processing' | 'error' | 'warning' | 'default'
```
#### `TypographyColor`
@tetherto/mdk-react-devkit/core
Typography color options including muted.
```tsx
type TypographyColor = 'default' | 'primary' | 'success' | 'warning' | 'error' | 'muted'
```
### Utility types
#### `UnknownRecord`
@tetherto/mdk-react-devkit/core
Generic type for objects with unknown structure.
```tsx
type UnknownRecord = Record
```
#### `Nullable` / `Optional` / `Maybe`
@tetherto/mdk-react-devkit/core
Null/undefined wrapper types.
```tsx
type Nullable = T | null
type Optional = T | undefined
type Maybe = T | null | undefined
```
#### `Status`
@tetherto/mdk-react-devkit/core
Async operation status.
```tsx
type Status = 'idle' | 'loading' | 'success' | 'error'
```
### API types
#### `PaginationParams`
@tetherto/mdk-react-devkit/core
Pagination request parameters.
```tsx
type PaginationParams = {
limit?: number
offset?: number
page?: number
}
```
#### `PaginatedResponse`
@tetherto/mdk-react-devkit/core
Paginated response wrapper.
```tsx
type PaginatedResponse = {
data: T[]
page: number
total: number
totalPages: number
}
```
#### `ApiResponse`
@tetherto/mdk-react-devkit/core
API response wrapper.
```tsx
type ApiResponse = {
data: T
message?: string
status: number
}
```
#### `ApiError`
@tetherto/mdk-react-devkit/core
API error response structure.
```tsx
type ApiError = {
error: string
message: string
status: number
data?: {
message?: string
}
}
```
### Data table types
@tetherto/mdk-react-devkit/core
Re-exported from TanStack Table for convenience.
```tsx
DataTableColumnDef,
DataTableExpandedState,
DataTablePaginationState,
DataTableRow,
DataTableRowSelectionState,
DataTableSortingState,
} from '@tetherto/mdk-react-devkit/core'
```
### Value types
#### `ValueUnit`
@tetherto/mdk-react-devkit/core
A value paired with a unit, used for display formatting.
```tsx
type ValueUnit = {
value: number | string | null
unit: string
realValue: number
}
```
#### `HashrateUnit` / `CurrencyUnit`
@tetherto/mdk-react-devkit/core
Specialized value-unit aliases.
```tsx
type HashrateUnit = ValueUnit
type CurrencyUnit = ValueUnit
```
#### `UnitLabel`
@tetherto/mdk-react-devkit/core
SI-prefix unit labels.
```tsx
type UnitLabel = 'decimal' | 'k' | 'M' | 'G' | 'T' | 'P'
```
### Time types
#### `TimeRangeFormatted`
@tetherto/mdk-react-devkit/core
A formatted time range.
```tsx
type TimeRangeFormatted = {
start: string
end: string
formatted: string
}
```
#### `TimeInterval`
@tetherto/mdk-react-devkit/core
A time interval with start/end timestamps.
```tsx
type TimeInterval = {
start: number
end: number
}
```
### Chart types
#### `ChartLegendPosition`
@tetherto/mdk-react-devkit/core
Chart legend position options.
```tsx
type ChartLegendPosition = 'top' | 'bottom' | 'left' | 'right' | 'center' | 'chartArea'
```
#### `WeightedAverageResult`
@tetherto/mdk-react-devkit/core
Result from weighted average calculation.
```tsx
type WeightedAverageResult = {
avg: number
totalWeight: number
weightedValue: number
}
```
#### `ErrorWithTimestamp`
@tetherto/mdk-react-devkit/core
An error with optional timestamp.
```tsx
type ErrorWithTimestamp = {
msg?: string
message?: string
timestamp?: number | string
}
```
## Foundation types
Foundation types describe the shape of devices, containers, alerts, site configuration, and settings data flowing through `@tetherto/mdk-react-devkit/foundation` components and API responses. They are organized into barrels including `alerts`, `config`, `device`, and `settings.types`.
### Prerequisites
- Complete the [@tetherto/mdk-react-devkit installation and add the dependency](/v0-0-2/ui/react/foundation#prerequisites)
### Import
@tetherto/mdk-react-devkit/foundation
```tsx
Alert,
Device,
DeviceLast,
DeviceInfo,
ContainerSnap,
ContainerStats,
MinerStats,
MinerConfig,
SettingsUser,
PermLevel,
GlobalConfig,
TimelineChartDataPoint,
TimelineChartDataset,
TimelineChartData,
ChartRange,
AxisTitleText,
MetricsEfficiencyLogEntry,
} from '@tetherto/mdk-react-devkit/foundation'
```
### Alert types
#### `Alert`
@tetherto/mdk-react-devkit/foundation
Raw alert record as it appears on a device's `alerts` array.
```tsx
type Alert = {
id?: string
severity: string
createdAt: number | string
name: string
description: string
message?: string
uuid?: string
code?: string | number
[key: string]: unknown
}
```
The `severity` field uses string values like `critical`, `high`, `medium`, `low`. The open `[key: string]: unknown` index signature allows
vendor-specific fields without breaking the type contract.
#### `LogFormattedAlertData`
@tetherto/mdk-react-devkit/foundation
Alert reshaped for log display components (e.g., `AlertsLog`).
```tsx
type LogFormattedAlertData = {
title: string
subtitle: string
status: string
severityLevel: number
creationDate: number | string
body: string
id: string
uuid?: string
[key: string]: unknown
}
```
### Device types
The `Device` family models everything that appears on the device explorer: miners, containers, power meters, temperature sensors, and cabinets.
The shape is intentionally permissive (open index signatures, optional fields) because devices come from a live API that adds vendor-specific fields over time.
#### `Device`
@tetherto/mdk-react-devkit/foundation
The root device record. Used pervasively in the [operations centre](/v0-0-2/ui/react/foundation/operations) components.
```tsx
type Device = {
id: string
type: string
tags?: string[]
rack?: string
last?: DeviceLast
username?: string
info?: DeviceInfo
containerId?: string
address?: string | null
code?: string
alerts?: Alert[] | null
powerMeters?: Device[]
tempSensors?: Device[]
transformerTempSensor?: Device
rootTempSensor?: Device
[key: string]: unknown
}
```
The `type` string discriminates devices by category (such as miner or container) and by vendor. The `last` field carries the latest snapshot from the device.
#### `DeviceLast`
@tetherto/mdk-react-devkit/foundation
The latest reading wrapper that lives on `Device.last`.
```tsx
type DeviceLast = {
err?: string | null
type?: string
snap?: ContainerSnap
alerts?: Alert[] | null
[key: string]: unknown
}
```
`err` is a connection or upstream error string when the device is unreachable. `snap` carries the actual stats and config payload.
#### `DeviceInfo`
@tetherto/mdk-react-devkit/foundation
Identification and placement metadata that lives on `Device.info`.
```tsx
type DeviceInfo = {
container?: string
pos?: string
poolConfig?: string
serialNum?: string
macAddress?: string | null
posHistory?: Partial
[key: string]: unknown
}
```
#### `PosHistoryEntry`
@tetherto/mdk-react-devkit/foundation
A single past placement of a miner.
```tsx
type PosHistoryEntry = {
container: string
pos: string
removedAt: number
}
```
#### `DeviceData`
@tetherto/mdk-react-devkit/foundation
A flattened version of `Device` with a guaranteed (non-optional) `snap` field, returned by the `getDeviceData` helper.
```tsx
type DeviceData = {
id: string
type: string
tags?: string[]
rack?: string
snap: ContainerSnap
alerts?: Alert[]
username?: string
info?: DeviceInfo
containerId?: string
address?: string
err?: string
[key: string]: unknown
}
```
### Container types
#### `Container`
@tetherto/mdk-react-devkit/foundation
A `Device` specialized for containers, with container-specific `info` and `last` shapes.
```tsx
type Container = {
info?: Partial
last?: Partial
} & Device
```
#### `ContainerInfo`
@tetherto/mdk-react-devkit/foundation
Cooling, supply, and pressure metadata for a container.
```tsx
type ContainerInfo = {
container: string
cooling_system: Record
cdu: Record
primary_supply_temp: number
second_supply_temp1: number
second_supply_temp2: number
supply_liquid_temp: number
supply_liquid_set_temp: number
supply_liquid_pressure: number
return_liquid_pressure: number
}
```
#### `ContainerPosInfo`
@tetherto/mdk-react-devkit/foundation
Position descriptor for a device inside a container (PDU/socket coordinates).
```tsx
type ContainerPosInfo = {
containerInfo: Partial<{ container: string; type: string }>
pdu: string | number
socket: string | number
pos: string
[key: string]: unknown
}
```
#### `ContainerLast`
@tetherto/mdk-react-devkit/foundation
Last-snapshot wrapper for a container.
```tsx
type ContainerLast = {
snap: {
stats?: Partial
}
alerts: unknown[] | null
err: string | null
}
```
#### `ContainerSnap`
@tetherto/mdk-react-devkit/foundation
The `snap` payload found on `DeviceLast.snap` for both miners and containers. `stats` is what most components read.
```tsx
type ContainerSnap = {
stats?: Partial
config?: Record
}
```
#### `ContainerStats`
@tetherto/mdk-react-devkit/foundation
The big stats blob produced by every container snapshot.
```tsx
type ContainerStats = {
status: string
ambient_temp_c: number
humidity_percent: number
power_w: number
container_specific: Partial
distribution_box1_power_w: number
distribution_box2_power_w: number
stats: Record
temperature_c: Partial
frequency_mhz: Partial
miner_specific: Partial
[key: string]: unknown
}
```
`status` is one of `running`, `offline`, `stopped` (see `CONTAINER_STATUS` in [Constants](/v0-0-2/reference/app-toolkit/ui-kit/constants#foundation-constants)).
#### `ContainerSpecific`
@tetherto/mdk-react-devkit/foundation
Container-specific stats (currently the PDU array).
```tsx
type ContainerSpecific = {
pdu_data: Partial[]
[key: string]: unknown
}
```
#### `ContainerPduData`
@tetherto/mdk-react-devkit/foundation
Per-PDU power and status reading.
```tsx
type ContainerPduData = {
power_w: number
status: number
}
```
#### `StatsTemperatureC`
@tetherto/mdk-react-devkit/foundation
Temperature stats with per-chip detail.
```tsx
type StatsTemperatureC = {
avg: number
min: number
max: number
chips: TempChipData[]
[key: string]: unknown
}
```
#### `StatsFrequencyMhz`
@tetherto/mdk-react-devkit/foundation
Frequency stats with per-chip detail.
```tsx
type StatsFrequencyMhz = {
avg: number
chips: ChipData[]
[key: string]: unknown
}
```
#### `ChipData` / `TempChipData`
@tetherto/mdk-react-devkit/foundation
Per-chip readings.
```tsx
type ChipData = {
index: number
current: number
}
type TempChipData = {
index: number
max?: number
min?: number
avg?: number
}
```
#### `MinerSpecificStats`
@tetherto/mdk-react-devkit/foundation
Miner-specific stats blob.
```tsx
type MinerSpecificStats = {
upfreq_speed: number
[key: string]: unknown
}
```
### Miner types
#### `MinerStats`
@tetherto/mdk-react-devkit/foundation
Per-miner stats reported on each snapshot.
```tsx
type MinerStats = {
status?: string
uptime_ms?: number
power_w?: number
hashrate_mhs?: MinerHashrateMhs
poolHashrate?: string
temperature_c?: { max?: number }
}
```
#### `MinerHashrateMhs`
@tetherto/mdk-react-devkit/foundation
Hashrate readings, currently just the rolling 5-minute window.
```tsx
type MinerHashrateMhs = {
t_5m?: number
}
```
#### `MinerInfo`
@tetherto/mdk-react-devkit/foundation
Identifying info for a miner (used by miner record cards).
```tsx
type MinerInfo = {
container?: string
pos?: string
macAddress?: string
serialNum?: string
}
```
#### `MinerConfig`
@tetherto/mdk-react-devkit/foundation
Mutable miner configuration.
```tsx
type MinerConfig = {
firmware_ver?: string
power_mode?: string
led_status?: boolean
}
```
`power_mode` values come from `MINER_POWER_MODE` (`sleep`, `low`, `normal`, `high`).
#### `MinerDeviceSnapshot`
@tetherto/mdk-react-devkit/foundation
Lightweight snapshot wrapper holding only `MinerConfig`.
```tsx
type MinerDeviceSnapshot = {
last?: { snap?: { config?: MinerConfig } }
}
```
#### `MinerRecord`
@tetherto/mdk-react-devkit/foundation
Combined miner record used by list/table views.
```tsx
type MinerRecord = {
id?: string
shortCode?: string
info?: MinerInfo
address?: string
type?: string
alerts?: unknown[]
stats?: MinerStats
config?: MinerConfig
device?: MinerDeviceSnapshot
error?: string
err?: string
isPoolStatsEnabled?: boolean
}
```
### Power and cabinet types
#### `PowerMeter`
@tetherto/mdk-react-devkit/foundation
Minimal power meter reading shape.
```tsx
type PowerMeter = {
last?: {
snap?: {
stats?: {
power_w?: number
}
}
}
}
```
#### `LvCabinetRecord`
@tetherto/mdk-react-devkit/foundation
LV cabinet record carrying its associated power meters.
```tsx
type LvCabinetRecord = {
id: string
powerMeters?: PowerMeter[]
}
```
### Config types
#### `GlobalConfig`
@tetherto/mdk-react-devkit/foundation
Site-wide configuration from your API or store, including nominal targets for reporting dashboards. Load this shape in your app from your API or
store and pass it into foundation reporting UI as needed.
```tsx
type GlobalConfig = {
nominalSiteHashrate_MHS?: number
nominalAvailablePowerMWh?: number
nominalPowerConsumption_MW?: number
nominalWeightedAvgEfficiency_WThs?: number
nominalMinerCapacity?: number
isAutoSleepAllowed?: boolean
siteEnergyDataThresholdMWh?: number
[key: string]: unknown
}
```
| Field | Type | Description |
|-------|------|-------------|
| `nominalSiteHashrate_MHS` | `number` | Nominal site hashrate (MH/s) |
| `nominalAvailablePowerMWh` | `number` | Nominal available power (MWh on the wire) |
| `nominalPowerConsumption_MW` | `number` | Nominal power consumption (MW) |
| `nominalWeightedAvgEfficiency_WThs` | `number` | Nominal weighted average efficiency (W/TH) |
| `nominalMinerCapacity` | `number` | Nominal miner capacity |
| `isAutoSleepAllowed` | `boolean` | Whether auto-sleep is permitted for the site |
| `siteEnergyDataThresholdMWh` | `number` | Energy data threshold (MWh) |
| `[key: string]` | `unknown` | Additional API fields without breaking the type |
### Timeline chart types
Types for [`TimelineChart`](/v0-0-2/ui/react/foundation/dashboard/charts#timelinechart) in `@tetherto/mdk-react-devkit/foundation`. Each segment uses `x: [startMs, endMs]` and `y` matching a row in `labels`.
#### `TimelineChartDataPoint`
@tetherto/mdk-react-devkit/foundation
One horizontal segment on a timeline row.
```tsx
type TimelineChartDataPoint = {
x: [number, number]
y: string | undefined
}
```
| Field | Type | Description |
|-------|------|-------------|
| `x` | `[number, number]` | Segment start and end (epoch ms) |
| `y` | `string \| undefined` | Row label; must match an entry in `TimelineChartData.labels` |
#### `TimelineChartDataset`
@tetherto/mdk-react-devkit/foundation
A named group of segments (legend entry).
```tsx
type TimelineChartDataset = {
label: string
data: TimelineChartDataPoint[]
borderColor?: string[]
backgroundColor?: string[]
color?: string
}
```
#### `TimelineChartData`
@tetherto/mdk-react-devkit/foundation
Full payload for `initialData` and `newData`.
```tsx
type TimelineChartData = {
labels: string[]
datasets: TimelineChartDataset[]
}
```
| Field | Type | Description |
|-------|------|-------------|
| `labels` | `string[]` | Row names (Y axis) |
| `datasets` | `TimelineChartDataset[]` | Segment groups keyed by `label` |
#### `ChartRange`
@tetherto/mdk-react-devkit/foundation
Visible time window for the `range` prop.
```tsx
type ChartRange = {
min: Date | number
max: Date | number
}
```
#### `AxisTitleText`
@tetherto/mdk-react-devkit/foundation
Axis title strings for `axisTitleText`.
```tsx
type AxisTitleText = {
x: string
y: string
}
```
### Metrics efficiency types
Types for **`/auth/metrics/efficiency`**, used by the site view tab on
[`OperationsEfficiency`](/v0-0-2/ui/react/foundation/reporting/operations-efficiency).
#### `MetricsEfficiencyLogEntry`
@tetherto/mdk-react-devkit/foundation
One point on the site efficiency time series.
```tsx
type MetricsEfficiencyLogEntry = {
ts: number
efficiencyWThs: number
}
```
| Field | Type | Description |
|-------|------|-------------|
| `ts` | `number` | Timestamp (epoch ms) |
| `efficiencyWThs` | `number` | Site efficiency (W/TH) at `ts` |
#### `MetricsEfficiencySummary`
@tetherto/mdk-react-devkit/foundation
Summary block returned with the efficiency metrics response.
```tsx
type MetricsEfficiencySummary = {
avgEfficiencyWThs: number | null
}
```
#### `MetricsEfficiencyResponse`
@tetherto/mdk-react-devkit/foundation
Wrapper for the efficiency metrics endpoint (`log` entries plus summary), using the shared `MetricsResponse` shape from `@tetherto/mdk-react-devkit/foundation`.
### Settings types
#### `SettingsUser`
@tetherto/mdk-react-devkit/foundation
A user record as it appears in user-management lists.
```tsx
type SettingsUser = {
id: string
name?: string
email: string
role: string
last_login?: string
lastActive?: string
[key: string]: unknown
}
```
#### `RoleOption`
@tetherto/mdk-react-devkit/foundation
Role option for select dropdowns. Also exported as the array `USER_ROLES` in [Constants](/v0-0-2/reference/app-toolkit/ui-kit/constants#foundation-constants).
```tsx
type RoleOption = {
label: string
value: string
}
```
#### `PermLevel`
@tetherto/mdk-react-devkit/foundation
Permission level values.
```tsx
type PermLevel = 'rw' | 'r' | false
```
#### `RolesPermissionsData`
@tetherto/mdk-react-devkit/foundation
The shape consumed by [`RBACControlSettings`](/v0-0-2/ui/react/foundation/settings/access-control).
```tsx
type RolesPermissionsData = {
permissions: Record>
labels: Record
}
```
#### `SettingsExportData`
@tetherto/mdk-react-devkit/foundation
The export envelope produced and consumed by [`ImportExportSettings`](/v0-0-2/ui/react/foundation/settings/import-export).
Generic `TExtra` lets you attach app-specific extras.
```tsx
type SettingsExportData = Record> = {
headerControls?: Record
featureFlags?: Record
timestamp?: string
version?: string
} & TExtra
```
#### `ImportResult`
@tetherto/mdk-react-devkit/foundation
Result returned from settings import operations.
```tsx
type ImportResult = {
success: boolean
applied?: string[]
errors?: string[]
message?: string
}
```
# Utilities (/v0-0-2/reference/app-toolkit/ui-kit/utilities)
This page documents helper functions exported by the MDK packages.
- [Core utilities](#core-utilities) ships **15 utility modules** with functions for formatting, dates, validation, conversions, class-name merging, and more
- [Foundation utilities](#foundation-utilities) currently ships a single public utility module (`settings-utils`) for parsing, validating, and exporting settings JSON
## Core utilities
Helper functions exported by `@tetherto/mdk-react-devkit/core` for formatting, dates, validation, conversions, and class-name merging.
### Prerequisites
- Complete the [@tetherto/mdk-react-devkit installation and add the dependency](/v0-0-2/ui/react/core#prerequisites)
### Import
@tetherto/mdk-react-devkit/core
```tsx
formatNumber,
formatHashrate,
formatDate,
formatRelativeTime,
cn,
isEmpty,
isValidEmail,
} from '@tetherto/mdk-react-devkit/core'
```
### Formatting utilities
#### `formatNumber`
@tetherto/mdk-react-devkit/core
Format numbers with locale formatting and configurable options.
```tsx
formatNumber(1234.567) // "1,234.57"
formatNumber(null) // "-"
formatNumber(1234, { minimumFractionDigits: 2 }) // "1,234.00"
formatNumber(undefined, {}, 'N/A') // "N/A"
```
#### `formatHashrate`
@tetherto/mdk-react-devkit/core
Format hashrate values with rounding.
```tsx
formatHashrate(150.456) // "150.46"
formatHashrate(null) // "-"
```
#### `formatCurrency`
@tetherto/mdk-react-devkit/core
Format currency values.
```tsx
formatCurrency(1234.56, 'USD') // "$1,234.56"
formatCurrency(0.00012345, 'BTC') // "βΏ0.00012345"
```
#### `getPercentFormattedNumber`
@tetherto/mdk-react-devkit/core
Format numbers as percentages.
```tsx
getPercentFormattedNumber(0.75) // "75%"
getPercentFormattedNumber(0.1234, 1) // "12.3%"
```
#### `formatValueUnit`
@tetherto/mdk-react-devkit/core
Format value-unit objects.
```tsx
formatValueUnit(150, 'TH/s') // "150 TH/s"
```
### Date utilities
#### `formatDate`
@tetherto/mdk-react-devkit/core
Format dates with customizable patterns.
```tsx
formatDate(new Date()) // "Jan 15, 2025"
formatDate(1705334400000, { format: 'yyyy-MM-dd' }) // "2025-01-15"
```
#### `formatRelativeTime`
@tetherto/mdk-react-devkit/core
Format dates as relative time strings.
```tsx
formatRelativeTime(new Date(Date.now() - 3600000)) // "1h ago"
formatRelativeTime(new Date(Date.now() - 86400000)) // "1d ago"
```
#### `formatChartDate`
@tetherto/mdk-react-devkit/core
Format timestamps for chart display.
```tsx
formatChartDate(1705334400) // "Jan 15"
formatChartDate(1705334400, true) // "Jan 15, 2025"
```
#### `isValidTimestamp`
@tetherto/mdk-react-devkit/core
Check if a timestamp is valid.
```tsx
isValidTimestamp(1705334400000) // true
isValidTimestamp('invalid') // false
```
#### `parseMonthLabelToDate`
@tetherto/mdk-react-devkit/core
Parse month labels to `Date` objects.
```tsx
parseMonthLabelToDate('01-26') // Date(2026, 0, 1)
parseMonthLabelToDate('03-2025') // Date(2025, 2, 1)
```
#### `getPastDateFromDate`
@tetherto/mdk-react-devkit/core
Get a date in the past.
```tsx
getPastDateFromDate({ dateTs: Date.now(), days: 7 }) // 7 days ago
```
### Validation utilities
#### `isEmpty`
@tetherto/mdk-react-devkit/core
Check if a value is empty.
```tsx
isEmpty(null) // true
isEmpty('') // true
isEmpty([]) // true
isEmpty({}) // true
isEmpty('hello') // false
isEmpty([1, 2, 3]) // false
```
#### `isValidEmail`
@tetherto/mdk-react-devkit/core
Validate email addresses.
```tsx
isValidEmail('user@example.com') // true
isValidEmail('invalid') // false
```
#### `isValidUrl`
@tetherto/mdk-react-devkit/core
Validate URLs.
```tsx
isValidUrl('https://example.com') // true
isValidUrl('not-a-url') // false
```
#### `isNil`
@tetherto/mdk-react-devkit/core
Check if value is `null` or `undefined`.
```tsx
isNil(null) // true
isNil(undefined) // true
isNil(0) // false
isNil('') // false
```
#### `isPlainObject`
@tetherto/mdk-react-devkit/core
Check if value is a plain object.
```tsx
isPlainObject({}) // true
isPlainObject({ a: 1 }) // true
isPlainObject([]) // false
isPlainObject(new Date()) // false
```
### Class name utilities
#### `cn`
@tetherto/mdk-react-devkit/core
Merge class names using [clsx](https://github.com/lukeed/clsx) and [tailwind-merge](https://github.com/dcastil/tailwind-merge).
```tsx
cn('px-4', 'py-2') // "px-4 py-2"
cn('text-red', isError && 'bg-red') // conditional classes
cn('p-4', { 'hidden': !visible }) // object syntax
```
### Conversion utilities
#### `toMW` / `toMWh`
@tetherto/mdk-react-devkit/core
Convert watts to megawatts.
```tsx
toMW(1000000) // 1
toMWh(1000000) // 1
```
#### `toPHS`
@tetherto/mdk-react-devkit/core
Convert raw hashrate to `PH/s`.
```tsx
toPHS(1000000000000000) // 1
```
#### `convertMpaToBar`
@tetherto/mdk-react-devkit/core
Convert pressure units.
```tsx
convertMpaToBar(0.1) // 1
```
#### `unitToKilo`
@tetherto/mdk-react-devkit/core
Convert to kilo units.
```tsx
unitToKilo(1000) // 1
```
### Number utilities
#### `percentage`
@tetherto/mdk-react-devkit/core
Calculate percentage.
```tsx
percentage(25, 100) // 25
percentage(1, 4) // 25
```
#### `getPercentChange`
@tetherto/mdk-react-devkit/core
Calculate percentage change.
```tsx
getPercentChange(110, 100) // 10
getPercentChange(90, 100) // -10
```
#### `convertUnits`
@tetherto/mdk-react-devkit/core
Convert between SI-prefix units.
```tsx
convertUnits(1, 'k', 'M') // 0.001
convertUnits(1000, 'decimal', 'k') // 1
```
#### `safeNumber`
@tetherto/mdk-react-devkit/core
Safely convert to number.
```tsx
safeNumber('123') // 123
safeNumber('invalid') // 0
safeNumber(null) // 0
```
### String utilities
#### `toTitleCase`
@tetherto/mdk-react-devkit/core
Convert string to Title Case.
```tsx
toTitleCase('hello world') // "Hello World"
```
#### `formatMacAddress`
@tetherto/mdk-react-devkit/core
Format MAC addresses.
```tsx
formatMacAddress('aa:bb:cc:dd:ee:ff') // "AA:BB:CC:DD:EE:FF"
```
#### `safeString`
@tetherto/mdk-react-devkit/core
Safely convert to string.
```tsx
safeString(123) // "123"
safeString(null) // ""
```
### Time utilities
#### `secondsToMs`
@tetherto/mdk-react-devkit/core
Convert seconds to milliseconds.
```tsx
secondsToMs(60) // 60000
```
#### `breakTimeIntoIntervals`
@tetherto/mdk-react-devkit/core
Split time range into intervals.
```tsx
breakTimeIntoIntervals(start, end, 3600000) // Array of 1-hour intervals
```
#### `timeRangeWalker`
@tetherto/mdk-react-devkit/core
Generator for iterating through time ranges.
```tsx
for (const interval of timeRangeWalker(start, end, duration)) {
// Process each interval
}
```
### Color utilities
#### `hexToRgba`
@tetherto/mdk-react-devkit/core
Convert hex color to rgba.
```tsx
hexToRgba('#72F59E', 0.5) // "rgba(114, 245, 158, 0.5)"
```
### Array utilities
#### `getNestedValue`
@tetherto/mdk-react-devkit/core
Get nested value by dot-path.
```tsx
getNestedValue({ a: { b: 1 } }, 'a.b') // 1
```
#### `getWeightedAverage`
@tetherto/mdk-react-devkit/core
Calculate weighted average.
```tsx
getWeightedAverage(items, 'value', 'weight')
```
#### `circularArrayAccess`
@tetherto/mdk-react-devkit/core
Create infinite cycling generator.
```tsx
const colors = circularArrayAccess(['red', 'green', 'blue'])
colors.next().value // 'red'
colors.next().value // 'green'
colors.next().value // 'blue'
colors.next().value // 'red' (cycles)
```
## Foundation utilities
Helpers exported by `@tetherto/mdk-react-devkit/foundation` for filtering, formatting, validating, parsing, and exporting settings data.
### Prerequisites
- Complete the [@tetherto/mdk-react-devkit installation and add the dependency](/v0-0-2/ui/react/foundation#prerequisites)
### Import
@tetherto/mdk-react-devkit/foundation
```tsx
filterUsers,
formatRoleLabel,
formatLastActive,
validateSettingsJson,
parseSettingsFile,
exportSettingsToFile,
} from '@tetherto/mdk-react-devkit/foundation'
```
### Settings utilities
#### `filterUsers`
@tetherto/mdk-react-devkit/foundation
Filter a `SettingsUser[]` list by email substring (case-insensitive) and exact role match. Used by the user-management table search.
```tsx
filterUsers({
users,
email: 'alice', // partial, case-insensitive match on user.email
role: 'admin', // exact match on user.role; pass null to skip
})
```
| Parameter | Type | Description |
|-----------|------|-------------|
| `users` | `SettingsUser[]` | Source list |
| `email` | `string \| null \| undefined` | Substring filter on `email` (case-insensitive). Skipped when falsy. |
| `role` | `string \| null \| undefined` | Exact role match. Skipped when falsy. |
#### `formatRoleLabel`
@tetherto/mdk-react-devkit/foundation
Convert a snake case role identifier to a human-readable Title Case label.
```tsx
formatRoleLabel('site_manager') // "Site Manager"
formatRoleLabel('reporting_tool_manager') // "Reporting Tool Manager"
formatRoleLabel('admin') // "Admin"
```
#### `formatLastActive`
@tetherto/mdk-react-devkit/foundation
Format a timestamp string as `MM/DD/YYYY - HH:MM`. Returns `'-'` when the input is missing or invalid.
```tsx
formatLastActive('2025-01-15T14:30:00Z') // "01/15/2025 - 14:30"
formatLastActive(undefined) // "-"
formatLastActive('not-a-date') // "-"
```
#### `validateSettingsJson`
@tetherto/mdk-react-devkit/foundation
Type-guard that checks whether an unknown value is a valid `SettingsExportData`. Returns `true` if the value is an object that contains at least one of `headerControls`, `featureFlags`, or `timestamp`.
```tsx
if (validateSettingsJson(parsed)) {
// parsed is now narrowed to SettingsExportData
}
```
#### `parseSettingsFile`
@tetherto/mdk-react-devkit/foundation
Read a `File` containing settings JSON, validate it, and resolve to `SettingsExportData`. Rejects on invalid JSON, an invalid format, or a file read error.
```tsx
try {
const settings = await parseSettingsFile(file)
applySettings(settings)
} catch (err) {
notifyError('Could not import settings', err.message)
}
```
| Throws | Reason |
|--------|--------|
| `` `Invalid settings file format. Please ensure the file is a valid ${WEBAPP_NAME} settings export.` `` | The JSON parsed but didn't match the `SettingsExportData` shape. The literal interpolates [`WEBAPP_NAME`](/v0-0-2/reference/app-toolkit/ui-kit/constants#webapp_name). |
| `Failed to parse JSON file. Please ensure the file is valid JSON.` | The file contents weren't valid JSON. |
| `Failed to read file.` | The browser couldn't read the file. |
#### `exportSettingsToFile`
@tetherto/mdk-react-devkit/foundation
Serialize `SettingsExportData` to JSON, package it as a downloadable Blob, and trigger a browser download. Returns the generated filename (e.g., `mdk-settings-2025-01-15T14-30-00-000Z.json`). The filename prefix is fixed in the foundation kit and does not interpolate [`WEBAPP_NAME`](/v0-0-2/reference/app-toolkit/ui-kit/constants#webapp_name).
```tsx
const filename = exportSettingsToFile({
headerControls: { poolMiners: true, consumption: false },
featureFlags: { betaCharts: true },
timestamp: new Date().toISOString(),
version: '1.0.0',
})
```
# ORK reference (/v0-0-2/reference/ork)
`@tetherto/mdk-ork` is the orchestration kernel of the MDK stack. This subsection holds the canonical specs for its internal
modules. For the architectural narrative explaining how these modules fit together, see
[Architecture](/v0-0-2/overview/architecture/ork).
## What's documented
- **[Modules](/v0-0-2/reference/ork/modules)**: per-module responsibility, interfaces, state machines, transition rules,
crash-recovery procedures, and scaling characteristics.
# ORK modules (/v0-0-2/reference/ork/modules)
`@tetherto/mdk-ork`'s coordination splits across single-purpose modules. Each owns its own state machine, persistence boundary,
and scaling characteristics. Six modules ship in v0.0.1; two more (Fault Supervisor, Concurrency Manager) are deferred
to a later release.
For the architectural overview that explains how these modules connect, see
[Architecture](/v0-0-2/overview/architecture/ork). This page is the per-module spec.
## How to read this page
Each accordion below covers one module:
- **Responsibility**: what the module owns and the example commands or events it handles.
- **Interfaces**: input and output channels, plus the public function names callers depend on.
- **State machine**: the canonical diagram for the module's internal lifecycle.
- **Crash recovery**: behavior on a fresh start, including how state is reconstructed.
- **Scalability**: where the module can be extracted, sharded, or replicated.
## Modules
**Responsibility**: validates incoming commands against the generic MDK schema, checks permissions, and resolves the correct
worker based on the `deviceId`.
*Example*: when an App Node sends a `reboot` command for `wm001`, the Dispatcher verifies that `wm001` exists, that `reboot` is a
valid capability for it, then passes the request to the State Machine.
**Interfaces**:
- *Input*: receives generic commands via Holepunch RPC (HRPC) and the MDK Protocol from the App Node or MCP Handler.
- *Output*: hands off validated commands to the Command State Machine.
- *Functions*: `dispatchCommand(deviceId, action, payload)`
**State machine**:
```mermaid
stateDiagram-v2
[*] --> Validating
Validating --> RoutingAction : Valid
Validating --> Rejected : Invalid
RoutingAction --> Enqueued
Enqueued --> [*]
```
**Crash recovery**: none needed. In-flight requests fail and must be retried by the client.
**Scalability**: extracted easily; can run independently to offload validation.
**Responsibility**: tracks the execution lifecycle of every single command in the system. Receives execution results directly via
the synchronous HRPC response. If the connection drops or a response is delayed, it relies on the Scheduler to fetch the latest
status via `state.pull` so commands cannot hang.
*Example*: once the `reboot` command is dispatched it transitions to `EXECUTING`. If the HRPC response returns OK it transitions
to `SUCCESS`. If the response hangs, the next `state.pull` fetches the true status from the worker.
**Interfaces**:
- *Input*: receives validated commands from the Dispatcher; status updates from HRPC responses or Scheduler ticks.
- *Output*: invokes the worker HRPC execution layer; emits terminal state results to the caller.
- *Functions*: `enqueue(command)`, `syncState(commandId)`, `cancel(commandId)`
**State machine**:
```mermaid
stateDiagram-v2
[*] --> QUEUED
QUEUED --> DISPATCHED
DISPATCHED --> EXECUTING : HRPC sent
EXECUTING --> SUCCESS : state.pull (response)
EXECUTING --> FAILED : state.pull (error)
EXECUTING --> TIMEOUT
TIMEOUT --> QUEUED : Retry allowed
TIMEOUT --> FAILED : Max retries
SUCCESS --> [*]
FAILED --> [*]
```
**Crash recovery**: on startup, performs a recovery sweep of pending commands. `DISPATCHED` and `EXECUTING` commands are forced
to `TIMEOUT` (and re-queued if retries are available); `QUEUED` commands are left untouched.
**Scalability**: requires state sharding (for example, by device or rack).
**Responsibility**: the phonebook for the entire `@tetherto/mdk-ork` ecosystem. Maps which physical device IDs belong to which connected
worker channels and stores their declared capabilities.
*Example*: a `whatsminer-worker` connects and declares it manages `wm001` and `wm002`. The Registry saves this topology so the
Dispatcher knows exactly where to route a command for `wm001`.
**Interfaces**:
- *Input*: `identity.register` requests from workers.
- *Output*: internal events triggering full state lifecycle binding.
- *Functions*: `resolveWorkerState(target)`
**State machine**:
```mermaid
stateDiagram-v2
[*] --> Unregistered
Unregistered --> Discovered : DHT peer detected
Discovered --> IdentitySaved : identity pulled
IdentitySaved --> Ready : capabilities pulled
Ready --> Terminated : eviction
Terminated --> [*]
```
**Crash recovery**: rebuilt from state, which serves as a baseline to detect workers that were registered but failed to reconnect.
**Scalability**: read-heavy by nature; can be extracted into a read-replica architecture or partitioned by region or rack.
**Responsibility**: a lightweight proxy and routing layer between the upper system (UI / AI) and the downstream workers. Rather
than `@tetherto/mdk-ork` performing heavy time-series aggregations, the *worker* is responsible for storing and aggregating data for the
specific devices it controls. The Collector simply provides an interface to query this data and proxies the response up to the
UI (via App Node) or the AI Agent.
**Worker data handling (telemetry context)**:
- *Compaction*: the worker handles compaction of metrics over large time frames.
- *Local storage*: workers should save telemetry data in a local Hyper DB.
- *As-requested serving*: the worker serves data strictly when `@tetherto/mdk-ork` asks for it, precisely as dictated by the telemetry
schemas in `mdk-contract.json`.
- *Internal scheduling*: to achieve this without blocking `@tetherto/mdk-ork`, the worker may run its own internal scheduler for device
polling.
**Interfaces**:
- *Input*: client or AI telemetry queries (for example, "fetch metrics for device wm001").
- *Output*: normalized telemetry payloads passed straight through from the Worker to the requesting layer.
- *Functions*: `proxyTelemetryFetch(deviceId, queryArgs)`
**State machine**:
```mermaid
stateDiagram-v2
[*] --> Idle
Idle --> Proxying : Request received
Proxying --> RoutingToClient : Worker returns data
Proxying --> Timeout : Worker unresponsive
RoutingToClient --> Idle
```
**Scalability**: because the heavy lifting of data storage and aggregation is pushed down into the isolated worker processes,
the Collector remains stateless and highly scalable as a pure asynchronous router.
**Responsibility**: the system metronome. Triggers repetitive tasks without holding any domain-specific logic itself.
*Example*: emits an internal `tick` event every 5 seconds that the Health Monitor listens to, prompting it to ping all workers.
**Interfaces**:
- *Input*: system clock and configured task intervals.
- *Output*: injects intents (for example, `telemetry.pull`, `health.ping`) into the Dispatcher or Collector.
- *Functions*: `addJob(interval, intent)`, `removeJob(jobId)`
**State machine**:
```mermaid
stateDiagram-v2
[*] --> Waiting
Waiting --> Triggered : Interval elapsed
Triggered --> Waiting
```
**Crash recovery**: timers re-initialize from zero on startup. Tasks are strictly idempotent.
**Scalability**: scales trivially. Requires basic distributed locking to avoid duplicate ticks in multi-process `@tetherto/mdk-ork`
deployments.
**Responsibility**: continuously evaluates the liveness and readiness of every registered worker to prevent routing messages to
dead nodes.
*Example*: if `health.ping` to a worker fails three times in a row, the Health Monitor marks the worker's status as `SICK` and
tells the Registry to halt routing new commands there.
**Interfaces**:
- *Input*: executes `health.ping` sequentially based on Scheduler ticks.
- *Output*: pushes status updates to the Registry.
- *Functions*: `pingWorker(workerId)`, `getHealth(workerId)`
**State machine**:
```mermaid
stateDiagram-v2
[*] --> UNKNOWN
UNKNOWN --> HEALTHY : Ping success
HEALTHY --> SICK : Ping failed (1)
SICK --> DEAD : Ping failed (threshold)
SICK --> HEALTHY : Ping success
DEAD --> HEALTHY : Reconnected
```
**Crash recovery**: blank slate on startup; re-evaluates all known workers immediately via ping.
**Scalability**: operates locally per `@tetherto/mdk-ork` kernel, or via independent lightweight ping agents.
**Idea**: implements circuit-breaker patterns to protect the overall system from cascading failures caused by bad hardware or
software bugs (for example, rejecting commands during a cooling period after repeated errors).
**Status**: deferred for the first cut to keep the core orchestrator simple. If the use case arises (such as complex retry
backoffs or cluster destabilization), it will be reintroduced.
**Idea**: provides guaranteed lock management and queue limits to ensure mutually exclusive commands do not overlap on physical
devices.
**Status**: deferred for the first cut. The system relies solely on the basic command queue. If explicit global locks or
backpressure limits become necessary, this module will be built out.
# Protocol reference (/v0-0-2/reference/protocol)
The MDK Protocol is the contract that crosses every layer of the stack: Workers, `@tetherto/mdk-ork`, and the App Node all
exchange the same envelope. This subsection holds the canonical specs. For the architectural narrative explaining
how the protocol fits together, see [Architecture](/v0-0-2/overview/architecture#the-mdk-protocol).
## What's documented
- **[Messages](/v0-0-2/reference/protocol/messages)**: envelope schema, request/response examples, the full action
catalogue, and the base command set.
# Protocol messages (/v0-0-2/reference/protocol/messages)
Every MDK Protocol message uses the same envelope regardless of which layers are talking. This page is the canonical
spec for that envelope, the full set of protocol actions, and the base command set every worker must support. For the
architectural narrative explaining when each action fires, see [Architecture](/v0-0-2/overview/architecture#the-mdk-protocol).
## Envelope
```json
{
"id": "uuid-v4",
"version": "0.1.0",
"type": "request | response | event",
"action": "",
"sender": "",
"target": " | null",
"deviceId": "string | null",
"timestamp": 1711640000000,
"payload": {}
}
```
External consumers (UI or AI agents) only provide `deviceId`; the `target` worker identity is internally resolved by `@tetherto/mdk-ork`.
A concrete request and response pair, end to end:
```json
// request: App Node asks @tetherto/mdk-ork to reboot device wm001
{
"id": "8d1c-e3a4",
"version": "0.1.0",
"type": "request",
"action": "command.request",
"sender": "appNode:fleet-api:1",
"target": null,
"deviceId": "wm001",
"timestamp": 1711640000000,
"payload": { "command": "reboot" }
}
// response: @tetherto/mdk-ork relays the worker's terminal result
{
"id": "1f9b-77c2",
"version": "0.1.0",
"type": "response",
"action": "command.result",
"sender": "ork:kernel:tx-1",
"target": "appNode:fleet-api:1",
"deviceId": "wm001",
"timestamp": 1711640002145,
"payload": { "status": "SUCCESS", "elapsedMs": 2145 }
}
```
## Actions
| Action | Type | Direction | Purpose |
|---|---|---|---|
| *(DHT presence)* | passive | Worker to DHT topic | Worker joins a known Hyperswarm topic; `@tetherto/mdk-ork` detects its peer connection automatically |
| `identity.request` | request | `@tetherto/mdk-ork` to Worker | Requests the worker's identity and managed devices |
| `capability.request` | request | `@tetherto/mdk-ork` to Worker | Asks the worker to declare its full capability schema |
| `state.pull` | request | `@tetherto/mdk-ork` to Worker | Worker returns a snapshot of state-machine status (low cadence tick, e.g., 60s) |
| `telemetry.pull` | request | `@tetherto/mdk-ork` to Worker | Worker returns device metrics plus history (medium cadence tick, e.g., 10s) |
| `command.request` | request | `@tetherto/mdk-ork` to Worker | Resolves the worker by `deviceId` and dispatches the command for execution |
| `health.ping` | request | `@tetherto/mdk-ork` to Worker | Liveness probe (high cadence tick, e.g., 5s) |
## Base command set
The protocol standardizes a Base Command Set supported by every worker:
- `getConfig`: retrieve current device configuration
- `setConfig`: update device configuration parameters
- `health`: fetch detailed diagnostic health status from the device
# MDK Repositories (/v0-0-2/resources/repositories)
The MDK monorepo makes the frontend and backend components publicly accessible:
- [https://github.com/tetherto/mdk](https://github.com/tetherto/mdk)
*MDK is developed by Tether and released under the [Apache 2.0 license](/v0-0-2/community/contributing#licensing).*
# Roadmap (/v0-0-2/resources/roadmap)
MDK follows a **monthly release cadence** to keep progress visible, collect feedback early, and progressively harden the platform from documentation and
developer tooling to production readiness.
## Roadmap principles
- **Release every month** to keep momentum and feedback loops short
- **Use milestone releases** to mark clear maturity jumps
- **Start with architecture and developer experience**, then harden through alpha and beta
- **Reach production readiness progressively**, not by a single large release
## 2026
2026 includes **four milestone releases**:
- **May β v0.1:** [Public foundation release](#v01-foundation-release)
- **August β v1.0:** [Developer preview](#v10-developer-preview)
- **October β v2.0:** [Pilot release](#v20-pilot-release)
- **December β v3.0:** [Stable production release](#v30-stable-production-release)
```mermaid
graph TB
subgraph foundation [Foundation phase]
v0_1([May 2026 v0.1 Foundation release Docs + initial React components])
iter_jun_jul[[JuneβJuly 2026 Monthly iterations Architecture feedback, DX improvements]]
end
subgraph alpha [Developer preview]
v1_0([August 2026 v1.0 Developer preview First testable developer version])
iter_sep[[September 2026 Monthly iterations Integration hardening, bug fixing]]
end
subgraph beta [Pilot release]
v2_0([October 2026 v2.0 Pilot release First production pilot version])
iter_nov[[November 2026 Monthly iterations Stability, security, observability]]
end
subgraph stable [Stable phase]
v3_0([December 2026 v3.0 Stable release Production-ready baseline])
end
v0_1 --> iter_jun_jul
iter_jun_jul --> v1_0
v1_0 --> iter_sep
iter_sep --> v2_0
v2_0 --> iter_nov
iter_nov --> v3_0
style foundation fill:#F7931A,stroke:#1A1A1A,color:#1A1A1A
style alpha fill:#F7931A,stroke:#1A1A1A,color:#1A1A1A
style beta fill:#F7931A,stroke:#1A1A1A,color:#1A1A1A
style stable fill:#F7931A,stroke:#1A1A1A,color:#1A1A1A
```
## Release plan
### v0.1 Foundation release
The first public release is mainly about **visibility, alignment, and feedback**.
It introduces the initial MDK documentation, core architecture concepts, and a set of React components for the frontend. The goal is to clearly show the direction of
MDK, explain the design choices, and invite feedback from developers, partners, and early contributors.
### JuneβJuly iterations
For June and July, the releases focus on incorporating community and internal feedback.
During this phase, the priority is to refine the architecture, improve documentation clarity, clean up developer experience, and validate whether the
abstractions are understandable and extensible before opening a wider alpha.
### v1.0 Developer preview
August's v1.0 release is the first **real developer alpha**.
At this stage, MDK should be testable end-to-end by external developers, even if still incomplete. The focus is on enabling experimentation, validating
the developer workflow, and surfacing integration gaps early. Stability is improving, but this release is still meant for controlled testing rather than
live production deployment.
### September iterations
After alpha feedback, September will be used for a platform hardening cycle. This month is dedicated to fixing critical issues, improving integration
reliability, expanding the most useful features, and preparing the system for real-world pilots.
### v2.0 Pilot release
October sees the release of v2.0, the first **beta release intended for production testing**.
The aim is that MDK should be stable enough to run in pilot environments with real operational pressure, while still being monitored closely. The objective is
to validate performance, robustness, deployment workflows, and operational fit in real scenarios before declaring the platform stable.
### November iterations
The month after the beta release is focused on closing the remaining production gaps. Priorities include bug fixing, security review, observability
improvements, documentation completion, and polishing the parts of the platform that are essential for broader production use.
### v3.0 Stable production release
The team aims to release v3.0, the first **stable release for production use** in December 2026.
By this point, MDK should offer a solid baseline for deployment in production environments, with stable core interfaces, validated workflows, and
documentation that supports adoption by operators, integrators, and developers building on top of the platform.
# Your first component (/v0-0-2/tutorials/react/first-component)
This tutorial assists first-time users to understand how to render a single MDK component in a React app as fast as possible.
This path skips `` which works for presentational `/core` and `/foundation` imports. For adapter hooks or connected foundation components,
continue with [Wire a React app](/v0-0-2/tutorials/react/tutorial).
## Prerequisites
../../../../_snippets/v0-0-2/ui-prerequisites.mdx
### Clone and build
The MDK UI monorepo (`mdk-ui`) is an npm workspace. Clone the repository, install at the repo root, and build all packages before adding an app or
running the demo.
#### 1.1 Clone the repository
```bash
git clone https://github.com/tetherto/mdk.git
cd mdk
```
```bash
# Requires an SSH key configured for your GitHub account
git clone git@github.com:tetherto/mdk.git
cd mdk
```
#### 1.2 Install and build
```bash
npm install
npm run build
```
This builds `@tetherto/mdk-react-devkit` and the other packages in the monorepo workspace.
### Create your app
#### 2.1 Scaffold a Vite React app
Create a new React app in the `apps/` folder:
```bash
cd apps
npm create vite@latest my-app -- --template react-ts
cd my-app
```
Ignore the CLI "Now run" instructions β follow the MDK-specific steps below.
#### 2.2 Add MDK to `package.json`
Open `package.json` and add the workspace dependency:
```jsonc
"@tetherto/mdk-react-devkit": "*",
```
```jsonc
{
"name": "my-app",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"@tetherto/mdk-react-devkit": "*", // [!code ++]
"react": "^19.2.6", // [!code highlight]
"react-dom": "^19.2.6" // [!code highlight]
},
}
```
Vite also adds a `devDependencies` block (`eslint`, `typescript`, `vite`, `@types/*`); leave that block as is.
#### 2.3 Install from the workspace root
```bash
cd ../.. # from apps/my-app back up to the monorepo root
npm install
```
### Import styles
#### 3.1 Import the MDK stylesheet
Import the MDK stylesheet once in your app's entry point (typically `src/main.tsx`):
```tsx
```
```tsx
createRoot(document.getElementById('root')!).render(
,
)
```
### Render your first component
> This example replaces `App.tsx` with a single mining metric tile that toggles between a normal and an alarmed state.
#### 4.1 Replace `App.tsx` with the demo
This pulls a button from `@tetherto/mdk-react-devkit/core` and a domain component (`SingleStatCard`) from `@tetherto/mdk-react-devkit/foundation`:
```tsx
function App() {
const [overheating, setOverheating] = useState(false)
return (
Miner status
)
}
```
#### 4.2 Run your app
From the monorepo root, start the dev server for your app. The `-w` name must match the `name` field in `apps/my-app/package.json` (`my-app` from step 2.1):
```bash
npm -w my-app run dev
```
Alternatively, from the app folder:
```bash
cd apps/my-app
npm run dev
```
You should see a card showing `Inlet temperature 28 Β°C` with a **Simulate overheat** button below it. Click the button and the card flips into an alarm
state: red border, red text, value jumps to `78 Β°C`, and the whole card pulses. Click **Cool down** to reset.
That is `@tetherto/mdk-react-devkit/core` (the `Button`) and `@tetherto/mdk-react-devkit/foundation` (the `SingleStatCard`) working together:
generic primitives plus mining-domain components, in one app.
## Next steps
- To browse the full kit in one app first, see [Explore the demo](/v0-0-2/ui/react/explore-the-demo)
- [Wire a React app](/v0-0-2/tutorials/react/tutorial): add `` and adapter hooks for connected foundation components
- [Quickstart](/v0-0-2/ui/react/quickstart): minimum integration reference (install, provider, stores, theming)
- [Core](/v0-0-2/ui/react/core): primitives reference (`@tetherto/mdk-react-devkit/core`)
- [Foundation](/v0-0-2/ui/react/foundation): mining-domain components (`@tetherto/mdk-react-devkit/foundation`)
# Wire a React app (/v0-0-2/tutorials/react/tutorial)
This tutorial walks you through the full React stack: three workspace packages, ``, and adapter hooks.
For a faster path that renders one presentational component without the provider, see [Your first component](/v0-0-2/tutorials/react/first-component).
## Prerequisites
../../../../_snippets/v0-0-2/ui-prerequisites.mdx
### Clone and build
The MDK UI monorepo (`mdk-ui`) is an npm workspace. Clone the repository, install at the repo root, and build all packages before adding an app or
running the demo.
#### 1.1 Clone the repository
```bash
git clone https://github.com/tetherto/mdk.git
cd mdk
```
```bash
# Requires an SSH key configured for your GitHub account
git clone git@github.com:tetherto/mdk.git
cd mdk
```
#### 1.2 Install and build
```bash
npm install
npm run build
```
This builds `@tetherto/mdk-react-devkit` and the other packages in the monorepo workspace.
### Create your app
#### 2.1 Scaffold a Vite React app
Create a new React app in the `apps/` folder:
```bash
cd apps
npm create vite@latest my-app -- --template react-ts
cd my-app
```
Ignore the CLI "Now run" instructions β follow the MDK-specific steps below.
#### 2.2 Add MDK to `package.json`
Open `package.json` and add all three workspace packages:
```jsonc
"@tetherto/mdk-react-devkit": "*",
"@tetherto/mdk-react-adapter": "*",
"@tetherto/mdk-ui-core": "*",
```
```jsonc
{
"name": "my-app",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"@tetherto/mdk-react-devkit": "*", // [!code ++]
"@tetherto/mdk-react-adapter": "*", // [!code ++]
"@tetherto/mdk-ui-core": "*", // [!code ++]
"react": "^19.2.6", // [!code highlight]
"react-dom": "^19.2.6" // [!code highlight]
},
}
```
Vite also adds a `devDependencies` block (`eslint`, `typescript`, `vite`, `@types/*`); leave that block as is.
#### 2.3 Install from the workspace root
```bash
cd ../.. # from apps/my-app back up to the monorepo root
npm install
```
### Wrap your app in MdkProvider
`` wires the headless stores from [`@tetherto/mdk-ui-core`](/v0-0-2/reference/app-toolkit/ui-core) into React and sets up the TanStack `QueryClient`. It is required for adapter hooks and connected foundation components. See also [Quickstart β Wrap your app in MdkProvider](/v0-0-2/ui/react/quickstart#wrap-your-app-in-mdkprovider).
#### 3.1 Update `main.tsx`
```tsx
```
Wrap ``:
```tsx
```
```tsx
createRoot(document.getElementById('root')!).render(
{/* [!code ++] */}
{/* [!code ++] */}
,
)
```
### Use adapter hooks and render a component
Adapter hooks subscribe to Zustand stores and re-render when the selected slice changes. This example uses `useAuth` and `useDevices` from the adapter, then renders a presentational `SingleStatCard` from foundation.
#### 4.1 Replace `App.tsx`
```tsx
function App() {
const { permissions } = useAuth()
const { selectedDevices } = useDevices()
return (
Operator dashboard
Selected devices: {selectedDevices?.length ?? 0}
Permissions loaded: {permissions ? 'yes' : 'no'}
)
}
```
Connected foundation components (device explorers, pool manager, settings dashboards) expect `` and often read multiple stores. Start with presentational imports while you wire your API; swap in connected components as your backend comes online.
### Run your app
From the monorepo root:
```bash
npm -w my-app run dev
```
Or from the app folder:
```bash
cd apps/my-app
npm run dev
```
You should see the operator dashboard with device and permission readouts plus a temperature card. The adapter hooks confirm `` is wired β without it, they would throw.
## Next steps
- [Quickstart](/v0-0-2/ui/react/quickstart): install reference, store hooks, and theming
- [Core](/v0-0-2/ui/react/core): primitives reference (`@tetherto/mdk-react-devkit/core`)
- [Foundation](/v0-0-2/ui/react/foundation): mining-domain components (`@tetherto/mdk-react-devkit/foundation`)
### Run the demo app
If you are already at the monorepo root:
```bash
npm run dev:demo
```
If your shell is still in the app folder:
```bash
cd ../.. # apps/my-app β apps β monorepo root
npm run dev:demo
```
Open [http://localhost:5173/mdk](http://localhost:5173/mdk) to browse examples.
# UI Kit (/v0-0-2/ui)
## TL;Dr
Today the published React path is complete; other frameworks are on the [roadmap](/v0-0-2/resources/roadmap) and will reuse the same headless core.
The MDK React UI Kit is a [three-layer stack](#how-the-layers-fit-together):
- [`@tetherto/mdk-ui-core`](/v0-0-2/reference/app-toolkit/ui-core): headless Zustand stores and TanStack Query client factory (framework-agnostic)
- [`@tetherto/mdk-react-adapter`](/v0-0-2/ui/react/quickstart#wrap-your-app-in-mdkprovider): `` and store hooks; binds the core into your UI runtime (React today)
- [`@tetherto/mdk-react-devkit`](/v0-0-2/ui/react/get-started): [`/core`](/v0-0-2/ui/react/core) primitives and [`/foundation`](/v0-0-2/ui/react/foundation) mining-domain components
## Framework quickstarts
}
title={React}
href="/v0-0-2/ui/react/get-started"
description={
Get started with the React MDK UI Kit
}
/>
Vue}
href="/v0-0-2/resources/roadmap"
description={
<>
Reactive hooks for Vue via @tetherto/mdk-vueLearn about the release schedule β
>
}
/>
Svelte}
href="/v0-0-2/resources/roadmap"
description={
<>
Reactive hooks for Svelte via @tetherto/mdk-svelteLearn about the release schedule β
>
}
/>
Web Components}
href="/v0-0-2/resources/roadmap"
description={
<>
Framework-agnostic Web Components via @tetherto/wcLearn about the release schedule β
>
}
/>
## How the layers fit together
The MDK React UI Kit and subsequent frameworks will share a similar architecture:
```mermaid
graph TB
subgraph headless [Layer 1: Headless]
uiCore([mdk-ui-core Zustand stores Β· Query client factory Framework-agnostic])
end
subgraph adapter [Layer 2: Framework adapter]
reactAdapter([mdk-react-adapter MdkProvider Β· store hooks React today])
end
subgraph uiLib [Layer 3: Framework UI library]
reactDevkit([mdk-react-devkit /core + /foundation Primitives and mining-domain UI])
end
uiCore --> reactAdapter
reactAdapter --> reactDevkit
style headless fill:#F7931A,stroke:#1A1A1A,color:#1A1A1A
style adapter fill:#F7931A,stroke:#1A1A1A,color:#1A1A1A
style uiLib fill:#F7931A,stroke:#1A1A1A,color:#1A1A1A
```
## Next steps
- [Quickstart](/v0-0-2/ui/react/quickstart): integrate the three packages into your React app
- [Explore the demo](/v0-0-2/ui/react/explore-the-demo): run the demo app in your browser
# @tetherto/mdk-react-devkit (/v0-0-2/ui/react/core)
[The core entrypoint](/v0-0-2/resources/repositories) (`@tetherto/mdk-react-devkit/core`) provides the foundational **React** UI components, utilities, and theme system for the MDK React UI Kit. This is the `/core` subpath of the devkit package, not the framework-agnostic [`@tetherto/mdk-ui-core`](/v0-0-2/reference/app-toolkit/ui-core) headless package.
Mining-domain components, hooks, and settings live on the [`/foundation`](/v0-0-2/ui/react/foundation) entrypoint. Both subpaths are exported from `@tetherto/mdk-react-devkit`.
## Prerequisites
../../../../../_snippets/v0-0-2/package-prerequisites-install.mdx
## What's included
### Components
Production-ready React components organized by category:
| Category | Description |
|----------|-------------|
| [Forms](/v0-0-2/ui/react/core/components/forms) | Input and form control components |
| [Overlays](/v0-0-2/ui/react/core/components/overlays) | Dialogs, popovers, tooltips, and toasts |
| [Data display](/v0-0-2/ui/react/core/components/data-display) | Cards, tables, tags, and data presentation |
| [Charts](/v0-0-2/ui/react/core/components/charts) | Data visualization components |
| [Navigation](/v0-0-2/ui/react/core/components/navigation) | Sidebar, tabs, and breadcrumbs |
| [Loading](/v0-0-2/ui/react/core/components/loading) | Spinners, loaders, and progress indicators |
| [Errors](/v0-0-2/ui/react/core/components/errors) | Error boundaries, error cards, and alerts |
| [Logs](/v0-0-2/ui/react/core/components/logs) | Log display components |
See the [Components reference](/v0-0-2/ui/react/core/components) for the full list with demo links.
### Icons
70+ purpose-built icons for Bitcoin mining applications:
- Navigation icons (Dashboard, Farms, Inventory, etc.)
- Status icons (Mining, Offline, Error, etc.)
- Weather icons (Sunny, Cloudy, Rainy, etc.)
- Alarm icons (Temperature, Pressure, Fluid, etc.)
### Utilities
Helper functions for common operations:
- [`formatNumber`](/v0-0-2/reference/app-toolkit/ui-kit/utilities#formatnumber), [`formatHashrate`](/v0-0-2/reference/app-toolkit/ui-kit/utilities#formathashrate), [`formatCurrency`](/v0-0-2/reference/app-toolkit/ui-kit/utilities#formatcurrency): Number formatting
- [`formatDate`](/v0-0-2/reference/app-toolkit/ui-kit/utilities#formatdate), [`formatRelativeTime`](/v0-0-2/reference/app-toolkit/ui-kit/utilities#formatrelativetime): Date formatting
- [`cn`](/v0-0-2/reference/app-toolkit/ui-kit/utilities#cn): Class name merging ([clsx](https://github.com/lukeed/clsx) wrapper)
- Conversion utilities for energy, hashrate, and units
- Validation utilities for email, URL, and empty checks
### Theme system
CSS custom properties and utilities for consistent styling:
- Color tokens (primary, gray scales)
- Spacing scale
- Border radius scale
- Font size scale
- Light/dark mode support
### Types
TypeScript types for type-safe development:
- [`ComponentSize`](/v0-0-2/reference/app-toolkit/ui-kit/types#componentsize), [`ButtonVariant`](/v0-0-2/reference/app-toolkit/ui-kit/types#buttonvariant), [`ColorVariant`](/v0-0-2/reference/app-toolkit/ui-kit/types#colorvariant)
- [`Status`](/v0-0-2/reference/app-toolkit/ui-kit/types#status), [`PaginationParams`](/v0-0-2/reference/app-toolkit/ui-kit/types#paginationparams), [`ApiResponse`](/v0-0-2/reference/app-toolkit/ui-kit/types#apiresponse)
- Chart types, table types, and utility types
## Reference
Detailed reference material lives in the unified [Reference section](/v0-0-2/reference). Core (`/core`) slices are:
- [Constants](/v0-0-2/reference/app-toolkit/ui-kit/constants#core-constants): colors, units, currency, chart configs
- [Types](/v0-0-2/reference/app-toolkit/ui-kit/types#core-types): UI primitives, API shapes, value-unit types
- [Utilities](/v0-0-2/reference/app-toolkit/ui-kit/utilities#core-utilities): formatting, validation, conversions, `cn` and friends
## Import examples
```tsx
// Import components
// Import utilities
// Import types
// Import icons
// Import styles (required for component styling)
```
## Troubleshooting
- **`npm install` warns about `engines.npm`, or `npm -v` is 10.x on Node 22** β [Upgrade npm](/v0-0-2/how-to/ui/react/upgrade-npm), then rerun `npm install` and `npm run build` from the monorepo root.
# Components (/v0-0-2/ui/react/core/components)
The `@tetherto/mdk-react-devkit/core` package provides production-ready React components. All components are built with accessibility in mind and support both light and dark themes.
## Import
### Prerequisites
- Complete the [installation](/v0-0-2/ui/react/quickstart)
../../../../../../_snippets/v0-0-2/clone-build.mdx
- Import the core styles in your app's entry point:
```tsx
```
### Import named components
Declare the components you require:
```tsx
```
## Supported components
## Styling
Components use BEM-style CSS classes (e.g., `.mdk-button`, `.mdk-card__header`) for styling consistency and easy customization.
## Component design principles
- **Composable**: Components are designed to work together
- **Accessible**: Built with ARIA attributes and keyboard navigation
- **Themeable**: Support light/dark modes via CSS custom properties
- **Type-safe**: Full TypeScript support with exported prop types
# Chart components (/v0-0-2/ui/react/core/components/charts)
Chart components for visualizing time-series data, metrics, and statistics. Built on [Chart.js](https://www.chartjs.org/) and
[Lightweight Charts](https://tradingview.github.io/lightweight-charts/), supporting:
- Chart types: components that render datasets (`AreaChart`, `BarChart`, `DoughnutChart`, `GaugeChart`, `LineChart`)
- [Composition elements](/v0-0-2/ui/react/core/components/charts/composition): layout chrome, legends, stats rows, and chart utilities (`ChartContainer`, `ChartStatsFooter`,
`DetailLegend`, helpers)
## Prerequisites
- Complete the [@tetherto/mdk-react-devkit installation and add the dependency](/v0-0-2/ui/react/core#prerequisites)
## All chart type components
## `AreaChart`
Presentational Chart.js area chart (line series with fill). Data and options follow the same Chart.js model as [`LineChart`](#linechart), without a
separate MDK data wrapper type.
### Import
```tsx
```
### Props
| Prop | Status | Type | Default | Description |
|------|--------|------|---------|-------------|
| `data` | Required | `ChartData` | none | [Chart.js](https://www.chartjs.org/) line chart `data` (datasets typically use `fill: true` for an area look) |
| `options` | Optional | `ChartOptions` | none | [Chart.js](https://www.chartjs.org/) options (merged with defaults) |
| `tooltip` | Optional | `ChartTooltipConfig` | none | Custom HTML tooltip config; when set, replaces the default Chart.js tooltip |
| `height` | Optional | `number` | `300` | Chart height in pixels |
| `className` | Optional | `string` | none | Root class name from the host app |
### Basic usage
```tsx
const data = {
labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'],
datasets: [
{
label: 'Revenue',
data: [100, 120, 115, 134, 168],
fill: true,
backgroundColor: 'rgba(114, 245, 158, 0.2)',
borderColor: '#72F59E',
},
],
}
```
## `BarChart`
The `BarChart` component renders Chart.js bar or column series. It manages:
1. **Data** (`data`): labels and datasets passed to Chart.js.
2. **Chart options** (`options`): merged with MDK defaults.
3. **Formatting** (`formatYLabel`, `formatDataLabel`, `tooltip`): axis labels, data labels, and HTML tooltips.
4. **Presentation** (`isStacked`, `isHorizontal`, `showLegend`, `legendPosition`, `legendAlign`, `showDataLabels`): layout and legend.
5. **Size** (`height`): chart height in pixels.
### Import
```tsx
```
### Props
| Prop | Status | Type | Default | Description |
|------|--------|------|---------|-------------|
| `data` | Required | `ChartData` | none | [Chart.js](https://www.chartjs.org/) data object |
| `options` | Optional | `ChartOptions` | none | [Chart.js](https://www.chartjs.org/) options (merged with defaults) |
| `formatYLabel` | Optional | `function` | none | Format Y-axis tick labels |
| `formatDataLabel` | Optional | `function` | none | Format data label values |
| `tooltip` | Optional | `ChartTooltipConfig` | none | Custom HTML tooltip config |
| `isStacked` | Optional | `boolean` | `false` | Stack bars on top of each other |
| `isHorizontal` | Optional | `boolean` | `false` | Render bars horizontally |
| `showLegend` | Optional | `boolean` | `true` | Show [Chart.js](https://www.chartjs.org/) legend |
| `legendPosition` | Optional | `'top' \| 'right' \| 'bottom' \| 'left'` | `'top'` | Legend position |
| `legendAlign` | Optional | `'start' \| 'center' \| 'end'` | `'start'` | Legend alignment |
| `showDataLabels` | Optional | `boolean` | `false` | Show values above bars |
| `height` | Optional | `number` | `300` | Chart height in pixels |
### Basic usage
```tsx
const data = {
labels: ['Jan', 'Feb', 'Mar', 'Apr'],
datasets: [
{
label: 'Hashrate (TH/s)',
data: [120, 150, 180, 200],
backgroundColor: '#72F59E',
},
],
}
```
### Data from hooks
Hand-built Chart.js `data` (above) is valid. When app hooks return `{ labels, series }` declarative input, convert with
[`buildBarChartData`](/v0-0-2/ui/react/core/components/charts/composition#hook-shaped-bar-data) in [chart utilities](/v0-0-2/ui/react/core/components/charts/composition#chart-utilities),
optionally merge per-series `datalabels`, then pass the result to `data`.
### Stacked bar chart
```tsx
const data = {
labels: ['Site A', 'Site B', 'Site C'],
datasets: [
{ label: 'Online', data: [100, 80, 120], backgroundColor: '#72F59E' },
{ label: 'Offline', data: [10, 20, 5], backgroundColor: '#FF6B6B' },
],
}
```
### Horizontal bar chart
```tsx
`${value} TH/s`}
/>
```
### With data labels
```tsx
`${value.toFixed(1)}%`}
/>
```
## `DoughnutChart`
### Import
```tsx
```
### Props
| Prop | Status | Type | Default | Description |
|------|--------|------|---------|-------------|
| `data` | Required | `DoughnutChartDataset[]` | none | Array of `{ label, value, color? }` slices (see [data shape](#data-shape-for-doughnut-charts)) |
| `unit` | Optional | `string` | `''` | Unit suffix shown in the default tooltip |
| `options` | Optional | `ChartOptions` | none | [Chart.js](https://www.chartjs.org/) doughnut options (merged with defaults) |
| `cutout` | Optional | `string` | `'75%'` | Inner radius cutout (doughnut ring thickness) |
| `borderWidth` | Optional | `number` | `4` | Border width between segments |
| `height` | Optional | `number` | `260` | Chart height in pixels |
| `legendPosition` | Optional | `string` | `'top'` | Where to place the custom legend relative to the chart |
| `tooltip` | Optional | `ChartTooltipConfig` | none | Custom HTML tooltip; when set, replaces the default doughnut tooltip |
| `className` | Optional | `string` | none | Root class name from the host app |
#### Data shape for doughnut charts
Pass `data` as an array of `{ label, value, color? }`. The chart maps this into Chart.js internally
(`labels`, single dataset, segment colors). Omit `color` to use the default palette rotation.
### Basic usage
```tsx
const data = [
{ label: 'Online', value: 85, color: '#72F59E' },
{ label: 'Offline', value: 10, color: '#FF6B6B' },
{ label: 'Maintenance', value: 5, color: '#FFD700' },
]
```
## `GaugeChart`
Speedometer-style presentational arc gauge for displaying a single normalized value, providing:
1. **Percent** (`percent`): fill level from `0` to `1` (values outside that range are clamped).
2. **Arc appearance** (`colors`, `arcWidth`, `nrOfLevels`): segment colors, relative thickness, and segment count.
3. **Center label** (`hideText`): percentage text in the center of the gauge (hidden when `hideText` is `true`).
4. **Accessibility** (`id`): stable id wired into SVG labels (defaults to `mdk-gauge-chart`).
5. **Layout** (`height`, `maxWidth`, `className`): container height, max width, and host root class.
The `GaugeChart` is SVG-based; strokes and labels may paint past the host elementβs layout bounds. For a hard edge, wrap the gauge in an element
with **`overflow: hidden`**, or add vertical spacing. [`ChartContainer`](/v0-0-2/ui/react/core/components/charts/composition#chartcontainer) does **not** set overflow clipping on the
chart slotβit is for title, loading/empty, and footer chrome like other charts (see [Composition](/v0-0-2/ui/react/core/components/charts/composition)); it may add spacing that makes
overlap less visible, but it is not a substitute for clipping when you need it.
### Import
```tsx
```
The gauge is driven by a **normalized** `percent` in the range **0β1** (for example `0.75` is 75%). It is not a `value` / `max` API, and it
does not accept `label` or `unit` props (see [`ChartContainer`](/v0-0-2/ui/react/core/components/charts/composition#chartcontainer) for layout notes).
### Props
| Prop | Status | Type | Default | Description |
|------|--------|------|---------|-------------|
| `percent` | Required | `number` | none | Fill level from `0` to `1` (clamped) |
| `colors` | Optional | `string[]` | green β soft green β red (theme) | Arc segment colors as HEX strings |
| `arcWidth` | Optional | `number` | `0.2` | Arc thickness as a fraction of the gauge radius (`0`β`1`) |
| `nrOfLevels` | Optional | `number` | `3` | Number of colored segments on the arc |
| `hideText` | Optional | `boolean` | `false` | Hide the percentage text in the center |
| `id` | Optional | `string` | `'mdk-gauge-chart'` | Stable id for SVG accessibility labels |
| `height` | Optional | `number` \| `string` | `200` | Container height (pixels or CSS length, e.g. `'50%'`) |
| `maxWidth` | Optional | `number` | `500` | Maximum width in pixels |
| `className` | Optional | `string` | none | Root class name from the host app |
### Basic usage
```tsx
```
### In a chart card (same shell as other charts)
[`ChartContainer`](/v0-0-2/ui/react/core/components/charts/composition#chartcontainer) adds title, loading, and empty chrome; it does **not** apply `overflow: hidden` to the chart body.
When you still see bleed next to siblings or footers, wrap `GaugeChart` in an extra element with **`overflow: hidden`**.
```tsx
```
### Custom colors and arc
```tsx
```
## `LineChart`
The `LineChart` component renders time-series lines. It manages:
1. **Series data** (`data`): `LineChartData` with millisecond `x` values (see [data shape](#data-shape-for-line-charts)).
2. **Context** (`timeline`, `unit`): timeline identifier and unit label for tooltips.
3. **Formatting** (`yTicksFormatter`, `priceFormatter`, `customLabel`): tick and tooltip text.
4. **Presentation** (`backgroundColor`, `beginAtZero`, `showPointMarkers`, `showDateInTooltip`, `uniformDistribution`): axis and marker behavior.
5. **Size** (`height`): chart height in pixels.
### Import
```tsx
```
### Props
| Prop | Status | Type | Default | Description |
|------|--------|------|---------|-------------|
| `data` | Required | `LineChartData` | none | Object with a `datasets` array (see [data shape](#data-shape-for-line-charts)) |
| `timeline` | Optional | `string` | none | Timeline identifier |
| `yTicksFormatter` | Optional | `function` | none | Format Y-axis ticks |
| `priceFormatter` | Optional | `function` | none | Format tooltip values |
| `customLabel` | Optional | `string` | none | Custom tooltip label |
| `unit` | Optional | `string` | `''` | Unit label for values |
| `backgroundColor` | Optional | `string` | none | Chart background color |
| `beginAtZero` | Optional | `boolean` | `false` | Start Y-axis at zero |
| `showPointMarkers` | Optional | `boolean` | `false` | Show data point markers |
| `showDateInTooltip` | Optional | `boolean` | `false` | Show date in tooltip |
| `uniformDistribution` | Optional | `boolean` | `false` | Uniform time distribution |
| `height` | Optional | `number` | `240` | Chart height in pixels |
#### Data shape for line charts
`LineChartData` is `{ datasets: LineDataset[] }`. Each `LineDataset` has `label`, `borderColor`, optional `visible` / `borderWidth` / `extraTooltipData`,
and `data: { x, y }[]` where `x` is a time value in **milliseconds** (for example from `Date.prototype.valueOf()`) and `y` is the series value
(number, or `null` / `undefined` for gaps).
### Basic usage
```tsx
const data = {
datasets: [
{
label: 'Hashrate',
borderColor: '#72F59E',
data: [
{ x: 1704067200000, y: 150 },
{ x: 1704153600000, y: 155 },
{ x: 1704240000000, y: 160 },
],
},
],
}
```
### Multiple lines
```tsx
const hashrateData = [
{ x: 1704067200000, y: 150 },
{ x: 1704153600000, y: 155 },
]
const targetData = [
{ x: 1704067200000, y: 140 },
{ x: 1704153600000, y: 145 },
]
const data = {
datasets: [
{
label: 'Hashrate',
borderColor: '#72F59E',
data: hashrateData,
},
{
label: 'Target',
borderColor: '#FFD700',
data: targetData,
},
],
}
```
# Chart composition (/v0-0-2/ui/react/core/components/charts/composition)
Compose chart types from [Chart components](/v0-0-2/ui/react/core/components) with wrappers and helpers. `ChartContainer` adds title, loading, and empty states; `ChartStatsFooter` and `DetailLegend` add summary rows and rich legends (for example [`LineChartCard`](/v0-0-2/ui/react/foundation/dashboard/charts#linechartcard)).
## Prerequisites
- Complete the [@tetherto/mdk-react-devkit installation and add the dependency](/v0-0-2/ui/react/core#prerequisites)
## Components
## `ChartContainer`
### Import
```tsx
```
### Props
| Prop | Status | Type | Default | Description |
|------|--------|------|---------|-------------|
| `children` | Required | `ReactNode` | none | Chart body (for example a `BarChart` or `LineChart`) |
| `title` | Optional | `string` | none | Title text |
| `header` | Optional | `ReactNode` | none | Custom header node |
| `legendData` | Optional | `LegendItem[]` | none | Legend entries rendered in the container chrome |
| `highlightedValue` | Optional | `object` | none | Highlighted metric (`value`, optional `unit`, `className`, `style`); highlight chrome renders only when this prop is set and the chart body is visible (not `loading` / not `empty`) |
| `rangeSelector` | Optional | `object` | none | Range selector props (`options`, `value`, `onChange`, optional `className`, `style`, `buttonClassName`) |
| `loading` | Optional | `boolean` | none | When `true`, shows a centered `Loader` overlay over the chart area |
| `empty` | Optional | `boolean` | none | When `true`, shows empty state |
| `emptyMessage` | Optional | `string` | `'No data available'` | Copy shown in the empty overlay when `empty` is `true` and `loading` is not `true` |
| `minMaxAvg` | Optional | `object` | none | Min / max / avg strings for the summary row |
| `timeRange` | Optional | `string` | none | Time range label |
| `footer` | Optional | `ReactNode` | none | Footer slot |
| `footerClassName` | Optional | `string` | none | Class name on the footer wrapper |
| `onToggleDataset` | Optional | `function` | none | Called with dataset index when legend toggles visibility |
| `className` | Optional | `string` | none | Root class name from the host app |
### Basic usage
```tsx
```
## `ChartStatsFooter`
### Import
```tsx
```
### Props
| Prop | Status | Type | Default | Description |
|------|--------|------|---------|-------------|
| `minMaxAvg` | Optional | `object` | none | Min, max, and average strings shown in the primary row when provided |
| `stats` | Optional | `ChartStatsFooterItem[]` | none | Additional stat rows (`label`, `value`) in a columnar grid |
| `statsPerColumn` | Optional | `number` | `1` | Number of stat items per column when `stats` is set |
| `secondaryLabel` | Optional | `object` | none | Secondary block with `title` and `value` when provided |
| `className` | Optional | `string` | none | Root class name from the host app |
The component renders **nothing** when `minMaxAvg`, `stats`, and `secondaryLabel` are all absent or empty.
### Basic usage
```tsx
```
## `DetailLegend`
### Import
```tsx
```
### Props
| Prop | Status | Type | Default | Description |
|------|--------|------|---------|-------------|
| `items` | Required | `DetailLegendItem[]` | none | Legend rows (`label`, `color`, optional `icon`, `currentValue`, `percentChange`, `hidden`) |
| `onToggle` | Optional | `function` | none | Called with `label` and index when a row is toggled |
| `className` | Optional | `string` | none | Root class name from the host app |
### Basic usage
```tsx
```
## Chart utilities
Pure functions for building Chart.js data and options. Import from the package root alongside components.
```tsx
defaultChartOptions,
defaultChartColors,
buildBarChartData,
buildBarChartOptions,
buildChartTooltip,
computeStats,
} from '@tetherto/mdk-react-devkit/core'
```
| Export | Role |
|--------|------|
| `defaultChartOptions` | Shared Chart.js defaults used by MDK chart components |
| `defaultChartColors` | Default dataset color palette |
| `buildBarChartData` | Map MDK bar input into Chart.js `data` |
| `buildBarChartOptions` | Build bar chart options (stacking, axes, formatters) |
| `buildChartTooltip` | HTML tooltip config for Chart.js |
| `computeStats` | Min, max, and average for a numeric array |
Types `BarChartInput`, `BarChartSeries`, `BarChartLine`, and `BarChartConstant` are exported from the same package for hook-shaped bar data.
### Hook-shaped bar data
App and reporting hooks often return declarative bar input instead of Chart.js `data`. `buildBarChartData` converts that shape
into `{ labels, datasets }` for [`BarChart`](/v0-0-2/ui/react/core/components/charts#barchart). The pipeline:
1. **Input** (`BarChartInput`): optional `labels`, required `series`, optional `lines` and `constants` for mixed bar/line overlays.
2. **Build** (`buildBarChartData`): returns Chart.js `data` with MDK gradient styling and layout defaults (`barWidth`, `categoryPercentage`, `barPercentage` are
optional on the input).
3. **Data labels** (optional): per-series overrides on the input (`formatter`, `anchor`, `align`, `offset`, `font`, `padding`, `display`, `clamp`, `clip`) map
to each built datasetβs Chart.js `datalabels` by series index.
4. **Render** (``): pass the built object to the component; pair with `buildBarChartOptions` when you need stacking, axes, or formatters.
`BarChartInput` shape
| Field | Type | Description |
|-------|------|-------------|
| `labels` | `string[]` | Optional category labels; omitted labels are derived from series `values` keys or indices |
| `series` | `BarChartSeries[]` | Bar datasets (`label`, `values`, optional `color`, `stack`, `gradient`, bar layout props) |
| `lines` | `BarChartLine[]` | Optional line overlays on the same chart |
| `constants` | `BarChartConstant[]` | Optional horizontal reference lines |
Each `BarChartSeries` uses `values` as either `number[]` (positional) or `Record` (keyed by category label).
#### Example
Hook output to `BarChart` example:
```tsx
BarChart,
buildBarChartData,
ChartContainer,
} from '@tetherto/mdk-react-devkit/core'
// Typical shape returned by app/reporting data hooks
const hookOutput = {
labels: ['Q1', 'Q2', 'Q3'],
series: [
{
label: 'Revenue',
values: [4.2, 3.8, 5.1],
color: '#72F59E',
dataLabels: {
formatter: (v: number) => `${v.toFixed(1)}M`,
anchor: 'end',
align: 'top',
},
},
{
label: 'OpEx',
values: [1.8, 2.0, 1.6],
color: '#FFD700',
},
],
}
const cleanSeries = hookOutput.series.map(({ dataLabels: _dl, ...s }) => s)
const base = buildBarChartData({ labels: hookOutput.labels, series: cleanSeries })
const chartData = {
labels: base.labels,
datasets: base.datasets.map((dataset, i) => {
const overrides = hookOutput.series[i]?.dataLabels
return overrides ? { ...dataset, datalabels: overrides } : dataset
}),
}
const isEmpty =
!hookOutput.series.length ||
hookOutput.series.every((s) => {
const vals = Array.isArray(s.values) ? s.values : Object.values(s.values)
return !vals.length || vals.every((v) => v === 0)
})
```
#### Empty and all-zero series
Treat bar data as empty when any of the following is true:
- `series` is missing or has length `0`
- Every series has empty `values` (array or record)
- Every numeric value across all series is `0`
Prefer `ChartContainer` `empty` or a placeholder instead of rendering a flat chart. After building Chart.js `data`, you can also
use [`useChartDataCheck`](/v0-0-2/reference/app-toolkit/hooks/components#usechartdatacheck) from `@tetherto/mdk-react-devkit/foundation`
with `{ data: chartData }`.
# Data display (/v0-0-2/ui/react/core/components/data-display)
Components for displaying data in cards, tables, badges, tags, and other visual formats.
## Prerequisites
- Complete the [@tetherto/mdk-react-devkit installation and add the dependency](/v0-0-2/ui/react/core#prerequisites)
## `DataTable`
### Import
```tsx
// Types for column definitions
DataTableColumnDef,
DataTableSortingState,
DataTablePaginationState,
DataTableRowSelectionState,
} from '@tetherto/mdk-react-devkit/core'
```
### Basic usage
```tsx
type Miner = {
id: string
name: string
hashrate: number
status: string
}
const columns: DataTableColumnDef[] = [
{
accessorKey: 'name',
header: 'Name',
},
{
accessorKey: 'hashrate',
header: 'Hashrate',
cell: ({ row }) => `${row.original.hashrate} TH/s`,
},
{
accessorKey: 'status',
header: 'Status',
},
]
function MinersTable() {
return
}
```
### With sorting and pagination
```tsx
const [sorting, setSorting] = useState([])
const [pagination, setPagination] = useState({
pageIndex: 0,
pageSize: 10,
})
```
### With row selection
```tsx
const [rowSelection, setRowSelection] = useState({})
```
### Styling
- `.mdk-data-table`: Root container
- `.mdk-data-table__header`: Header row
- `.mdk-data-table__body`: Table body
- `.mdk-data-table__row`: Data row
- `.mdk-data-table__cell`: Table cell
## `Card`
### Import
```tsx
```
### Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `className` | `string` | none | Additional CSS class |
| `onClick` | `function` | none | Click handler (adds clickable styling) |
| `children` | `ReactNode` | none | Card content |
### Basic usage
```tsx
TitleContent goes hereActions
```
Default children are automatically wrapped in the body slot:
```tsx
Title
This content goes to the body automatically
```
### Clickable card
```tsx
navigate('/details')}>
Click meNavigates on click
```
### Styling
- `.mdk-card`: Root container
- `.mdk-card--clickable`: Applied when `onClick` is provided
- `.mdk-card__header`: Header slot
- `.mdk-card__body`: Body slot
- `.mdk-card__footer`: Footer slot
## `Accordion`
### Import
```tsx
Accordion,
AccordionItem,
AccordionTrigger,
AccordionContent,
} from '@tetherto/mdk-react-devkit/core'
```
### `Accordion` props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `title` | `string` | `''` | Accordion header title |
| `isOpened` | `boolean` | `false` | Initially expanded |
| `isRow` | `boolean` | `false` | Row layout for content |
| `unpadded` | `boolean` | `false` | Remove content padding |
| `noBorder` | `boolean` | `false` | Remove trigger border |
| `solidBackground` | `boolean` | `false` | Solid background color |
| `showToggleIcon` | `boolean` | `true` | Show expand/collapse icon |
| `toggleIconPosition` | `'left' \| 'right'` | `'left'` | Icon position |
| `customLabel` | `ReactNode` | none | Custom label next to title |
| `onValueChange` | `function` | none | Callback when state changes |
### Basic usage
```tsx