Metadata-Version: 2.4
Name: peaq-os-sdk
Version: 0.7.0
Summary: Python SDK for peaqOS, the operating system for the machine economy — on-chain identity, credit rating, and omnichain infrastructure for robots and machines.
Author-email: peaqOS <info@peaq.xyz>
License: Apache-2.0
Project-URL: Homepage, https://www.peaq.xyz
Project-URL: Documentation, https://docs.peaq.xyz
Project-URL: Repository, https://github.com/peaqnetwork/peaq-os-sdk-py
Project-URL: Issues, https://github.com/peaqnetwork/peaq-os-sdk-py/issues
Keywords: peaq,peaqos,peaq-os,machine-economy,depin,robotics,ai-agents,blockchain,omnichain,peaqid,did,machine-nft,machine-tokenization,machine-credit-rating,mcr,sdk,python
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: cryptography>=43.0.0
Requires-Dist: pycryptodome>=3.20.0
Requires-Dist: PyNaCl>=1.5.0
Requires-Dist: web3>=6.0
Requires-Dist: eth-account
Requires-Dist: requests
Requires-Dist: posthog>=3.0
Requires-Dist: PyYAML>=6.0
Requires-Dist: google-re2>=1.1
Provides-Extra: ows
Requires-Dist: open-wallet-standard>=1.3.2; extra == "ows"
Provides-Extra: solana
Requires-Dist: solders>=0.21.0; extra == "solana"
Requires-Dist: solana>=0.36.0; extra == "solana"
Provides-Extra: p2p
Requires-Dist: libp2p<1.0,>=0.6.0; extra == "p2p"
Provides-Extra: x402
Requires-Dist: x402>=2.14.0; extra == "x402"
Provides-Extra: dev
Requires-Dist: pytest; extra == "dev"
Requires-Dist: pytest-cov; extra == "dev"
Requires-Dist: mypy; extra == "dev"
Requires-Dist: ruff; extra == "dev"
Requires-Dist: black; extra == "dev"
Requires-Dist: build; extra == "dev"
Requires-Dist: flake8; extra == "dev"
Requires-Dist: python-dotenv; extra == "dev"
Requires-Dist: solders>=0.21.0; extra == "dev"
Requires-Dist: solana>=0.36.0; extra == "dev"
Requires-Dist: x402>=2.14.0; extra == "dev"
Requires-Dist: open-wallet-standard>=1.3.2; extra == "dev"
Dynamic: license-file

# peaq-os-sdk

Python SDK for the peaqOS protocol. Provides a typed wrapper around the peaq on-chain capabilities so integrators can onboard machines, submit events, mint NFTs, and query credit ratings without writing raw `web3.py` boilerplate.

---

## Tokenomics 2.0 machine-ID compatibility

Tokenomics machine IDs are non-boolean Python `int` values in the full range `0 <= machine_id < 2**256`. At JSON, URL, log, or other text boundaries they must be canonical base-10 strings. ID size never selects a mode: a non-`None` `client.tokenomics20` configuration explicitly selects Tokenomics mode.

```python
from peaq_os_sdk import (
    parse_tokenomics_machine_id,
    serialize_tokenomics_machine_id,
    validate_tokenomics_machine_id,
)

machine_id = parse_tokenomics_machine_id("9223372036854775808")
validate_tokenomics_machine_id(machine_id)
assert serialize_tokenomics_machine_id(machine_id) == "9223372036854775808"
```

| Surface | Tokenomics 2.0 status |
|---|---|
| Machine-ID validator and decimal parser/serializer | Migrated and lossless through `2**256 - 1` |
| Event submission | Disabled locally before validation, rate-limit mutation, or RPC because the deployed resolver is unverified |
| Legacy machine/proxy DID writers | Disabled locally before batch transaction work |
| MCR queries | Disabled locally before HTTP because the current API returns legacy JSON numbers |
| Orchestration identity-dependent machine reads/writes, pairing challenge, and market search | Disabled locally before HTTP because these endpoints resolve, return, or require unverified MCR identity state |
| Other Machine Markets records, purchases, delivery, and P2P | Supported as opaque strings such as `mach_...` when the endpoint does not resolve or verify MCR identity; never convert these IDs to integers or pass them to contracts |
| Registration, NFT, and bridge wrappers | Legacy-only; replacement and local disablement require coordinated Tokenomics activation work |
| Monetisation toggle/read | Migrated for approved paired deployments: full-width decimal IDs, `MachineRegistry` owner/controller signatures, live MCR compatibility checks, and independent Tokenomics state |

