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

# Onboard a machine on Solana

> Activate an Economics 2.0 machine whose home chain is Solana: reserve and bond on peaq with an operator wallet, then create the machine on Solana with its owner wallet.

export const KeyTerms = ({all = {}, ids = [], title = "Key terms in this guide"}) => <details className="not-prose my-4 rounded-xl border border-zinc-200 px-4 py-3 dark:border-zinc-800">
    <summary className="cursor-pointer font-medium text-zinc-900 dark:text-zinc-100">
      {title}
    </summary>
    <div className="mt-3 space-y-2 text-sm text-zinc-700 dark:text-zinc-300">
      {ids.map(id => {
  const t = all[id] || ({});
  return <div key={id}>
            <strong>{t.term}.</strong> {t.def}
          </div>;
})}
    </div>
  </details>;

export const G = {
  onchain: {
    id: "onchain",
    cat: "chain-infra",
    term: "On-chain vs off-chain",
    def: "On-chain means written to the shared public ledger every machine agrees on: permanent and readable by anyone. Off-chain means kept on a normal private server instead."
  },
  blockchain: {
    id: "blockchain",
    cat: "chain-infra",
    term: "Chain / blockchain",
    def: "A shared, tamper-resistant public database maintained by a whole network of computers with no single owner. Different chains are separate such networks."
  },
  peaqChain: {
    id: "peaqChain",
    cat: "chain-infra",
    term: "peaq chain",
    def: "The machine-focused blockchain peaqOS uses as home base for identity and credit records."
  },
  transaction: {
    id: "transaction",
    cat: "chain-infra",
    term: "Transaction (tx) / tx hash",
    def: "A single signed request that changes the ledger. Its hash is a unique, receipt-like ID you can use to look it up later."
  },
  rpcUrl: {
    id: "rpcUrl",
    cat: "chain-infra",
    term: "RPC URL / endpoint",
    def: "The network address your code calls to read from or write to a chain, like the base URL of the chain's API server."
  },
  mainnet: {
    id: "mainnet",
    cat: "chain-infra",
    term: "Mainnet / testnet (agung)",
    def: "Mainnet is the real, live network where tokens have real value. Testnet is a free practice copy with worthless tokens; peaq's is called agung."
  },
  evm: {
    id: "evm",
    cat: "chain-infra",
    term: "EVM / EVM-compatible",
    def: "The Ethereum Virtual Machine: the standard runtime many chains share, so the same 0x... addresses and tools work across all of them. peaq is EVM-compatible."
  },
  node: {
    id: "node",
    cat: "chain-infra",
    term: "Node (RPC node)",
    def: "A server running the blockchain software that holds a copy of the ledger and answers queries. NOT a ROS 2 node, despite the shared word."
  },
  chainId: {
    id: "chainId",
    cat: "chain-infra",
    term: "Chain ID",
    def: "A number that uniquely labels a chain so software doesn't confuse networks (peaq is 3338, Base is 8453)."
  },
  precompile: {
    id: "precompile",
    cat: "chain-infra",
    term: "Precompile",
    def: "A built-in function baked into the chain at a fixed address that acts like a contract but runs as faster native code. The batch one bundles several actions into one all-or-nothing transaction."
  },
  dataHash: {
    id: "dataHash",
    cat: "chain-infra",
    term: "Data hash (keccak256)",
    def: "A short, fixed-length fingerprint of a file, stored on-chain instead of the file itself, so data can be verified later while the raw data stays off-chain."
  },
  wallet: {
    id: "wallet",
    cat: "wallet-keys",
    term: "Wallet",
    def: "An account on the chain, identified by a public address, that holds a machine's funds and approves its actions. Really just a pair of keys, not a place money is stored."
  },
  keypair: {
    id: "keypair",
    cat: "wallet-keys",
    term: "Keypair",
    def: "The two matched secrets behind a wallet: a public address you can share, and a private key you keep secret that signs actions."
  },
  privateKey: {
    id: "privateKey",
    cat: "wallet-keys",
    term: "Private key",
    def: "The secret string that proves you control a wallet. Anyone who has it has full control, like a master password that can never be reset."
  },
  sign: {
    id: "sign",
    cat: "wallet-keys",
    term: "Sign / signature",
    def: "Using your private key to produce a cryptographic stamp proving you approved a specific action, without ever revealing the key."
  },
  signer: {
    id: "signer",
    cat: "wallet-keys",
    term: "Signer / signing identity",
    def: "The wallet whose private key authorizes an action: the account the network treats as the one taking it. NOT a file or an app."
  },
  address: {
    id: "address",
    cat: "wallet-keys",
    term: "Address (0x...)",
    def: "The public 0x... identifier of a wallet or contract you can freely share so others can send to it or look it up, like an account number."
  },
  eoa: {
    id: "eoa",
    cat: "wallet-keys",
    term: "EOA (externally owned account)",
    def: "A plain wallet controlled directly by a private key, as opposed to one controlled by code. Here, the account that IS the machine."
  },
  ows: {
    id: "ows",
    cat: "wallet-keys",
    term: "OWS / wallet vault",
    def: "An open standard for storing wallet keys in an encrypted local file (a vault) with a backup phrase and an activity log, instead of a bare key in a text file."
  },
  passphrase: {
    id: "passphrase",
    cat: "wallet-keys",
    term: "Passphrase (OWS_PASSPHRASE)",
    def: "The password that unlocks the encrypted wallet vault so its key can be used to sign."
  },
  mnemonic: {
    id: "mnemonic",
    cat: "wallet-keys",
    term: "Mnemonic / seed phrase",
    def: "A list of 12 or 24 ordinary words that encodes a wallet's secret key, used to back it up and recover it. Whoever has the words controls the wallet."
  },
  derivation: {
    id: "derivation",
    cat: "wallet-keys",
    term: "Derivation path",
    def: "The deterministic recipe that turns one backup phrase into many specific keys and addresses, one per network or index."
  },
  challenge: {
    id: "challenge",
    cat: "wallet-keys",
    term: "Challenge (sign-to-prove)",
    def: "A login-style handshake: the server sends a random message, you sign it with your key, and the signature proves you control the account without sending the key."
  },
  eip191: {
    id: "eip191",
    cat: "wallet-keys",
    term: "EIP-191 / personal_sign",
    def: "A standard way to sign a plain message to prove you control an account, without sending any on-chain transaction."
  },
  did: {
    id: "did",
    cat: "identity",
    term: "DID / peaqID",
    def: "A globally unique, self-owned ID for a machine that lives on the chain and isn't issued by any single company. peaqID is peaq's version, written did:peaq:0x..."
  },
  register: {
    id: "register",
    cat: "identity",
    term: "Register a machine",
    def: "Putting a machine on the network for the first time, which gives it an ID, a DID, an ownership token, and a locked deposit. Under Economics 2.0 this is one activateMachine call signed by the machine's owner; registerMachine and registerFor are the older Tokenomics 1.0 calls."
  },
  machineId: {
    id: "machineId",
    cat: "identity",
    term: "Machine ID",
    def: "Your machine's handle in every later call. Under Economics 2.0 it is derived from the machine type and credential subject (a large uint256, written as a decimal string); Tokenomics 1.0 machines got a small sequential number."
  },
  ownerOperator: {
    id: "ownerOperator",
    cat: "identity",
    term: "Owner / operator",
    def: "The owner owns a machine; the operator runs it. They can be the same account (self-managed) or different (proxy-managed)."
  },
  proxyOperator: {
    id: "proxyOperator",
    cat: "identity",
    term: "Proxy operator",
    def: "One account that registers and manages many machines on behalf of their owners, so a fleet operator can handle a whole fleet from one wallet."
  },
  didAttributes: {
    id: "didAttributes",
    cat: "identity",
    term: "DID attributes",
    def: "Public name-value facts (a docs link, a data endpoint) attached to a machine's DID and stored on-chain for anyone to read. Writing them is a separate transaction from registration."
  },
  pairing: {
    id: "pairing",
    cat: "identity",
    term: "Pairing / pairing token",
    def: "The verified link between an AI agent and a machine, set up by signing a challenge. The pairing token is the signed credential the agent sends with each request, like a temporary access badge."
  },
  hardwareAttestation: {
    id: "hardwareAttestation",
    cat: "identity",
    term: "Hardware attestation",
    def: "A tamper-resistant chip on the machine cryptographically vouching that it's genuine hardware, so its identity can't be faked in software. This is the Verify layer."
  },
  gas: {
    id: "gas",
    cat: "tokens-economics",
    term: "Gas",
    def: "The small fee, paid in the chain's token, that every action writing to the ledger costs, like a per-write transaction cost."
  },
  peaqToken: {
    id: "peaqToken",
    cat: "tokens-economics",
    term: "PEAQ (token)",
    def: "The peaq network's own token, used to pay gas fees and to lock up as the deposit when registering a machine."
  },
  gasStation: {
    id: "gasStation",
    cat: "tokens-economics",
    term: "Gas Station / faucet",
    def: "A peaq service that hands a brand-new, empty wallet a tiny starting amount of tokens so it can afford its first network fees. Gated by 2FA."
  },
  bond: {
    id: "bond",
    cat: "tokens-economics",
    term: "Bond",
    def: "PEAQ locked when a machine activates, proving skin in the game. Under Economics 2.0 it is priced per tier in USD and converted to PEAQ at the oracle rate, and it is not withdrawable; Tokenomics 1.0 machines locked a fixed 1 PEAQ. Bonded means the deposit is in place."
  },
  nft: {
    id: "nft",
    cat: "tokens-economics",
    term: "NFT",
    def: "A unique, one-of-a-kind ownership token recorded on the chain. Unlike a coin, no two are interchangeable."
  },
  mint: {
    id: "mint",
    cat: "tokens-economics",
    term: "Mint / minting",
    def: "Creating a brand-new token on the chain and assigning it to an owner, like stamping a fresh serial-numbered certificate into existence."
  },
  machineNft: {
    id: "machineNft",
    cat: "tokens-economics",
    term: "Machine NFT",
    def: "The unique token representing one specific physical machine and its financial profile. It can be sold or bridged on its own, separate from the machine's identity."
  },
  identityNft: {
    id: "identityNft",
    cat: "tokens-economics",
    term: "Identity NFT",
    def: "A non-transferable (soulbound) token minted automatically when a machine registers, representing its identity. Its token ID equals the machine ID."
  },
  tokenId: {
    id: "tokenId",
    cat: "tokens-economics",
    term: "Token ID",
    def: "The unique number identifying one specific token within a collection, like a serial number."
  },
  mcr: {
    id: "mcr",
    cat: "tokens-economics",
    term: "Machine Credit Rating (MCR)",
    def: "A creditworthiness score for a machine (a Moody's-style grade AAA down to NR, plus a 0-100 number) computed from its recorded earnings and activity. Like a credit score for a robot."
  },
  mcrApi: {
    id: "mcrApi",
    cat: "tokens-economics",
    term: "MCR API",
    def: "The public web service you call to fetch a machine's credit score and profile as JSON, and the one place a machine's monetization is switched on or off with a signed message. No login needed: reads are open, and the write is authorized by the signature itself."
  },
  provisioned: {
    id: "provisioned",
    cat: "tokens-economics",
    term: "Provisioned / NR (Not Rated)",
    def: "Early MCR statuses. Provisioned means registered and bonded but with too little history to score yet. NR means no grade, because the score is too low or the machine isn't bonded."
  },
  event: {
    id: "event",
    cat: "tokens-economics",
    term: "Event (revenue / activity)",
    def: "A recorded data point about a machine's work, submitted to the chain to feed its credit score. Revenue events report money earned; activity events report work with no money. NOT a ROS topic message."
  },
  trustLevel: {
    id: "trustLevel",
    cat: "tokens-economics",
    term: "Trust level",
    def: "A label on each submitted event saying how strongly its truth is backed: the machine's word (0), a checkable on-chain record (1), or tamper-proof hardware proof (2)."
  },
  escrow: {
    id: "escrow",
    cat: "tokens-economics",
    term: "Escrow",
    def: "Holding a buyer's payment in a neutral locked place until the service is delivered, then releasing it, so neither side has to trust the other first."
  },
  paymentRail: {
    id: "paymentRail",
    cat: "tokens-economics",
    term: "Payment rail",
    def: "The specific method or channel a payment moves through, like choosing card vs bank transfer vs a particular token."
  },
  x402: {
    id: "x402",
    cat: "tokens-economics",
    term: "x402",
    def: "A web payment standard where a server answers 'payment required' with exact instructions, and the buyer's wallet signs an authorization instead of sending a separate transaction — built for machines and agents paying per request."
  },
  usdt: {
    id: "usdt",
    cat: "tokens-economics",
    term: "USDT",
    def: "A stablecoin token meant to hold a value of one US dollar, used to pay service providers without price swings."
  },
  fractionalize: {
    id: "fractionalize",
    cat: "tokens-economics",
    term: "Fractionalize (ERC-3643)",
    def: "Splitting ownership of one machine into many small tradable shares so multiple people can each own a piece. ERC-3643 is the regulated-securities token standard used to do it."
  },
  smartContract: {
    id: "smartContract",
    cat: "smart-contracts",
    term: "Smart contract / contract address",
    def: "A program deployed on the chain that runs exactly as written and that anyone can call, identified by its own 0x... address."
  },
  registryContracts: {
    id: "registryContracts",
    cat: "smart-contracts",
    term: "Registry contracts",
    def: "On-chain programs that each keep an official, lookup-able list: IdentityRegistry tracks which machines exist, EventRegistry stores their events, IdentityStaking holds their deposits."
  },
  smartAccount: {
    id: "smartAccount",
    cat: "smart-contracts",
    term: "Smart account (ERC-4337)",
    def: "A programmable wallet controlled by code instead of a single key, so it can enforce rules like spending limits. Each machine gets one at activation."
  },
  submitEvent: {
    id: "submitEvent",
    cat: "smart-contracts",
    term: "submitEvent / batchSubmitEvents",
    def: "The calls that record one or many of a machine's revenue or activity entries onto the chain."
  },
  revert: {
    id: "revert",
    cat: "smart-contracts",
    term: "Revert",
    def: "When an on-chain call is rejected and fully undone because a rule was broken, leaving no changes and usually a named error."
  },
  soulbound: {
    id: "soulbound",
    cat: "smart-contracts",
    term: "Soulbound",
    def: "A token that can never be transferred or sold and stays permanently attached to one owner. The Identity NFT is soulbound."
  },
  bridge: {
    id: "bridge",
    cat: "cross-chain",
    term: "Bridge / bridging",
    def: "Moving a token from one chain to another, so the same Machine NFT can exist on a different chain. peaq and Base are live today; bridging is mainnet-only."
  },
  base: {
    id: "base",
    cat: "cross-chain",
    term: "Base",
    def: "Another blockchain network (built by Coinbase) that peaqOS can move Machine NFTs to and from. Paying fees on Base needs Base ETH."
  },
  omniChain: {
    id: "omniChain",
    cat: "cross-chain",
    term: "Omni-chain / cross-chain",
    def: "Working across many separate chains at once, so a machine's identity and credit created on peaq can be read or used on other chains."
  },
  homeChain: {
    id: "homeChain",
    cat: "cross-chain",
    term: "Home chain",
    def: "The chain where a record's canonical, authoritative copy lives. For a peaqOS machine that is peaq, or Solana for an Economics 2.0 machine created there (since 2026-09-16); other chains hold mirrors. The subscription bond stays on peaq either way."
  },
  satelliteChain: {
    id: "satelliteChain",
    cat: "cross-chain",
    term: "Satellite chain",
    def: "A chain carrying a read-only, automatically synced mirror of home-chain records, so apps there can use them without crossing back to the home chain."
  },
  sourceChainId: {
    id: "sourceChainId",
    cat: "cross-chain",
    term: "sourceChainId / sourceTxHash",
    def: "Two fields recording which chain an action happened on and its hash there, so a cross-chain event can be traced back and verified."
  },
  machineAgent: {
    id: "machineAgent",
    cat: "general-web3",
    term: "Machine Agent",
    def: "A third-party AI program (Claude, OpenAI, a custom bot) paired to a machine and given limited permission to find, buy, and pay for services on its behalf."
  },
  delegationPolicy: {
    id: "delegationPolicy",
    cat: "general-web3",
    term: "Delegation policy",
    def: "The rules an owner gives an AI agent that cap how much it can spend per transaction and per day and which services it may use, so it transacts within guardrails."
  },
  machineMarkets: {
    id: "machineMarkets",
    cat: "general-web3",
    term: "Machine Markets / Service Registry",
    def: "peaqOS's marketplace where machines list services they offer (Service Registry) and where agents discover, order, pay for, and run services from others."
  },
  sdk: {
    id: "sdk",
    cat: "general-web3",
    term: "SDK (peaq-os-sdk)",
    def: "peaq's code library (Python and JS) you install to call all this functionality without writing low-level blockchain calls yourself."
  },
  stream: {
    id: "stream",
    cat: "data-stream",
    term: "Stream (Data-as-a-Service)",
    def: "The peaqOS function where a machine sells the data it generates: it signs the data, encrypts what's sensitive, and grants buyers access. Selling data, as opposed to selling capacity (that's Monetize)."
  },
  edgeAgent: {
    id: "edgeAgent",
    cat: "data-stream",
    term: "peaqOS Edge Agent",
    def: "Software that runs on the machine itself (as a ROS 2 node) and signs, encrypts, and ships the data it produces. The on-machine half of Stream."
  },
  dataPackage: {
    id: "dataPackage",
    cat: "data-stream",
    term: "Signed data package",
    def: "A bundle of machine data stamped with the machine's identity (DID, timestamp, sequence number) and a signature, so anyone can prove which machine produced it and that it wasn't altered."
  },
  dataEventMap: {
    id: "dataEventMap",
    cat: "data-stream",
    term: "Data Event Map",
    def: "The policy file a machine owner writes to control what streams out: which topics to read, which fields to keep, drop, or encrypt, and where the signed data goes."
  },
  chunk: {
    id: "chunk",
    cat: "data-stream",
    term: "Chunk",
    def: "A bounded, individually encrypted slice of a data stream (by time window or size). The unit a buyer actually purchases and decrypts."
  },
  chunkChain: {
    id: "chunkChain",
    cat: "data-stream",
    term: "Chunk chain",
    def: "A run of chunks linked in order, each referencing the one before it, so missing, reordered, or edited chunks are detectable. Tamper-evidence for a continuous stream."
  },
  manifest: {
    id: "manifest",
    cat: "data-stream",
    term: "Manifest",
    def: "A signed record describing a chunk or dataset — its hashes, storage location, and encryption details — without the data itself. Buyers verify the manifest before trusting or buying."
  },
  dataset: {
    id: "dataset",
    cat: "data-stream",
    term: "Dataset",
    def: "A group of chunks for one topic and time range, packaged for sale with a single fingerprint (a Merkle root) that covers every chunk in it."
  },
  merkleRoot: {
    id: "merkleRoot",
    cat: "data-stream",
    term: "Merkle root",
    def: "One short hash that stands in for a whole set of items, letting you later prove a specific chunk belongs to a dataset without revealing the rest."
  },
  envelopeEncryption: {
    id: "envelopeEncryption",
    cat: "data-stream",
    term: "Envelope encryption / key wrapping",
    def: "Encrypt the data once with a random key, then lock that key separately for each authorized reader. Granting a buyer access re-locks the key to their public key — the data itself is never re-encrypted."
  },
  accessGrant: {
    id: "accessGrant",
    cat: "data-stream",
    term: "Access grant",
    def: "What a buyer receives after paying: the chunk keys they bought, each locked to their public key. They unlock with their private key and decrypt only those chunks."
  },
  contextProvider: {
    id: "contextProvider",
    cat: "data-stream",
    term: "Context Provider",
    def: "A third party that buys machine data, normalizes it into datasets, and serves or resells it (for example, for AI training). The buyer side of Stream, such as DataHive."
  },
  computeProvider: {
    id: "computeProvider",
    cat: "monetize",
    term: "Compute provider",
    def: "A machine that rents out its processing power to a compute network. Instead of sitting idle, the machine runs other people's workloads and earns for it."
  },
  provisioningManifest: {
    id: "provisioningManifest",
    cat: "monetize",
    term: "Provisioning manifest",
    def: "A published, step-by-step install plan that turns a machine into a compute provider: the checks to run first, the commands to execute, and the probes that prove it worked. The machine pulls it on demand and runs it locally."
  },
  heartbeat: {
    id: "heartbeat",
    cat: "monetize",
    term: "Heartbeat",
    def: "A short signed 'I'm here' message a machine sends out at a regular interval. As long as heartbeats keep arriving, the machine counts as online; if they stop, it's marked offline automatically."
  },
  aggregator: {
    id: "aggregator",
    cat: "monetize",
    term: "Aggregator",
    def: "A network that pools capacity from many machines and sells it on, such as a decentralized compute marketplace. Machines connect to an aggregator, which then dispatches work to them."
  },
  machineWallet: {
    id: "machineWallet",
    cat: "monetize",
    term: "Machine wallet",
    def: "The wallet that belongs to the machine itself, separate from its owner's or operator's wallet. Earnings from the machine's work land here by default."
  },
  walrus: {
    id: "walrus",
    cat: "chain-infra",
    term: "Walrus",
    def: "A decentralized storage network where encrypted data chunks can be parked, referenced by walrus:// links. The data stays off the blockchain; only its reference and fingerprint are tracked on-chain."
  },
  solana: {
    id: "solana",
    cat: "cross-chain",
    term: "Solana",
    def: "A high-throughput blockchain. peaqOS wallets hold a Solana account and sign Solana transactions, machine-economy payments can settle there, and since 2026-09-16 an Economics 2.0 machine can be homed there (identity, DID and NFT as Solana accounts, bond on peaq)."
  }
};

