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

# Omni-chain

> How peaqOS mirrors identity, DID, and credit state from peaq to every supported chain via signed-push Lite contracts and an off-chain Signer Daemon.

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. registerMachine is self-managed; registerFor is on someone else's behalf."
  },
  machineId: {
    id: "machineId",
    cat: "identity",
    term: "Machine ID",
    def: "The number the network assigns your machine when it registers, used as its handle in every later call."
  },
  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: "A refundable deposit (currently 1 PEAQ) you lock up to register a machine, proving skin in the game, like a security deposit. 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. No login needed."
  },
  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."
  },
  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 peaqOS that is peaq chain; every other chain holds a mirror."
  },
  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 services (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."
  },
  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 can hold a Solana account and sign Solana transactions, and machine-economy payments can settle there."
  }
};

<Info>
  **Omni-chain V1 shipped with Scale.** Lite contracts and the Signer Daemon are live across supported chains; per-chain proxy addresses live in the launch manifest.
</Info>

peaq stays canonical. Every other <Tooltip tip={G.blockchain.def}>chain</Tooltip> runs a thin, read-only mirror of peaq state, kept in sync by a Signer Daemon that watches finalized peaq events, packages them into EIP-712 batches, <Tooltip tip={G.sign.def}>signs</Tooltip> them with a per-chain push key, and pushes them to satellite Lite contracts.

This means a <Tooltip tip={G.smartContract.def}>contract</Tooltip> or app on <Tooltip tip={G.base.def}>Base</Tooltip>, Ethereum, Polygon, or any other supported chain can resolve a machine's <Tooltip tip={G.did.def}>peaqID</Tooltip>, identity, <Tooltip tip={G.mcr.def}>MCR</Tooltip>, and <Tooltip tip={G.bond.def}>bond</Tooltip> status without an <Tooltip tip={G.rpcUrl.def}>RPC</Tooltip> hop to peaq, and without trusting an <Tooltip tip={G.onchain.def}>off-chain</Tooltip> oracle on top.

<KeyTerms all={G} ids={["blockchain","onchain","did","mcr","bond","base","smartContract","rpcUrl","precompile","omniChain","mainnet","sdk"]} />

## What the mesh looks like

```text theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
                 peaq home chain
                 ┌────────────────────────┐
                 │ IdentityRegistry       │
                 │ peaq DID precompile    │
                 │ IdentityStaking        │
                 │ MCR pipeline           │
                 └──────────┬─────────────┘
                            │ finalized events
                            ▼
                 ┌────────────────────────┐
                 │ Signer Daemon          │
                 │ (off-chain, peaq-side) │
                 │ batches + EIP-712 sign │
                 └─┬────────┬─────────────┘
                   │        │
        ┌──────────┘        └──────────┐
        ▼                              ▼
  ┌───────────────┐              ┌───────────────┐
  │ Base satellite│              │ other chains  │
  │ IdentityLite  │              │ IdentityLite  │
  │ DIDLite       │              │ DIDLite       │
  │ StakingLite   │              │ StakingLite   │
  └───────────────┘              └───────────────┘
```

Domain separator: `name = "PeaqosLite"`, `version = "1.0.0"`. Each Lite is identified by `(chainId, verifyingContract)` per EIP-712.

## DIDLite

DIDLite mirrors the peaq DID <Tooltip tip={G.precompile.def}>precompile</Tooltip>'s per-attribute records onto every satellite chain. Consumers on the satellite resolve any peaq DID account's attributes (including the locked `"peaqID"` attribute) without an extra <Tooltip tip={G.omniChain.def}>cross-chain</Tooltip> hop. Records are written exclusively by signed batches from the Signer Daemon; consumers only read.

### Public consumer views

```solidity theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
function readAttribute(address didAccount, bytes calldata attrName)
    external view
    returns (
        bytes memory value,
        uint32 validity,
        uint64 lastUpdatedHomeBlock,
        uint64 lastBatchAcceptedAtTs
    );

function readAttributeRaw(address didAccount, bytes calldata attrName)
    external view
    returns (DIDAttributeRecord memory record, uint64 lastBatchAcceptedAtTs);
    // admin/debug only; ignores the pause gate; returns soft-deleted records as-is

function lastHomeBlockApplied() external view returns (uint64);

struct DIDAttributeRecord {
    bytes  value;                  // raw bytes from peaq; empty after soft-delete
    uint32 validity;               // peaq DID validity field
    uint64 lastUpdatedHomeBlock;
    bool   removed;                // soft-delete sentinel
}
```

`readAttribute` <Tooltip tip={G.revert.def}>reverts</Tooltip> `LitePaused` while the Lite is paused. `readAttributeRaw` is an admin/debug carve-out that ignores the gate. `lastHomeBlockApplied()` is intentionally not pause-gated so consumers can still read the global watermark when reads are paused.