Disabled surfaces raise `TokenomicsIntegrationUnavailableError` with code `TOKENOMICS_INTEGRATION_UNAVAILABLE`, plus `.integration` and `.owning_ticket`, at their first local boundary. See the [machine-ID inventory](docs/17_MACHINE_ID_INVENTORY.md) for every audited field and owner.

---

## Tokenomics 2.0 machine activation

One transaction mints the machine NFT, stores its DID document, activates its subscription, and registers its home chain — replacing the deprecated `register_machine()` + `mint_nft()` pair.

Activation is opt-in. Supplying `tokenomics20` selects a deployment by ID from the SDK's approved snapshot; contract addresses are never passed by callers. A client without it keeps its existing behaviour unchanged.

```python
from peaq_os_sdk import PeaqosClient, Tokenomics20Config

client = PeaqosClient(
    rpc_url="https://peaq-agung.api.onfinality.io/public",
    private_key=os.environ["PRIVATE_KEY"],
    # ... existing contract addresses ...
    tokenomics20=Tokenomics20Config(deployment_id="agung-2026-08-28"),
)

preview = client.preview_machine_activation(params)   # no signing, no writes
result = client.activate_machine(params)              # one atomic transaction
print(result.machine_id, result.net_peaq_amount)
```

Selecting a deployment also **disables** the deprecated registration, mint, and bridge entry points, which then raise `TokenomicsUnsupportedError` locally — before any RPC, approval, or signature — rather than quietly doing the old thing. Outside Tokenomics mode they keep working and emit a `DeprecationWarning`.

| Deprecated | Code | Replacement |
|---|---|---|
| `register_machine()` | `LEGACY_REGISTRATION_UNSUPPORTED` | `activate_machine()` |
| `register_for()` | `SPONSORED_ACTIVATION_UNSUPPORTED` | none — operator-sponsored onboarding has no equivalent |
| `mint_nft()` | `LEGACY_REGISTRATION_UNSUPPORTED` | minting happens inside `activate_machine()` |
| `token_id_of()` | `LEGACY_REGISTRATION_UNSUPPORTED` | `get_machine_owner()` — the ID *is* the token ID |
| `bridge_nft()` | `MACHINE_RELOCATION_UNAVAILABLE` | none in this release |

See [docs/18_TOKENOMICS_ACTIVATION.md](docs/18_TOKENOMICS_ACTIVATION.md) for configuration, the preview/activate flow, the bond and voucher arithmetic, the full error taxonomy, reconciling an unconfirmed transaction, the machine-signed/operator-controlled migration path, and the release-day procedure for adding a network.

---

## Tokenomics 2.0 machine management

After onboarding, the same deployment-selected client exposes aggregate state and availability reads, suspend/resume, PEAQ and USDT subscriptions, ERC-721 ownership, DID updates, side-effect-free action previews, and read-only reconciliation for a known transaction hash. Owner, controller, and payer authority remain distinct, and every write correlates contract-scoped events before reconciling post-state.

See [docs/19_TOKENOMICS_MACHINE_MANAGEMENT.md](docs/19_TOKENOMICS_MACHINE_MANAGEMENT.md) for the authority matrix, bounded payment flows, migration notes, relocation-read semantics, and remaining release gates.

---

## Install

```bash
pip install peaq-os-sdk
```

Dependencies installed automatically: `web3`, `eth-account`, `requests`, `posthog`.

For OWS wallet management (optional):

```bash
pip install peaq-os-sdk[ows]
```

For Solana transaction signing via OWS (optional):

```bash
pip install peaq-os-sdk[ows,solana]
```

For Stream buyer SPL token payments (optional):

```bash
pip install peaq-os-sdk[solana]
```

The `solana` extra installs `solders` (native SOL transfers and signing) and `solana` (provides `spl.token` for SPL transfers such as USDC).

