The matrix CLI
matrix drives a running node: health, providers, jobs, balances, inference, bridge lock/attestation/reconciliation, and a local wallet that signs transfers so the node never sees a private key.
Build it
# from services/core
go build -o matrix ./cmd/matrix
go install ./cmd/matrix # or put it on your PATH
matrix --help # the full command treeOr take a prebuilt binary from a release.
Global flags
--addr(default127.0.0.1:9091) - the market gRPC endpoint. Almost every command talks to this one.--inference-addr(default127.0.0.1:9092) - the inference gRPC endpoint. It is a separate server on a separate port, so pointing--addrat a remote node is not enough formatrix inference; it is a flag on theinferencecommands only.--api-key- sent as theauthorizationgRPC metadata header. Required on a node withsecurity.enable_aclson, which is the default a generated config writes.--timeout(default10s) and--jsonfor machine-readable output.
The whole loop in one command
matrix --api-key <key> quickstart
# wallet -> funded buyer -> registered provider -> job -> settlement, in one command
# --provider --capacity --price --units --fund --wallet all override the defaultsNode and providers
matrix status # endpoint + serving status
matrix health # the gRPC health Check: SERVING / NOT_SERVING
# --price is the final manual quote in native MATRIX base units per compute unit.
matrix provider register --id gpu-1 --capacity 100 --price 5
matrix provider register --id gpu-2 --capacity 100 --price 3 \
--models llama-3.3-70b,qwen-2.5-72b # what a model request routes on
# Refresh existing quotes this way; re-registering can reset reserved capacity.
matrix provider quote-update --id gpu-2 --price 4
matrix provider list # local providers with fresh quotes
matrix provider list --include-remote # plus providers discovered over p2pprovider list on a freshly initialized node already has one entry - the GPU-free echo provider the node registers so the inference path works before you have a model server:
ID CAPACITY AVAILABLE PRICE/UNIT COST/UNIT MARKUP(BPS) QUOTE VERSION OBSERVED VALID UNTIL ORIGIN PEER MODELS
gpu-2 100 100 3 0 0 q-8f2... 1 2026-01-01T00:00:00Z 2026-01-02T00:00:00Z local llama-3.3-70b,qwen-2.5-72b--price is a manually supplied final quote in native MATRIX base units per compute unit, not credits, USD or a stablecoin peg. The marketplace gives each registration a quote identity and expiry; a submitted job snapshots the accepted price and quote metadata so a later provider refresh cannot reprice it. Refresh an expired manual quote before accepting more work.
Jobs and balances
matrix job submit --buyer <account-id> --provider gpu-1 --units 10
matrix job complete --id <job-id> # settles: moves native MATRIX buyer -> provider
matrix job cancel --id <job-id> # releases the reserved capacity, charges nothing
matrix balance --account <account-id>
matrix fund --account <account-id> --amount 1000000 # from the genesis reward poolfund moves native MATRIX out of the genesis reward pool; it never mints, so the billion-MATRIX cap holds however often you call it.
On a validator set it refuses instead, with FailedPrecondition. Funding from the pool is not consensus-ordered: it moves value on the node you called and nowhere else, so the pools diverge and the provider emission forks. This was found by running two nodes - funding an account on one left the other reporting a balance of zero for it. Put the allocation in every node's genesis instead, or move value with a signed transfer, which every node applies from the committed block.
job complete settles through consensus: the payment is a transfer signed by the buyer that a quorum commits and every node applies. The node therefore needs the buyer's signing key, which it resolves from the wallet files under ~/.matrix. A job whose buyer it holds no key for is refused with FailedPrecondition and the reservation left intact, so you can cancel it. That refusal is the point: paying out of an account without its owner's signature is what this path exists to stop.
Wallet
An ed25519 keypair at ~/.matrix/wallet.json, mode 0600 and encrypted under a passphrase you choose (scrypt, then AES-256-GCM). The private key is never printed or logged: a transfer is signed locally and only the public key, the signature and the transfer fields go to the node.
create shows a 12-word BIP-39 recovery phrase, once. It is the only way to restore the account if the file is lost, and it is derived at the standard SLIP-0010 ed25519 path m/44'/9004'/0'/0', so it also restores in other wallets that implement it. A mistyped word fails BIP-39's checksum and is refused, rather than silently deriving a different, empty account.
Wallets written before this existed stored the key in the clear. They are still read, so nothing is stranded, but every command that signs with one prints a warning: there is no migration a tool can do unasked, because it cannot invent a passphrase. Restore the phrase with wallet import, or move the balance to a new wallet.
matrix wallet create # encrypted, and shows a 12-word recovery phrase once
matrix wallet import # restore that phrase (omit --mnemonic to be asked)
matrix wallet show # the account id. No passphrase: it is a public value
matrix wallet balance # likewise
matrix wallet transfer --to <recipient-account-id> --amount 200 # this one unlocks
# The passphrase comes from MATRIX_WALLET_PASSPHRASE when set, otherwise a
# prompt that does not echo. A non-interactive caller must set the variable;
# the tool refuses to read a passphrase off a pipe.
#
# every wallet command takes --wallet to use a file other than ~/.matrix/wallet.jsonInference
matrix inference submit \
--buyer <account-id> \
--provider demo-inference-provider \
--model demo \
--prompt "one sentence about peer-to-peer compute"
matrix inference get --id <job-id>
# --fulfill defaults to true: the job is reserved, run and settled in one call.
# --fulfill=false reserves only.
matrix inference submit --client-signed \
--buyer <your wallet's account-id> \
--provider gpu-2 --model llama-3.3-70b --prompt "hello"
# The node runs the model, returns the exact transfer to sign and withholds the
# completion; this signs it with the local wallet and the node settles. Use it
# where the node should not hold your key.What a run against the demo provider prints:
id: 13ac1c62-5013-4c31-8cbd-c5f3b7848d23
buyer: c5b097824f2e2222a278af3dbe4b519fa653a96e44ff08891668b3a2a708d5d9
provider: demo-inference-provider
model: demo
status: completed
units: 5
completion: echo: user: one sentence about peer-to-peer computeAn inference job is a compute job with a prompt attached: it reserves capacity from a registered provider, runs on that provider's backend, and settles at that provider's price through consensus - which means the node needs the buyer's signing key. It resolves one from the wallet files under ~/.matrix, so the wallet you created above works and an arbitrary account id does not.
Base bridge
# One-time operator setup: create a fixed EVM committee key.
matrix bridge attestor-new --out ~/.matrix/bridge-attestor.json
# User flow: minimum 100 MATRIX (100000000000 native base units).
matrix bridge lock --wallet ~/.matrix/wallet.json --to 0x<base-recipient> --amount 100000000000
matrix bridge attestation --lock-id 0x<lock-id> --validator validator-1.example.org:9091 --validator validator-2.example.org:9091 > attestations.json
# Compare native escrow accounting with WrappedMatrix.totalSupply().
matrix bridge reconcileattestor-new creates one encrypted secp256k1 key for an address in the fixed WrappedMatrix committee selected at deployment. It is not granted to every native validator, and bonded-open validator membership does not change the contract committee. lock commits native escrow, attestation emits the JSON signatures the Base mint consumes, and reconcile reports the 1:1 backing invariant.
Before the web bridge asks MetaMask to sign a native lock, it reads the exact WrappedMatrix code, threshold, attestor count, cap, supply, and conversion. It then sends one random 32-byte challenge to every configured validator's rate-limited public GetBridgeReadiness RPC, recovers unique registered signers, and requires the live threshold. A chain, contract, minimum, challenge, signature, registration, conversion, or cap mismatch stops before payment signing, local recovery storage, or native submission. Post-lock attestation and reconciliation checks remain required.
Base Sepolia is chain 84532 for rehearsal and Base is 8453 for production. The user broadcasts mint, burn, approval or swap transactions and pays Base gas from their wallet; Matrix provides no gas relayer. A burn release is always ordered by native consensus. The production wrapped mint ceiling is 6% of native maximum supply, and wMATRIX remains a native-backed mirror rather than a USD or USDC stablecoin.
Transactions
matrix tx list --limit 20 # ascending commit order
matrix tx list --start 10 --limit 20
matrix tx get --index 3What tx list shows
These read the consensus transaction history: the ordered sequence of value transfers that committed blocks carried. A wallet transfer, SubmitSignedTransfer, and the buyer-to-provider payment behind a compute or inference settlement all settle through consensus, so they all appear here in commit order. Because the history comes from the committed block chain, two nodes list the same transfers in the same order. Reserved consensus operations (bonds, withdrawals, validator set changes) are protocol state, not transfers, so they are omitted.
Next
- Quickstart - a node and a settled job in about a minute
- Configuration - every field, including where the API key lives
- The CLI's own README