Skip to content

nexus-xyz/nexus-exchange-cli

Repository files navigation

nexus-exchange-cli

nexus — a command-line interface for the Nexus Exchange API, built on the official nexus-exchange Rust SDK.

Status: the full command surface is wired up — public market data, the authenticated account, order placement/cancellation, and live WebSocket streaming. It is a thin command/output layer over the SDK: every request goes through the SDK's Client, which owns request signing, the HTTP/WebSocket transport, retries, rate-limit pacing, and the wire types. The CLI adds no transport of its own.

Install

The quickest way — download and run the installer for the latest release:

# macOS + Linux
curl https://cli.nexus.xyz | sh
# Windows
irm https://cli.nexus.xyz | iex

cli.nexus.xyz serves the cargo-dist installer for the most recent GitHub release; it picks the shell or PowerShell variant automatically. The host itself lives in installer/.

Or pin the invocation to a specific GitHub release artifact:

Prebuilt, checksummed, signed binaries are published for every tagged release (macOS arm64/x64, Linux x64/arm64, Windows x64) by cargo-dist. A Windows .msi is also attached to each release.

macOS / Linux — shell installer:

curl --proto '=https' --tlsv1.2 -LsSf \
  https://github.com/nexus-xyz/nexus-exchange-cli/releases/latest/download/nexus-exchange-cli-installer.sh | sh

Windows — PowerShell installer:

powershell -ExecutionPolicy Bypass -c "irm https://github.com/nexus-xyz/nexus-exchange-cli/releases/latest/download/nexus-exchange-cli-installer.ps1 | iex"

Homebrew:

brew install nexus-xyz/tap/nexus

cargo-binstall (prebuilt binary, no compile) — the crate isn't on crates.io yet, so point binstall at the repo directly:

cargo binstall --git https://github.com/nexus-xyz/nexus-exchange-cli nexus-exchange-cli

From source:

cargo install --path .
# or, from a checkout:
cargo build --release   # binary at target/release/nexus

Verifying downloads

Every release also ships per-artifact *.sha256 files and a combined sha256.sum. Beyond checksums, each binary artifact carries two independent signatures:

1. minisign signatures (.minisig, offline — no service to trust). Verify an artifact against the project's public key:

minisign -Vm nexus-exchange-cli-x86_64-unknown-linux-gnu.tar.gz \
  -P 'RWQ5th6qraoqAGncPLWGZthh5ObywWnTc8j0r1w8e0cX4kH9vuVc06ek'