To resolve a peaqID, compose: `readAttribute(didAccount, "peaqID")`. The orchestrator does not ship a dedicated `peaqIDOf` view; clients decode the 32-byte value themselves.

### Consumer view errors

* `AttributeNotFound(address didAccount, bytes attrName)`
* `AttributeRemoved(address didAccount, bytes attrName, uint64 removedAtHomeBlock)`
* `LitePaused()`

## IdentityLite

IdentityLite mirrors the peaq `IdentityRegistry` per-machineId record. Consumers gate writes that depend on having seen a specific peaq approval by reading `lastCursorPacked()`.

### Public consumer views

```solidity theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
function getIdentity(uint256 machineId)
    external view
    returns (IdentityRecord memory record, uint64 lastBatchAcceptedAt);
    // pause-gated; lastBatchAcceptedAt == 0 is the cold-start sentinel

function getIdentityRaw(uint256 machineId)
    external view
    returns (IdentityRecord memory record, uint64 lastBatchAcceptedAt);
    // admin/debug only; ignores pause gate

function lastCursor()
    external view
    returns (uint64 blockNumber, uint32 txIndex, uint32 logIndex);
    // unpacked; NOT pause-gated (cross-Lite read invariant)

function lastCursorPacked() external view returns (uint128);
    // packed = (block << 64) | (txIndex << 32) | logIndex
    // NOT pause-gated

function lastUpdatedHomeBlock(uint256 machineId) external view returns (uint64);
    // per-record cursor; pause-gated

// Home-style accessors (additive to getIdentity)
function getOwnerIfExists(uint256 machineId)
    external view
    returns (address owner_, bool exists_);

function operatorOf(uint256 machineId) external view returns (address);

function getMachineStatus(uint256 machineId) external view returns (MachineStatus);

function getMachineURI(uint256 machineId) external view returns (string memory);

struct IdentityRecord {
    address owner;        // immutable post-Registered (soulbound on peaq)
    address operator;     // mutable; address(0) clears
    string  machineURI;   // immutable post-Registered
    MachineStatus status;
    uint64  lastHomeBlock;
}

enum MachineStatus { None, Pending, Verified, Rejected, Deactivated }
// Ordinals match peaq IdentityRegistry. Do not reorder.
```

### Consumer view errors

* `MachineNotFound(uint256 machineId)`
* `LiteUninitialized(address lite)` — not raised by the Lite itself. Read `(record, lastAt)` and `require(lastAt > 0, LiteUninitialized(address(this)))`.
* `LitePaused()`

### Cold-start pattern

The Lite returns `(record, 0)` when it has never accepted a batch, rather than reverting, so callers choose the policy:

```solidity theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
(IdentityRecord memory rec, uint64 lastAt) = identityLite.getIdentity(machineId);
require(lastAt > 0, LiteUninitialized(address(identityLite)));
require(rec.status == MachineStatus.Verified, "not verified");
```

## StakingLite

StakingLite mirrors the peaq `IdentityStaking` per-machine stake record. Consumers gate authorisation or service eligibility on stake state without crossing chains.

### Public consumer views

```solidity theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
function getStake(uint256 machineId)
    external view
    returns (StakeRecord memory record, uint64 lastBatchAcceptedAt_);
    // pause-gated; lastBatchAcceptedAt_ == 0 is the cold-start sentinel

function getStakeRaw(uint256 machineId)
    external view
    returns (StakeRecord memory record, uint64 lastBatchAcceptedAt_);
    // admin/debug only; ignores pause gate

function isStaked(uint256 machineId) external view returns (bool);
    // pause-gated

function isAuthorized(address wallet)
    external view
    returns (bool isAuth, uint64 lastBatchAcceptedAt_);
    // pause-gated; cold-start sentinel applies

function totalStaked() external view returns (uint256);
    // pause-gated; aggregate of applied Staked.amount events
```

Same cold-start pattern as IdentityLite — `require(lastBatchAcceptedAt_ > 0, LiteUninitialized(address(this)))` before trusting reads. StakingLite has its own pause flag and its own EIP-712 schema (`StakingEvent`), but shares the `PeaqosLite` domain and the cross-language daemon parity gate.

## EIP-712 schemas

### 7-field IdentityEvent

```text theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
IdentityEvent(uint8 kind,uint256 machineId,address subject,string machineURI,uint64 homeBlockNumber,uint32 txIndex,uint32 logIndex)
```

`txIndex` and `logIndex` were added vs the earlier 5-field shape so the on-chain cursor can be sub-block-precise. The daemon-side encoder is bit-identical to Solidity (parity-gated by the EIP-712 fixture suite).