A machine can live on <Tooltip tip={G.solana.def}>Solana</Tooltip> instead of peaq. Its identity record, DID document and Machine NFT are then Solana accounts owned by a Solana key, while the subscription <Tooltip tip={G.bond.def}>bond</Tooltip> and the peaq-side registry entry stay on peaq. Onboarding therefore happens in two places and three phases, in a fixed order: reserve the machine ID on peaq, activate its subscription on peaq, wait for peaq to mirror both to Solana, then create the machine on Solana. The CLI runs one phase per invocation; the SDKs expose the same phases.

<Note>
  **Solana support needs CLI `0.0.12`, `@peaqos/peaq-os-sdk` `0.8.0` and `peaq-os-sdk` `0.8.0` (all released 2026-09-16).** Install with `pip install -U 'peaq-os-cli[solana,ows]>=0.0.12'` or `npm install @peaqos/peaq-os-sdk@latest @solana/web3.js @coral-xyz/anchor`. CLI 0.0.10 has no `--chain solana`. CLI 0.0.11 has it, but its `solana` extra pins `peaq-os-sdk<0.8.0` next to the CLI's own `>=0.8.0` requirement, so `pip install 'peaq-os-cli[solana]==0.0.11'` fails with `ResolutionImpossible` (older pip backtracks for minutes instead). Releases before CLI 0.0.10 and SDK 0.8.0 also fail `peaq-mainnet` activation and renewal with `READ_FAILED` (CLI: `RPC_FAILED`) since the 2026-09-15 mainnet upgrade renamed `fullMode()` to `isEconomicAuthority()`.
