929 lines
38 KiB
Markdown
929 lines
38 KiB
Markdown
# Quickstart — Running a node and testing inference
|
||
|
||
Get from zero to a live inference request in **three terminals**: install once, start
|
||
the tracker, start a node, send a request.
|
||
|
||
Tested on: AMD Ryzen AI Max (Strix Halo APU), 124 GB RAM, Linux CPU inference.
|
||
ROCm GPU setup is covered below, but must be verified on the host because ROCm
|
||
support depends on the exact AMD GPU/APU, kernel, driver, and ROCm runtime.
|
||
|
||
**Support here is evidence, not a promise.** No combination of GPU, torch build,
|
||
optional accelerator package and model is "supported" until `meshnet-node doctor`
|
||
has pushed a real forward through the exact shard you intend to serve, on that
|
||
machine. Detecting a GPU proves nothing; installing an optional package proves
|
||
nothing. See [Validate before you serve](#validate-before-you-serve-meshnet-node-doctor).
|
||
|
||
**Active development models** — what we run day-to-day, so their setup notes are the
|
||
most exercised. This is *not* a support matrix: any model the node can load is fair
|
||
game, and any model on this list can still fail `doctor` on your hardware.
|
||
|
||
| Role | `--model` / alias | HF repo | Notes |
|
||
|------|-------------------|---------|-------|
|
||
| Smoke tests, small splits | `Qwen/Qwen2.5-0.5B-Instruct` | same | 24 layers, ~1 GB BF16, no gating — default for new setups |
|
||
| Alpha / production target | `qwen3.6-35b-a3b` | `unsloth/Qwen3.6-35B-A3B` | 40 layers, ~72 GB BF16, hybrid linear-attention MoE; aliases include `Qwen3.6-35B-A3B`, `Qwen/Qwen3.6-35B-A3B` |
|
||
| Recommended dense model | `qwen3.6-27b` | `Qwen/Qwen3.6-27B` | 64 layers, ~56 GB BF16, text-only; canonical revision is pinned by the tracker |
|
||
|
||
---
|
||
|
||
## Models
|
||
|
||
The tracker advertises recommended models through `GET /v1/models`; the chat
|
||
dashboard shows the same catalog. `Qwen/Qwen3.6-27B` is recommended even before
|
||
it has complete coverage, so users and operators can see that the network intends
|
||
to serve it.
|
||
|
||
```bash
|
||
curl -s http://localhost:8080/v1/models | python -m json.tool
|
||
```
|
||
|
||
Each model reports its shard coverage and its selectable quantizations. A
|
||
quantization is selectable only when the tracker can build a complete route from
|
||
shards at that precision or higher:
|
||
|
||
- `bfloat16` requires BF16 for every shard.
|
||
- `int8` may combine INT8 and BF16 shards.
|
||
- `nf4` may combine NF4, INT8, and BF16 shards.
|
||
|
||
The chat UI chooses the highest complete precision by default. Uncovered variants
|
||
remain visible as coverage requests: use the small **Vote for coverage** control
|
||
to register demand without sending an unusable chat request.
|
||
|
||
Clients can request a minimum precision explicitly. Omit `quantization` to
|
||
request BF16:
|
||
|
||
```bash
|
||
curl http://localhost:8080/v1/chat/completions \
|
||
-H 'Content-Type: application/json' \
|
||
-d '{
|
||
"model": "qwen3.6-27b",
|
||
"quantization": "int8",
|
||
"messages": [{"role": "user", "content": "Explain sharded inference."}]
|
||
}'
|
||
```
|
||
|
||
The first valid request for a recommended model is demand proof. When the shared
|
||
tracker pool has enough spare eligible capacity, it queues a tracker-managed
|
||
initial deployment. Until a complete route exists, the request returns retryable
|
||
`503 model_loading` rather than silently lowering precision.
|
||
|
||
Nodes may serve BF16, INT8, or NF4 according to their declared capability. The
|
||
official BF16 snapshot remains the canonical Qwen source; tracker/peer sources
|
||
are preferred and Hugging Face is the fallback. A node's startup-selected
|
||
`(model, shard range, quantization)` is pinned, while spare capacity can be used
|
||
for tracker-managed work.
|
||
|
||
---
|
||
|
||
## At a glance
|
||
|
||
| Step | What | Terminals |
|
||
|------|------|-----------|
|
||
| **0** | Install Python packages | once per machine |
|
||
| **1** | Start tracker (and relay if needed) | 1–2 |
|
||
| **2** | Start node(s) | 1+ |
|
||
| **3** | Send inference request | 1 |
|
||
|
||
**Pick your connectivity mode** — this determines which flags you need on the node:
|
||
|
||
| Mode | When to use | Tracker URL | Node extras |
|
||
|------|-------------|-------------|-------------|
|
||
| **Local dev** | Everything on one machine | `http://localhost:8080` | none |
|
||
| **Direct LAN** | Node has a real LAN IP other machines can reach | `http://<tracker-ip>:8080` | `--host 0.0.0.0 --advertise-host <your-lan-ip>` + firewall |
|
||
| **Relay / public** | WSL2, NAT, 5G, or any unreachable inbound port | `https://ai.neuron.d-popov.com` (or your public URL) | none — relay handles routing |
|
||
|
||
> **WSL2:** not reachable from other LAN machines by default. Use the **relay / public**
|
||
> tracker URL, or run the node in native Windows PowerShell with direct LAN mode.
|
||
|
||
**Command prefix by shell** (used in examples below):
|
||
|
||
| Shell | Prefix | Model cache env |
|
||
|-------|--------|-----------------|
|
||
| Linux / WSL CPU | `.venv/bin/` | `HF_HOME=/path/to/models` |
|
||
| Linux AMD ROCm / Radeon | `.venv-rocm/bin/` | `HF_HOME=/path/to/models` |
|
||
| Windows PowerShell | `.\.venv\Scripts\` | `$env:HF_HOME = "D:\DEV\models"` |
|
||
|
||
> **Ryzen AI Max / Radeon 8060S developers:** use `.venv-rocm/bin/` for every
|
||
> node command and test that needs real GPU inference. The repository's default
|
||
> `.venv` currently uses Python 3.14 and is not the ROCm node runtime.
|
||
|
||
---
|
||
|
||
## 0. Install prerequisites (once per machine)
|
||
|
||
Editable installs point wrappers at this source tree — code edits apply without
|
||
reinstalling.
|
||
|
||
### Node machine — full install
|
||
|
||
<details>
|
||
<summary><strong>Linux / WSL</strong></summary>
|
||
|
||
```bash
|
||
cd /path/to/neuron-tai
|
||
python3 -m venv .venv
|
||
source .venv/bin/activate
|
||
.venv/bin/python -m pip install --upgrade pip setuptools wheel
|
||
.venv/bin/python -m pip install -e packages/tracker -e packages/node -e packages/p2p -e packages/gateway -e packages/relay
|
||
.venv/bin/python -m pip install "transformers>=5.12" accelerate
|
||
.venv/bin/meshnet-node --help
|
||
```
|
||
|
||
</details>
|
||
|
||
<details>
|
||
<summary><strong>Windows PowerShell (.venv)</strong></summary>
|
||
|
||
Requires Python 3.11+ and Git for Windows.
|
||
|
||
```powershell
|
||
cd D:\DEV\workspace\REPOS\git.d-popov.com\neuron-tai
|
||
python -m venv .venv
|
||
.\.venv\Scripts\Activate.ps1
|
||
.\.venv\Scripts\python.exe -m pip install --upgrade pip setuptools wheel
|
||
.\.venv\Scripts\python.exe -m pip install -e .\packages\tracker -e .\packages\node -e .\packages\p2p -e .\packages\gateway -e .\packages\relay
|
||
.\.venv\Scripts\python.exe -m pip install "transformers>=5.12" accelerate
|
||
.\.venv\Scripts\meshnet-node.exe --help
|
||
```
|
||
|
||
|
||
</details>
|
||
|
||
<details>
|
||
<summary><strong>Windows — conda/miniforge with CUDA (skip if using .venv above)</strong></summary>
|
||
|
||
```powershell
|
||
conda activate base
|
||
deactivate # drop any layered .venv; safe no-op if none active
|
||
cd D:\DEV\workspace\REPOS\git.d-popov.com\neuron-tai
|
||
pip install -e packages\tracker -e packages\node -e packages\p2p -e packages\gateway -e packages\relay
|
||
pip install "transformers>=5.12" accelerate safetensors
|
||
python -c "import torch; print(torch.__version__, torch.cuda.is_available())"
|
||
# Expected: 2.x.x+cuXXX True
|
||
```
|
||
|
||
If `torch` import fails despite pip saying "already satisfied", force-reinstall all
|
||
three together (never upgrade `torch` alone — breaks `torchvision`):
|
||
|
||
```powershell
|
||
pip install --force-reinstall torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
|
||
```
|
||
|
||
If `.venv\Scripts\meshnet-node.exe` shadows the conda binary, use the full path:
|
||
`C:\Users\<you>\miniforge3\Scripts\meshnet-node.exe`.
|
||
|
||
</details>
|
||
|
||
> Run Linux/WSL commands from **WSL**, not Git Bash. From Git Bash: `wsl`, then `cd`
|
||
> to the repo under `/mnt/d/...`.
|
||
|
||
### Tracker host — lightweight install
|
||
|
||
Tracker + relay only; skip node packages unless this machine also runs nodes.
|
||
|
||
```bash
|
||
git clone https://git.d-popov.com/popov/neuron-tai.git AI && cd AI
|
||
python3 -m venv .venv
|
||
.venv/bin/python -m pip install --upgrade pip setuptools wheel
|
||
.venv/bin/pip install -e packages/tracker -e packages/relay -e packages/gateway
|
||
```
|
||
|
||
### PyTorch variant
|
||
|
||
Install **one** torch line into the same env as `meshnet-node`:
|
||
|
||
| Hardware | Install |
|
||
|----------|---------|
|
||
| NVIDIA CUDA | `pip install torch` (default index) |
|
||
| CPU only | `pip install torch --index-url https://download.pytorch.org/whl/cpu` |
|
||
| AMD ROCm (discrete, arch in official wheels) | `pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/rocm6.3` |
|
||
| AMD Strix Halo / Ryzen AI Max (gfx1151) | `pip install torch torchvision torchaudio --index-url https://rocm.nightlies.amd.com/v2/gfx1151/` |
|
||
|
||
On Windows `.venv`, prefix with `.\.venv\Scripts\pip.exe`. Conda users with CUDA
|
||
torch already installed can skip this step.
|
||
|
||
### Linux AMD ROCm GPU install
|
||
|
||
Use this when the node machine has an AMD GPU/APU and you want PyTorch to run on
|
||
ROCm instead of CPU. The Python wheel is not enough by itself: the host must have
|
||
working AMD GPU device access and a compatible ROCm runtime.
|
||
|
||
**Host prerequisites:**
|
||
|
||
1. Confirm the AMD GPU is visible:
|
||
|
||
```bash
|
||
lspci | grep -Ei 'vga|3d|display|amd|ati'
|
||
ls -l /dev/kfd /dev/dri/renderD* 2>/dev/null
|
||
```
|
||
|
||
2. Make sure the node user can access GPU devices. AMD ROCm documents the normal
|
||
Linux permission path as membership in both `video` and `render`:
|
||
|
||
```bash
|
||
groups
|
||
sudo usermod -a -G video,render "$LOGNAME"
|
||
# log out and back in before continuing
|
||
```
|
||
|
||
3. Confirm the ROCm runtime tools work if they are installed:
|
||
|
||
```bash
|
||
rocminfo | head
|
||
```
|
||
|
||
If `rocminfo` is missing or cannot see the GPU, fix the host ROCm install first.
|
||
Do not debug `meshnet-node` until this works.
|
||
|
||
**Install ROCm PyTorch into the node env:**
|
||
|
||
```bash
|
||
cd /path/to/neuron-tai
|
||
python3.12 -m venv .venv-rocm
|
||
source .venv-rocm/bin/activate
|
||
python -m pip install --upgrade pip setuptools wheel
|
||
python -m pip install -e packages/tracker -e packages/node -e packages/p2p -e packages/gateway -e packages/relay
|
||
python -m pip install "transformers>=5.12" accelerate safetensors
|
||
python -m pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/rocm6.3
|
||
```
|
||
|
||
**Strix Halo / Ryzen AI Max APUs (gfx1151, e.g. Radeon 8060S):** the official
|
||
`download.pytorch.org` ROCm wheels do NOT ship gfx1151 kernels — every GPU op
|
||
fails with `HIP error: invalid device function` and `HSA_OVERRIDE_GFX_VERSION`
|
||
does not help. Install AMD's gfx1151-native builds instead (TheRock nightlies,
|
||
self-contained, no system ROCm required):
|
||
|
||
```bash
|
||
python -m pip install torch torchvision torchaudio --index-url https://rocm.nightlies.amd.com/v2/gfx1151/
|
||
```
|
||
|
||
Check that your arch is actually in the wheel:
|
||
`python -c "import torch; print(torch.cuda.get_arch_list())"` must list your
|
||
GPU's `gcnArchName` (from `torch.cuda.get_device_properties(0)`).
|
||
|
||
Keep this separate from a known-good CPU `.venv` until ROCm is verified on that
|
||
machine. ROCm wheels are large and host-runtime-sensitive; a failed ROCm install
|
||
should not break the CPU fallback environment.
|
||
|
||
Use Python 3.12 for this env. Python 3.14 is currently a bad fit for the
|
||
Qwen3.6/FLA path because `torch.compile` is not supported there.
|
||
|
||
**Verify PyTorch sees ROCm:**
|
||
|
||
```bash
|
||
python - <<'PY'
|
||
import torch
|
||
print("torch", torch.__version__)
|
||
print("hip", torch.version.hip)
|
||
print("cuda api available", torch.cuda.is_available())
|
||
if torch.cuda.is_available():
|
||
print("device", torch.cuda.get_device_name(0))
|
||
x = torch.ones((1,), device="cuda")
|
||
torch.cuda.synchronize()
|
||
print("tensor", x)
|
||
PY
|
||
```
|
||
|
||
Expected: `torch.version.hip` is not `None`, `torch.cuda.is_available()` is
|
||
`True`, and the tensor allocation succeeds. PyTorch intentionally exposes ROCm
|
||
through the `torch.cuda` API.
|
||
|
||
**Start an AMD ROCm node:**
|
||
|
||
```bash
|
||
HF_HOME=/path/to/models .venv-rocm/bin/meshnet-node start \
|
||
--tracker <tracker-url> \
|
||
--model Qwen/Qwen2.5-0.5B-Instruct \
|
||
--quantization bfloat16
|
||
```
|
||
|
||
A model whose recipe uses an optional accelerator needs that package in the *same*
|
||
env. For example, `qwen3.6-35b-a3b` on Linux ROCm can use FLA:
|
||
|
||
```bash
|
||
.venv-rocm/bin/pip install 'flash-linear-attention[rocm]'
|
||
HF_HOME=/path/to/models .venv-rocm/bin/meshnet-node start \
|
||
--tracker <tracker-url> \
|
||
--model qwen3.6-35b-a3b \
|
||
--quantization bfloat16
|
||
```
|
||
|
||
This is a per-model, per-platform example. A successful `pip install` is not evidence
|
||
that the kernel runs on your GPU — optional-kernel support varies by architecture, and
|
||
we make no universal claim here. Prove it with
|
||
`.venv-rocm/bin/meshnet-node doctor --model <model>` before starting the node; startup
|
||
will refuse to register anyway if the shard cannot execute.
|
||
|
||
### Linux ROCm: Triton JIT compiler prerequisite
|
||
|
||
Some model/runtime paths invoke Triton at the first real forward. Triton builds a local HIP
|
||
support module before that kernel can run, so a host C compiler must be discoverable on
|
||
`PATH`. A successful PyTorch allocation or Node startup does not prove this prerequisite;
|
||
without it, the first `/forward` can fail with `Failed to find C compiler`.
|
||
|
||
On Fedora, install Clang once:
|
||
|
||
```bash
|
||
sudo dnf install -y clang
|
||
```
|
||
|
||
Restart the Node from a shell where `clang` is on `PATH`. If a custom shell or service does
|
||
not inherit the system path, set the compiler explicitly for that launch:
|
||
|
||
```bash
|
||
CC=/usr/bin/clang CXX=/usr/bin/clang++ \
|
||
HF_HOME=/path/to/models .venv-rocm/bin/meshnet-node start \
|
||
--tracker <tracker-url> --model <selected-model>
|
||
```
|
||
|
||
`CC`/`CXX` are normally unnecessary after Clang is installed; they are a diagnostic override,
|
||
not a Python dependency. Other Linux distributions should install their system `clang` package
|
||
through the OS package manager.
|
||
|
||
**Windows NVIDIA/Triton:** in native PowerShell, using the **same Python environment that
|
||
runs `meshnet-node`** (e.g. the miniforge/conda env — check with `where.exe python`):
|
||
|
||
```powershell
|
||
pip install triton-windows
|
||
pip install -U flash-linear-attention
|
||
python -c "import triton, fla; print('triton', triton.__version__, 'fla ok')"
|
||
```
|
||
|
||
Order matters: `triton-windows` must be installed before FLA so FLA detects it. It bundles
|
||
its own compiler — do **not** apply Linux `dnf`/`CC` instructions, do **not** install a CUDA
|
||
toolkit, `causal-conv1d`, or the `[cuda]` extra (Linux-only pins; see the Qwen3.5/3.6-MoE
|
||
notes below). If step 3 prints ok but the Node still reports a compiler error, capture that
|
||
exact error plus the printed triton version before changing anything else.
|
||
|
||
**Troubleshooting notes:**
|
||
|
||
- `torch.version.hip is None` means you installed a CPU/CUDA torch build, not ROCm.
|
||
- `torch.cuda.is_available() == False` with a ROCm build usually means host driver,
|
||
permissions, unsupported hardware, or missing runtime libraries.
|
||
- `which meshnet-node` should not point at `~/.local/bin/meshnet-node` for ROCm
|
||
testing. Run `.venv-rocm/bin/meshnet-node ...` so the node uses the same ROCm
|
||
PyTorch, `transformers`, and FLA packages you verified.
|
||
- Missing libraries such as `libamdhip64.so`, `libMIOpen.so`, `librocsolver.so`,
|
||
or `libroctx64.so` are host ROCm runtime problems, not meshnet-node problems.
|
||
- Some AMD APUs and consumer GPUs require newer ROCm/Radeon support than server
|
||
Instinct cards. Check AMD's ROCm Radeon/Ryzen support matrix for the exact model.
|
||
- `HIP error: invalid device function` (or `no kernel image is available`) on a
|
||
working driver means the installed torch wheel has no kernels compiled for
|
||
your GPU arch. Compare `torch.cuda.get_device_properties(0).gcnArchName`
|
||
against `torch.cuda.get_arch_list()`; if your arch is missing, install a wheel
|
||
built for it (see the Strix Halo/gfx1151 note above).
|
||
- `Failed to find C compiler` during a real forward is a Triton JIT host-toolchain failure.
|
||
On Fedora install `clang` as shown above, confirm `command -v clang`, and restart the Node;
|
||
it is separate from ROCm driver and device-access troubleshooting.
|
||
|
||
### Qwen3.5/3.6-MoE notes
|
||
|
||
Applies to **`qwen3.6-35b-a3b`** and other hybrid linear-attention models. **`Qwen2.5-0.5B`**
|
||
does not need any of this — it is a standard transformer with no FLA fast path.
|
||
|
||
- **transformers ≥ 5.12 required** — older versions fail with
|
||
`'Qwen3_5MoeConfig' object has no attribute 'vocab_size'`. Check:
|
||
`python -c "import transformers; print(transformers.__version__)"`.
|
||
- **GPU fast path (optional)** — without it inference still works; startup prints
|
||
`The fast path is not available…` and linear-attention layers use a slower PyTorch
|
||
fallback. **The fast path runs on NVIDIA CUDA GPUs on both Linux and native
|
||
Windows** — the FLA kernels are Triton-compiled, and `triton-windows` compiles them
|
||
for CUDA on Windows just like Linux Triton does. Only the pip command differs per
|
||
platform. Install **only for your platform**:
|
||
|
||
| Platform | Install | Notes |
|
||
|----------|---------|-------|
|
||
| **Native Windows + NVIDIA CUDA** | `pip install triton-windows` then `pip install flash-linear-attention` | **Fast path works on the CUDA GPU** — no CUDA toolkit / `nvcc` needed; `triton-windows` bundles its own compiler. FLA [officially supports `triton-windows`](https://github.com/fla-org/flash-linear-attention/pull/757) (tested Win11, PyTorch 2.10, triton-windows 3.6). Do **not** use the `[cuda]` extra on Windows — that extra only pins Linux PyPI `triton` and fails; it is a packaging name, not a GPU requirement. Do **not** install `causal-conv1d` — FLA ≥0.3.2 ships Triton conv1d; the separate package is Linux-only and breaks on Windows (`bare_metal_version` / nvcc errors). |
|
||
| **Linux + NVIDIA CUDA** | `pip install flash-linear-attention[cuda]` | `causal-conv1d` optional (same FLA built-in conv1d note). Needs CUDA toolkit (`nvcc`) matching torch, or a prebuilt wheel. |
|
||
| **Linux + AMD ROCm** | `pip install flash-linear-attention[rocm]` | Same optional `causal-conv1d` note. |
|
||
|
||
**Windows verify** (after install):
|
||
|
||
```powershell
|
||
python -c "import triton; import fla; print('triton', triton.__version__, 'fla ok')"
|
||
```
|
||
|
||
`triton-windows` is also pulled by `meshnet-node` on Windows. Without it, Qwen3.6-MoE
|
||
startup fails with misleading `Could not import module 'Qwen3_5MoeForCausalLM'`.
|
||
|
||
<details>
|
||
<summary><strong>Windows fast path — what failed and what actually works</strong></summary>
|
||
|
||
The command that failed — `pip install flash-linear-attention[cuda] causal-conv1d` — mixes
|
||
two different things:
|
||
|
||
1. **`flash-linear-attention[cuda]` on Windows** — wrong extra. `[cuda]` pulls PyPI
|
||
`triton>=3.3`, which does not exist for Windows (`No matching distribution found`).
|
||
Use plain `pip install flash-linear-attention` **after** `triton-windows` is already
|
||
installed; FLA detects `triton-windows` and uses it.
|
||
2. **`causal-conv1d`** — separate Dao-AILab CUDA extension, **not required** for FLA or
|
||
Qwen3.6 when FLA is installed. No official Windows wheels. Source builds need `nvcc`
|
||
whose major version matches torch's CUDA (e.g. torch `+cu118` needs CUDA 11.8 toolkit,
|
||
not 12.5). Community wheels exist for narrow Python/torch combos
|
||
([PR #46](https://github.com/Dao-AILab/causal-conv1d/pull/46)) but we skip them.
|
||
|
||
**Working Windows stack** (confirmed on this repo's dev machine: Python 3.12, torch
|
||
2.7.1+cu118, triton-windows 3.7.1, flash-linear-attention 0.5.0):
|
||
|
||
```powershell
|
||
pip install triton-windows
|
||
pip install -U flash-linear-attention
|
||
python -c "import triton; import fla; print('ok')"
|
||
```
|
||
|
||
If the fast-path warning persists after that, upgrade FLA to ≥0.5.1 (includes the
|
||
`triton-windows` detection from PR #757) and restart the node.
|
||
|
||
</details>
|
||
|
||
- `pip install nvidia-ml-py` silences the pynvml deprecation warning on NVIDIA hosts.
|
||
|
||
---
|
||
|
||
## 1. Start the tracker
|
||
|
||
### LAN tracker (private network, direct node reachability)
|
||
|
||
**Terminal 1:**
|
||
|
||
```bash
|
||
.venv/bin/meshnet-tracker start --host 0.0.0.0 --port 8080
|
||
# Optional devnet billing: --starting-credit 1 --devnet-topup 10
|
||
```
|
||
|
||
Expected: `Tracker listening on 0.0.0.0:8080`. Open the port on the host firewall
|
||
if other machines will join.
|
||
|
||
**Verify:**
|
||
|
||
```bash
|
||
curl -s http://localhost:8080/v1/network/map | python3 -m json.tool
|
||
curl -s http://192.168.0.179:8080/v1/network/map | python3 -m json.tool # from another LAN machine
|
||
```
|
||
|
||
### Public tracker + relay (NAT / WSL2 / internet nodes)
|
||
|
||
Nodes behind NAT cannot receive inbound connections. Keep the relay as its own
|
||
component, but for alpha deployments you can run it **embedded in the tracker
|
||
process**. This still uses the same `meshnet_relay.RelayServer` class as a
|
||
relay-only node, so relay-only hosts remain a clean scaling path later.
|
||
|
||
**Recommended alpha: one process on the tracker host, relay bound locally and
|
||
published through the same public hostname:**
|
||
|
||
```bash
|
||
.venv/bin/meshnet-tracker start \
|
||
--host 0.0.0.0 --port 8081 \
|
||
--self-url https://ai.neuron.d-popov.com \
|
||
--embedded-relay --relay-host 127.0.0.1 --relay-port 8765
|
||
```
|
||
|
||
The tracker advertises `wss://ai.neuron.d-popov.com/ws` in `/v1/network/map`.
|
||
If you want a relay-only process instead, keep running:
|
||
|
||
```bash
|
||
.venv/bin/meshnet-relay --host 0.0.0.0 --port 8765
|
||
.venv/bin/meshnet-tracker start --host 0.0.0.0 --port 8081 --relay-url wss://ai.neuron.d-popov.com/ws
|
||
```
|
||
|
||
**Verify:**
|
||
|
||
```bash
|
||
curl -s https://ai.neuron.d-popov.com/v1/network/map | python3 -m json.tool
|
||
```
|
||
|
||
Nodes should log `Relay connected — wss://…/rpc/<peer_id>` on startup.
|
||
|
||
### Connecting a LAN tracker to the internet tracker
|
||
|
||
Use the public hostname (`https://ai.d-popov.com`), not `192.168.0.179`, as the
|
||
peer URL from the internet tracker. Configure the same `MESHNET_HIVE_SECRET` on
|
||
both trackers. Start the local tracker as one compact command:
|
||
|
||
```bash
|
||
meshnet-tracker start --host 0.0.0.0 --port 8081 --self-url https://ai.d-popov.com --cluster-peers https://meshnet.2.d-popov.com --relay-url wss://meshnet.2.d-popov.com/ws --heartbeat-timeout 120 --hive-secret "$MESHNET_HIVE_SECRET"
|
||
```
|
||
|
||
Configure the internet tracker with the reverse peer direction:
|
||
|
||
```bash
|
||
meshnet-tracker start --host 0.0.0.0 --port 8081 --self-url https://meshnet.2.d-popov.com --cluster-peers https://ai.d-popov.com --relay-url wss://meshnet.2.d-popov.com/ws --heartbeat-timeout 120 --hive-secret "$MESHNET_HIVE_SECRET"
|
||
```
|
||
|
||
Verify both trackers with `curl -s https://<tracker>/v1/raft/status` and
|
||
`curl -s https://<tracker>/v1/network/map`. For this NAT-safe setup, both
|
||
trackers advertise the internet relay URL so nodes registered through the LAN
|
||
tracker can still be reached by the internet tracker.
|
||
|
||
<details>
|
||
<summary><strong>Nginx Proxy Manager setup (public hostname)</strong></summary>
|
||
|
||
Architecture:
|
||
|
||
```
|
||
Client → HTTPS → ai.neuron.d-popov.com (nginx)
|
||
├─ /v1/* → meshnet-tracker :8081
|
||
├─ /ws → meshnet-relay :8765 (node persistent outbound WS)
|
||
└─ /rpc/* → meshnet-relay :8765 (caller opens WS per hop)
|
||
```
|
||
|
||
Use **one** proxy host. Route sub-paths via **Custom locations** — do not create
|
||
a second host for the same domain.
|
||
|
||
**Details tab** (default `/` → tracker):
|
||
|
||
| Field | Value |
|
||
|-------|--------|
|
||
| Domain Names | `ai.neuron.d-popov.com` |
|
||
| Scheme | `http` |
|
||
| Forward Hostname / IP | LAN IP of tracker machine (e.g. `192.168.0.179`) |
|
||
| Forward Port | `8081` |
|
||
| Websockets Support | ON |
|
||
|
||
**Custom locations** (both → relay port `8765`, sub-folder path empty):
|
||
|
||
| Location | Forward to |
|
||
|----------|--------------|
|
||
| `/ws` | `192.168.0.179:8765` |
|
||
| `/rpc` | `192.168.0.179:8765` |
|
||
|
||
**Advanced tab** (only if WebSocket upgrade fails):
|
||
|
||
```nginx
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Upgrade $http_upgrade;
|
||
proxy_set_header Connection $http_connection;
|
||
proxy_read_timeout 3600s;
|
||
proxy_send_timeout 3600s;
|
||
```
|
||
|
||
**Verify routing:**
|
||
|
||
```bash
|
||
curl -s https://ai.neuron.d-popov.com/v1/network/map | python3 -m json.tool
|
||
curl -sI https://ai.neuron.d-popov.com/ws
|
||
curl -sI https://ai.neuron.d-popov.com/rpc/test-peer
|
||
```
|
||
|
||
</details>
|
||
|
||
---
|
||
|
||
## 2. Start a node
|
||
|
||
**Starter model:** `Qwen/Qwen2.5-0.5B-Instruct` — 0.5B params, ~1 GB BF16, 24 layers,
|
||
no HuggingFace gating. Best for first-time setup.
|
||
|
||
**Alpha model:** `qwen3.6-35b-a3b` — 40 layers, ~72 GB BF16 download, MoE with hybrid
|
||
linear attention. Tracker accepts the alias or full repo id (`unsloth/Qwen3.6-35B-A3B`).
|
||
Some models have an *optional* accelerator path (for this one, Triton +
|
||
`flash-linear-attention`); those installs are per-model, per-platform examples, not a
|
||
requirement the node imposes and not a guarantee the path will work — see
|
||
[Optional accelerator packages](#optional-accelerator-packages).
|
||
|
||
Downloads cache under `~/.meshnet/models/` (or `$HF_HOME` / `$env:HF_HOME`).
|
||
|
||
Shard range is auto-detected from the curated catalog. For unknown repos the node
|
||
fetches only `config.json`. Override with `--shard-start` / `--shard-end` for partial
|
||
shards or multi-node splits.
|
||
|
||
### Validate before you serve: `meshnet-node doctor`
|
||
|
||
`doctor` resolves exactly the model, shard, quantization and recipe that `start` would
|
||
load, loads it, and pushes one bounded **real forward** through it. It is offline — it
|
||
never contacts the tracker — and it is the only thing that turns a guess into evidence.
|
||
|
||
```bash
|
||
HF_HOME=/path/to/models .venv/bin/meshnet-node doctor --model <model> --quantization bfloat16
|
||
```
|
||
|
||
```
|
||
meshnet-node doctor
|
||
Model: <model>
|
||
Shard: layers 0–23; 24 of 24
|
||
Quantization: bfloat16
|
||
|
||
[PASS] recipe default (v1) on cuda — 412 ms
|
||
|
||
OK — the selected shard ran a real forward for 1 recipe.
|
||
Capability report: ~/.meshnet/capability.json
|
||
```
|
||
|
||
Exit code is 0 on pass, 1 on failure. It writes a **capability report** either way — a
|
||
failure is evidence too. Useful flags: `--recipe <id>` to validate one named recipe,
|
||
`--all-recipes` to try every recipe for the selection (CI and diagnosis), `--report PATH`
|
||
to choose where the report lands, `--json` for machine-readable output, `--debug` for the
|
||
full traceback behind a failure. The selection flags (`--model`, `--quantization`,
|
||
`--shard-start`/`--shard-end`, `--cpu`) work the same as on `start`.
|
||
|
||
**Three states — do not confuse them:**
|
||
|
||
| State | What it means | How it is established |
|
||
|-------|---------------|-----------------------|
|
||
| **Detected hardware** | A GPU, a torch build, and an optional package are *present*. | Inventory/`torch.cuda.is_available()`. Proves nothing about whether your shard runs. |
|
||
| **Validated recipe** | This machine executed a real forward for *this* model + shard + recipe + device. | A passing `doctor` run → a capability report. |
|
||
| **Routable Node** | The tracker will send paid work here. | Startup re-proves the loaded shard and registers the report; the tracker admits it. |
|
||
|
||
Each state is strictly stronger than the one above it. A machine can have a working GPU
|
||
(detected) and still fail to execute a shard (no validated recipe), and a node can hold a
|
||
valid report and still not route (the tracker moved it to a shard range it never proved).
|
||
|
||
**Startup is fail-closed.** `meshnet-node start` runs the same validation against the
|
||
backend it just loaded, *before* it opens a port or registers. If the shard cannot execute,
|
||
the node exits non-zero — it never binds a socket and never registers. There is no
|
||
production bypass; a node that cannot do the work never advertises that it can.
|
||
|
||
**The tracker admits, it does not re-run.** The node ships its report with registration.
|
||
Registration always succeeds, so a broken node is visible rather than invisible — but only
|
||
a report that *covers what the node advertises* makes it routable. The verdict comes back
|
||
in the registration response, is logged, and is exposed per node on `GET /v1/network/map`
|
||
under `capability` (`admitted`, `absent`, `invalid`, `failed`, `stale`, `model-mismatch`,
|
||
`shard-mismatch`, `recipe-mismatch`, `catalogue-incompatible`). A node that is registered
|
||
but dark is showing you one of those. Full semantics, and the transitional `compat` vs
|
||
`enforce` policy, are in [ADR-0023](docs/adr/0023-model-agnostic-node-capability-admission.md).
|
||
|
||
**When `doctor` fails** it prints a category and an actionable hint, not a traceback:
|
||
|
||
| Category | Do this |
|
||
|----------|---------|
|
||
| `no-model-selected` | Pass `--model`, or run `meshnet-node` once to save a config. |
|
||
| `missing-dependency` | Install the node's model extras (torch, transformers, safetensors, accelerate). |
|
||
| `model-unavailable` | Check the model id, `--download-dir`, and that the artifact is downloaded/reachable. |
|
||
| `insufficient-memory` | Serve fewer layers (`--shard-start`/`--shard-end`) or a smaller quantization (`-q int8`, `-q nf4`). |
|
||
| `invalid-shard` | The layer range does not exist in this model — check it against the layer count. |
|
||
| `unsupported-recipe` | This backend cannot apply that execution setting; pick another `--recipe`. |
|
||
| `load-failed` | The shard would not load; re-run with `--debug`. |
|
||
| `forward-failed` | It loaded but cannot execute. This machine cannot serve this shard; re-run with `--debug`. |
|
||
|
||
**Scope:** the node validates recipes it already ships. It does **not** download executable
|
||
recipes, install Python/OS packages, or update drivers for you — every install on this page
|
||
is something you run deliberately. Signed node updates are a deliberate follow-up, not part
|
||
of this release.
|
||
|
||
### Core command
|
||
|
||
Replace `<tracker-url>` and adjust the prefix for your shell (see table above).
|
||
|
||
**Linux / WSL:**
|
||
|
||
```bash
|
||
HF_HOME=/path/to/models .venv/bin/meshnet-node start --tracker <tracker-url> --model Qwen/Qwen2.5-0.5B-Instruct --quantization bfloat16
|
||
```
|
||
|
||
**Windows PowerShell:**
|
||
|
||
```powershell
|
||
$env:HF_HOME = "D:\DEV\models"
|
||
.\.venv\Scripts\meshnet-node.exe start --tracker <tracker-url> --model Qwen/Qwen2.5-0.5B-Instruct --quantization bfloat16
|
||
```
|
||
|
||
### Ready-to-run examples
|
||
|
||
**Local dev (same machine as tracker):**
|
||
|
||
```bash
|
||
HF_HOME=/path/to/models .venv/bin/meshnet-node start --tracker http://localhost:8080 --model Qwen/Qwen2.5-0.5B-Instruct --quantization bfloat16 --port 8001
|
||
```
|
||
|
||
**Public / relay (works from WSL2, NAT, 5G — no extra flags):**
|
||
|
||
```bash
|
||
.venv/bin/meshnet-node start --tracker https://ai.neuron.d-popov.com --model Qwen/Qwen2.5-0.5B-Instruct
|
||
```
|
||
|
||
#### Optional accelerator packages
|
||
|
||
Some models have an optional fast path that a *specific* model's recipe can use if the
|
||
package is importable. The examples below are exactly that — **worked examples for one
|
||
development model on one platform**, not a supported-configuration list. Installing the
|
||
package does not mean the fast path works on your GPU: a package can import cleanly and
|
||
still fail to execute a kernel on your architecture. `doctor` is what tells you, and the
|
||
node serves the recipe that actually validated. If none of this is installed, the model
|
||
still runs on the standard path.
|
||
|
||
**Example — `qwen3.6-35b-a3b` (Triton + FLA), Windows GPU:**
|
||
|
||
```powershell
|
||
$env:HF_HOME = "D:\DEV\models"
|
||
pip install triton-windows
|
||
pip install -U flash-linear-attention
|
||
meshnet-node doctor --model qwen3.6-35b-a3b --quantization bfloat16
|
||
meshnet-node start --tracker http://192.168.0.179:8080 --model qwen3.6-35b-a3b --quantization bfloat16
|
||
```
|
||
|
||
Do not add `causal-conv1d` or `flash-linear-attention[cuda]` on Windows (see Qwen3.5/3.6 notes).
|
||
|
||
**Example — `qwen3.6-35b-a3b`, Linux NVIDIA GPU:**
|
||
|
||
```bash
|
||
# Install once on that machine: pip install flash-linear-attention[cuda]
|
||
HF_HOME=/path/to/models .venv/bin/meshnet-node start --tracker <tracker-url> --model qwen3.6-35b-a3b --quantization bfloat16
|
||
```
|
||
|
||
**Example — `qwen3.6-35b-a3b`, Linux AMD ROCm GPU:**
|
||
|
||
```bash
|
||
# Install once on that machine: .venv-rocm/bin/pip install 'flash-linear-attention[rocm]'
|
||
HF_HOME=/path/to/models .venv-rocm/bin/meshnet-node start --tracker <tracker-url> --model qwen3.6-35b-a3b --quantization bfloat16
|
||
```
|
||
|
||
Run `doctor` after any of these installs: it is the only way to learn whether the optional
|
||
path executes here, and it costs one bounded forward.
|
||
|
||
After the first node registers a model, later nodes can join with only the tracker
|
||
URL (shard auto-assigned):
|
||
|
||
```bash
|
||
.venv/bin/meshnet-node start --tracker https://ai.neuron.d-popov.com
|
||
```
|
||
|
||
**Direct LAN (Windows node reachable by IP):**
|
||
|
||
```powershell
|
||
$env:HF_HOME = "D:\DEV\models"
|
||
.\.venv\Scripts\meshnet-node.exe start --tracker http://192.168.0.179:8081 --model Qwen/Qwen2.5-0.5B-Instruct --shard-start 12 --shard-end 23 --quantization bfloat16 --host 0.0.0.0 --advertise-host 192.168.0.42 --port 8005
|
||
```
|
||
|
||
<details>
|
||
<summary><strong>Windows direct LAN — firewall and IP checklist</strong></summary>
|
||
|
||
1. Find LAN IP: `ipconfig` — use active Ethernet/Wi-Fi IPv4 (e.g. `192.168.0.42`).
|
||
Avoid WSL/Docker/Hyper-V addresses (`172.x.x.x`).
|
||
2. Allow inbound port (Administrator PowerShell, once):
|
||
|
||
```powershell
|
||
New-NetFirewallRule -DisplayName "Meshnet node 8005" -Direction Inbound -Action Allow -Protocol TCP -LocalPort 8005
|
||
```
|
||
|
||
3. Verify from tracker machine:
|
||
|
||
```bash
|
||
curl http://192.168.0.42:8005/v1/health
|
||
```
|
||
|
||
404/501 is fine — it proves TCP reached the node. Timeout = check firewall,
|
||
`--host 0.0.0.0`, and `--advertise-host`.
|
||
|
||
</details>
|
||
|
||
<details>
|
||
<summary><strong>Two-node split (same or different machines)</strong></summary>
|
||
|
||
**Node A — layers 0–11 (head, serves chat):**
|
||
|
||
```bash
|
||
HF_HOME=/path/to/models .venv/bin/meshnet-node start --tracker <tracker-url> --model Qwen/Qwen2.5-0.5B-Instruct --shard-start 0 --shard-end 11 --quantization bfloat16 --port 8001
|
||
```
|
||
|
||
**Node B — layers 12–23:**
|
||
|
||
```bash
|
||
HF_HOME=/path/to/models .venv/bin/meshnet-node start --tracker <tracker-url> --model Qwen/Qwen2.5-0.5B-Instruct --shard-start 12 --shard-end 23 --quantization bfloat16 --port 8002
|
||
```
|
||
|
||
Send inference to Node A. For cross-machine LAN tests see `docs/TWO_MACHINE_TEST.md`.
|
||
|
||
</details>
|
||
|
||
### Useful flags
|
||
|
||
| Flag | Purpose |
|
||
|------|---------|
|
||
| `--port 8001` | Fixed listen port (default: first free ≥ 7000) |
|
||
| `--host 0.0.0.0` | Bind all interfaces (needed for direct LAN) |
|
||
| `--advertise-host <ip>` | LAN IP the tracker tells other nodes (direct LAN only) |
|
||
| `--shard-start N --shard-end M` | Partial layer range |
|
||
| `--debug` | Verbose per-hop pipeline logs (noisy; off by default) |
|
||
|
||
`--host 0.0.0.0` binds locally; `--advertise-host` is what peers use for direct
|
||
hops. Omit both when using the relay path.
|
||
|
||
### Expected output
|
||
|
||
```
|
||
Auto-detected 24 layers → shard 0–23
|
||
Relay connected — wss://ai.neuron.d-popov.com/rpc/abc1def2ef3f4567 # relay mode only
|
||
================================
|
||
meshnet-node ready
|
||
Wallet: <address>
|
||
Model ID: Qwen/Qwen2.5-0.5B-Instruct
|
||
Shard: layers 0–23; 24 of 24
|
||
Quantization: bfloat16
|
||
Endpoint: http://<host>:8001
|
||
Node ID: <id>
|
||
Hardware: CPU
|
||
================================
|
||
```
|
||
|
||
The `Endpoint` is the local address. In relay mode, peers reach this node via
|
||
`wss://<relay>/rpc/<peer_id>` instead.
|
||
|
||
### Other CPU-friendly models
|
||
|
||
| Model | `--model` / alias | Layers | BF16 size | Notes |
|
||
|-------|-------------------|--------|-----------|-------|
|
||
| **Qwen2.5-0.5B** (dev default) | `Qwen/Qwen2.5-0.5B-Instruct` | 24 | ~1 GB | Fastest, no gating |
|
||
| **Qwen3.6-35B-A3B** (alpha) | `qwen3.6-35b-a3b` | 40 | ~72 GB | MoE; needs transformers ≥5.12; see Qwen3.5/3.6 notes |
|
||
| Qwen2.5-1.5B | `Qwen/Qwen2.5-1.5B-Instruct` | 28 | ~3 GB | Better quality |
|
||
| Phi-3-mini | `microsoft/Phi-3-mini-4k-instruct` | 32 | ~7.5 GB | Best CPU quality |
|
||
| Llama-3.2-1B | `meta-llama/Llama-3.2-1B-Instruct` | 16 | ~2 GB | Requires HF login |
|
||
| Llama-3.2-3B | `meta-llama/Llama-3.2-3B-Instruct` | 28 | ~6 GB | Requires HF login |
|
||
|
||
For gated models (Llama): `huggingface-cli login` first.
|
||
|
||
Browse more: `.venv/bin/meshnet-node models` or `.venv/bin/meshnet-node models --browse`.
|
||
|
||
---
|
||
|
||
## 3. Send an inference request
|
||
|
||
**Terminal 3** — direct to the head node (replace port if you omitted `--port`):
|
||
|
||
```bash
|
||
curl -s http://localhost:8001/v1/chat/completions -H "Content-Type: application/json" -d '{"model": "qwen2.5-0.5b", "messages": [{"role": "user", "content": "What is 7 times 8? Answer in one word."}], "stream": false}' | python3 -m json.tool
|
||
```
|
||
|
||
**Via tracker** (tests routing / proxying):
|
||
|
||
```bash
|
||
curl -s http://localhost:8080/v1/chat/completions -H "Content-Type: application/json" -d '{"model": "qwen2.5-0.5b", "messages": [{"role": "user", "content": "What is 7 times 8? Answer in one word."}], "stream": false}' | python3 -m json.tool
|
||
```
|
||
|
||
**Public tracker:**
|
||
|
||
```bash
|
||
curl -s https://ai.neuron.d-popov.com/v1/chat/completions -H "Content-Type: application/json" -H "Authorization: Bearer sk-mesh-<your-key>" -d '{"model": "Qwen/Qwen2.5-0.5B-Instruct", "messages": [{"role": "user", "content": "What is 7 times 8?"}], "stream": false}' | python3 -m json.tool
|
||
```
|
||
|
||
**Test script:**
|
||
|
||
```bash
|
||
.venv/bin/python scripts/test_lan_inference.py --tracker http://localhost:8080 --gateway http://localhost:8001
|
||
```
|
||
|
||
**Verify registration on public tracker:**
|
||
|
||
```bash
|
||
curl -s "https://ai.neuron.d-popov.com/v1/network/map" | python3 -m json.tool
|
||
curl -s "https://ai.neuron.d-popov.com/v1/route?model=qwen2.5-0.5b" | python3 -m json.tool
|
||
```
|
||
|
||
<details>
|
||
<summary><strong>Accounts, API keys, and credit (billing-enabled trackers)</strong></summary>
|
||
|
||
Public trackers require a real API key for `/v1/chat/completions`. Unknown bearer →
|
||
`401`; zero balance → `402 insufficient balance`.
|
||
|
||
**Dashboard:** open `https://<tracker>/dashboard`, register, click **+ new key**.
|
||
With `--starting-credit`, the first key is pre-funded. With `--devnet-topup`, use
|
||
**+$N (devnet)** to refill during testing.
|
||
|
||
**Curl flow:**
|
||
|
||
```bash
|
||
curl -s https://<tracker>/v1/auth/register -H "Content-Type: application/json" -d '{"email": "you@example.com", "password": "hunter22-or-better"}'
|
||
curl -s https://<tracker>/v1/account/keys -X POST -H "Authorization: Bearer <session_token>"
|
||
curl -s https://<tracker>/v1/account -H "Authorization: Bearer <session_token>"
|
||
curl -s https://<tracker>/v1/account/topup -X POST -H "Authorization: Bearer <session_token>" -H "Content-Type: application/json" -d '{"api_key": "sk-mesh-..."}'
|
||
```
|
||
|
||
Operator defaults: `--starting-credit` and `--devnet-topup` both default to 1 USDT.
|
||
Set both to 0 on mainnet.
|
||
|
||
</details>
|
||
|
||
---
|
||
|
||
## Reference
|
||
|
||
### How relay hops work
|
||
|
||
When node A forwards activations to node B (behind NAT):
|
||
|
||
1. Tracker injects `X-Meshnet-Route` with `relay_addr` for behind-NAT hops.
|
||
2. Node A opens WebSocket to `wss://relay/rpc/{peer_id_B}`.
|
||
3. Relay forwards `relay-http-request` to Node B's persistent connection.
|
||
4. Node B processes `/forward`, returns `relay-http-response`.
|
||
5. Relay sends response back to Node A; Node A continues the pipeline.
|
||
|
||
Activations (bfloat16) are Base64-encoded in JSON — no precision loss. On relay
|
||
failure the node logs a warning and falls back to direct HTTP before erroring.
|
||
|
||
### Interactive wizard
|
||
|
||
```bash
|
||
.venv/bin/meshnet-node # first run: wizard; later: saved config
|
||
.venv/bin/meshnet-node --reset-config
|
||
```
|
||
|
||
### Run all tests
|
||
|
||
```bash
|
||
.venv/bin/python -m pytest -q
|
||
```
|
||
|
||
Tests marked `integration` download models or need a GPU; the default lane is
|
||
`pytest -m "not integration"`. The opt-in real-model doctor check takes its model from the
|
||
environment and skips without it — see
|
||
[docs/dev/certified-hardware-lanes.md](docs/dev/certified-hardware-lanes.md) for the
|
||
release contract that certifies a hardware lane.
|