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

# Configure a development network

> Run a single-validator Quantum Chain devnet and a local Qustody API for end-to-end integration tests.

This guide spins up a fully self-contained dev environment: one Quantum Chain node sealing blocks every 5 seconds, a local Qustody API talking to it, and a pre-funded address you can transact with.

By the end you will have:

* A dev chain at `http://127.0.0.1:8545`
* A Qustody API at `http://127.0.0.1:8080`
* A pre-funded wallet registered with Qustody

## Prerequisites

* `geth` and `keygenerator` from `make all` — see [Installation](/quantum-chain/installation)
* `qc-custody` binary, also built by `make all`
* PostgreSQL 16 running locally (or via Docker)

## Step 1. Generate a sealer keypair

```bash theme={null}
mkdir -p ~/devnet/keystore
cd ~/devnet
/path/to/build/bin/keygenerator --passphrase "devpass" > sealer.txt
echo devpass > password.txt
mv UTC--*.json keystore/
SEALER_ADDR=$(grep -oE '0x[0-9a-fA-F]{40}' sealer.txt | head -1)
SEALER_PUBKEY=$(grep -oE 'Public key: 0x[0-9a-fA-F]+' sealer.txt | awk '{print $3}')
echo "Sealer address: $SEALER_ADDR"
```

## Step 2. Build the genesis file

`extraData` encodes the sealer set: 32 vanity bytes + each sealer's 20-byte address + 65 zero bytes for the signature placeholder.

```bash theme={null}
SEALER_NO_PREFIX=${SEALER_ADDR#0x}
EXTRA="0x$(printf '0%.0s' $(seq 1 64))${SEALER_NO_PREFIX}$(printf '0%.0s' $(seq 1 130))"

cat > genesis.json <<EOF
{
  "config": {
    "chainId": 20803,
    "homesteadBlock": 0,
    "eip150Block": 0,
    "eip155Block": 0,
    "eip158Block": 0,
    "byzantiumBlock": 0,
    "constantinopleBlock": 0,
    "petersburgBlock": 0,
    "istanbulBlock": 0,
    "berlinBlock": 0,
    "londonBlock": 0,
    "quantumVerificationBlock": 0,
    "clique": { "period": 5, "epoch": 30000 }
  },
  "publicKey": "${SEALER_PUBKEY}",
  "difficulty": "1",
  "gasLimit": "30000000",
  "extraData": "${EXTRA}",
  "alloc": {
    "${SEALER_ADDR}": { "balance": "1000000000000000000000000" }
  }
}
EOF
```

## Step 3. Initialize and start the node

```bash theme={null}
geth --datadir ~/devnet init genesis.json

geth \
  --datadir ~/devnet \
  --networkid 20803 \
  --port 30303 \
  --nodiscover \
  --mine \
  --miner.etherbase "$SEALER_ADDR" \
  --unlock "$SEALER_ADDR" \
  --password ~/devnet/password.txt \
  --allow-insecure-unlock \
  --http --http.addr 127.0.0.1 --http.port 8545 \
  --http.api eth,net,web3,debug,personal,clique,txpool \
  --http.corsdomain "*" \
  --ws --ws.addr 127.0.0.1 --ws.port 8546 \
  --ws.api eth,net,web3 \
  --verbosity 3
```

Confirm blocks are advancing:

```bash theme={null}
curl -s -X POST http://127.0.0.1:8545 \
  -H 'Content-Type: application/json' \
  --data '{"jsonrpc":"2.0","method":"eth_blockNumber","id":1,"params":[]}'
```

The result hex should increment every 5 seconds.

## Step 4. Start PostgreSQL

```bash theme={null}
docker run -d --name qustody-pg \
  -p 5432:5432 \
  -e POSTGRES_PASSWORD=devpass \
  -e POSTGRES_DB=qc_custody \
  -e POSTGRES_USER=custody \
  postgres:16
```

## Step 5. Start a local signer (callback mode)

For development you can run a minimal callback-mode signer. Create a file `local-signer.go` that reads pending transactions from Qustody, signs with your local keystore, and POSTs back. See [External signing](/guides/external-signing) for the payload contract.

For end-to-end tests, the simpler path is to use **callback mode** with a script that polls every 2 seconds. Or use the reference signer service `qustody-signer` if you have it built locally.

## Step 6. Start the Qustody API

```bash theme={null}
export CUSTODY_DB_HOST=127.0.0.1
export CUSTODY_DB_PORT=5432
export CUSTODY_DB_USER=custody
export CUSTODY_DB_PASSWORD=devpass
export CUSTODY_DB_NAME=qc_custody
export CUSTODY_DB_SSLMODE=disable
export CUSTODY_DB_MIGRATE_ON_START=true

export CUSTODY_NODE_RPC_URL=http://127.0.0.1:8545
export CUSTODY_NODE_WS_URL=ws://127.0.0.1:8546
export CUSTODY_CHAIN_ID=20803
export CUSTODY_CONFIRMATION_DEPTH=2

export CUSTODY_AUTH_ENABLED=false      # dev only — never in production
export CUSTODY_RBAC_ENABLED=false
export CUSTODY_SIGNER_TYPE=callback
export CUSTODY_ENV=development

/path/to/build/bin/qc-custody
```

Once `/readyz` returns 200:

```bash theme={null}
curl http://127.0.0.1:8080/readyz
```

## Step 7. Register a wallet and transact

```bash theme={null}
BASE_URL=http://127.0.0.1:8080

# Vault
VAULT_ID=$(curl -s -X POST $BASE_URL/v1/vaults \
  -H 'Content-Type: application/json' \
  -d '{"name":"dev"}' | jq -r .id)

# Wallet pointing to the sealer (which has the dev funds)
WALLET_ID=$(curl -s -X POST $BASE_URL/v1/wallets \
  -H 'Content-Type: application/json' \
  -d "{
    \"vault_id\": \"$VAULT_ID\",
    \"public_key\": \"$SEALER_PUBKEY\",
    \"address\": \"$SEALER_ADDR\"
  }" | jq -r .id)

# Send a transaction
TX_ID=$(curl -s -X POST $BASE_URL/v1/transactions \
  -H 'Content-Type: application/json' \
  -d "{
    \"wallet_id\": \"$WALLET_ID\",
    \"to_address\": \"0x000000000000000000000000000000000000dEaD\",
    \"value\": \"1\",
    \"asset\": \"QRC\"
  }" | jq -r .id)

# Watch state
watch -n 1 "curl -s $BASE_URL/v1/transactions/$TX_ID | jq .status"
```

You will see `SUBMITTED → APPROVED → PENDING_SIGNATURE`. Your local signer fulfils the signing payload, then the transaction transitions to `BROADCAST → CONFIRMED`.

## Tear down

```bash theme={null}
docker rm -f qustody-pg
pkill -f 'geth --datadir ~/devnet'
pkill qc-custody
rm -rf ~/devnet
```

## Where to go next

* [Integrate Qustody with Quantum Chain](/guides/integrate-qustody-with-quantum-chain) — production wiring
* [Move from local signing to custody](/guides/move-from-local-signing-to-custody)
* [Networks and validators](/quantum-chain/networks)