</Note>

<KeyTerms all={G} ids={["solana", "homeChain", "bond", "ows", "wallet", "did", "machineId", "machineNft", "rpcUrl", "mainnet"]} />

## What you need

| Item                               | Notes                                                                                                                                                                                             |
| :--------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| CLI or SDK from the Solana release | See the note above. CLI: `pip install -U 'peaq-os-cli[solana,ows]>=0.0.12'`. Python: `pip install "peaq-os-sdk[solana,ows]"`. JS: `@solana/web3.js` and `@coral-xyz/anchor` as peer dependencies. |
| A peaq **operator** wallet         | Reserves the machine ID and pays the bond and gas on peaq mainnet. Needs PEAQ for gas in every phase; with `--payment usdt` the bond is paid in USDT on top, not instead.                         |
| A Solana **owner** wallet          | Creates the machine on Solana mainnet-beta and pays SOL fees and rent. Needs SOL.                                                                                                                 |
| Two RPC endpoints                  | One for peaq, one for Solana. Choosing a Solana cluster never selects the machine's home; the flags do.                                                                                           |
| A DID document                     | Same three-field JSON as on peaq. Verification-method controllers are base58 Solana keys.                                                                                                         |

Mainnet only. The Solana half of the deployment record exists for `peaq-mainnet`; `agung-2026-08-28` has none, and there is no testnet walkthrough.