For P2P stream delivery (optional):

```bash
pip install peaq-os-sdk[p2p]
```

The `p2p` extra enables machine-to-machine P2P delivery via the `peaqos-p2p` transport (`P2PDeliveryChannel` seller, `P2PDeliveryReceiver` buyer).

### Solana signing from an OWS wallet

When `open-wallet-standard` and `solders` are installed, construct a standalone
Solana signer from a vault wallet that has a Solana account:

```python
from solders.hash import Hash
from solders.keypair import Keypair
from solders.message import Message
from solders.pubkey import Pubkey
from solders.system_program import TransferParams, transfer
from solders.transaction import Transaction

from peaq_os_sdk import PeaqosClient, SOLANA_MAINNET_CHAIN_ID

signer = PeaqosClient.solana_signer_from_wallet("my-wallet", "s3cret")
print(signer.address, signer.chain_id)  # chain_id == SOLANA_MAINNET_CHAIN_ID

from_pubkey = Pubkey.from_string(signer.address)
instruction = transfer(
    TransferParams(
        from_pubkey=from_pubkey,
        to_pubkey=Keypair().pubkey(),
        lamports=1_000,
    ),
)
message = Message.new_with_blockhash([instruction], from_pubkey, Hash.default())
tx = Transaction.new_unsigned(message)

signed = signer.sign_transaction(tx)
# broadcast with your Solana RPC client using signed.serialize()
```

By default (`ows_signing=True`), the ed25519 key is decrypted only per sign via OWS.
Pass `ows_signing=False` to decrypt at construction and sign locally with
`solders`. See [`docs/06_WALLET.md`](docs/06_WALLET.md) for details.

### Requirements

- Python `>= 3.10`
- A peaq RPC endpoint
- A funded wallet for the proxy operator (or for the machine itself, in self-managed mode)

---

## Quick start

```python
from peaq_os_sdk import PeaqosClient

client = PeaqosClient(
    rpc_url="https://peaq.api.onfinality.io/public",
    private_key="0xYOUR_PRIVATE_KEY",
    identity_registry="0x...",
    identity_staking="0x...",
    event_registry="0x...",
    machine_nft="0x...",
    did_registry="0x...",
    batch_precompile="0x...",
)

print("signer address:", client.address)
```

`PeaqosClient` is the only class consumers instantiate. All feature methods hang off it. The constructor performs synchronous validation and wires up the underlying `Web3` provider + signing account — no network I/O is issued at construction time.

[**Read More here >>**](docs/01_QUICK_START.md)

---

## MCR queries

Three read-only methods talk to the off-chain MCR API server at `client.api_url` (override via the `api_url` kwarg or `PEAQOS_MCR_API_URL`; defaults to `http://127.0.0.1:8000`). All three validate the DID prefix (`did:peaq:0x…`) locally and share a single `requests.Session` for connection pooling.

| Method | HTTP endpoint | Returns |
|---|---|---|
| `client.query_mcr(did)` | `GET /mcr/{did}` | `MCRResponse` TypedDict |
| `client.query_machine(did)` | `GET /machine/{did}` | `MachineProfileResponse` (NFT metadata JSON v1.0 as `dict[str, Any]`) |
| `client.query_operator_machines(did)` | `GET /operator/{did}/machines` | `OperatorMachinesResponse` |

`MCRResponse`:

```python
{
    "did": str,
    "machine_id": int,
    "mcr_score": int | None,    # 0–100, None if Provisioned
    "mcr": str,                 # "AAA" | "AA" | "A" | "BBB" | "BB" | "B" | "NR" | "Provisioned"
    "bond_status": str,         # "bonded" | "unbonded"
    "negative_flag": bool,      # active negative event flag
    "event_count": int,
    "revenue_event_count": int,
    "activity_event_count": int,
    "revenue_trend": str,       # "up" | "stable" | "down" | "insufficient"
    "total_revenue": float,
    "average_revenue_per_event": float,
    "last_updated": int | None,
}
```

