---
sidebar_position: 62
description: Reference for the `lium provider` CLI namespace — portal session, node lifecycle, central-miner-server config, sync, billing, and machine-request queries. Mirrors every action the provider portal frontend exposes.
keywords:
  - lium provider
  - lium provider portal
  - lium provider node
  - lium provider config
  - lium provider sync
  - lium provider billing
  - lium provider machine-request
  - lium provider machine
  - lium provider status
  - provider portal automation
---

> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lium.io/llms.txt
> Use this file to discover all available pages before exploring further.

# `lium provider`

Provider-side CLI. The `lium provider` namespace covers everything the provider portal frontend at [lium.io/portal](https://lium.io/portal) does — portal authentication, node lifecycle, central-miner-server configuration, batch sync, and read-only billing / machine-request queries — so an AI agent (or any automation) can run a Lium provider account end-to-end without a browser.

Distinct from [`lium mine`](./mine.md), which is the on-host bootstrap: it clones the miner repo, installs the executor tooling and starts it on the GPU machine itself. Both are provider-side — the CLI's own `lium provider --help` blurb calls `lium mine` a renter workflow, which is wrong. Hotkey registration on SN51 itself is performed directly with `btcli subnet register`; everything after that is `lium provider …`.

```bash
lium provider [OPTIONS] SUBCOMMAND [SUBCOMMAND-OPTIONS]
```

## Group-level options

Set once on the `lium provider` group; every subcommand inherits them. CLI flags beat env vars beat `~/.lium/config.ini`.

| Flag | Env var | Config key | Effect |
|------|---------|------------|--------|
| `-w, --coldkey NAME` | `LIUM_PROVIDER_COLDKEY` | `provider.coldkey` | Bittensor coldkey (wallet) name |
| `-k, --hotkey NAME`  | `LIUM_PROVIDER_HOTKEY` | `provider.hotkey`  | Bittensor hotkey on the coldkey |
| `--portal-url URL`   | `LIUM_PORTAL_URL`      | `provider.portal_url` | Override portal base URL (default: production) |
| `--json`             | —                      | —                  | Emit one machine-readable JSON envelope per command |
| `--debug`            | —                      | —                  | Verbose logging; include error context on stderr |
| `-y, --yes`          | `LIUM_PROVIDER_ACK=1`  | —                  | Auto-confirm the persona gate for spend-affecting subcommands |
| `--dry-run`          | —                      | —                  | Skip irreversible subprocess calls (e.g. `ssh`) and report intent only |

Persist hotkey/coldkey across sessions; the saved values are used when the flags are omitted:

```bash
lium config set provider.coldkey miner-prod
lium config set provider.hotkey  miner-1
```

After that, every `lium provider …` invocation auto-resolves the wallet identity from `~/.lium/config.ini`.

## Subcommand map

| Group | Purpose | Spend-affecting? |
|-------|---------|------------------|
| [`portal`](#lium-provider-portal) | JWT session against the provider portal | no |
| [`status`](#lium-provider-status) | Aggregated provider snapshot | no |
| [`node`](#lium-provider-node) | GPU node lifecycle on the portal | most subcommands yes |
| [`config`](#lium-provider-config) | Portal-account, Discord, password & central-miner-server settings | some mutating subcommands |
| [`sync`](#lium-provider-sync) | Batch sync between portal and central miner server | yes |
| [`billing`](#lium-provider-billing) | Paginated billing history | no |
| [`machine-request`](#lium-provider-machine-request) | Pending tenant machine requests | no |
| [`machine`](#lium-provider-machine) | GPU machine catalog + reward estimates | no |

Spend-affecting subcommands run a persona gate that prompts unless `--yes` is set or `LIUM_PROVIDER_ACK=1` is exported.

## `lium provider portal`

Manage the cached JWT used to authenticate against the provider portal. The token is keyed by hotkey ss58 and stored under `~/.lium/provider/`.

```bash
lium provider portal login [--force]    # Exchange a hotkey signature for a JWT
lium provider portal logout             # Drop the cached JWT for this hotkey
lium provider portal whoami             # Call /auth/me with the cached token
```

| Flag (on `login`) | Effect |
|-------------------|--------|
| `--force` | Bypass the local token cache and re-authenticate |

`login` requires `--hotkey` (or `LIUM_PROVIDER_HOTKEY`, or `provider.hotkey` in config). `logout` and `whoami` do too.

In JSON mode, `login` includes Discord connection and extra incentive eligibility when the portal profile is available:

```json
{
  "discord_connected": false,
  "extra_incentive_eligible": false,
  "hotkey": "5...",
  "provider_id": "...",
  "token_present": true
}
```

In human mode, if Discord is not connected, `login` also shows the next command to run:

```bash
lium provider config connect-discord
```

## `lium provider status`

One-shot health snapshot composed from the subtensor metagraph, portal `/auth/me`, the node list, extra incentive eligibility, and validator weights. Sources that fail are skipped and surfaced as `warnings` in the JSON envelope.

```bash
lium provider status [--netuid 51]
```

| Flag | Effect |
|------|--------|
| `--netuid INT` | Subnet to query (default: `51`) |

When the portal profile is available, `status` includes `discord_connected` and `extra_incentive_eligible`. If Discord is not connected, human output shows the same extra incentive next step as `portal login`.

## `lium provider node`

Node lifecycle on the portal. Mutating subcommands (`add`, `rm`, `update-*`, `min-gpu set/unset`, `notice-period set/unset`, `notify-added`) run the persona gate.

```bash
lium provider node list [--miner-hotkey HK | --all] [--page N] [--limit N]
lium provider node get  <NODE_ID>
lium provider node add  --gpu-type TYPE --ip IP [--port 8080] [--price USD] --gpu-count N
lium provider node rm   <NODE_ID>
lium provider node update-price <NODE_ID> --price USD
lium provider node update-gpu   <NODE_ID> --gpu-type TYPE --gpu-count N
lium provider node min-gpu set   <NODE_ID> <COUNT>
lium provider node min-gpu unset <NODE_ID>
lium provider node pods             <NODE_ID>
lium provider node machine-requests <NODE_ID>
lium provider node notice-period set   <NODE_ID> --start ISO8601 (--minutes N | --permanent) [--reason TEXT]
lium provider node notice-period unset <NODE_ID>
lium provider node notify-added <NODE_ID> --request-id REQ
```

| Subcommand | Args / flags | Purpose |
|------------|--------------|---------|
| `list` | `--miner-hotkey HK`, `--all`, `--page N`, `--limit N` | Paginated node listing, scoped to the active hotkey by default. `--miner-hotkey` shows another provider's nodes; `--all` is the portal's *All miners* view — every provider's nodes, unfiltered |
| `get` | `NODE_ID` | Fetch a single node record |
| `add` | `--gpu-type`, `--ip`, `--port` (default `8080`), `--price` (optional; auto-filled from public [shared-config](#default-prices--shared-config) when omitted), `--gpu-count` (default `1`) | Queue a new node addition (`POST /executors`). Persona-gated |
| `rm` | `NODE_ID` | Delete the node. Persona-gated |
| `update-price` | `NODE_ID --price USD` | Set price-per-GPU/hour. Persona-gated |
| `update-gpu` | `NODE_ID --gpu-type TYPE --gpu-count N` | Change GPU type/count. Persona-gated |
| `min-gpu set` | `NODE_ID COUNT` | Minimum GPU count required for rental matchmaking. Persona-gated |
| `min-gpu unset` | `NODE_ID` | Clear the minimum-GPU rule. Persona-gated |
| `pods` | `NODE_ID` | List pods currently rented on this node |
| `machine-requests` | `NODE_ID` | List pending tenant requests targeting this node |
| `notice-period set` | `NODE_ID --start ISO8601`, `--minutes N` or `--permanent`, optional `--reason TEXT` | Open a notice period before maintenance or a permanent removal ([flags](#notice-period-set)). Persona-gated |
| `notice-period unset` | `NODE_ID` | Cancel an open notice period. Persona-gated |
| `notify-added` | `NODE_ID --request-id REQ` | Mark a tenant machine request fulfilled (`POST /machine-added`). Persona-gated |

The portal still exposes these routes under `/executors/...`; the CLI verb is `node` because that is the user-facing terminology after the 2026 Provider rename.

### `notice-period set`

Tells the renters of a node that it will be down, and tells the platform the downtime is planned. Pass `--start` and exactly one of `--minutes` (a maintenance window) or `--permanent` (a removal).

| Flag | Default | Effect |
|------|---------|--------|
| `--start ISO8601` | required | Start time, ISO 8601 with a UTC offset, e.g. `2026-10-01T09:00:00+00:00` (`Z` is accepted). The portal wants it at least 24 hours ahead for maintenance and 48 hours ahead for `--permanent` |
| `--minutes N` | — | Maintenance window length in minutes, `1`-`60` |
| `--permanent` | off | Permanent removal: at the start time the renter's pod is deleted and the node is marked removed. Takes no `--minutes` |
| `--reason TEXT` | — | Reason shown to the renters of the node |

```bash
# A 60-minute maintenance window
lium provider node notice-period set <NODE_ID> --start 2026-10-01T09:00:00+00:00 --minutes 60 --reason "disk swap" --yes

# Take the node out of service for good
lium provider node notice-period set <NODE_ID> --start 2026-10-02T09:00:00+00:00 --permanent --yes
```

Every renter with a pod on the node gets an e-mail when the notice is saved; `notice-period unset` before the start time cancels it.

Passing neither or both of `--minutes` and `--permanent`, or a start time without a UTC offset, exits `1` with `ARG_INVALID` before anything is sent. A `--minutes` value outside `1`-`60` exits `2` with a usage error. A start time too close to now is refused by the portal (`400`) and shown as a request rejection.

These flags need a `lium.io` release that includes them (check `lium provider node notice-period set --help`); older releases send no start time, and the portal refuses the call.

### Default prices & shared-config

`lium provider node add` accepts `--price` as **optional**. When omitted, the CLI fetches the public, unauthenticated `GET /v1/shared-config` snapshot from `https://lium.io/api` (the same data the provider portal frontend uses to populate its Add-Node modal) and looks up the base USD/GPU/hour for `--gpu-type`. The chosen price is printed to stderr so you can confirm before the call hits the portal.

If the GPU type isn't in the public price table, the CLI exits `1` with `ARG_INVALID` and lists known types — pass `--price` explicitly or correct the spelling. To browse all known GPU types interactively, use [`lium provider machine list`](#lium-provider-machine).

Override the snapshot URL with the `LIUM_SHARED_CONFIG_URL` environment variable (useful when pointing at staging or a self-hosted backend).

## `lium provider config`

Portal-*account* state held server-side — distinct from `lium config` (CLI-side `~/.lium/config.ini`) and `lium provider portal` (JWT/session). Spend-affecting and existing account-mutating subcommands note when they are persona-gated.

```bash
lium provider config show
lium provider config opt-in              # use lium.io's central miner server
lium provider config opt-out             # run your own central miner server
lium provider config set-email <EMAIL>
lium provider config set-password [--password PASSWORD]
lium provider config connect-discord [--no-wait] [--timeout N] [--poll-interval S]
lium provider config set-subscriptions [--gpu TYPE]...   # repeat --gpu, or pass none to clear
```

| Subcommand | Effect |
|------------|--------|
| `show` | Full `GET /auth/me` profile, including `discord_connected` and `extra_incentive_eligible` |
| `opt-in` / `opt-out` | Toggle the lium.io central miner server. Persona-gated |
| `set-email` | Update the contact email. Persona-gated |
| `set-password` | Set the Provider Portal password using fresh hotkey signature authentication. Prompts when `--password` is omitted outside JSON mode; automation can use `--password` or `LIUM_PROVIDER_NEW_PASSWORD` |
| `connect-discord` | Start Discord OAuth linking. Best-effort opens a browser; always prints/returns the authorization URL |
| `set-subscriptions` | Set machine-request notification subscriptions by GPU type (repeat `--gpu`); pass none to clear. Persona-gated |

By default, `connect-discord` opens the Discord authorization URL when a browser is available, then waits up to 120 seconds for the portal profile to show Discord as connected. Use `--no-wait` to print/return the URL immediately.

`connect-discord --json --no-wait` is the recommended agent/headless form. It returns:

```json
{
  "authorization_url": "https://discord.com/oauth2/authorize?...",
  "browser_opened": false,
  "discord_connected": false,
  "extra_incentive_eligible": false,
  "next_action": "open_authorization_url_and_complete_discord_oauth"
}
```

Open `authorization_url` as the provider's Discord account owner, then rerun `lium provider status --json` to confirm `discord_connected` and `extra_incentive_eligible` are true. Without Discord connected, extra subnet incentives are disabled.

## `lium provider sync`

Batch sync between the portal and the central miner server — the same buttons the portal frontend exposes as "Sync From Miner Server" and "Sync Into Miner Server". Both subcommands are persona-gated.

```bash
lium provider sync from-miner-server     # pull node state from the central miner server
lium provider sync to-miner-server       # push node state to the central miner server
```

## `lium provider billing`

```bash
lium provider billing list [--miner-hotkey HK | --all] [--page N] [--limit N]
```

Paginated billing-history entries, scoped to the active hotkey by default. `--miner-hotkey HK` shows another provider's history; `--all` returns every provider's entries (the portal's global view). The same scoping applies to `lium provider status`, whose node count is yours, not the marketplace's.

## `lium provider machine-request`

```bash
lium provider machine-request list           # pending tenant requests (counts only until a node is verified)
lium provider machine-request get <REQ_ID>   # single tenant machine request
```

The portal and the API show per-request detail only to providers with at least one node verified by a validator.
The CLI (lium.io 0.3.0 and later) shows the aggregate: until a node is verified,
both subcommands answer without the detail:

- `list` returns the feed's aggregate — how many open requests per GPU class and per hourly budget band
  (`<$1/h`, `$1-3/h`, `$3-10/h`, `$10+/h`, and `unknown` for a request with no GPU count), and nothing that names a
  request or a requester. A script branches on `.data.tier == "aggregate"` in the `--json` envelope; the rows, when
  they come, are a list under `.data.data`. The table prints one count table per non-empty breakdown.
- `get` exits `2` with `PORTAL_FORBIDDEN`.

Once a node is verified, `list` returns the rows again and `get` returns one of them.

## `lium provider machine`

```bash
lium provider machine list
lium provider machine estimate --gpu-type TYPE --gpu-count N [--gpu-price USD]
```

| Subcommand | Args | Purpose |
|------------|------|---------|
| `list` | — | Available GPU machine catalog |
| `estimate` | `--gpu-type`, `--gpu-count`, optional `--gpu-price` | Estimated rewards for a given GPU configuration |

## End-to-end examples

```bash
# One-time setup: persist wallet identity so flags aren't needed on every call
lium config set provider.coldkey miner-prod
lium config set provider.hotkey  miner-1

# Authenticate against the provider portal
lium provider portal login
lium provider portal whoami

# Connect Discord for extra incentive eligibility
lium provider config connect-discord

# Browserless/headless form for agents
lium provider config connect-discord --json --no-wait

# Health snapshot (registration, portal session, node count, validator weights)
lium provider status
lium provider --json status   # JSON envelope for scripts / agents

# Provision: opt in to the lium.io central miner server, then add a node
lium provider config opt-in --yes

# Explicit price (override):
lium provider node add \
  --gpu-type "NVIDIA H200 NVL" \
  --gpu-count 8 \
  --ip 203.0.113.42 \
  --port 8080 \
  --price 1.85 \
  --yes

# Or omit --price to auto-fill from the public shared-config baseline:
lium provider node add \
  --gpu-type "NVIDIA H200 NVL" \
  --gpu-count 8 \
  --ip 203.0.113.42 \
  --yes

# Inspect & manage the node
lium provider node list --limit 50
lium provider node get  <NODE_ID>
lium provider node pods <NODE_ID>
lium provider node update-price <NODE_ID> --price 2.10 --yes

# Reward forecasting and tenant requests
lium provider machine estimate --gpu-type "NVIDIA H200 NVL" --gpu-count 8
lium provider machine-request list
lium provider node machine-requests <NODE_ID>
lium provider node notify-added    <NODE_ID> --request-id <REQ_ID> --yes

# Schedule maintenance, then close out
lium provider node notice-period set   <NODE_ID> --start 2026-10-01T09:00:00+00:00 --minutes 60 --yes
# ... maintenance happens ...
lium provider node notice-period unset <NODE_ID> --yes

# Sync state between portal and central miner server
lium provider sync from-miner-server --yes
lium provider sync to-miner-server   --yes

# Billing & history
lium provider billing list --page 1 --limit 50

# Drop the token when rotating hotkeys
lium provider portal logout
```

Add `--json` to any command for a machine-readable envelope (scripted automation, CI runs, AI-agent loops). Pair with `--yes` (or `LIUM_PROVIDER_ACK=1`) to skip the persona prompt on spend-affecting calls.

## Exit codes & errors

`lium provider …` does **not** use the exit codes in the [CLI reference index](./index.md#exit-codes) — it has its own map, and the same number means something different here:

| Exit code | Meaning here | Error codes |
|-----------|--------------|-------------|
| `0` | Success | — |
| `1` | User / argument error, and the fallback for anything unmapped | `ARG_INVALID`, `PORTS_INVALID`, `HOTKEY_NOT_REGISTERED` |
| `2` | Authentication error, and a valid token refused for this hotkey | `PORTAL_AUTH_INVALID`, `PORTAL_AUTH_EXPIRED`, `WALLET_NOT_FOUND`, `PORTAL_FORBIDDEN` (since lium 0.3.0) |
| `3` | Portal error (server-side, not auth) | `PORTAL_SERVER_ERROR`, `PORTAL_NOT_FOUND`, `PORTAL_CONTRACT_DRIFT`, `PORTAL_RATE_LIMIT` |
| `5` | SSH error | `SSH_UNREACHABLE`, `SSH_AUTH_FAILED` |
| `6` | Config error | `CONFIG_MISSING` |
| `7` | Token-cache contention — another `lium provider` process is refreshing the JWT; retry | `PORTAL_AUTH_REFRESH_RACE` |

The error code is the stable thing to branch on. Under `--json` it arrives as `{"ok": false, "error": {"code": "...", "message": "...", "hint": "...", "context": {}}}`:

| Error code | Meaning |
|-----------|---------|
| `ARG_INVALID` | Required flag or argument missing (e.g. `--hotkey`) |
| `PORTAL_AUTH_INVALID` | JWT rejected — run `lium provider portal login` |
| `PORTAL_AUTH_EXPIRED` | JWT expired — run `lium provider portal login` |
| `PORTAL_FORBIDDEN` | Portal returned 403: the token is fine, the action is not allowed for this hotkey (e.g. machine-request detail before a node is verified). Re-login does not help (since lium 0.3.0) |
| `PORTAL_CONTRACT_DRIFT` | Portal returned an unexpected envelope shape (422) |
| `PORTAL_SERVER_ERROR` | Portal 5xx — retry with `--debug` for context |
| `PORTAL_NOT_FOUND` | Portal returned 404 — wrong UUID, or already removed |
| `PORTAL_RATE_LIMIT` | Backing off; retry shortly |
| `WALLET_NOT_FOUND` | The named coldkey/hotkey does not exist locally |
| `HOTKEY_NOT_REGISTERED` | Register on SN51 first with `btcli subnet register` |
| `SSH_UNREACHABLE` / `SSH_AUTH_FAILED` | Host unreachable, or key/user rejected |
| `INSTALLER_PARTIAL_FAIL` | `mine.sh` did not finish — see `/tmp/lium-mine.log` on the host |
| `CONFIG_MISSING` | A required config value is unset |

## See also

- [`lium mine`](./mine.md) — install and start the node binary on the host
- [`lium gpu-splitting`](./gpu-splitting.md) — prepare Docker storage for multi-tenant rental
- [Provider Portal — Overview](/providers/portal/overview)
- [Provider Portal — Managing Nodes](/providers/portal/managing-nodes)
- [Provider Portal — Machine Requests](/providers/portal/machine-requests)
- [AI Agents integration guide](../../agents.md)