## Configure

```bash theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
mkdir -p solana-onboarding && cd solana-onboarding   # one directory per attempt, keep it
export PEAQOS_NETWORK=peaq
export TOKENOMICS_DEPLOYMENT_ID=peaq-mainnet
export PEAQOS_RPC_URL=https://peaq.api.onfinality.io/public
export PEAQOS_SVM_NETWORK=mainnet-beta
export PEAQOS_SVM_RPC_URL=https://api.mainnet-beta.solana.com
```

Every CLI client, keyless previews included, also needs the six Tokenomics 1.0 contract addresses: run `peaqos init` in this directory first (or copy a `.env` that has them), otherwise the first preview exits `3` naming `IDENTITY_REGISTRY_ADDRESS`. `--svm-rpc-url` and `--svm-network` before the command name override the environment, which overrides `.env`. `peaqos whoami` shows the cluster and a redacted RPC origin once either variable is set. The SDK journal `peaqos.log` and the original inputs must stay in this directory for every later phase; the same journal is what lets a rerun reconcile instead of resubmitting.

Create the two wallets in the <Tooltip tip={G.ows.def}>OWS</Tooltip> vault and note their public addresses. Fund them yourself; wallet creation does not.

```bash theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
peaqos wallet create svm-operator     # peaq address pays gas and the bond
peaqos wallet create svm-owner        # Solana address pays fees and rent
peaqos wallet show svm-owner --json
unset PEAQOS_PRIVATE_KEY              # let the selected OWS wallet sign
```