`OperatorMachinesResponse` carries `operator_did`, a `machines` list of `{did, machine_id, mcr_score, mcr, negative_flag}` entries, and a `pagination` object. `MachineProfileResponse` returns structured NFT metadata with a validated `peaqos` sub-object. Every failure path is an `ApiError` with a stable `.code` (`NOT_FOUND`, `SERVICE_UNAVAILABLE`, `SERVER_ERROR`, `HTTP_ERROR`, `BAD_RESPONSE`, `TIMEOUT`, `NETWORK_ERROR`). See [docs/03_QUERIES.md](docs/03_QUERIES.md) for the full endpoint and error reference.

---

## Smart account deployment

ERC-4337 smart accounts are provisioned via the `MachineAccountFactory` contract using CREATE2 — the deployed address is deterministic from `(owner, machine, daily_limit, salt)`, so the same tuple always resolves to the same address.

| Method | On-chain | Returns |
|---|---|---|
| `client.get_smart_account_address(owner, machine, daily_limit, salt)` | view call — no gas | predicted address |
| `client.deploy_smart_account(owner, machine, daily_limit, salt)` | `createAccount` tx | deployed address |

Because CREATE2 is deterministic, `predicted == deployed` for the same inputs — callers can preview the address, pre-fund it, or display it in a UI before paying gas. Configure the factory address via the optional `machine_account_factory` kwarg on `PeaqosClient` or the `MACHINE_ACCOUNT_FACTORY_ADDRESS` env var. See [docs/04_SMART_ACCOUNTS.md](docs/04_SMART_ACCOUNTS.md) for parameter rules and receipt-decoding error codes.

---

## Cross-chain NFT bridging

Machine NFTs move between peaq and Base over LayerZero v2. Two chains are recognised today; direction is inferred from the `source` / `destination` arguments.

| Chain | `SUPPORTED_CHAINS` id | `LAYERZERO_EIDS` |
|---|---|---|
| `"peaq"` | 3338 | **30302** |
| `"base"` | 8453 | **30184** |

| Direction | Source contract | Effect | `dstEid` |
|---|---|---|---|
| peaq → base | `MachineNFTAdapter` on peaq | NFT locked on peaq, minted on Base | `30184` |
| base → peaq | `MachineNFTBase` on Base | NFT burned on Base, unlocked on peaq | `30302` |

`client.bridge_nft(...)` estimates the LayerZero messaging fee via `quoteSend` **before** broadcasting and attaches that fee as `msg.value` on the actual `send` transaction so the message is correctly paid for. `PeaqosClient.wait_for_bridge_arrival(...)` is a static method (no client instance needed) that polls `MachineNFT.ownerOf(token_id)` on the destination every 10 seconds and returns `True` on arrival or `False` at timeout (default 300 s). See [docs/05_BRIDGE.md](docs/05_BRIDGE.md) for the full walkthrough including `options` handling, Base-source setup, and the complete error-code table.