### DIDEvent

```text theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
DIDEvent(uint8 kind,address didAccount,bytes attrName,bytes attrValue,uint32 validity,uint64 homeBlockNumber)
```

Both `bytes` fields are pre-hashed with `keccak256(bytes(...))` per EIP-712 dynamic-bytes rule. `kind` ordinal: `0 = Add, 1 = Update, 2 = Remove`. Removes must carry `value.length == 0` and `validity == 0`.

### Batch envelope (shared)

```text theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
Batch(uint64 nonce,uint64 deadline,uint8 schemaVersion,bytes32 eventsRoot,uint64 cursorLo_block,uint32 cursorLo_txIndex,uint32 cursorLo_logIndex,uint64 cursorHi_block,uint32 cursorHi_txIndex,uint32 cursorHi_logIndex)
```

Schema version is per-Lite. A typehash bump requires lockstep upgrade of both the Lite and the daemon.

## Signer Daemon

Off-chain. Python package, one instance per `(home, satellite, Lite)` triple. V1 shipped 2026-05-21 with a **six-daemon fleet** across two pipelines: three for peaq <Tooltip tip={G.mainnet.def}>mainnet</Tooltip> (home) → Agung (satellite) and three for Agung (home) → Base Sepolia (satellite). Each daemon binds to one `LITE_NAME` (`IdentityLite | DIDLite | StakingLite`), one push key (`currentSigner` on the Lite), one `HEALTH_PORT`, and one `CURSOR_FILE_PATH`. Six unique push keys total — reuse triggers nonce races. A direct peaq mainnet → Base Sepolia pipeline is registered in the <Tooltip tip={G.sdk.def}>SDK</Tooltip> satellite registry but not yet rolled out as a daemon deployment.

| Property                | Behaviour                                                                                                                                                                                                                                                                                                                                                                                 |
| :---------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Finality                | GRANDPA-finalized only. No `latest-N` fallback. Daemon freezes if GRANDPA stalls rather than serve unfinalized state.                                                                                                                                                                                                                                                                     |
| Batching                | Critical events flush immediately; non-critical aggregate up to `flush_after_sec=30` or `max_batch_size=50`. `MAX_BATCH_SIZE` env override bounded `[1, 50]`; required `=10` for DIDLite (byte-payload events exceed Agung block gas at 50).                                                                                                                                              |
| Nonce ordering          | Per-chain EVM nonce lock + per-Lite monotonic batch nonce. `AlreadyApplied` → skip-advance. `NonceOutOfOrder` → backfill missing nonce(s).                                                                                                                                                                                                                                                |
| Restart catchup         | On boot, `resume_cursor = max(localCursor, liteCursor)`. Prevents replay after a crash between Lite acceptance and local commit.                                                                                                                                                                                                                                                          |
| Pause retries           | `WritesPaused` from `applyBatch` triggers exponential backoff `(1, 5, 15, 60, 300)s` rather than fatal exit, so admin pauses do not crash the fleet.                                                                                                                                                                                                                                      |
| Poison events (DIDLite) | Orphan `Update`/`Remove` events whose `Add` predates the deploy block trigger `AttributeDoesNotExist` or `AttributeAlreadySoftDeleted`. The daemon decodes the revert, isolates the offending event, pushes the rest of the batch, and increments `poison_event_skipped_per_lite[lite]` on `/health`. Under partial-history replay the satellite is best-effort cache, not authoritative. |
| Cross-language parity   | EIP-712 typehashes are constants in Solidity (`EVENT_TYPEHASH_V1`, `BATCH_TYPEHASH_V1`) and in Python. A Hardhat task dumps canonical fixtures; Python recomputes identical hashes (17 fixtures, all 65-byte signatures bit-identical).                                                                                                                                                   |
| Hot-wallet floor        | `HOT_WALLET_MIN_BALANCE_WEI` (default `0.02` native). Below the floor the daemon stops signing and `/health.hot_wallet_low[chain_id] = true`.                                                                                                                                                                                                                                             |

### Health endpoint

Each daemon exposes a loopback-only `/health` (default port unique per instance, conventionally 8080–8085):

```text theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
{
  signer_key_id,
  last_push_at,
  hot_wallet_low: { [chain_id]: bool },
  queue_depth,
  poison_event_skipped_per_lite: { [lite_name]: int }
}
```

Operator runbooks (env vars, deploy steps, monitoring, troubleshooting) for the Signer Daemon will live in the Operate section: `signer-daemon-deploy`, `signer-daemon-monitoring`, and `signer-daemon-troubleshooting` (coming soon).

## Pause and emergency model

Every Lite has two independent pause flags and one emergency flag, all `external onlyOwner`:

```solidity theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
pauseLite(string reason)         // pause user-facing reads
unpauseLite()                     // resume reads (blocked while in emergency)
pauseApplyBatch(string reason)    // pause inbound writes
unpauseApplyBatch()               // resume writes (allowed even in emergency)
setSigner(address newSigner)      // routine PUSH_KEY rotation
emergencyRotatePushKey(address)   // incident-response rotation
exitEmergencyMode()                // requires currentSigner != snapshot
adminReplayEvents(...)             // owner-driven replay/rewind
```

Routine `setSigner` rotation opens a `GRACE_BLOCKS = 600` (\~1h) window where the previous PUSH\_KEY remains valid so in-flight signed batches do not fail mid-flight. Emergency rotation does not keep the previous key valid.

### EmergencyMode

EmergencyMode is two booleans plus a snapshot <Tooltip tip={G.address.def}>address</Tooltip>, not an enum:

```solidity theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
bool    inEmergencyMode;
address emergencyEnteredBySigner;   // snapshot at first incident pause
bool    litePaused;                 // read pause
bool    applyBatchPaused;           // write pause
```

A first `pauseLite` or `pauseApplyBatch` sets `inEmergencyMode = true` and snapshots `emergencyEnteredBySigner = currentSigner`. To exit, the owner calls `emergencyRotatePushKey(newKey)` (rotation must actually change the signer) then `exitEmergencyMode()`. Pause flags are not auto-cleared.

### Hot/cold key collapse defense

The cold key is `owner()` (admin and upgrade authority). The hot key is `currentSigner` (the PUSH\_KEY on the daemon server). A single <Tooltip tip={G.transaction.def}>transaction</Tooltip> must never collapse them onto the same address. Enforcement points:

* `initialize(owner_, pushKey_)` reverts if `pushKey_ == owner_`.
* `setSigner(newSigner)` reverts if `newSigner ∈ {owner(), pendingOwner()}`.
* `emergencyRotatePushKey(newPushKey)` reverts on the same membership check.
* `transferOwnership` / `_transferOwnership` reject `newOwner ∈ {currentSigner, previousSigner}`.
* `renounceOwnership` is permanently disabled.

A stolen daemon key cannot also seize upgrade authority.

## Staleness policy

The Lite does **not** staleness-revert. SDK and ops layers apply staleness gates. Consumers should compare `lastBatchAcceptedAt` to `block.timestamp` and reject reads older than their own SLO. The Lite only blocks cold-start via the consumer-side sentinel pattern.

## Soft delete (DIDLite)

Removed DID attributes stay in storage with `removed = true` and `value = ""`. `readAttribute` reverts `AttributeRemoved(...)` for these. `readAttributeRaw` returns them as-is for admin/debug.

## What an integrator does

1. **Resolve a peaqID on a satellite chain.** Call `readAttribute(didAccount, "peaqID")` and decode the returned `bytes` as a `bytes32`. Always pair with `require(lastBatchAcceptedAtTs > 0)` to handle cold start.
2. **Read identity status.** `getIdentity(machineId)` returns the full record. Same cold-start check. Or use the granular home-style getters: `getOwnerIfExists`, `operatorOf`, `getMachineStatus`, `getMachineURI`.
3. **Read stake state.** `StakingLite.getStake(machineId)`, `isStaked(machineId)`, `isAuthorized(wallet)`, or `totalStaked()`.
4. **Gate on a specific peaq approval.** Read `IdentityLite.lastCursorPacked() >= myExpectedCursor` before letting a satellite action depend on it. Use the unpaused `lastCursor()` view if you need the unpacked form.
5. **Apply your own staleness SLO.** Compare `lastBatchAcceptedAt` to `block.timestamp`. The Lite intentionally does not enforce one.

## Addresses

Per-chain proxy addresses are pasted into operator `.env` files generated from templates in `signer-daemon/deploy/` and surfaced through the SDK satellite registry. There is no single deploy-manifest JSON in the repo. Track the launch announcement for the canonical Agung and Base Sepolia address list, or pull them from `peaqos/concepts/contracts` once published.

## Related

* Satellite mirrors — propagation and cursor-independence model (coming soon)
* Signer daemon — runtime surface (coming soon)
* Satellite SDK overview — JS/Python bindings (coming soon)
* Lite views (Solidity) — Solidity consumer surface (coming soon)
* Operate → Signer daemon deploy — production runbooks (coming soon)
* [Scale function](/peaqos/functions/scale)
* [Activate function](/peaqos/functions/activate)
* [Qualify function](/peaqos/functions/qualify)
* [Machine NFT concept](/peaqos/concepts/machine-nft)
* [peaqID concept](/peaqos/concepts/peaqid)
* [Roadmap](/roadmap)