## The DID document

Save as `native-did.json`. `authentication` entries are zero-based indices into `verificationMethods`. The optional machine-level controller is a separate flag (`--solana-controller`), not a field here.

```json theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
{
  "verificationMethods": [
    {
      "id": "#key-1",
      "methodType": "Ed25519",
      "controller": "<base58 Solana public key>",
      "publicKeyMultibase": "<multibase public key>"
    }
  ],
  "authentication": [0],
  "serviceEndpoints": [
    { "id": "#telemetry", "serviceType": "Telemetry", "serviceEndpoint": "https://example.com/telemetry" }
  ]
}
```

Limits come from the Solana program: 8 verification methods, 8 authentication indices, 8 service endpoints, 128 UTF-8 bytes per string field, 64 bytes for the machine type, 512 bytes of credential subject. Oversized input is rejected before anything is signed; nothing is truncated.

## The argument set

Every phase takes the **same complete arguments**. A changed machine type or credential computes a different machine ID, so the CLI treats it as a new attempt with its own journal entries instead of continuing this one; a changed wallet or ceiling on the same machine ID is refused. Keep the original inputs. Ceilings are explicit integers in base units; `0` is a strict cap, never "unlimited"; the USDT cap is enforced on chain, the PEAQ cap is checked before signing and against the mined receipt.

