diff --git a/operator/alternative-key-management/dvt/img/obol_dkg_flow.png b/operator/alternative-key-management/dvt/img/obol_dkg_flow.png
new file mode 100644
index 00000000..5ae202a7
Binary files /dev/null and b/operator/alternative-key-management/dvt/img/obol_dkg_flow.png differ
diff --git a/operator/alternative-key-management/dvt/img/validator_registration_flow.png b/operator/alternative-key-management/dvt/img/validator_registration_flow.png
new file mode 100644
index 00000000..5455d2fb
Binary files /dev/null and b/operator/alternative-key-management/dvt/img/validator_registration_flow.png differ
diff --git a/operator/alternative-key-management/dvt/obol-setup.mdx b/operator/alternative-key-management/dvt/obol-setup.mdx
index c710bc54..368c51e3 100644
--- a/operator/alternative-key-management/dvt/obol-setup.mdx
+++ b/operator/alternative-key-management/dvt/obol-setup.mdx
@@ -1,42 +1,58 @@
---
title: Obol Setup
-description: Set up a distributed validator cluster with Obol Charon and StakeWise Vaults. Split validator keys, configure Docker, and run Charon nodes.
+description: Set up distributed validators with Obol Charon and StakeWise Vaults. Run solo by splitting existing keys, or as a group through a DKG ceremony, with registration handled by the DVT Relayer.
---
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+import Image from '@theme/IdealImage';
+
# Obol Setup
-
Setting Up a Distributed Validator Cluster with Obol Network and StakeWise Vaults
+Obol provides permissionless access to distributed validators. A distributed validator (DV) is an Ethereum proof-of-stake validator that runs on more than one node. Each node is the set of clients an operator runs: execution and consensus layer clients, a validator client, and [Charon ↗](https://docs.obol.org/learn/charon/intro) — Obol's software that acts as middleware between the validator client and the consensus client.
+
+The validator client talks to Charon instead of the beacon node. Charon runs a consensus algorithm letting the nodes agree on what data to sign (attestations and proposals). Each node's validator client signs that data with its key share, and Charon then aggregates a threshold of the nodes' partial signatures into the validator's final signature.
-Follow these steps to set up a Distributed Validator Cluster using Obol Network and integrate it with StakeWise Vaults.
+These key shares are created through a Distributed Key Generation (DKG) ceremony: instead of one party generating the full private key and splitting it among the nodes, every operator's Charon client takes part so the shares are generated together. The full private key is never constructed on any machine.
+
+:::custom-info[Operator Service Role]
+The Operator Service's role depends on how the validator keys are generated:
+
+- **Keys generated centrally:** the Operator Service can access them and [registers the validators](/docs/vaults/how-vaults-work#validator-registration) itself.
+- **Keys generated through a DKG ceremony:** no party ever holds the full key, so the Operator Service can't register on its own. Registration instead runs through the StakeWise DVT components (the Sidecars and the DVT Relayer). See the [DVT registration flow](/operator/alternative-key-management/dvt/overview#validator-registration-process).
+:::
+
+StakeWise Vault operators can use the Obol DVT to run fault-tolerant distributed validators.
## Prerequisites
Before proceeding, ensure you have the following:
-1. Install [Docker Engine ↗](https://docs.docker.com/engine/install/)
-2. Ensure Docker is running:
+1. Install [Docker Engine ↗](https://docs.docker.com/engine/install/). Verify it is running:
```bash
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
```
-3. [Create Vault →](/operator/create-regular-vault)
-4. [Launch Operator Service →](/operator/launch-operator-service#installation)
+2. [Create Vault](/operator/create-regular-vault)
+3. [Launch Operator Service](/operator/launch-operator-service#installation)
-:::custom-warning[Important - Keystore Password Configuration]
-When using the `create-keys` command, add the `--per-keystore-password` flag to generate a keystore with a separate password file for each keystore. This is necessary for the normal operation of the Charon CLI, especially when splitting keys.
+:::custom-notes[Hardware Requirements]
+Each node runs an execution and a consensus client, which need to sync the chain, so the disk, RAM, and CPU add up quickly, even on a testnet. Check Obol's [hardware specifications ↗](https://docs.obol.org/run-a-dv/prepare/deployment-best-practices#hardware-specifications) and size your machine(s) for the network you are validating.
:::
-## Step 1: Setup Obol Cluster
+The steps below cover both a solo operator running distributed validators across multiple machines and a group of operators who each run their own node in the cluster.
-[Charon ↗](https://docs.obol.org/learn/charon/intro) is Obol's implementation of DVT that acts as a middleware between validator clients and the beacon node, intercepting and proxying API traffic. It uses an event-driven, multi-component architecture where each component handles a specific task in the signing workflow—from scheduling duties and reaching consensus, to collecting partial signatures and aggregating them into valid beacon chain signatures.
+## Create a DV Alone
-### Create Environment Configuration
+It is possible for a single operator to manage all of the nodes of a DV cluster. The nodes can be run on a single machine, which is only suitable for testing, or the nodes can be run on multiple machines, which is expected for a production setup.
-First, create a `.env` file with Charon settings:
+### Step 1: Create the Cluster
-:::custom-notes[Address Format]
-Addresses must be in lowercase.
-:::
+The example below creates the key shares by splitting your existing [validator keys](/operator/validator-keys), which requires the full keys to exist on your machine. Alternatively, the shares can be created in a lower-trust manner through a DKG ceremony, which avoids the validator private key being stored in full anywhere, at any point in its lifecycle. Follow [Create a DV With a Group](#create-a-dv-with-a-group) for that case instead.
+
+You can also use Obol's [DV Launchpad ↗](https://launchpad.obol.org) to configure your DV cluster. The Launchpad will give you a docker command to create your cluster.
+
+1. Create a `.env` file with Charon settings, with addresses in lowercase:
```bash
cat < .env
@@ -46,43 +62,51 @@ export NETWORK=[ENTER YOUR NETWORK NAME]
EOF
```
-:::custom-notes[Finding Your Addresses]
-- **Vault Contract Address**: Found in the Vault page URL or Details section
-- **Block Reward Recipient**: Located in the "Block reward recipient" field on your Vault page settings
+:::custom-warning[StakeWise Addresses]
+Set `VAULT_CONTRACT_ADDR` to your **Vault contract address** and `FEE_RECIPIENT_ADDR` to your **Block reward recipient**. These become the cluster's `--withdrawal-addresses` and `--fee-recipient-addresses`.
:::
-
-### Create the Cluster
-
-Split your validator keys across multiple nodes:
+2. Split your validator keys across multiple nodes, replacing the example values for `--nodes` and `--name`:
```bash
source .env
-docker run --rm -v "$(pwd):/opt/charon" -v "$HOME/.stakewise:/.stakewise" obolnetwork/charon:v1.7.1 \
+docker run --rm -v "$(pwd):/opt/charon" -v "$HOME/.stakewise:/.stakewise" obolnetwork/charon:v1.10.2 \
create cluster \
--name="cluster-name" \
--cluster-dir=".charon/cluster/" \
--withdrawal-addresses=$VAULT_CONTRACT_ADDR \
--fee-recipient-addresses=$FEE_RECIPIENT_ADDR \
- --nodes 3 \
+ --nodes 4 \
--network $NETWORK \
--split-existing-keys \
--split-keys-dir /.stakewise/$VAULT_CONTRACT_ADDR/keystores
```
+:::custom-tips[Verify Cluster Creation]
+Check that your cluster was created successfully:
+```bash
+ls -la .charon/cluster/
+```
+You should see `node0/`, `node1/`, `node2/`, and `node3/` subdirectories.
+:::
+
:::custom-info[Command Flags Explained]
| Flag | Description |
|------|-------------|
| `--name` | The cluster name |
-| `--cluster-dir` | Parent directory containing `.charon` subdirectories from the required threshold of nodes (default `"./"`) |
-| `--withdrawal-addresses` | Comma separated list of Ethereum addresses to receive returned stake and rewards. Provide single address or one per validator |
-| `--fee-recipient-addresses` | Comma separated list of fee recipient addresses. Provide single address or one per validator. **Must match** the "Block reward recipient" address on your Vault page |
-| `--nodes` | Number of Charon nodes in the cluster. Minimum is 3 |
+| `--cluster-dir` | Target directory to write the cluster into; each node gets its own subfolder (default `"./"`) |
+| `--withdrawal-addresses` | Comma-separated list of Ethereum addresses to receive returned stake and rewards. Provide single address or one per validator |
+| `--fee-recipient-addresses` | Comma-separated list of fee recipient addresses. Provide single address or one per validator. **Must match** the "Block reward recipient" address on your Vault page |
+| `--nodes` | Number of Charon nodes in the cluster. Minimum is 4 |
| `--network` | Ethereum network. Options: mainnet, hoodi, gnosis (default "mainnet") |
| `--split-existing-keys` | Split existing validator private key into distributed key shares |
| `--split-keys-dir` | Directory containing keys to split. Expects `keystore-*.json` and `keystore-*.txt`. Requires `--split-existing-keys` |
+
+For the full list of optional flags, see the [Charon CLI reference ↗](https://docs.obol.org/learn/charon/charon-cli-reference#create-a-full-cluster-locally)
:::
+The docker command above produces a full set of distributed validators under `.charon/cluster/`, with one subdirectory per node. Each contains that node's **private key share**, a **`charon-enr-private-key`** (the node's networking identity), a **`cluster-lock.json`** that defines the cluster, and a **`deposit-data.json`** used to activate the validators.
+
Example Output
@@ -97,7 +121,7 @@ Created charon cluster:
--split-existing-keys=true
/opt/charon/.charon/cluster/
-├─ node[0-2]/ Directory for each node
+├─ node[0-3]/ Directory for each node
│ ├─ charon-enr-private-key Charon networking private key for node authentication
│ ├─ cluster-lock.json Cluster lock file signed by all nodes
│ ├─ deposit-data.json Deposit data to activate a Distributed Validator
@@ -108,23 +132,19 @@ Created charon cluster:
-:::custom-tips[Verify Cluster Creation]
-Check that your cluster was created successfully:
-```bash
-ls -la .charon/cluster/
-```
-You should see `node0/`, `node1/`, and `node2/` directories.
+:::custom-warning[Protect Your Key Shares]
+- **Keep them secret:** anyone with access to the files in `.charon/cluster/` can get your validators slashed.
+- **Back them up:** store a secure copy of `.charon/cluster/`. If you lose these files, you cannot recover the validators.
:::
-:::custom-warning[Backup Required]
-Backup the `./.charon` folder before proceeding to deployment.
-:::
+### Step 2: Run the Nodes
-## Step 2: Run Nodes, Validators and Charon Client
+
+
-This `docker-compose.yml` launches a full Obol/Charon distributed validator stack for Ethereum mainnet,
-consisting of Geth (execution client), Lighthouse (consensus client), MEV-boost, Charon (relay + 3 nodes), and validator clients (3x Teku: one per Charon node) (vc0/vc1/vc2).
-All services run on a shared cluster Docker network and are wired so that validator clients talk to Charon, and Charon talks to Lighthouse and Geth.
+Running the whole cluster on one machine is suitable for testing only. If that machine fails, the cluster fails too.
+
+1. Copy the `docker-compose.yml` file below to the same directory as your `.charon` folder and update the `--validators-proposer-default-fee-recipient` flag for each validator client with your Block reward recipient address:
`docker-compose.yml`
@@ -146,7 +166,7 @@ x-node-base:
# Pegged charon version (update this for each release).
&node-base
# Main Charon image (version can be overridden via CHARON_VERSION env var)
- image: obolnetwork/charon:${CHARON_VERSION:-v1.7.1}
+ image: obolnetwork/charon:${CHARON_VERSION:-v1.10.2}
restart: unless-stopped
networks: [ cluster ]
depends_on: [ relay ]
@@ -206,7 +226,7 @@ services:
networks: [ cluster ]
lighthouse:
- image: sigp/lighthouse:v8.0.1
+ image: sigp/lighthouse:v8.1.3
restart: always
command: >
lighthouse
@@ -250,7 +270,7 @@ services:
volumes:
- ./data/relay:/opt/charon/relay:rw
- # CHARON NODES: 3-node distributed validator cluster (node0, node1, node2)
+ # CHARON NODES: 4-node distributed validator cluster (node0, node1, node2, node3)
node0:
<<: *node-base
environment:
@@ -275,7 +295,15 @@ services:
CHARON_LOCK_FILE: /opt/charon/.charon/cluster/node2/cluster-lock.json
CHARON_P2P_EXTERNAL_HOSTNAME: node2
- # TEKU VALIDATOR CLIENTS: one VC per Charon node (vc0, vc1, vc2)
+ node3:
+ <<: *node-base
+ environment:
+ <<: *node-env
+ CHARON_PRIVATE_KEY_FILE: /opt/charon/.charon/cluster/node3/charon-enr-private-key
+ CHARON_LOCK_FILE: /opt/charon/.charon/cluster/node3/cluster-lock.json
+ CHARON_P2P_EXTERNAL_HOSTNAME: node3
+
+ # TEKU VALIDATOR CLIENTS: one VC per Charon node (vc0, vc1, vc2, vc3)
#
# Each Teku instance:
# - connects to its local Charon node’s validator API
@@ -330,20 +358,171 @@ services:
volumes:
- .charon/cluster/node2/validator_keys:/opt/charon/validator_keys
- ./data/vc2:/opt/charon/teku
+
+ vc3-teku:
+ image: consensys/teku:${TEKU_VERSION:-25.11.0}
+ networks: [ cluster ]
+ depends_on: [ node3 ]
+ restart: unless-stopped
+ command: |
+ validator-client
+ --network=auto
+ --beacon-node-api-endpoint="http://node3:3600"
+ --validators-proposer-default-fee-recipient=[ENTER YOUR BLOCK REWARD RECIPIENT ADDRESS HERE]
+ --validators-builder-registration-default-enabled=true
+ --validator-keys="/opt/charon/validator_keys:/opt/charon/validator_keys"
+ --validators-keystore-locking-enabled=false
+ volumes:
+ - .charon/cluster/node3/validator_keys:/opt/charon/validator_keys
+ - ./data/vc3:/opt/charon/teku
```
-:::custom-warning[Block Reward Recipient Address]
-Replace `[ENTER YOUR BLOCK REWARD RECIPIENT ADDRESS HERE]` inside `docker-compose.yml` file with your Block reward recipient address.
+The `docker-compose.yml` launches a full Obol/Charon distributed validator stack for Ethereum mainnet, consisting of Geth (execution client), Lighthouse (consensus client), MEV-Boost, Charon (relay + 4 nodes), and validator clients (4× Teku, one per Charon node: vc0/vc1/vc2/vc3).
+All services run on a shared cluster Docker network and are wired so that validator clients talk to Charon, and Charon talks to Lighthouse and Geth.
+
+2. Start the distributed validator cluster:
+
+```bash
+docker compose up -d
+```
+
+
+
+
+For production fault tolerance, run each node on its own machine, so the cluster keeps signing even if one machine fails.
+
+Step 1 produced one folder per node under `.charon/cluster/` (`node0/`, `node1/`, `node2/`, `node3/`), each holding that node's key share, networking key, and cluster lock file. Give each node folder to a different machine:
+
+1. On each machine, clone Obol's [charon-distributed-validator-node ↗](https://github.com/ObolNetwork/charon-distributed-validator-node) (CDVN) repository. Its `docker-compose.yml` runs a single node.
+2. Copy one node's folder into the clone, renamed from `nodeX/` to `.charon` (the directory CDVN expects). For example, machine 1 gets `node0/` → `.charon`, machine 2 gets `node1/` → `.charon`.
+3. On that machine, set your network in the repo's `.env` and run `docker compose up -d`.
+
+You don't set the Vault or fee recipient here; they're baked into the cluster files from Step 1, and Charon applies them automatically. The nodes discover each other through the relay and operate together as one distributed validator.
+
+
+
+
+Once the nodes are running, the validators still need to be registered with your Vault. Because the solo flow splits existing keys, the full keystores remain available to the Operator Service, which [registers the validators](/docs/vaults/how-vaults-work#validator-registration) itself, as it does for non-DVT validators. The DVT Sidecar and Relayer described below are only needed when the keys are generated through a DKG ceremony, where no party holds the full key.
+
+## Create a DV With a Group
+
+A DV cluster consists of multiple operators each provided with one of the M-of-N threshold BLS private key shares per validator. These shares are created together through a DKG ceremony.
+
+
+
+### Step 1: Create the Cluster via DKG
+
+The operators create the validator key shares together through a DKG ceremony, so the full key is never assembled on any machine. Follow Obol's [Create a DV with a group ↗](https://docs.obol.org/run-a-dv/start/create-a-dv-with-a-group) guide. It walks each operator through generating an ENR, building the `cluster-definition.json`, running the ceremony, and starting their node.
+
+:::custom-warning[StakeWise Addresses]
+When creating the cluster definition, set the `--withdrawal-addresses` to your **Vault contract address** and the `--fee-recipient-addresses` to your **Block reward recipient**.
:::
-1. Copy the `docker-compose.yml` file below to the same directory as your `.charon` folder
-2. Update the fee recipient address
-3. Run `docker compose up -d`
+After the ceremony, each operator's `.charon/` holds their `validator_keys/` (key shares), `cluster-lock.json`, and `deposit-data.json`.
+
+### Step 2: Run the DVT Sidecar
+
+Each operator runs one [DVT Sidecar ↗](https://github.com/stakewise/dvt-operator-sidecar) on the machine running their node. It loads the node's key shares, polls the Relayer for validator data, and submits deposit and exit signature shares back to it.
-You can run all components on a single server or distribute across multiple servers.
+1. Create the `.env` file from the repository's template:
-##
Additional Resources
+```bash
+cp .env.example .env
+```
+
+2. Set the values for your Obol node:
+
+```ini
+# Network: mainnet or hoodi
+NETWORK=mainnet
+
+# DVT cluster type
+CLUSTER_TYPE=OBOL
+
+# URL of your DVT Relayer
+RELAYER_ENDPOINT=http://relayer:8000
+
+# Directory with this node's key shares (keystore-*.json + keystore-*.txt)
+OBOL_KEYSTORES_DIR=validator_keys
+# The cluster-lock.json from the DKG ceremony
+OBOL_CLUSTER_LOCK_FILE=cluster-lock.json
+# This node's index in the cluster (0-based), used to select its share from the cluster lock
+OBOL_NODE_INDEX=0
+
+# Your execution and consensus client endpoints
+EXECUTION_ENDPOINT=http://execution:8545
+CONSENSUS_ENDPOINT=http://consensus:5052
+```
+
+3. Run the container:
+
+```bash
+docker run \
+ -u $(id -u):$(id -g) \
+ --env-file .env \
+ -v $(pwd)/data:/data \
+ europe-west4-docker.pkg.dev/stakewiselabs/public/dvt-operator-sidecar:v2.0.0
+```
+
+### Step 3: Run the DVT Relayer
+
+One [DVT Relayer ↗](https://github.com/stakewise/dvt-relayer) serves the whole cluster. It collects the Sidecars' signature shares, reconstructs the full signatures, and serves the registration data to the Operator Service. It holds the Vault's Validators Manager wallet to sign registrations, but never has access to the validator keystores.
+
+1. Create the `.env` from the repository's template:
+
+```bash
+cp .env.example .env
+```
+
+2. In a `data/` directory, place the files the Relayer reads:
+
+ - `validators-manager-key.json` and `validators-manager-password.txt`: the wallet set as your Vault's [Validators Manager](/operator/validators-manager).
+ - `public_keys.txt`: the cluster's validator public keys, one per line.
+
+3. Set the values (file paths point inside the mounted `/data`):
+
+```ini
+# API server
+RELAYER_HOST=0.0.0.0
+RELAYER_PORT=8000
+
+# BLS signature threshold — must match your cluster's threshold
+SIGNATURE_THRESHOLD=3
+
+# Network: mainnet, hoodi, or gnosis
+NETWORK=mainnet
+
+# Execution and consensus client endpoints
+EXECUTION_ENDPOINT=https://execution
+CONSENSUS_ENDPOINT=https://consensus
+
+# Validator public keys to register, one per line
+PUBLIC_KEYS_FILE=/data/public_keys.txt
+
+# The Validators Manager wallet that signs registrations
+VALIDATORS_MANAGER_KEY_FILE=/data/validators-manager-key.json
+VALIDATORS_MANAGER_PASSWORD_FILE=/data/validators-manager-password.txt
+```
+
+4. Pull and run the Relayer:
+
+```bash
+export DVT_RELAYER_VERSION=v1.0.0
+docker run --rm -ti \
+ --env-file .env \
+ -v $(pwd)/data:/data \
+ -p 8000:8000 \
+ europe-west4-docker.pkg.dev/stakewiselabs/public/dvt-relayer:$DVT_RELAYER_VERSION
+```
+
+### Step 4: Start the Operator Service
+
+Because the Operator Service has no keystores, start it in Relayer mode pointing at your DVT Relayer:
+
+```bash
+./operator start-relayer
+```
-For more information, refer to the [Obol Documentation ↗](https://docs.obol.org/next/run-a-dv/start/create-a-dv-with-a-group).
+If you run the nodes with Obol's CDVN setup (the solo production tab above), it ships a local Grafana dashboard. Check your nodes' health at `http://localhost:3000/d/charonoverview/`. For hosted dashboards and alerting in any setup, see [Obol monitoring ↗](https://docs.obol.org/run-a-dv/start/obol-monitoring).
diff --git a/operator/alternative-key-management/dvt/overview.mdx b/operator/alternative-key-management/dvt/overview.mdx
index a10f7e1c..d4fa49ce 100644
--- a/operator/alternative-key-management/dvt/overview.mdx
+++ b/operator/alternative-key-management/dvt/overview.mdx
@@ -9,30 +9,39 @@ import Image from '@theme/IdealImage'
Distributed Validator Technology (DVT) is a security mechanism that distributes validator key management and signing operations across multiple participants. This eliminates single points of failure and increases validator resilience.
-Validators have a single public-private key pair (the validator key) for consensus participation (block proposals, attestations) and a withdrawal address that determines where staked funds are sent upon exit. Validator keys must stay online continuously, making them vulnerable to compromise.
+StakeWise Vaults support both [Obol ↗](https://obol.org) and [SSV ↗](https://docs.ssv.network/) DVT technologies, offering an additional layer of security and decentralization.
-DVT addresses this vulnerability by encrypting and splitting the validator key into shares distributed across multiple nodes. Stakers can keep the original key in cold storage while the network operates using these shares. A threshold number of shares (e.g., 3 out of 4) can collectively produce valid signatures, meaning one node can fail without disrupting validator operations. The system relies on distributed key generation, threshold signature schemes, and multiparty computation to ensure no single node ever possesses the complete key.
-
-StakeWise Vaults support both [Obol ↗](https://obol.org) and [SSV ↗](https://docs.ssv.network/) DVT technologies, offering an additional layer of security and decentralization. This gives operators flexibility in how they secure their validators.
+## How DVT Works
-## DVT setup components
+Validators have a single public-private key pair (the validator key) for consensus participation (block proposals, attestations) and a withdrawal address that determines where staked funds are sent upon exit. The validator key must stay online continuously, so running it on a single node is a single point of failure: if that node goes offline, the validator stops performing its duties.
+
+DVT addresses this vulnerability by distributing the validator key across multiple nodes as a group of BLS private keys that together operate as a threshold key for participating in proof-of-stake consensus. Each node signs with its own key share, producing a signature share. A threshold number of shares (e.g., 3 out of 4) can collectively produce valid signatures, so the validator keeps signing even if some nodes go offline.
+
+## DVT Setup Components
+
+A DVT setup is made up of several components that work together:
+
+- **DVT cluster**: A group of connected nodes that together operate distributed validators (DVs).
+- [Operator Service ↗](https://github.com/stakewise/v3-operator): Registers DVs in the Vault. It has no access to the DVs' keystores.
+- [DVT Sidecar ↗](https://github.com/stakewise/dvt-operator-sidecar): Runs on each node with access to its key share. It retrieves validator data from the Relayer and submits the node's deposit and exit signature shares back to it.
+- [DVT Relayer ↗](https://github.com/stakewise/dvt-relayer): Collects deposit and exit signature shares from the Sidecars, reconstructs the full signatures, and serves the registration data to the Operator Service through its API.
+
+## Validator Registration Process
-- **DVT cluster of distributed validators** — Performs validator duties such as attestation, block proposal, and consensus participation.
-- **[StakeWise Operator →](/operator/launch-operator-service)** — Registers validators in the Vault contract. The Operator does not have access to validator keystores.
-- **[DVT Relayer →](/operator/alternative-key-management/api-mode)** — Collects deposit and exit signatures from validators (via Sidecars) and forwards them to oracles.
-- **DVT Sidecars** — Provide deposit and exit signature shares to the DVT Relayer. Each distributed validator node should run a DVT Sidecar instance with access to the validator key shares on the related DVT node.
+
-## Validator registration procedure
+During registration, the DVT Relayer coordinates the operators' Sidecars and collects their signature shares.
-1. The Operator Service determines the start validator index, calculates the list of amounts to deposit, and sends them to the Relayer.
-2. The Operator polls the Relayer for the creation and status of the corresponding **pending validators**.
-3. The Relayer creates these pending validators and exposes them via the API to Sidecars.
-4. The DVT Sidecars poll the Relayer for pending validators that are awaiting signatures, create deposit and exit signatures for those validators, and push the signatures back to the Relayer.
-5. The Relayer processes deposit signatures and exit signatures:
+1. The Operator Service determines the starting validator index and deposit amounts and sends a **registration request** to the Relayer.
+2. The Relayer creates the corresponding **pending validators** and exposes them via its API to the Sidecars.
+3. The Sidecars poll the Relayer for pending validators that are awaiting signatures, create **deposit and exit signature shares** for those validators, and push the shares back to the Relayer.
+4. The Relayer processes the signature shares:
- - Receives signature shares from the Sidecars.
- - Restores full signatures from Sidecar shares and verifies the restored signatures.
- - Creates shares for oracles and encrypts them with oracles' public keys. It then fills signatures in pending validators. The encrypted shares for oracles are available through the Relayer API.
-6. The Operator fetches validators from the Relayer that have all required signatures and are ready for registration, and then registers those validators in the Vault contract.
+ - Receives the signature shares from the Sidecars.
+ - Restores the full **deposit and exit signatures** from the Sidecar shares and verifies the restored signatures.
+ - For the exit signature, splits that full signature into a new set of shares — one per Oracle — and encrypts each with that Oracle's public key; the raw exit signature is never exposed. The deposit signature is kept whole. It then fills in the signatures on the pending validators, and the **encrypted Oracle shares** are available through the Relayer API.
+5. The Operator Service repeats the registration request each block, polling until the Relayer has reconstructed every validator's signatures and returned the **Validators Manager signature** that authorizes the registration.
+6. The Operator Service requests approval from the StakeWise Oracles, forwarding the validators and their encrypted Oracle shares. The Oracles validate the data and return a combined **approval** once enough of them vote.
+7. The Operator Service submits the registration to the Vault contract, providing both authorizations: the **Oracles' approval** and the **Validators Manager signature**. The Vault deposits the stake, and the validators join the Beacon Chain's activation queue. Each becomes active once its deposit is processed, which can take a while depending on how many deposits are ahead of it in the queue.
diff --git a/operator/alternative-key-management/dvt/ssv-setup.mdx b/operator/alternative-key-management/dvt/ssv-setup.mdx
index 1db9e6ce..f61d98e1 100644
--- a/operator/alternative-key-management/dvt/ssv-setup.mdx
+++ b/operator/alternative-key-management/dvt/ssv-setup.mdx
@@ -24,8 +24,8 @@ Before proceeding, ensure you have the following:
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
```
-3. [Create Vault →](/operator/create-regular-vault)
-4. Operator Service [installed →](/operator/launch-operator-service#installation)
+3. [Create Vault](/operator/create-regular-vault)
+4. Operator Service [installed](/operator/launch-operator-service#installation)
:::custom-notes[Hardware Requirements]
SSV node requires (in addition to your beacon/execution nodes): **2 cores**, **2GB RAM**, **20GB storage**, **>8K IOPS**. See [full requirements ↗](https://docs.ssv.network/operators/operator-node/node-setup/hardware-requirements).