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

# Signing transactions

> How post-quantum signing works in Quantum Chain — flows for keystores, Clef, and external signers.

Every Quantum Chain transaction carries a **post-quantum signature** that authorizes the spend. Block validators verify the signature against the sender's registered public key on every block at or after `quantumVerificationBlock`.

The signing primitive is different from Ethereum's ECDSA, but the transaction *envelope* is unchanged: legacy, EIP-1559, and EIP-2930 transactions are all supported.

## Three signing models

| Model                      | Where the key lives                         | Best for                            |
| -------------------------- | ------------------------------------------- | ----------------------------------- |
| Local keystore             | `geth` process memory                       | Development, single-user nodes      |
| External signer (Clef)     | A separate process you control              | Hot wallets with policy enforcement |
| Custody platform (Qustody) | A signing service behind a network boundary | Institutional, multi-user, audited  |

## Local keystore signing

This is the path used by `--unlock` and `personal_sendTransaction`. geth reads the encrypted key from the keystore, decrypts it in memory using the unlock passphrase, and signs each transaction internally.

```javascript theme={null}
// Geth IPC console
personal.unlockAccount("0xfF81...", "strong-pass", 60)
eth.sendTransaction({
  from: "0xfF81...",
  to:   "0x1865...",
  value: web3.toWei(1, "ether")
})
```

<Warning>
  Local keystore signing exposes your key inside the geth process. Anyone with read access to the host or to the RPC interface can drain the wallet. Use this only for development.
</Warning>

## External signing with Clef

Clef is a separate process that owns the keystore. geth talks to clef over an IPC socket and never touches private keys.

```mermaid theme={null}
sequenceDiagram
    participant App as dApp / wallet
    participant Geth as geth node
    participant Clef as clef signer
    participant KS as keystore

    App->>Geth: eth_sendTransaction
    Geth->>Clef: account_signTransaction
    Clef->>KS: read encrypted key
    KS-->>Clef: ciphertext
    Clef->>Clef: prompt operator (or rule)
    Clef->>Clef: produce post-quantum signature
    Clef-->>Geth: signed tx
    Geth-->>App: tx hash
```

Start clef:

```bash theme={null}
./build/bin/clef \
  --keystore ~/.quantum-chain/keystore \
  --chainid 20803 \
  --ipcpath /tmp/clef.ipc
```

Connect geth:

```bash theme={null}
geth ... --signer /tmp/clef.ipc --nousb
```

Each signing request appears in clef's terminal for approval, or you can write a JavaScript rule file (`clef --rules rules.js`) that auto-approves matching requests.

## External signing via Qustody

For multi-user, policy-driven, auditable signing, use the [Qustody platform](/qustody/security-model).

The flow:

1. Your application calls Qustody's `POST /v1/transactions` with `{wallet_id, to, value}`.
2. Qustody runs its policy engine and AML screening, then enters `PENDING_SIGNATURE`.
3. Your **post-quantum signing service** fetches the signing payload via `GET /v1/transactions/{id}/signing-payload`.
4. The signing service produces a post-quantum signature using the wallet's private key.
5. The service POSTs the signature to `POST /v1/transactions/{id}/signature`.
6. Qustody verifies the signature, broadcasts via JSON-RPC, and emits webhooks for each subsequent state.

```mermaid theme={null}
sequenceDiagram
    participant App
    participant Qustody as Qustody API
    participant Signer as Post-quantum<br/>signing service
    participant Node as Quantum Chain RPC

    App->>Qustody: POST /v1/transactions
    Qustody->>Qustody: policy + AML
    Qustody-->>App: 201 PENDING_SIGNATURE
    Signer->>Qustody: GET /signing-payload
    Qustody-->>Signer: digest + nonce + chainId
    Signer->>Signer: sign with post-quantum key
    Signer->>Qustody: POST /signature
    Qustody->>Qustody: verify signature
    Qustody->>Node: eth_sendRawTransaction
    Node-->>Qustody: tx hash
    Qustody-->>App: webhook transaction.broadcast
```

See [External signing](/guides/external-signing) for the full payload schemas.

## Building a transaction by hand

The signing payload is the **Keccak-256 hash** of the RLP-encoded transaction without signature fields. The signature is a post-quantum signature over that 32-byte digest. Reassemble the signed transaction by appending the signature in the transaction's signature fields and broadcasting via `eth_sendRawTransaction`.

For library users, ethers.js v6, web3.py, and viem support custom signers — plug in a signer that produces post-quantum signatures and the rest of the transaction-building stack works unchanged.

## Verifying a signature

Use the standard JSON-RPC:

```bash theme={null}
curl -s -X POST $RPC \
  -H 'Content-Type: application/json' \
  --data '{
    "jsonrpc":"2.0",
    "method":"eth_getTransactionByHash",
    "params":["0x..."],
    "id":1
  }'
```

The response includes the post-quantum public key and signature fields. A node will reject any block containing a transaction whose signature does not verify against the sender's public key.

## Where to go next

* [Sending transactions](/quantum-chain/sending-transactions)
* [Accounts and keys](/quantum-chain/accounts-and-keys)
* [External signing (Qustody)](/guides/external-signing)