Set the ten shell variables first (`OWNER`, `OPERATOR`, `MANUFACTURER`, `CREDENTIAL_SUBJECT_HEX`, `PEAQ_CAP`, `FEE_CAP`, `RENT_CAP`, `HISTORY_START` = the peaq block number just before your reservation, `COMPUTE_LIMIT`, `PRIORITY_PRICE`); the `:?` guards make the shell stop on an unset one instead of passing an empty argument.

```bash theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
ARGS=(
  --chain solana --json
  --solana-owner "${OWNER:?}"                       # base58, the Solana owner wallet
  --evm-operator "${OPERATOR:?}"                    # 0x…, the peaq operator wallet
  --manufacturer "${MANUFACTURER:?}"                # base58, recorded without verification
  --machine-type Sensor
  --credential-subject-hex "${CREDENTIAL_SUBJECT_HEX:?}"
  --tier basic --did-document ./native-did.json
  --payment peaq --max-net-peaq-amount "${PEAQ_CAP:?}"
  --max-native-fee-lamports "${FEE_CAP:?}"          # network + priority fees, excludes rent
  --max-native-rent-lamports "${RENT_CAP:?}"        # machine + state account rent
  --from-block "${HISTORY_START:?}"                 # inclusive peaq block where this attempt's history starts
  --compute-unit-limit "${COMPUTE_LIMIT:?}"         # 1 to 1,400,000
  --compute-unit-price-micro-lamports "${PRIORITY_PRICE:?}"
)
```

| Flag                                                          | Meaning                                                                                                                                                                                                                                                                                                                                              |
| :------------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--chain solana`                                              | Home the machine on Solana. Without it `peaqos activate` is the one-transaction peaq flow.                                                                                                                                                                                                                                                           |
| `--phase`                                                     | `reservation`, `subscription`, or `native_onboarding`. Required for a write; omit it with `--dry-run` to preview the whole plan.                                                                                                                                                                                                                     |
| `--solana-owner`                                              | Required. The reserved owner's base58 public key.                                                                                                                                                                                                                                                                                                    |
| `--solana-controller`                                         | Optional. Omit for the owner-only default.                                                                                                                                                                                                                                                                                                           |
| `--evm-operator`                                              | Optional assertion, checked against the reservation. Omit to use the matching reservation or the configured wallet.                                                                                                                                                                                                                                  |
| `--max-net-peaq-amount` or `--max-usdt-amount`                | Required ceiling for the selected payment asset. USDT needs the deployment's foreign-funding support and must be chosen before the first phase.                                                                                                                                                                                                      |
| `--max-native-fee-lamports`, `--max-native-rent-lamports`     | Required. Fees exclude rent; rent excludes external delivery rent.                                                                                                                                                                                                                                                                                   |
| `--from-block`                                                | Required. Inclusive peaq history start covering the original attempt. Use the peaq block just before your reservation: the SDK reads the reservation history from `CrossChainMirror` with `eth_getLogs` from this block to the current block, and a start far in the past (or `0`) is rejected or timed out by the public peaq RPCs (`READ_FAILED`). |
| `--compute-unit-limit`, `--compute-unit-price-micro-lamports` | Required native compute budget and priority price.                                                                                                                                                                                                                                                                                                   |

Tier `entry` is not offered on Solana: `basic` or `pro`. `--for`, `--machine-key`, and `--slippage-bps` are rejected with `--chain solana`. The machine ID is the same derivation as on peaq, `uint256(keccak256(abi.encode(machineType, credentialSubject)))`, a decimal string in JSON and the DID `did:peaq:<decimal id>`.

## Run the three phases

<Steps>
  <Step title="Preview">
    Keyless. Reports each stage's unresolved prerequisites and the native fee and rent estimate. A preview is not transaction evidence, and `did` is `null` before the machine exists.

    ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
    peaqos activate "${ARGS[@]}" --dry-run
    ```
  </Step>

  <Step title="Reserve on peaq (operator wallet)">
    Reserves the machine ID for the Solana owner. `--yes` accepts the displayed terms for this phase only.

    ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
    PEAQOS_OWS_WALLET=svm-operator peaqos activate "${ARGS[@]}" --phase reservation --yes
    ```
  </Step>

  <Step title="Activate the subscription on peaq (operator wallet)">
    Bonds the tier. Approval and activation are separate transactions; an approval receipt alone is not a subscription. The preflight requires `isEconomicAuthority()` on peaq and reads both technical pause flags before the approval; a pause before the first approval stops with `TECHNICALLY_PAUSED` and spends nothing; a pause that starts after the approval confirmed ends with the same code, the approval gas spent and the allowance in place, so read the retained transactions before assuming nothing moved.

    ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
    peaqos activate "${ARGS[@]}" --phase subscription --dry-run
    PEAQOS_OWS_WALLET=svm-operator peaqos activate "${ARGS[@]}" --phase subscription --yes
    ```
  </Step>

  <Step title="Wait for the reservation mirror and the subscription status">
    peaq pushes the reservation to Solana, and the subscription status reaches the machine's `SubscriptionTerminal` account (it must read Active or Grace with a non-zero sequence that matches peaq's subscription signal, and its tier must match your plan). Delivery is external to the CLI; a peaq receipt does not prove it. Repeat the native preview until the SDK reports both ready.

    ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
    peaqos activate "${ARGS[@]}" --phase native_onboarding --dry-run
    ```
  </Step>

  <Step title="Create the machine on Solana (owner wallet)">
    ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
    PEAQOS_OWS_WALLET=svm-owner peaqos activate "${ARGS[@]}" --phase native_onboarding --yes
    ```

    Exit `0` means the Solana transaction confirmed. It does **not** mean onboarding is complete: peaq still has to apply the link. Rerun the same phase without `--yes` to reconcile; done is `onboarding_state.evidence.stage.phase` equal to `complete`.
  </Step>
