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

# Wallets (OWS)

> Encrypted, mnemonic-backed wallets for peaqOS via the Open Wallet Standard. Optional, opt-in, fully local.

peaqOS supports the [Open Wallet Standard v1.3](https://docs.openwallet.sh/) for wallet generation, encrypted storage, signing, and display. The current raw-key flow uses hex private keys in `.env` files, protected only by file permissions. That works, but it has limits:

* No encryption at rest
* No mnemonic backup or recovery
* No multi-chain visibility. One secp256k1 key derives addresses on every EVM chain, Cosmos, Solana, Bitcoin, Tron, and more, but nothing surfaces them
* No standard import / export to wallets like MetaMask or hardware devices
* No audit log of key creation, signing, or export events

OWS solves all of these with an encrypted local vault, BIP-39 derivation, CAIP-2 chain identifiers, and an append-only audit log: fully local, no cloud, no remote services.

OWS is **optional**. Use the SDK helpers below to manage wallets. The raw-key path keeps working unchanged.

## Multi-chain accounts from one mnemonic

A mnemonic-derived wallet generates accounts across every OWS-supported chain family from a single 12- or 24-word phrase.

| Family                                    | Curve     | Coin Type | Default Path                | Address Format               |
| :---------------------------------------- | :-------- | :-------- | :-------------------------- | :--------------------------- |
| EVM (peaq, Base, Ethereum, Polygon, etc.) | secp256k1 | 60        | `m/44'/60'/0'/0/{index}`    | EIP-55 checksummed hex       |
| Solana                                    | ed25519   | 501       | `m/44'/501'/{index}'/0'`    | Base58 public key            |
| Bitcoin                                   | secp256k1 | 0         | `m/84'/0'/0'/0/{index}`     | Bech32 native segwit         |
| Cosmos                                    | secp256k1 | 118       | `m/44'/118'/0'/0/{index}`   | Bech32                       |
| Tron                                      | secp256k1 | 195       | `m/44'/195'/0'/0/{index}`   | Base58Check                  |
| TON                                       | ed25519   | 607       | `m/44'/607'/{index}'`       | Base64url wallet v5r1        |
| Sui                                       | ed25519   | 784       | `m/44'/784'/{index}'/0'/0'` | 0x + BLAKE2b-256 hex         |
| XRPL                                      | secp256k1 | 144       | `m/44'/144'/0'/0/{index}`   | Base58Check                  |
| Spark                                     | secp256k1 | 8797555   | `m/84'/0'/0'/0/{index}`     | `spark:` + compressed pubkey |
| Filecoin                                  | secp256k1 | 461       | `m/44'/461'/0'/0/{index}`   | `f1` + base32                |

The SDK guarantees an account entry for every protocol-recognized EVM chain (`SUPPORTED_CHAIN_IDS`). If OWS doesn't return one, it's synthesized from the wallet's primary EVM address, which is identical across all EVM chains:

| Network  | CAIP-2         | Chain ID |
| :------- | :------------- | :------- |
| peaq     | `eip155:3338`  | 3338     |
| ethereum | `eip155:1`     | 1        |
| base     | `eip155:8453`  | 8453     |
| polygon  | `eip155:137`   | 137      |
| arbitrum | `eip155:42161` | 42161    |
| optimism | `eip155:10`    | 10       |

Each account is exposed as a [CAIP-10](https://chainagnostic.org/CAIPs/caip-10) account ID (`eip155:3338:0x...`), so a single peaqOS wallet can sign on peaq, Base (for `bridgeNft`), and any other supported chain without ever exporting the key.

## Vault layout

The vault lives at `~/.ows/` (override with `OWS_VAULT_PATH`). Structure is set by OWS; peaqOS uses it as-is.

```
~/.ows/
├── config.json               # OWS settings (700)
├── wallets/                  # (700)
│   └── {uuid}.json           # one per wallet (600)
├── keys/                     # (700, future use)
├── policies/                 # (755, future use)
└── logs/
    └── audit.jsonl           # append-only operation log (600)
```

Each wallet file holds:

* `id` (UUID v4) and `name`
* `key_type`: `"mnemonic"` or `"private_key"`
* `accounts[]` with one entry per supported chain (CAIP-10, address, derivation path)
* `crypto` with `aes-256-gcm` ciphertext, `scrypt` KDF params, IV, salt, and auth tag
* `created_at` (ISO 8601)

## SDK methods

Wallet lifecycle is exposed through the SDKs.

### JavaScript / TypeScript

Available as both module-level imports and static methods on `PeaqosClient`. Pick whichever matches your style.

```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
import {
  PeaqosClient,
  createWallet,
  importWallet,
  importWalletMnemonic,
  listWallets,
  getWallet,
  exportWallet,
  deleteWallet,
} from "@peaqos/peaq-os-sdk";

// Create
const wallet = await createWallet("my-machine", passphrase, 12);
//   or:  await PeaqosClient.createWallet("my-machine", passphrase, 12);

// Import
await importWallet("my-machine", "0x...64hex", passphrase);
//   default chain: "evm". Pass a different OWS chain (e.g. "solana", "bitcoin", "cosmos")
//   when the private key is for that chain's curve and address format:
//   await importWallet("my-machine", "0x...64hex", passphrase, "solana");
await importWalletMnemonic("my-machine", "twelve words ...", passphrase);

// Inspect
const all = await listWallets();
const one = await getWallet("my-machine");

// Export / delete
const phrase = await exportWallet("my-machine", passphrase);
await deleteWallet("my-machine");
```

Every function takes an optional final `WalletOptions` arg with `vaultPath` to point at a vault directory other than `~/.ows/`. Passphrase falls back to `OWS_PASSPHRASE` when omitted; if both are missing, the call throws `PeaqosError`. `importWallet` validates the hex shape eagerly and throws `ValidationError` for malformed keys; `importWalletMnemonic` accepts an optional `index` (default `0`) before the `WalletOptions` arg to derive a non-default account.

`WalletInfo` carries `id`, `name`, `createdAt`, `keyType`, `peaqAddress` (convenience), and `accounts: readonly AccountInfo[]` with every chain. `AccountInfo` has `accountId` (CAIP-10), `address`, `chainId` (CAIP-2), `network` (human-readable name like `"peaq"`, `"base"`, `"solana"`), and `derivationPath`. Both shapes are deep-frozen.

#### Building a client straight from a vault wallet

`PeaqosClient.fromWallet` skips the manual private-key plumbing. It loads a wallet from the vault and returns a configured client whose `account` is the wallet's peaq address.

```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
import { PeaqosClient } from "@peaqos/peaq-os-sdk";

// OWS-native signing (default): key is decrypted per-sign and wiped immediately.
// The passphrase is verified on the FIRST signTransaction call, not at construction.
const client = await PeaqosClient.fromWallet("my-machine", "s3cret", true, {
  rpcUrl: "https://peaq-rpc.example.com",
  contracts: { /* ... */ },
});

// Raw-key mode: decrypts the key once at construction (eager passphrase check)
// and signs through viem locally. Use this when you need viem features OWS
// doesn't expose (e.g. signMessage, signTypedData).
const eager = await PeaqosClient.fromWallet("my-machine", "s3cret", false, {
  rpcUrl: "https://peaq-rpc.example.com",
  contracts: { /* ... */ },
});
```

Signature: `fromWallet(nameOrId, passphrase, owsSigning, config, options?)`. `owsSigning` defaults to `true`. In OWS-native mode the SDK never holds the private key: `signMessage` and `signTypedData` throw because OWS only exposes `signTransaction`. Switch to raw-key mode if you need either.

OWS signing surfaces typed errors via `OwsSigningErrorCode` (`WALLET_NOT_FOUND`, `INVALID_PASSPHRASE`, `INVALID_INPUT`, `POLICY_DENIED`, `CHAIN_NOT_SUPPORTED`). `INVALID_INPUT` becomes a `ValidationError`; the rest become `PeaqosError` with the original error preserved as `.cause`.

### Python

Available under `peaq_os_sdk.wallet` and as `@staticmethod`s on `PeaqosClient`.

```python theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
from peaq_os_sdk import PeaqosClient
from peaq_os_sdk.wallet import (
    create_wallet,
    import_wallet,
    import_wallet_mnemonic,
    list_wallets,
    get_wallet,
    export_wallet,
    delete_wallet,
)

# Create
info = create_wallet("my-machine", passphrase=passphrase, words=12)
#   or: PeaqosClient.create_wallet("my-machine", passphrase=passphrase, words=12)

# Import
import_wallet("my-machine", "0x...64hex", passphrase=passphrase)
#   default chain="evm". Pass a different OWS chain (e.g. "solana", "bitcoin", "cosmos")
#   when the private key is for that chain's curve and address format:
#   import_wallet("my-machine", "0x...64hex", passphrase=passphrase, chain="solana")
import_wallet_mnemonic("my-machine", "twelve words ...", passphrase=passphrase)

# Inspect
all_wallets = list_wallets()
one = get_wallet("my-machine")

# Export / delete
phrase = export_wallet("my-machine", passphrase=passphrase)
delete_wallet("my-machine")
```

Each function accepts a final `vault_path` keyword for a custom vault directory. Passphrase falls back to the `OWS_PASSPHRASE` env var when `None`; missing both raises `PeaqosError`.

On the Python side, `WalletInfo` carries `id`, `name`, `created_at`, `key_type`, `peaq_address` (convenience), and `accounts: list[AccountInfo]` with every chain. `AccountInfo` has `account_id` (CAIP-10), `address`, `chain_id` (CAIP-2), `network` (human-readable name), and `derivation_path`.

#### Building a client straight from a vault wallet

`PeaqosClient.from_wallet` mirrors the JS factory above. It loads the wallet, verifies the passphrase, and returns a fully configured client.

```python theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
from peaq_os_sdk import PeaqosClient

client = PeaqosClient.from_wallet(
    "my-machine",
    passphrase="s3cret",
    rpc_url="https://peaq-rpc.example.com",
    identity_registry="0x...",
    identity_staking="0x...",
    event_registry="0x...",
    machine_nft="0x...",
    did_registry="0x0000000000000000000000000000000000000800",
    batch_precompile="0x0000000000000000000000000000000000000805",
)

# Eager-decrypt: identical to PeaqosClient(private_key=...).
eager = PeaqosClient.from_wallet(
    "my-machine",
    passphrase="s3cret",
    ows_signing=False,
    rpc_url="https://peaq-rpc.example.com",
    # ...
)
```

Signature: `from_wallet(name_or_id, passphrase=None, ows_signing=True, vault_path=None, **config_kwargs)`. `passphrase` falls back to `OWS_PASSPHRASE`. In OWS-native mode the SDK never holds the private key — `OWSAccount` decrypts it inside the OWS Rust FFI for each `sign_transaction` call and wipes it immediately. `ows_signing=False` exports and decrypts the key at construction time, behaving identically to a raw-key client. Each transaction's `chainId` drives both the CAIP-2 OWS arg and the EIP-155 `v`, so the same client transparently signs both peaq (`eip155:3338`) and Base (`eip155:8453`) bridge transactions.

OWS signing surfaces typed errors via 5 OWS error codes (`WALLET_NOT_FOUND`, `INVALID_PASSPHRASE`, `INVALID_INPUT`, `POLICY_DENIED`, `CHAIN_NOT_SUPPORTED`). `INVALID_INPUT` raises `ValidationError`; the rest raise `PeaqosError`. See [SDK errors: OWS signing error codes](/peaqos/sdk-reference/errors#ows-signing-error-codes).

### Pulling the peaq address out of a wallet

`peaq_address` / `peaqAddress` is already populated by every wallet method, so most callers never need anything else. If you're working with a raw `accounts` list (e.g. building a wallet response by hand in tests, or reading a vault file directly), use the helper:

```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
import { extractPeaqAddress } from "@peaqos/peaq-os-sdk";
const address = extractPeaqAddress(wallet.accounts);
```

```python theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
from peaq_os_sdk.wallet import extract_peaq_address
address = extract_peaq_address(wallet.accounts)
```

It returns the `eip155:3338` account if present, otherwise the first `eip155:*` account (EVM addresses match across EVM chains), and raises `PeaqosError` when no EVM account exists.

## From the CLI

Every SDK wallet helper is also exposed through `peaqos wallet`. Install the optional extra to get them:

```bash theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
pip install 'peaq-os-cli[ows]'
```

Then:

```bash theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
peaqos wallet create my-wallet              # mnemonic-backed, 12 words
peaqos wallet import my-wallet --mnemonic   # hidden prompt for the phrase
peaqos wallet list
peaqos wallet show my-wallet                # full multi-chain address table
peaqos wallet use my-wallet                 # writes PEAQOS_OWS_WALLET=my-wallet to .env
peaqos wallet export my-wallet              # reveals phrase / key after confirm
peaqos wallet delete my-wallet              # secure overwrite + unlink after confirm
```

`peaqos init` also offers `wallet` as a third `Private key source` choice and writes `PEAQOS_OWS_WALLET` instead of `PEAQOS_PRIVATE_KEY`. Once a wallet is active, `peaqos activate`, `peaqos qualify event`, and the rest of the command surface sign through it. See [peaqOS CLI: wallet](/peaqos/cli#peaqos-wallet) for the full command reference.

## Security model

* **Vault encryption.** AES-256-GCM with scrypt KDF (`n=65536, r=8, p=1`).
* **Passphrase handling.** Sourced from the explicit argument or the `OWS_PASSPHRASE` env var; if neither is set, the SDK raises `PeaqosError` rather than prompting. Never stored on disk. In CI, pass via secret manager or env injection.
* **Mnemonic exposure.** Never returned by `createWallet` / `create_wallet`. The `WalletInfo` response only carries addresses and metadata. To recover the seed phrase you must call `exportWallet` / `export_wallet` with the vault passphrase.
* **Single-mnemonic blast radius.** One phrase controls accounts on every supported chain. Treat exported phrases accordingly: anyone with the phrase has access to every chain account.
* **Audit log.** Every wallet operation (create, import, export, delete) appends to `~/.ows/logs/audit.jsonl`.

## See also

<CardGroup cols={3}>
  <Card title="peaqOS CLI" icon="terminal" href="/peaqos/cli">
    The full peaqOS CLI command reference.
  </Card>

  <Card title="Install" icon="download" href="/peaqos/install">
    The `[ows]` extra and the raw-key flow side-by-side.
  </Card>

  <Card title="OWS specification" icon="up-right-from-square" href="https://docs.openwallet.sh/">
    Full upstream Open Wallet Standard v1.3 spec.
  </Card>
</CardGroup>