> **Deprecated in Tokenomics mode.** `bridge_nft` raises `TokenomicsUnsupportedError` (`MACHINE_RELOCATION_UNAVAILABLE`) when the client selected a Tokenomics deployment — before the adapter approval it would otherwise submit. Tokenomics 2.0 relocates the whole machine record rather than sending an ONFT, and relocation is not exposed in this release. `wait_for_bridge_arrival` takes no client and so cannot detect the mode; it is deprecated but not gated. See [docs/18_TOKENOMICS_ACTIVATION.md](docs/18_TOKENOMICS_ACTIVATION.md#relocation).

---

## Provider-node provisioning

The `peaq_os_sdk.provisioning` namespace is a **generic, provider-agnostic runner** that turns a schema-driven `ProviderProvisioningManifest` (`node-provider.peaq.network/v1alpha1`) into an ordered, auditable install. All provider-specific complexity lives in the manifest — the runner understands only the schema. It runs entirely on the machine and calls no external API.

```python
from peaq_os_sdk import provisioning

manifest = provisioning.fetch_manifest("akash", "latest", repo_base_url="https://manifests.example")
inputs = provisioning.resolve_inputs(manifest, {"providerDomain": "node.example.com", ...})
executor = provisioning.LocalShellExecutor()

preflight = provisioning.run_preflight(manifest, executor)
preflight.raise_if_blocked()                       # gate: no phase starts while blocked

result = provisioning.provision(
    manifest, inputs, mode="manual", executor=executor,
    context={"machine_wallet_address": "0x..."},   # never the operator's
    on_owner_action=lambda e: input(e.instructions) or "confirmed",
)

verification = provisioning.verify_provisioning(
    manifest, executor, inputs=inputs.values, captures=result.captures
)
verification.raise_if_failed()                     # live only when all probes pass
```

- **Manual vs auto modes** are declared per step (`manual` / `auto` / `both`). Eligibility is asymmetric: a manual-only step still runs (confirmed) in an auto run, while an **auto-only step is skipped in a manual run** and left un-recorded for a later auto pass — so `completed` means *this selected-mode pass* finished. Auto mode requires a `SudoGrant` scoped to the manifest's `allowedCommands`; a `sudo: required` command outside the grant **fails closed**, and unmarked commands are never elevated.
- **Owner-action handoffs** (funding, DNS, SSH, signing) are real pause points — `on_owner_action` must return the step's `expectedConfirmation`; they are never auto-confirmed.
- **Secrets never leak — and are never persisted.** Secret inputs and secret captures are redacted before any output is emitted; a secret capture with no `pattern` withholds its step's raw stream entirely. Errors and resume state carry no secret material, and secret captures are never written to a resume state (not even encrypted).
- **Idempotency & durable resume.** A step's idempotency `check` short-circuits already-provisioned work. Every run emits a typed, JSON-serializable `ResumeState` (with a `schema_version`, completed `phase/step` ids, and resolved non-secret inputs) at each step boundary via `on_state_change` — and as `ProvisionResult.resume_state` / `StepExecutionError.resume_state` — which you pass back as `resume_state=` to skip completed steps and restore non-secret captures. A resumed step needing a non-persisted secret re-runs its producer when `rerun: safe`, else the caller can re-provide it via `resume_secrets` (manifest-declared secret keys only), else `ResumeNotPossibleError`.
- **Deterministic manifest pinning.** `fetch_manifest(..., include_source=True)` returns a `FetchedManifest` exposing the raw source bytes, so a caller can pin a stable `sha256:<hex>` digest.
- **Verification, not exit codes, defines success** — `verify_provisioning` succeeds only when every `success.all_of` probe passes (the caller passes `inputs=inputs.values` and a redaction registry seeded from `inputs.secret_values()`).

The runner is standalone; a CLI (or any other consumer) calls these functions. CLI-surface decisions (headless vs prompts) and provider manifest content (commission/API-key fields) are the consumer's and the manifest's concern, not the runner's. Remote execution is out of scope — only the default local-shell executor and a swappable local agent/session executor are supported.

---

## Telemetry

The SDK collects anonymous, aggregate usage telemetry via PostHog to measure install counts, onboarding success rates, and MCR query volume. **No PII is collected** — GeoIP is disabled, IP addresses are nulled, and all UUIDs are randomly generated.

Telemetry is enabled by default and can be disabled via environment variables:

| Env var | Effect |
|---|---|
| `DO_NOT_TRACK=1` | Disables telemetry unconditionally (industry standard) |
| `PEAQOS_TELEMETRY=0` | Disables telemetry (project-specific toggle) |

`DO_NOT_TRACK=1` always takes precedence. When telemetry is disabled, no PostHog client is created and no events are sent.

Analytics events are captured automatically from existing SDK methods — no additional API calls are needed:

```python
client = PeaqosClient(...)                         # peaqos_sdk_initialize (first install only)
addr, key = PeaqosClient.generate_keypair()        # peaqos_sdk_generate_keypair (via active instance)
machine_id = client.register_machine()             # peaqos_sdk_generate_wallet
tx = client.write_machine_did_attributes(...)      # peaqos_sdk_generate_did
tx = client.mint_nft(machine_id, addr)             # peaqos_sdk_generate_nft
mcr = client.query_mcr("did:peaq:0x...")           # peaqos_sdk_mcr_request
client.close()                                     # flush queued events
```

All analytics calls are fire-and-forget — they never block or raise. See [docs/10_ANALYTICS.md](docs/10_ANALYTICS.md) for the full event reference and privacy details.

---

## Documentation

Per-feature deep-dives live under [`docs/`](docs/):

- [**Quick Start**](docs/01_QUICK_START.md) — client initialization, configuration, environment variables, types, exception classes, validation, utilities, and constants.
- [**Machine identity & registration**](docs/02_REGISTRATION.md) — faucet 2FA enrollment, gas-station funding, self-managed and proxy-managed registration. Includes the full faucet error reference and the on-chain revert mapping.
- [**MCR queries**](docs/03_QUERIES.md) — `query_mcr`, `query_machine`, `query_operator_machines`, response shapes, rating tiers, and the full HTTP error-code table.
- [**Smart account deployment**](docs/04_SMART_ACCOUNTS.md) — `deploy_smart_account` and `get_smart_account_address`, CREATE2 determinism, parameter rules, and receipt-decoding error codes.
- [**Cross-chain NFT bridging**](docs/05_BRIDGE.md) — `bridge_nft` direction handling (peaq ↔ Base via LayerZero v2), supported chain IDs and LayerZero EIDs, LayerZero fee estimation, and `wait_for_bridge_arrival` polling semantics.
- [**Provider-node provisioning**](docs/15_PROVISIONING.md) — the generic, manifest-driven `peaq_os_sdk.provisioning` runner: fetch/validate a `ProviderProvisioningManifest`, resolve inputs, gate on pre-flight, execute phases/steps in manual/auto modes with scoped sudo and owner-action handoffs, redact secrets before emission, thread captures, verify success by probes, and resume interrupted runs. Covers the `CommandExecutor` protocol, machine-wallet context, idempotency, the error hierarchy, and the consumer/runner boundary.
- [**Monetisation opt-in**](docs/16_MONETIZATION.md) — the Tokenomics 2.0 signed on/off toggle and public state read: deployment-selected MCR configuration, full `uint256` IDs and decimal DIDs, owner/controller authorization, live compatibility checks, bounded retries, one end-to-end deadline, strict response correlation, typed failures, and migration from independent legacy state.
- [**Tokenomics 2.0 machine activation**](docs/18_TOKENOMICS_ACTIVATION.md) — atomic PEAQ activation: deployment selection, `preview_machine_activation` / `activate_machine`, the bond/voucher arithmetic and why only the net amount is approved, receipt event correlation and post-state reconciliation, `reconcile_activation_transaction` for a submitted-but-unconfirmed hash, the typed error taxonomy, migrating off register-then-mint, and the release-day diff for adding a network.
- [**Tokenomics 2.0 machine management**](docs/19_TOKENOMICS_MACHINE_MANAGEMENT.md) — post-onboarding reads and previews, lifecycle, bounded PEAQ/USDT subscriptions, ERC-721 ownership, DID updates, relocation evidence, authority rules, and known-hash reconciliation.
- [**Event submission**](docs/03_EVENTS.md) — single and batch event submission, pipeline details, limits, hashing, metadata mode, and rate-limiting behavior.
- [**NFT minting & DID attributes**](docs/04_NFT_AND_DID.md) — minting machine NFTs, querying token IDs, writing machine and proxy DID attributes atomically via the Batch precompile. Covers attribute key reference, data visibility options, and the atomic batch guarantee.
- [**OWS wallet lifecycle**](docs/06_WALLET.md) — create, import, list, get, export, and delete encrypted wallets via the Open Wallet Standard. Multi-chain accounts, passphrase management, vault storage, OWS-native EVM signing via `from_wallet()`, and Solana signing via `solana_signer_from_wallet()`.
- [**Orchestration service**](docs/07_ORCHESTRATION.md) *(experimental)* — `client.orchestration`, policies, observability, market lifecycle, pagination, challenge-sign workflows, planned type stubs, and live integration tests.
- [**Stream data signing & encryption**](docs/08_STREAM.md) *(experimental)* — local field-level privacy rules, EIP-191 data package signing, and verification (Phase 2 Step 2; no network/distribution).
- [**Stream data chunking**](docs/09_CHUNKING.md) *(experimental)* — per-chunk XChaCha20-Poly1305 encryption, inline Ed25519 signatures, owner/operator/machine key wrapping, and separate buyer access docs; `build_chunk_chain` / `verify_chunk_chain` / `decrypt_chunk` / `create_buyer_access_entry` / `build_buyer_access_files`.
- [**Analytics & telemetry**](docs/10_ANALYTICS.md) — anonymous usage telemetry, event inventory, opt-out, privacy guarantees, and onboarding flow integration.
- [**Stream distribution**](docs/11_DISTRIBUTION.md) *(experimental)* — seller-side payment confirmation and delivery (`distribute_data`, `PollingConfirmationProvider`, `S3DeliveryChannel`) plus buyer-side token payment (`transfer_token`, `submit_payment_proof`, `pay_and_submit_proof`) on peaq, Base, and Solana.
- [**P2P delivery**](docs/14_P2P_DELIVERY.md) *(experimental)* — machine-to-machine delivery via the `peaqos-p2p` transport: `P2PDeliveryChannel` (seller) and `P2PDeliveryReceiver` (buyer) with verify-before-decrypt chunk reception, session routing, and replay protection.
- [**Stream delivery setup**](docs/12_STREAM_DELIVERY_SETUP.md) *(in development)* — pre-purchase payment rails discovery, delivery transports discovery, delivery capability registration, and stream listing purchase field updates.
- [**Stream purchases**](docs/13_STREAM_PURCHASES.md) *(in development)* — full purchase lifecycle: creation with buyer identity, payment intent, payment proof, delivery retrieval, and purchase events.

---

## Development

```bash
git clone https://github.com/peaqnetwork/peaq-os-sdk-py.git
cd peaq-os-sdk-py
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
```

### Activate `.env` for Testing
```
set -a
source .env
set +a
```

### Quality gates

```bash
ruff check src tests       # lint — zero warnings
black --check src tests    # formatting
mypy src                   # strict type check, zero errors
pytest -q                  # all green
```

The unit tests are hermetic — they mock `Web3` and `requests.Session` with handwritten stubs and never touch the network.

The Tokenomics activation surface additionally carries a branch-coverage floor:

```bash
pytest -q -m "not integration" \
  --cov=src/peaq_os_sdk/tokenomics --cov-branch --cov-fail-under=90
```

Coverage is measured from the hermetic suite alone, so the floor is meaningful without spending PEAQ.

### Optional integration tests

The repo ships an opt-in suite that talks to a real peaq devnet. It is skipped silently in normal `pytest -q` and only runs when the required environment variables are supplied.

**Registration integration suite** ([`tests/integration/registration/test_registration_integration.py`](tests/integration/registration/test_registration_integration.py)) — three end-to-end tests for the self-managed flow, proxy-managed flow, and double-registration revert. Gated behind the `integration` marker plus seven environment variables:

```bash
PEAQOS_RPC_URL=https://peaq.api.onfinality.io/public
PEAQOS_PRIVATE_KEY=0xYOUR_FUNDED_TREASURY_KEY
IDENTITY_REGISTRY_ADDRESS=0xYOUR_DEPLOYED_REGISTRY_ADDRESS
IDENTITY_STAKING_ADDRESS=0xYOUR_DEPLOYED_STAKING_ADDRESS
PEAQOS_OWNER_ADDRESS=5GrwvaEF...
PEAQOS_FAUCET_URL=https://depinstation.peaq.xyz
PEAQOS_2FA_CODE=123456
```

```bash
pytest -m integration tests/integration/
```

See [docs/02_REGISTRATION.md → Integration tests](docs/02_REGISTRATION.md#integration-tests) for the full breakdown of what each test verifies and operational warnings.

**NFT & DID integration suite** ([`tests/integration/nft_did/test_nft_and_did_integration.py`](tests/integration/nft_did/test_nft_and_did_integration.py)) — end-to-end tests for the full NFT lifecycle, atomic DID writes, and atomicity guarantee verification (all-or-nothing batch semantics). Includes 7 tests across minting, token queries, machine DID attributes, proxy DID attributes, and atomic revert scenarios. Requires the same env vars as the registration suite plus:

```bash
EVENT_REGISTRY_ADDRESS=0x...
MACHINE_NFT_ADDRESS=0x...
DID_REGISTRY_ADDRESS=0x0000000000000000000000000000000000000800
BATCH_PRECOMPILE_ADDRESS=0x0000000000000000000000000000000000000805
```

See [docs/04_NFT_AND_DID.md](docs/04_NFT_AND_DID.md) for the full breakdown.

**OWS wallet integration suite** ([`tests/integration/wallet/test_wallet_integration.py`](tests/integration/wallet/test_wallet_integration.py)) — six end-to-end tests for wallet lifecycle (create, list, get, delete), create-export-reimport round-trip, private key import, empty vault, and error paths. Gated behind `PEAQOS_INTEGRATION=1` and requires the `open-wallet-standard` package:

```bash
pip install peaq-os-sdk[ows]
PEAQOS_INTEGRATION=1 pytest -m integration tests/integration/wallet/
```

See [docs/06_WALLET.md](docs/06_WALLET.md) for the full wallet API reference.

**Orchestration live API suite** ([`tests/integration/orchestration/test_orchestration_integration.py`](tests/integration/orchestration/test_orchestration_integration.py)) — health, readiness, machine lifecycle, market lifecycle, policy CRUD, and audit-event listing against a real orchestration deployment. Gated behind `PEAQOS_ORCHESTRATION_INTEGRATION=1`:

```bash
PEAQOS_ORCHESTRATION_INTEGRATION=1
PEAQOS_ORCHESTRATION_URL=https://markets.peaq.xyz
PEAQOS_API_KEY=your-platform-api-key
pytest -m integration tests/integration/orchestration/test_orchestration_integration.py
```

Optional overrides: `PEAQOS_ORCHESTRATION_PROVIDER_KEY`, `PEAQOS_ORCHESTRATION_ENDPOINT_URL`, `PEAQOS_ORCHESTRATION_MARKET_SERVICE_TYPE`, `PEAQOS_ORCHESTRATION_MARKET_OPERATION`, `PEAQOS_ORCHESTRATION_MARKET_ORDER_INPUT` (JSON object), `PEAQOS_ORCHESTRATION_MARKET_PAYMENT_RAIL`. See [docs/07_ORCHESTRATION.md → Integration tests](docs/07_ORCHESTRATION.md#integration-tests).

A separate **mocked** market lifecycle test lives in [`tests/integration/orchestration/test_market_lifecycle_integration.py`](tests/integration/orchestration/test_market_lifecycle_integration.py) and runs without orchestration env vars.

**Tokenomics integration suite** ([`tests/integration/tokenomics/test_activation_integration.py`](tests/integration/tokenomics/test_activation_integration.py)) — live Agung coverage for snapshot and `InfoDesk.peer` drift, pricing, deterministic IDs, genuine revert decoding, write-free previews, activation guards, atomic activations, management reads, reversible suspend/resume, submission reporting, and known-hash reconciliation.

> **This suite spends real PEAQ and cannot be undone.** Activation mints a permanent machine identity and bonds tokens against it — there is no deactivate and no refund. A full run costs roughly **0.8 PEAQ of bond plus activation and management gas** at the tier-0 price stamped on Agung. It is gated behind a dedicated variable on top of the `integration` marker.

```bash
PEAQOS_TOKENOMICS_INTEGRATION=1 \
PEAQOS_RPC_URL=https://peaq-agung.api.onfinality.io/public \
PEAQOS_PRIVATE_KEY=0xYOUR_FUNDED_KEY \
pytest -m integration tests/integration/tokenomics/
```

Contract addresses are deliberately **not** environment variables — resolving them from the SDK's approved snapshot is the behaviour under test. See the [activation](docs/18_TOKENOMICS_ACTIVATION.md) and [management](docs/19_TOKENOMICS_MACHINE_MANAGEMENT.md) guides for the API reference and live-suite limitations.

`PEAQOS_PRIVATE_KEY` must match `^0x[0-9a-fA-F]{64}$`. **Never commit a real key. Never run the suite against mainnet.**

### Build

```bash
python -m build
```

Produces `dist/peaq_os_sdk-<version>-py3-none-any.whl` and `dist/peaq_os_sdk-<version>.tar.gz` for downstream testing.

## License

See [LICENSE](./LICENSE) for details.