</Steps>

## Read the state

With the machine ID alone, no wallet, journal or DID document:

```bash theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
peaqos machine status "$MACHINE_ID" --json
```

Two read modes. Without `--chain solana`, the CLI falls back to the Solana read when the peaq record is missing or homed elsewhere and `PEAQOS_SVM_RPC_URL` is configured; that result has `status: "observed"` and a `native_current_state` with separate peaq (finalized) and Solana (confirmed) observations, not an atomic snapshot. Native states: `absent`, `reserved`, `pending_external`, `present`, `conflict`, `in_flight`, `unavailable`. With `--chain solana` the CLI reads the native management state instead and prints a different object (`execution_chain`, `identity`, `mirrors` with `subscription_source`, `subscription_terminal_status` and `subscription_terminal_last_seq`, `peaq` with `subscription_status`, `operator_synchronization`, pause flags), not `native_current_state`; it needs the native machine to exist, so before native creation use the fallback read. `present` proves the accounts exist, not that this attempt created them or that the link is complete. Pending or conflicting state exits `0`; an unavailable configuration exits `3`; read failures exit `2`.

## Resume after an interruption

Never delete `peaqos.log`, never change the inputs, never submit a replacement transaction on a hunch. The SDK records peaq hashes and nonces before it waits for a receipt, and Solana signatures with their blockhash validity before broadcast.

| You see                                                      | Do                                                                                                  |
| :----------------------------------------------------------- | :-------------------------------------------------------------------------------------------------- |
| Reservation or subscription already confirmed                | Rerun that phase with the original inputs and without `--yes`: it reconciles, it does not resubmit. |
| Approval confirmed, subscription not submitted               | Rerun `--phase subscription` with the operator wallet and fresh consent.                            |
| Timeout, cancel, uncertain receipt, expired Solana blockhash | Keep the journal, reconcile first. None of these authorizes a replacement.                          |
| Reservation mirror or subscription terminal status pending   | Wait for delivery, repeat the native preview.                                                       |
| Native confirmed, link pending                               | Rerun `--phase native_onboarding` without `--yes`.                                                  |
| Input, signer, deployment or account conflict                | Fix the mismatch. Do not erase history to get past it.                                              |

Exit codes: `1` input or consent, `2` chain, read or transaction failure, `3` configuration or missing dependency. With `--json`, read the `error_code` and the retained evidence, not only the exit code: a phase failure sits in `errors[]` with its own `error_code`, and a native transaction can be `confirmed` at top level while the linkage read afterwards fails with exit `1`, `2` or `3`, so a nonzero exit is not proof that nothing was created.

## The same flow in the SDKs

