Skip to main content

lium provider

Provider-side CLI. The lium provider namespace covers everything the provider portal frontend at 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, 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 ….

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.

FlagEnv varConfig keyEffect
-w, --coldkey NAMELIUM_PROVIDER_COLDKEYprovider.coldkeyBittensor coldkey (wallet) name
-k, --hotkey NAMELIUM_PROVIDER_HOTKEYprovider.hotkeyBittensor hotkey on the coldkey
--portal-url URLLIUM_PORTAL_URLprovider.portal_urlOverride portal base URL (default: production)
--json——Emit one machine-readable JSON envelope per command
--debug——Verbose logging; include error context on stderr
-y, --yesLIUM_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:

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​

GroupPurposeSpend-affecting?
portalJWT session against the provider portalno
statusAggregated provider snapshotno
nodeGPU node lifecycle on the portalmost subcommands yes
configPortal-account, Discord, password & central-miner-server settingssome mutating subcommands
syncBatch sync between portal and central miner serveryes
billingPaginated billing historyno
machine-requestPending tenant machine requestsno
machineGPU machine catalog + reward estimatesno

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/.

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
--forceBypass 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:

{
"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:

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.

lium provider status [--netuid 51]
FlagEffect
--netuid INTSubnet 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.

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
SubcommandArgs / flagsPurpose
list--miner-hotkey HK, --all, --page N, --limit NPaginated 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
getNODE_IDFetch a single node record
add--gpu-type, --ip, --port (default 8080), --price (optional; auto-filled from public shared-config when omitted), --gpu-count (default 1)Queue a new node addition (POST /executors). Persona-gated
rmNODE_IDDelete the node. Persona-gated
update-priceNODE_ID --price USDSet price-per-GPU/hour. Persona-gated
update-gpuNODE_ID --gpu-type TYPE --gpu-count NChange GPU type/count. Persona-gated
min-gpu setNODE_ID COUNTMinimum GPU count required for rental matchmaking. Persona-gated
min-gpu unsetNODE_IDClear the minimum-GPU rule. Persona-gated
podsNODE_IDList pods currently rented on this node
machine-requestsNODE_IDList pending tenant requests targeting this node
notice-period setNODE_ID --start ISO8601, --minutes N or --permanent, optional --reason TEXTOpen a notice period before maintenance or a permanent removal (flags). Persona-gated
notice-period unsetNODE_IDCancel an open notice period. Persona-gated
notify-addedNODE_ID --request-id REQMark 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).

FlagDefaultEffect
--start ISO8601requiredStart 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
--permanentoffPermanent 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
# 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.

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.

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
SubcommandEffect
showFull GET /auth/me profile, including discord_connected and extra_incentive_eligible
opt-in / opt-outToggle the lium.io central miner server. Persona-gated
set-emailUpdate the contact email. Persona-gated
set-passwordSet 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-discordStart Discord OAuth linking. Best-effort opens a browser; always prints/returns the authorization URL
set-subscriptionsSet 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:

{
"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.

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​

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​

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​

lium provider machine list
lium provider machine estimate --gpu-type TYPE --gpu-count N [--gpu-price USD]
SubcommandArgsPurpose
list—Available GPU machine catalog
estimate--gpu-type, --gpu-count, optional --gpu-priceEstimated rewards for a given GPU configuration

End-to-end examples​

# 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 — it has its own map, and the same number means something different here:

Exit codeMeaning hereError codes
0Success—
1User / argument error, and the fallback for anything unmappedARG_INVALID, PORTS_INVALID, HOTKEY_NOT_REGISTERED
2Authentication error, and a valid token refused for this hotkeyPORTAL_AUTH_INVALID, PORTAL_AUTH_EXPIRED, WALLET_NOT_FOUND, PORTAL_FORBIDDEN (since lium 0.3.0)
3Portal error (server-side, not auth)PORTAL_SERVER_ERROR, PORTAL_NOT_FOUND, PORTAL_CONTRACT_DRIFT, PORTAL_RATE_LIMIT
5SSH errorSSH_UNREACHABLE, SSH_AUTH_FAILED
6Config errorCONFIG_MISSING
7Token-cache contention — another lium provider process is refreshing the JWT; retryPORTAL_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 codeMeaning
ARG_INVALIDRequired flag or argument missing (e.g. --hotkey)
PORTAL_AUTH_INVALIDJWT rejected — run lium provider portal login
PORTAL_AUTH_EXPIREDJWT expired — run lium provider portal login
PORTAL_FORBIDDENPortal 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_DRIFTPortal returned an unexpected envelope shape (422)
PORTAL_SERVER_ERRORPortal 5xx — retry with --debug for context
PORTAL_NOT_FOUNDPortal returned 404 — wrong UUID, or already removed
PORTAL_RATE_LIMITBacking off; retry shortly
WALLET_NOT_FOUNDThe named coldkey/hotkey does not exist locally
HOTKEY_NOT_REGISTEREDRegister on SN51 first with btcli subnet register
SSH_UNREACHABLE / SSH_AUTH_FAILEDHost unreachable, or key/user rejected
INSTALLER_PARTIAL_FAILmine.sh did not finish — see /tmp/lium-mine.log on the host
CONFIG_MISSINGA required config value is unset

See also​