# How the Execution Client Communicates with the Op-Node in Base

> Discover how the execution client communicates with the op-node in Base using the authenticated Engine JSON-RPC API and a JWT secret on port 8551 for secure data exchange.

- Repository: [Base/node](https://github.com/base/node)
- Tags: internals
- Published: 2026-03-03

---

**The execution client and op-node communicate through the authenticated Engine JSON-RPC API, sharing a JWT secret to establish a protected channel on port 8551.**

In the Base network architecture, the execution client (such as Geth, Reth, or Nethermind) and the consensus-layer op-node coordinate block execution and state synchronization through a standardized protocol. This analysis examines the `base/node` repository implementation, detailing exactly how the execution client communicates with the op-node using the Engine API with JWT authentication.

## Engine API Architecture

The communication protocol relies on the **Engine JSON-RPC API**, a set of methods defined in the Optimism specification that allows the op-node to drive block execution. The execution client exposes this API on an authenticated HTTP endpoint, typically port **8551**, supporting methods such as `engine_newPayloadV1` and `engine_forkchoiceUpdatedV1`.

According to the source code in `base/node`, the execution client does not initiate connections to the op-node. Instead, the op-node acts as the client, sending requests to the execution client's Engine endpoint to submit L2 blocks and query execution state. The execution client responds with status codes and state data, creating a unidirectional control flow driven by the op-node.

## JWT Authentication Setup

Security between the two components relies on **shared JWT (JSON Web Token) authentication**. Both services must use the identical JWT secret to validate requests, preventing unauthorized access to the Engine API.

### Execution Client Configuration

The execution client entrypoint script generates and configures the JWT secret before starting the client binary. In `geth/geth-entrypoint`, the script writes the raw JWT secret to a file specified by the environment variable `OP_NODE_L2_ENGINE_AUTH`, then passes this file to Geth via the `--authrpc.jwtsecret` flag.

```bash
echo "$OP_NODE_L2_ENGINE_AUTH_RAW" > "$OP_NODE_L2_ENGINE_AUTH"

exec ./geth \
    --authrpc.jwtsecret="$OP_NODE_L2_ENGINE_AUTH" \
    --authrpc.port=8551 \
    --http.api=web3,debug,eth,net,engine \
    ...

```

This configuration ensures Geth listens on port 8551 and requires valid JWT tokens for all Engine API requests.

### Op-Node Configuration

Before launching the op-node binary, the `op-node-entrypoint` script performs two critical steps. First, it waits for the execution client's Engine endpoint to become reachable. Second, it writes the same raw JWT secret to the shared file path used by the execution client.

```bash
until [ "$(curl -s --max-time 10 --connect-timeout 5 \
        -w '%{http_code}' -o /dev/null "${OP_NODE_L2_ENGINE_RPC/ws/http}")" -eq 401 ]; do
  echo "waiting for execution client to be ready"
  sleep 5
done

echo "$OP_NODE_L2_ENGINE_AUTH_RAW" > "$OP_NODE_L2_ENGINE_AUTH"
exec ./op-node

```

The op-node binary reads this JWT file and includes the secret in the authorization headers of its Engine API calls.

## Readiness Detection and Handshake

The orchestration scripts implement a specific **health-check mechanism** to ensure proper startup sequencing. The op-node entrypoint repeatedly probes the execution client's Engine RPC URL (converted from WebSocket to HTTP protocol) until it receives an **HTTP 401** response.

A 401 status indicates the execution client is online and accepting connections, but rejecting unauthenticated requests. This confirms the Engine API is ready to receive authenticated calls from the op-node, preventing race conditions during container startup.

## Docker Compose Orchestration

The [`docker-compose.yml`](https://github.com/base/node/blob/main/docker-compose.yml) file in `base/node` defines two distinct services that implement this architecture:

- **`execution`**: Runs the execution client entrypoint, exposing port 8551 for the Engine API
- **`node`**: Runs the op-node entrypoint, configured to connect to the execution service's Engine endpoint

The services share the JWT secret through environment variables (`OP_NODE_L2_ENGINE_AUTH` and `OP_NODE_L2_ENGINE_AUTH_RAW`) and volume mounts, ensuring both containers access the same authentication credentials without hardcoding secrets into the images.

## Summary

- The execution client and op-node communicate via the **Engine JSON-RPC API** on port 8551, using methods like `engine_newPayloadV1`.
- **JWT authentication** protects the Engine endpoint, with both services sharing the same secret written to `OP_NODE_L2_ENGINE_AUTH`.
- The execution client (configured in `geth/geth-entrypoint`) exposes the authenticated API using the `--authrpc.jwtsecret` flag.
- The op-node (configured in `op-node-entrypoint`) waits for HTTP 401 responses to confirm readiness, then authenticates requests using the shared JWT.
- Docker Compose orchestrates the two services, ensuring proper secret sharing and network connectivity.

## Frequently Asked Questions

### What API specification does Base use for execution client communication?

Base implements the **Engine API** defined in the Optimism specification, which extends the Ethereum execution-layer consensus protocol. This API includes methods such as `engine_newPayloadV1` and `engine_forkchoiceUpdatedV1`, allowing the op-node to instruct the execution client to process L2 blocks and update the fork choice state.

### Why does the op-node entrypoint wait for an HTTP 401 response?

The entrypoint script waits for HTTP 401 (Unauthorized) because this status confirms the execution client's Engine API is **actively listening and requiring authentication**. A connection refusal or timeout would indicate the client hasn't finished initializing, while a 401 proves the service is ready to accept properly authenticated requests from the op-node.

### Can the op-node communicate with execution clients other than Geth?

Yes, the architecture supports any execution client that implements the **Optimism Engine API**, including Reth, Nethermind, or Erigon. The `base/node` repository provides the Geth implementation as the default, but the JWT authentication pattern and Engine API interface remain consistent across compatible clients, provided they expose the standard RPC methods on the authenticated endpoint.

### Where is the JWT secret stored in the Base node setup?

The JWT secret is stored in a file path specified by the `OP_NODE_L2_ENGINE_AUTH` environment variable, typically located at `/jwtsecret` or a similar path inside the container. Both the execution client and op-node entrypoint scripts write the raw secret from `OP_NODE_L2_ENGINE_AUTH_RAW` to this file before starting their respective binaries, ensuring both services use identical authentication tokens.