Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ pages = [
"run/run-a-generator/install/index.md",
"run/run-a-generator/register/index.md",
"run/run-a-generator/config-reference/index.md",
"run/run-a-generator/security/index.md",
]
subsections = [
{ label = "Deployment Examples", pages = [
Expand Down
4 changes: 4 additions & 0 deletions content/reference/components/generator-client/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,10 @@ Bootstrap peers are configured in the DefraDB config. Peers are also discovered

See the [hardware requirements page](/run/run-a-generator/hardware-requirements/) for current minimum and recommended specs.

## Security

The [Security](/run/run-a-generator/security/) page covers deployment security: key separation from the validator, the write-only P2P model, and which ports to expose versus keep private.

## Configuration

```plaintext
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ The Generator and Geth share a single machine. The Generator connects to Geth ov

- Docker installed on the validator machine.
- Geth running and synced, with HTTP and WebSocket enabled on ports 8545 and 8546. See the [Geth documentation](https://geth.ethereum.org/docs/fundamentals/security) for configuration.
- Ports 9171, 9181, and 8080 available on the validator machine. See [exposed ports](/run/run-a-generator/install#exposed-ports) for details.
- Ports 9171, 9181, and 8080 available on the validator machine. See [exposed ports](/run/run-a-generator/install#exposed-ports) for details, and [Security](/run/run-a-generator/security/) for which of these to publish and which to keep private.

## Run the Generator

Expand All @@ -48,7 +48,7 @@ docker run -d \
-e DEFRADB_P2P_ENABLED=true \
-e DEFRADB_P2P_LISTEN_ADDR=/ip4/0.0.0.0/tcp/9171 \
-e LOGGER_DEBUG=false \
-p 9181:9181 \
-p 127.0.0.1:9181:9181 \
-p 9171:9171 \
-p 8080:8080 \
ghcr.io/shinzonetwork/shinzo-generator-client:ethereum-mainnet-latest
Expand Down
14 changes: 7 additions & 7 deletions content/run/run-a-generator/install/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ You do not need to be a validator, or to run a validator, just to install and ru
-e DEFRADB_P2P_ENABLED=true \
-e DEFRADB_P2P_LISTEN_ADDR=/ip4/0.0.0.0/tcp/9171 \
-e LOGGER_DEBUG=true \
-p 9181:9181 \
-p 127.0.0.1:9181:9181 \
-p 9171:9171 \
-p 8080:8080 \
ghcr.io/shinzonetwork/shinzo-generator-client:ethereum-mainnet-latest
Expand Down Expand Up @@ -174,13 +174,13 @@ Set `GETH_API_KEY_TYPE` to the header name your provider expects.

## Exposed ports

The following ports must be exposed and available on the machine.
The following ports must be available on the machine. Not all of them should be published to the network. See [Security](../security/) for the full exposure rules.

| Port | Service |
| --- | --- |
| `8080` | Health endpoint (`/health`), metrics (`/metrics`), and registration (`/registration`). |
| `9171` | DefraDB P2P. |
| `9181` | DefraDB GraphQL API. |
| Port | Service | Publish publicly? |
| --- | --- | --- |
| `8080` | Health (`/health`), metrics (`/metrics`), registration (`/registration`). | No. Keep private, or put behind a reverse-proxy allowlist in production. |
| `9171` | DefraDB P2P. | Yes. This is how Hosts receive data. |
| `9181` | DefraDB GraphQL API. | No. Localhost only. Raw, unauthenticated read/write access to the local database. |

## Troubleshooting

Expand Down
94 changes: 94 additions & 0 deletions content/run/run-a-generator/security/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
+++
title = "Security"
description = "Security guidance for placing the Generator client relative to your validator: same-machine vs separate-machine deployments, key separation, and port exposure."
aliases = ["/generator/security", "/generators/security"]
[extra]
mermaid = true
+++

The Generator client runs as a sidecar next to an execution node, not as part of your validator's consensus process. This page covers the security implications of the two deployment shapes operators ask about most: running the Generator on the same machine as your validator, and running it on a separate machine connected over a network.

## Same-machine deployment

Running the Generator client on the same machine as your validator (and its execution node) is the default shape and is safe. The Generator client is a separate process with its own key, its own resources, and a one-way relationship with the execution node. It does not touch consensus.

### Key separation

The Generator client generates and stores its own operator (delegate) key locally. It never reads, writes, or holds your validator's consensus key or withdrawal key:

| Key | Held by | Used for |
| --- | --- | --- |
| Operator / delegate key | Generator client (local keyring) | Signing block batches, registering on ShinzoHub. |
| Consensus key | Validator client | Signing blocks and participating in consensus. |
| Withdrawal key | Browser wallet (off-machine) | Signing the one-time assertion that links the operator key to your validator during [registration](../register). |

The withdrawal key is used exactly once, in a browser wallet, to sign the assertion that ties the operator key to the validator's identity. After that, the Generator client runs on the operator key alone. See [Registration](../register) and the [architecture reference](/reference/architecture/#generator-registration) for the full flow.

Because no key material is shared, a compromise of the Generator client's operator key does not expose your validator's consensus or withdrawal keys.

### Reject inbound replication

The Generator client is a write-only data producer. It reads blocks from the execution node and publishes signed documents to Hosts over P2P, but it rejects all inbound P2P replication. This is enforced by a replication filter in `pkg/generator/replication_filter.go` and by `defradb.p2p.accept_incoming`, which defaults to `false`. The Generator client never accepts documents from peers, so even a peer that connects to it cannot push data into its database.

See the [Generator client reference](/reference/components/generator-client/#p2p-data-distribution) for details.

### Independent resource budget

The Generator client is a lightweight sidecar: a ~50 MB binary with its own CPU and memory budget (4-8 CPU / 8-16 GB RAM for verifiable indexing of Ethereum Mainnet). It is sized independently of the execution node and the validator, so its load does not compete with consensus for resources when you run it as a separate process with its own limits. See [hardware requirements](../hardware-requirements/) for sizing.

For a worked same-machine example, see the [Validator with Geth](../deployment-examples/validator-with-geth/) deployment guide.

## Separate-machine deployment

When the Generator client and the execution node (or validator) run on different machines, the question becomes which ports to expose across the network boundary. The rule of thumb: expose the P2P port, restrict the management API behind a reverse proxy, and keep the raw database API private.

### Topology

{% mermaid() %}
flowchart LR
subgraph Exec["Execution node machine"]
Node["<b>Execution node</b><br/>:8545 JSON-RPC<br/>:8546 WebSocket"]
end

subgraph GenVM["Generator machine"]
direction TB
Nginx["<b>Reverse proxy</b><br/>:443 TLS<br/>path allowlist"]
Gen["<b>Generator client</b><br/>:9171 P2P<br/>:8080 health/metrics/registration<br/>:9181 DefraDB API (localhost)"]
Nginx -- "safe paths only" --> Gen
end

Hosts["Hosts"]
Ops["Operator / monitoring"]

Node -- "RPC + WS<br/>VPC, restricted to Gen IP" --> Gen
Gen -- "P2P (libp2p)<br/>:9171 public" --> Hosts
Ops -- "HTTPS<br/>/health /metrics /snapshots" --> Nginx
{% end %}

The execution node feeds the Generator client over a restricted private link. The Generator client publishes to Host clients over P2P on `9171`. Operators and monitoring reach only the safe management paths through a reverse proxy on `443`. The raw DefraDB API on `9181` stays bound to localhost and never crosses the firewall.

### Port exposure

| Port | Service | Expose publicly? | Recommendation |
| --- | --- | --- | --- |
| `9171` | DefraDB P2P (libp2p) | Yes | Open on the firewall. This is how Hosts subscribe and receive data. |
| `8080` | Health, metrics, registration, schema | Only behind a reverse proxy | Do not publish raw. Put an nginx (or equivalent) allowlist in front that proxies only the safe paths (`/health`, `/registration`, `/registration-app`, `/metrics`, `/snapshots`, `/api/v1/schema`) and returns 404 for everything else. See the [nginx with snapshots](../deployment-examples/nginx-with-snapshots/) example for a working config. |
| `9181` | DefraDB GraphQL / REST API | No | Bind to localhost or a private network. This port gives raw, unauthenticated read/write access to the local DefraDB database. Publishing it to `0.0.0.0` lets anyone read or mutate the Generator's data. |

The shipped production tooling (`docker-compose-prod.yml` and `indexer-prod-setup.sh` in the `shinzo-generator-client` repo) follows this pattern: it publishes `9171`, fronts `8080` with an nginx allowlist, and never publishes `9181`.

### How this differs from a Host

Host clients intentionally expose `9181` because serving GraphQL queries to subscribers is their job. Generator clients only produce data and have no reason to answer external queries, so their `9181` should stay closed. The two roles have opposite exposure profiles on the same port.

### Execution-node side

On the execution node side of the link, restrict its JSON-RPC and WebSocket ports (commonly `8545` and `8546`) to the Generator's IP via a private network or VPC firewall. The Generator client only reads from the node; it never writes to it. If the connection must cross a public boundary, authenticate it with an API key or a reverse proxy rather than exposing the node unauthenticated. See the [install page's API key guidance](../install/#do-you-need-an-api-key) for the header configuration.

### Schema endpoint auth

The shipped production scripts set `SCHEMA_AUTH_MODE=none`, which disables authentication on the `/api/v1/schema` endpoints. This is acceptable when `8080` is already behind a reverse-proxy allowlist that only proxies known-safe paths. If you expose schema management more broadly, switch `SCHEMA_AUTH_MODE` to `token` and provide accepted tokens via `SCHEMA_API_KEYS`. See the [config reference](../config-reference/#indexer) for the full set of values.

## Need help

{{ need_help(client="Generator", repo_name="shinzo-generator-client", repo="https://github.com/shinzonetwork/shinzo-generator-client/issues") }}
Loading