2. GitHub build-provenance attestations (proves the artifact was built by this repo's release workflow):

gh attestation verify nexus-exchange-cli-x86_64-unknown-linux-gnu.tar.gz \
  --repo nexus-xyz/nexus-exchange-cli

Cutting a release

Releases are automated by release-please (release-type: rust), complementary to the cargo-dist pipeline (see EDR-003). You do not bump the version or tag by hand.

  1. Land changes on main using Conventional Commits (feat:, fix:, feat!: / BREAKING CHANGE:).
  2. The release-please workflow keeps a standing release PR that bumps version in Cargo.toml + Cargo.lock and writes CHANGELOG.md. Semver follows commit type: feat: → minor, fix: → patch, feat!: / BREAKING CHANGE: → major.
  3. Merging that release PR creates the v<version> tag.
  4. The tag fires the Release workflow (generated by dist generate), which builds, checksums, attests, signs, and publishes the binaries and installers.

release-please only edits Cargo.toml / Cargo.lock / CHANGELOG.md / the manifest — it never touches the generated release.yml, so it does not conflict with the dist generate --check assertion in CI. The two pipelines stay separate by design.

Do not edit CHANGELOG.md or bump the version manually — release-please owns both and will overwrite or conflict with hand edits.

Builds also run on every PR as a dry-run (pr-run-mode = "upload"), so the full cross-platform build is a blocking check — no release is created and no publish jobs run on PRs.

First release (one-time bootstrap)

The very first cut (v0.1.0) needs two one-time nudges, because release-please derives the next version by bumping the last released version — and there is no prior release to bump from yet:

  • .release-please-manifest.json is seeded at 0.0.0 (the "nothing released yet" baseline), so release-please proposes a brand-new version rather than treating the 0.1.0 already in Cargo.toml as shipped.
  • release-as: "0.1.0" in release-please-config.json pins that first proposal to exactly 0.1.0, independent of how the conventional commits would otherwise bump a 0.x baseline.

To cut it, in order:

  1. Complete the signing setup below first. The minisign publish job fails closed, so a release cut before the key/variable exist would publish an unsigned v0.1.0 — defeating the point. Verify MINISIGN_PUBLIC_KEY (the repo variable and the README key) plus the MINISIGN_SECRET_KEY / MINISIGN_PASSWORD / RELEASE_PLEASE_TOKEN / HOMEBREW_TAP_TOKEN secrets are all set.
  2. Merge this bootstrap to main. release-please opens a release 0.1.0 PR.
  3. Merge that PR → it tags v0.1.0 and opens the draft Release → release.yml builds, attests, signs (minisign), uploads into the draft, and undrafts it.
  4. Remove release-as immediately afterwards. It is sticky: left in place it pins every future release to 0.1.0, so the next release PR would be stuck at the same version. Open a one-line follow-up PR deleting the "release-as": "0.1.0" line. The manifest will by then read 0.1.0, so subsequent releases bump from there normally. (Leaving it in is fail-safe — it blocks the next release loudly rather than mis-versioning, but remove it so the flow self-advances.)

After undrafting, confirm the install path end-to-end (this is the actual "done"):

# The cargo-dist "latest" installer asset now resolves (no longer 404s):
curl -fsSL https://github.com/nexus-xyz/nexus-exchange-cli/releases/latest/download/nexus-exchange-cli-installer.sh | head -5
# Once the cli.nexus.xyz Worker is deployed, the one-liner installs a working nexus:
curl https://cli.nexus.xyz | sh && nexus --version

Signing setup (maintainers)

Configure these repository secrets before the first signed release. The minisign and Homebrew publish jobs fail closed — if their secret is missing they error rather than quietly shipping a release without the signatures/formula it promises. (dist's macOS/Windows code-signing steps are skipped when their secrets are absent; the artifacts are still produced, just unsigned.)

Secret Used for
RELEASE_PLEASE_TOKEN PAT (or GitHub App token) for the release-please workflow. Required so the tag it pushes on release-PR merge triggers the tag-listening release.yml — the default GITHUB_TOKEN cannot trigger downstream workflows. Scope: contents:write + pull-requests:write on this repo.
MINISIGN_SECRET_KEY minisign secret key contents (per-artifact .minisig)
MINISIGN_PASSWORD password for the minisign secret key
HOMEBREW_TAP_TOKEN push access to the nexus-xyz/homebrew-tap repo
CODESIGN_CERTIFICATE, CODESIGN_CERTIFICATE_PASSWORD, CODESIGN_IDENTITY Apple Developer ID code-signing (macOS)
SSLDOTCOM_USERNAME, SSLDOTCOM_PASSWORD, SSLDOTCOM_CREDENTIAL_ID, SSLDOTCOM_TOTP_SECRET SSL.com eSigner Windows Authenticode signing

Set the MINISIGN_PUBLIC_KEY repository variable (public keys are not secret) to the published public key — the same one in "Verifying downloads" above. This is required: the release workflow verifies every signature against it before uploading and fails closed if it is unset, so the first signed release is gated until both this variable and the README key are populated.

Generate the minisign keypair offline and keep the secret key off CI:

minisign -G -p minisign.pub -s minisign.key
# store minisign.key contents in MINISIGN_SECRET_KEY, its password in MINISIGN_PASSWORD,
# and paste minisign.pub into the "Verifying downloads" section above + the var.

macOS code-signing is currently disabled (macos-sign = false). dist's macOS signing path does not gracefully skip a missing certificate — it fails the build — and since builds now run on every PR, enabling it without a cert would break all builds. To enable: add the CODESIGN_* secrets above and flip macos-sign = true in dist-workspace.toml. macOS notarization is a separate, further step (dist signs but does not notarize, and a loose CLI binary cannot be stapled), so it only matters behind a .pkg/.dmg installer.

Usage

Runnable, copy-pasteable recipes for each flow live in examples/.

nexus --help

# Public market data
nexus markets                       # tradable markets and their rules
nexus ticker BTC-USDX-PERP          # ticker for one market
nexus tickers                       # tickers for every market
nexus summaries                     # per-market 24h summaries
nexus mark-price BTC-USDX-PERP      # current mark price
nexus market-status BTC-USDX-PERP   # lifecycle / halt status
nexus funding-rates BTC-USDX-PERP --limit 50
nexus orderbook BTC-USDX-PERP       # bids/asks
nexus trades BTC-USDX-PERP --limit 50
nexus candles BTC-USDX-PERP --timeframe 1m --limit 100
nexus health                        # indexer health snapshot

# Per-market data
nexus market summary                       # 24h volume + halt state per market
nexus market status BTC-USDX-PERP          # lifecycle / halt status
nexus market mark-price BTC-USDX-PERP      # current mark price

# Authenticated account (see Credentials below)
nexus balance                       # balance, collateral, equity, margin
nexus account rate-limit            # current rate-limit tier / remaining / reset
nexus positions                     # open positions
nexus fills --limit 50              # recent executions
nexus withdrawals --limit 50        # withdrawal history
nexus orders                        # open orders
nexus funding-payments --limit 50   # funding booked against the account
nexus withdrawals                   # withdrawal history

# Trading (prompts for confirmation; pass --yes to skip)
nexus order place --market BTC-USDX-PERP --side buy --type limit \
  --price 84000 --quantity 0.01 --tif GTC
nexus order get <ORDER_ID>          # fetch one order
nexus order amend <ORDER_ID> --price 85000 --quantity 0.02
nexus order batch orders.json       # submit a JSON array of orders ('-' = stdin)
nexus order cancel <ORDER_ID>
nexus order cancel --all

# Account management (see Credentials below)
nexus account deposit 1000          # deposit collateral
nexus account credit --amount 500   # claim testnet USDX (omit --amount for the daily max)
nexus account rate-limit            # rate-limit tier / remaining tokens
nexus account leverage BTC-USDX-PERP 10
nexus account margin-mode BTC-USDX-PERP isolated

# Wallet-signed auth (EVM key; see Credentials below)
nexus auth login                    # EIP-191 sign-in; prompts for the key,
                                    # stores the session token (mode 0600)
nexus agents register --agent 0x<AGENT_ADDR>   # EIP-712; prompts for the key

# API keys, agents, transfers, sub-accounts
nexus keys list
nexus keys create                   # secret is shown ONCE — store it now
nexus keys delete <KEY_ID>
nexus agents list
nexus agents revoke <AGENT_ADDRESS>
nexus transfers list
nexus transfers create --from <ACCT> --to <SUBACCT> --amount 100
nexus sub-accounts list
nexus sub-accounts create "trading-bot-1"

# Live streaming over WebSocket (Ctrl-C to stop)
nexus ws trades --market BTC-USDX-PERP      # public channels need --market
nexus ws orders fills positions             # account channels (need credentials)

# First-time setup (interactive)
nexus setup

The order batch file is a JSON array of order objects mirroring the order place flags, with string amounts to preserve precision:

[
  {"market": "BTC-USDX-PERP", "side": "buy", "type": "limit",
   "price": "84000", "quantity": "0.01", "tif": "gtc"},
  {"market": "ETH-USDX-PERP", "side": "sell", "type": "market", "quantity": "1"}
]

Every subcommand supports --help.

Network selection

By default the CLI targets the stable network. Override per-invocation:

nexus --network beta markets
nexus --network local markets
nexus --base-url http://127.0.0.1:9090 markets   # any custom base URL
Flag Env Default
--network <stable|beta|local> NEXUS_NETWORK stable
--base-url <URL> NEXUS_BASE_URL — (overrides --network)

Output format

By default commands print human-readable tables. Pass --output json (or set NEXUS_OUTPUT=json) to emit pretty-printed JSON instead — handy for scripting and piping into tools like jq. It works for every data command; nexus ws emits one JSON object per line so it streams cleanly into jq.

nexus --output json markets
NEXUS_OUTPUT=json nexus ticker BTC-USDX-PERP
nexus --output json health | jq .
nexus --output json ws trades --market BTC-USDX-PERP | jq .payload
Flag Env Default
--output <human|json> NEXUS_OUTPUT human

Credentials

Authenticated commands (balance, account …, positions, fills, withdrawals, orders, order …, and account WebSocket channels) HMAC-sign each request. Public market-data commands don't need credentials.

Credentials resolve in this order, highest priority first:

  1. --api-key / --api-secret flags
  2. NEXUS_API_KEY / NEXUS_API_SECRET environment variables
  3. the config file written by nexus setup
Flag Env
--api-key <KEY> NEXUS_API_KEY
--api-secret <SECRET> NEXUS_API_SECRET
nexus setup                 # interactive; stores config at
                            # $XDG_CONFIG_HOME/nexus/config.json (mode 0600)

# …or per-shell:
export NEXUS_API_KEY=nx_...
export NEXUS_API_SECRET=...
nexus balance

Prefer nexus setup or the environment variables over --api-secret: flags are visible in your shell history and in the process list. The secret is never echoed during setup, never printed back, and the config file is created owner-read/write only (0600).

Wallet sign-in (session token)

As an alternative to an HMAC key pair, you can authenticate with an EVM wallet. nexus auth login reads a raw private key, signs the fixed sign-in challenge (EIP-191), and exchanges it for a session token stored in the same config file (mode 0600) under session_token. The session token authenticates session-scoped routes; the HMAC pair, when present, takes precedence as the request signer.

The private key is read from --private-key, the NEXUS_PRIVATE_KEY environment variable, or — when neither is set and you're at a terminal — a hidden interactive prompt. It is used only to produce the signature and is never written to disk or echoed.

Flag Env
--private-key <KEY> (on auth login / agents register) NEXUS_PRIVATE_KEY
--session-token <TOKEN> NEXUS_SESSION_TOKEN
export NEXUS_PRIVATE_KEY=0x<your-evm-key>
nexus auth login            # stores the session token; prints the address
nexus balance               # now authenticated via the stored token

# Register an agent key, authorized by an EIP-712 signature from your wallet
# (unauthenticated request — the signature is the authorization):
nexus agents register --agent 0x<agent-address> --label my-bot

agents register defaults the expiry to 30 days out, the nonce to the current Unix-ms timestamp, and the EIP-712 chain-id to the exchange chain (393); override any with --expires-at / --nonce / --chain-id.

Shell completions

Generate a completion script for your shell and source it:

# Bash
nexus completions bash > ~/.local/share/bash-completion/completions/nexus

# Zsh
nexus completions zsh > ~/.zfunc/_nexus   # ensure ~/.zfunc is in $fpath

# Fish
nexus completions fish > ~/.config/fish/completions/nexus.fish

# PowerShell
nexus completions powershell >> $PROFILE

# Elvish
nexus completions elvish >> ~/.elvish/rc.elv

Development

cargo fmt --all --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test --all-features

CI runs the same three checks on every push and pull request.

API coverage

The CLI targets a specific released version of the Exchange API spec, pinned in .api-version (currently v0.4.0, matching the wrapped nexus-exchange SDK). endpoints.txt lists the spec operations the CLI's commands actually exercise, and scripts/check_spec_drift.py verifies — in the spec-drift CI workflow — that:

  • every endpoint in endpoints.txt exists in the pinned spec (no rename/typo/ removal slips through), and
  • the set stays in sync with the SDK methods the CLI actually calls (parsed from src/main.rs / src/wsclient.rs), modulo two documented allowlists in the script (ops that are ahead of the pinned spec, and the WebSocket upgrade).

The check also prints the coverage number the dashboard reads: the CLI currently exercises 31 of 40 spec operations (77.5%). Run it locally with a fetched spec:

curl -fsSL https://raw.githubusercontent.com/nexus-xyz/nexus-exchange-api/$(cat .api-version)/openapi.json -o openapi.pinned.json
python3 scripts/check_spec_drift.py openapi.pinned.json

See examples/ for copy-pasteable recipes covering each flow.

License

Dual-licensed under MIT or Apache-2.0, at your option.

About

No description, website, or topics provided.

Resources

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT

Stars

Watchers

Forks

Packages

 
 
 

Contributors