<Tabs>
  <Tab title="JavaScript">
    ```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
    import { createPublicClient, http } from "viem";
    import {
      previewMachineActivation, activateMachine, getMachineActivationState,
      resolveTokenomics20Deployment, resolveSvmDeployment,
      serializeSvmOnboardingProgress, type SvmActivationReadContext, type SvmOnboardingInput,
    } from "@peaqos/peaq-os-sdk";

    const context: SvmActivationReadContext = {
      publicClient: createPublicClient({ transport: http(process.env.PEAQOS_RPC_URL!) }),
      tokenomics20: resolveTokenomics20Deployment("peaq-mainnet"),
      svm: resolveSvmDeployment("solana-mainnet"),
      svmRpcUrl: process.env.PEAQOS_SVM_RPC_URL!,
    };
    const input: SvmOnboardingInput = {
      chain: "solana", owner, controller: owner, manufacturer, evmOperator,
      machineType: "Sensor", credentialSubject: "0x0102",
      verificationMethods: [], authentication: [], serviceEndpoints: [],
      tier: 1,                                                   // 1 basic, 2 pro
      funding: { payment: "peaq", maxNetPeaqAmount: 20n * 10n ** 18n },   // 20 PEAQ; the Basic bond the preview reported on 2026-09-17 was 8.4 PEAQ, 1 PEAQ is refused
      maxNativeFeeLamports: 100_000n, maxNativeRentLamports: 10_000_000n,
    };
    const preview = await previewMachineActivation(context, {
      ...input, commitment: "confirmed",
      native: { computeUnitLimit: 200_000, computeUnitPriceMicroLamports: 0n },
    });
    const result = await activateMachine(context, {
      chain: "solana", plan: preview.plan, phase: "reservation",   // then "subscription", then "native_onboarding"
      commitment: "confirmed", timeoutMs: 120_000,
      onProgress: (p) => persist(serializeSvmOnboardingProgress(p)),
    });
    const state = await getMachineActivationState(context, preview.plan.machineId, { plan: preview.plan, commitment: "confirmed" });
    ```

    Store the accepted `plan` and the serialized progress; restore them with `restoreSvmOnboardingProgress(plan, json)` before the next phase. In JS 0.8.0 `activateMachine` previews, reconciles and reports the Solana flow but does not submit its write phases: the public path carries read and linkage capabilities only, so a new reservation, subscription or native creation is reported `unavailable` and nothing is signed. In the same release `getMachineActivationState` and `activateMachine` with `chain: "solana"` read the reservation history from `CrossChainMirror` with `eth_getLogs` from block `0` to the current block, with no `fromBlock` option in the public types; the public peaq RPCs reject that range, so both fail with `READ_FAILED` (checked against four public endpoints on 2026-09-17). `previewMachineActivation` does not need that read and works. Until a JS release bounds the read, run the phases, status and recovery from the CLI or Python; JS against a public RPC is preview only. Reference: [SDK JS: Solana](/peaqos/sdk-reference/sdk-js#solana-svm).
  </Tab>

  <Tab title="Python">
    ```python theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
    from dataclasses import replace
    from peaq_os_sdk import (
        PeaqosClient, Tokenomics20Config,
        SolanaOnboardingInputs, PeaqOnboardingFunding, normalize_solana_controller,
        SolanaVerificationMethodInput, ServiceEndpointInput,
        PreviewSolanaMachineActivationParams, ActivateSolanaMachineParams,
        SolanaMachineCurrentStateParams,
    )

    client = PeaqosClient.from_wallet("svm-operator", passphrase, ...,
        tokenomics20=Tokenomics20Config(deployment_id="peaq-mainnet",
                                        creation_home="solana",
                                        solana_rpc_url=SVM_RPC))

    inputs = SolanaOnboardingInputs(                     # the same values as the CLI ARGS above
        solana_owner=OWNER, peaq_operator=OPERATOR,      # base58 owner wallet, 0x operator wallet
        controller=normalize_solana_controller(),        # owner-only
        manufacturer=MANUFACTURER, machine_type="Sensor",
        credential_subject=bytes.fromhex(CREDENTIAL_SUBJECT_HEX[2:]),
        verification_methods=(SolanaVerificationMethodInput(
            id="#key-1", method_type="Ed25519", controller=OWNER, public_key_multibase=OWNER_MULTIBASE),),
        authentication=(0,),
        service_endpoints=(ServiceEndpointInput(
            id="#telemetry", service_type="Telemetry", service_endpoint="https://example.com/telemetry"),),
        tier=1,                                          # 1 basic, 2 pro
        funding=PeaqOnboardingFunding(max_net_peaq_amount=20 * 10**18),   # 20 PEAQ, above the Basic bond the preview reports
        max_native_fee_lamports=100_000, max_native_rent_lamports=10_000_000,
    )
    plan = client.preview_machine_activation(PreviewSolanaMachineActivationParams(
        inputs=inputs, from_block=history_start,
        compute_unit_limit=600_000, compute_unit_price_micro_lamports=0))

    params = ActivateSolanaMachineParams(
        inputs=inputs, accepted_plan=plan, phase="reservation",
        expected_machine_id=plan.binding.machine_id, records=(),
        from_block=history_start, compute_unit_limit=600_000,
        compute_unit_price_micro_lamports=0, on_transaction_submitted=persist)
    reservation = client.activate_machine(params)
    subscription = client.activate_machine(replace(params, phase="subscription",
        records=reservation.recovery.history.records))
    native = owner_client.activate_machine(replace(params, phase="native_onboarding",
        records=subscription.recovery.history.records, solana_signer=owner_signer))

    current = client.get_machine_activation_state(machine_id, solana=SolanaMachineCurrentStateParams())
    ```

    `records` carries every attempt discovered so far; pass the full history, never a filtered one. Reference: [SDK Python: Solana](/peaqos/sdk-reference/sdk-python#solana).
  </Tab>
</Tabs>

## After onboarding

* **Manage the machine** with the same `peaqos machine` commands plus `--chain solana` (`status`, `transfer --unsafe`, `suspend`, `resume`, `did set-controller`, the DID setters, and the Solana-only `set-evm-operator`), or the same SDK methods; the SDK resolves the home chain from the machine ID and refuses the wrong chain. Transfer, controller and the peaq operator link need the Solana **owner** signature; DID lists, suspend and resume take owner or controller. There is no safe transfer on Solana (`transfer` requires `--unsafe`, `--data-hex` is refused), and `approve` / `approve-all` do not exist; the SDKs refuse `approveMachine`, `setMachineApprovalForAll` and `safeTransferMachine` by name. Details: [SDK JS](/peaqos/sdk-reference/sdk-js#solana-svm), [SDK Python](/peaqos/sdk-reference/sdk-python#solana).
* **Submit events** with `submitEvent` / `submit_event`: the SDK routes a Solana-homed machine to the Solana `EventRegistry` program and signs with the owner's or controller's OWS Solana signer (the EVM operator does not qualify). In Python set `client.solana_signer` to an `OwsSolanaSigner` (for example from `PeaqosClient.solana_signer_from_wallet(...)`) before the call, or `submit_event` raises `SIGNER_UNAVAILABLE`; the CLI attaches it itself. The first event for a machine also pays rent for its event-log account. A confirmed event is not yet a rating; the 2.0 MCR indexes Solana events asynchronously.
* **Credit rating**: the 2.0 MCR scores a Solana-homed machine once its Solana indexer has the machine's events; until then the response carries `home_chain: 5` and a `rating_unavailable` reason (any non-empty reason means the score is a floor, not a measurement). What `mcr-20.peaq.xyz` returns for such a machine was not verified live as of 2026-09-17. See the [API reference](/peaqos/api-reference/overview#tokenomics-2-0-machines).
* **Monetization opt-in** for a Solana-homed machine (signed by the Solana owner) is built into the SDKs but refused with `SOLANA_MONETIZATION_UNVERIFIED` until the server contract is confirmed.
* **Relocation** between peaq and Solana stays switched off on chain. Onboarding on Solana is not a relocation: the machine is created there.

## Related

* [Activate](/peaqos/functions/activate): the one-transaction flow on peaq
* [Omni-chain: Solana](/peaqos/concepts/omni-chain#solana): what is live on Solana
* [Smart contracts: Solana mainnet](/peaqos/concepts/contracts#solana-mainnet-addresses): the eleven program IDs
* [CLI: activate](/peaqos/cli#peaqos-activate)
