chore: initial commit for v0.0.1
DChain single-node blockchain + React Native messenger client. Core: - PBFT consensus with multi-sig validator admission + equivocation slashing - BadgerDB + schema migration scaffold (CurrentSchemaVersion=0) - libp2p gossipsub (tx/v1, blocks/v1, relay/v1, version/v1) - Native Go contracts (username_registry) alongside WASM (wazero) - WebSocket gateway with topic-based fanout + Ed25519-nonce auth - Relay mailbox with NaCl envelope encryption (X25519 + Ed25519) - Prometheus /metrics, per-IP rate limit, body-size cap Deployment: - Single-node compose (deploy/single/) with Caddy TLS + optional Prometheus - 3-node dev compose (docker-compose.yml) with mocked internet topology - 3-validator prod compose (deploy/prod/) for federation - Auto-update from Gitea via /api/update-check + systemd timer - Build-time version injection (ldflags → node --version) - UI / Swagger toggle flags (DCHAIN_DISABLE_UI, DCHAIN_DISABLE_SWAGGER) Client (client-app/): - Expo / React Native / NativeWind - E2E NaCl encryption, typing indicator, contact requests - Auto-discovery of canonical contracts, chain_id aware, WS reconnect on node switch Documentation: - README.md, CHANGELOG.md, CONTEXT.md - deploy/single/README.md with 6 operator scenarios - deploy/UPDATE_STRATEGY.md with 4-layer forward-compat design - docs/contracts/*.md per contract
This commit is contained in:
63
deploy/prod/Dockerfile.slim
Normal file
63
deploy/prod/Dockerfile.slim
Normal file
@@ -0,0 +1,63 @@
|
||||
# Production image for dchain-node.
|
||||
#
|
||||
# Differs from the repo-root Dockerfile in two ways:
|
||||
# 1. No testdata / contract WASMs baked in — a fresh node uses the native
|
||||
# username_registry (shipped in-binary) and starts with an empty keys
|
||||
# directory; identities and optional WASM contracts come in via
|
||||
# mounted volumes or docker-compose bind mounts.
|
||||
# 2. Builds only `node` and `client` — no wallet/peerid helpers that
|
||||
# aren't needed in production.
|
||||
#
|
||||
# The resulting image is ~20 MB vs ~60 MB for the dev one, and has no
|
||||
# pre-installed keys that an attacker could exploit to impersonate a
|
||||
# testnet validator.
|
||||
|
||||
# ---- build stage ----
|
||||
FROM golang:1.24-alpine AS builder
|
||||
WORKDIR /app
|
||||
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
COPY . .
|
||||
|
||||
# Build-time version metadata. All four args are injected via -ldflags -X
|
||||
# into go-blockchain/node/version so `node --version` and
|
||||
# /api/well-known-version report the real commit, not the "dev" default.
|
||||
# Callers pass these with `docker build --build-arg VERSION_TAG=... …`;
|
||||
# the deploy/single/update.sh script derives them from git automatically.
|
||||
ARG VERSION_TAG=dev
|
||||
ARG VERSION_COMMIT=none
|
||||
ARG VERSION_DATE=unknown
|
||||
ARG VERSION_DIRTY=false
|
||||
|
||||
RUN LDFLAGS="-s -w \
|
||||
-X go-blockchain/node/version.Tag=${VERSION_TAG} \
|
||||
-X go-blockchain/node/version.Commit=${VERSION_COMMIT} \
|
||||
-X go-blockchain/node/version.Date=${VERSION_DATE} \
|
||||
-X go-blockchain/node/version.Dirty=${VERSION_DIRTY}" && \
|
||||
CGO_ENABLED=0 GOOS=linux go build -trimpath -ldflags="$LDFLAGS" -o /bin/node ./cmd/node && \
|
||||
CGO_ENABLED=0 GOOS=linux go build -trimpath -ldflags="$LDFLAGS" -o /bin/client ./cmd/client
|
||||
|
||||
# ---- runtime stage ----
|
||||
FROM alpine:3.19
|
||||
|
||||
RUN apk add --no-cache ca-certificates tzdata
|
||||
|
||||
# Run as unprivileged user by default. Operators can override with --user root
|
||||
# if they need to bind privileged ports (shouldn't be necessary behind Caddy).
|
||||
RUN addgroup -S dchain && adduser -S -G dchain dchain
|
||||
|
||||
COPY --from=builder /bin/node /usr/local/bin/node
|
||||
COPY --from=builder /bin/client /usr/local/bin/client
|
||||
|
||||
USER dchain
|
||||
|
||||
# Default data location; override in compose with a named volume.
|
||||
VOLUME /data
|
||||
|
||||
# libp2p P2P port + HTTP (serves /api/*, /metrics, /api/ws).
|
||||
EXPOSE 4001/tcp
|
||||
EXPOSE 8080/tcp
|
||||
|
||||
ENTRYPOINT ["/usr/local/bin/node"]
|
||||
163
deploy/prod/README.md
Normal file
163
deploy/prod/README.md
Normal file
@@ -0,0 +1,163 @@
|
||||
# DChain production deployment
|
||||
|
||||
Turn-key-ish stack: 3 validators + Caddy TLS edge + optional
|
||||
Prometheus/Grafana, behind auto-HTTPS.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Docker + Compose v2
|
||||
- A public IP and open ports `80`, `443`, `4001` (libp2p) on every host
|
||||
- DNS `A`-record pointing `DOMAIN` at the host running Caddy
|
||||
- Basic familiarity with editing env files
|
||||
|
||||
## Layout (single-host pilot)
|
||||
|
||||
```
|
||||
┌─ Caddy :443 ── TLS terminate ──┬─ node1:8080 ──┐
|
||||
internet ────────→│ ├─ node2:8080 │ round-robin /api/*
|
||||
└─ Caddy :4001 (passthrough) └─ node3:8080 │ ip_hash /api/ws
|
||||
...
|
||||
Prometheus → node{1,2,3}:8080/metrics
|
||||
Grafana ← Prometheus data source
|
||||
```
|
||||
|
||||
For a real multi-datacentre deployment, copy this whole directory onto each
|
||||
VPS, edit `docker-compose.yml` to keep only the node that runs there, and
|
||||
put Caddy on one dedicated edge host (or none — point clients at one node
|
||||
directly and accept the lower availability).
|
||||
|
||||
## First-boot procedure
|
||||
|
||||
1. **Generate keys** for each validator. Easiest way:
|
||||
|
||||
```bash
|
||||
# On any box with the repo checked out
|
||||
docker build -t dchain-node-slim -f deploy/prod/Dockerfile.slim .
|
||||
mkdir -p deploy/prod/keys
|
||||
for i in 1 2 3; do
|
||||
docker run --rm -v "$PWD/deploy/prod/keys:/out" dchain-node-slim \
|
||||
/usr/local/bin/client keygen --out /out/node$i.json
|
||||
done
|
||||
cat deploy/prod/keys/node*.json | jq -r .pub_key # → copy into DCHAIN_VALIDATORS
|
||||
```
|
||||
|
||||
2. **Configure env files**. Copy `node.env.example` to `node1.env`,
|
||||
`node2.env`, `node3.env`. Paste the three pubkeys from step 1 into
|
||||
`DCHAIN_VALIDATORS` in ALL THREE files. Set `DOMAIN` to your public host.
|
||||
|
||||
3. **Start the network**:
|
||||
|
||||
```bash
|
||||
DOMAIN=dchain.example.com docker compose up -d
|
||||
docker compose logs -f node1 # watch genesis + first blocks
|
||||
```
|
||||
|
||||
First block is genesis (index 0), created only by `node1` because it has
|
||||
the `--genesis` flag. After you see blocks #1, #2, #3… committing,
|
||||
**edit `docker-compose.yml` and remove the `--genesis` flag from node1's
|
||||
command section**, then `docker compose up -d node1` to re-create it
|
||||
without that flag. Leaving `--genesis` in makes no-op on a non-empty DB
|
||||
but is noise in the logs.
|
||||
|
||||
4. **Verify HTTPS** and HTTP-to-HTTPS redirect:
|
||||
|
||||
```bash
|
||||
curl -s https://$DOMAIN/api/netstats | jq
|
||||
curl -s https://$DOMAIN/api/well-known-contracts | jq
|
||||
```
|
||||
|
||||
Caddy should have issued a cert automatically from Let's Encrypt.
|
||||
|
||||
5. **(Optional) observability**:
|
||||
|
||||
```bash
|
||||
GRAFANA_ADMIN_PW=$(openssl rand -hex 24) docker compose --profile monitor up -d
|
||||
# Grafana at http://<host>:3000, user admin, password from env
|
||||
```
|
||||
|
||||
Add a "Prometheus" data source pointing at `http://prometheus:9090`,
|
||||
then import a dashboard that graphs:
|
||||
- `dchain_blocks_total` (rate)
|
||||
- `dchain_tx_submit_accepted_total` / `rejected_total`
|
||||
- `dchain_ws_connections`
|
||||
- `dchain_peer_count_live`
|
||||
- `rate(dchain_block_commit_seconds_sum[5m]) / rate(dchain_block_commit_seconds_count[5m])`
|
||||
|
||||
## Common tasks
|
||||
|
||||
### Add a 4th validator
|
||||
|
||||
The new node joins as an observer via `--join`, then an existing validator
|
||||
promotes it on-chain:
|
||||
|
||||
```bash
|
||||
# On the new box
|
||||
docker run -d --name node4 \
|
||||
--volumes chaindata:/data \
|
||||
-e DCHAIN_ANNOUNCE=/ip4/<public-ip>/tcp/4001 \
|
||||
dchain-node-slim \
|
||||
--db=/data/chain --join=https://$DOMAIN --register-relay
|
||||
```
|
||||
|
||||
Then from any existing validator:
|
||||
|
||||
```bash
|
||||
docker compose exec node1 /usr/local/bin/client add-validator \
|
||||
--key /keys/node.json \
|
||||
--node http://localhost:8080 \
|
||||
--target <NEW_PUBKEY>
|
||||
```
|
||||
|
||||
The new node starts signing as soon as it sees itself in the validator set
|
||||
on-chain — no restart needed.
|
||||
|
||||
### Upgrade without downtime
|
||||
|
||||
PBFT tolerates `f` faulty nodes out of `3f+1`. For 3 validators that means
|
||||
**zero** — any offline node halts consensus. So for 3-node clusters:
|
||||
|
||||
1. `docker compose pull && docker compose build` on all three hosts first.
|
||||
2. Graceful one-at-a-time: `docker compose up -d --no-deps node1`, wait for
|
||||
`/api/netstats` to show it catching up, then do node2, then node3.
|
||||
|
||||
For 4+ nodes you can afford one-at-a-time hot rolls.
|
||||
|
||||
### Back up the chain
|
||||
|
||||
```bash
|
||||
docker run --rm -v node1_data:/data -v "$PWD":/bak alpine \
|
||||
tar czf /bak/dchain-backup-$(date +%F).tar.gz -C /data .
|
||||
```
|
||||
|
||||
Restore by swapping the file back into a fresh named volume before node
|
||||
startup.
|
||||
|
||||
### Remove a bad validator
|
||||
|
||||
Same as adding but with `remove-validator`. Only works if a majority of
|
||||
CURRENT validators cosign the removal — intentional, keeps one rogue
|
||||
validator from kicking others unilaterally (see ROADMAP P2.1).
|
||||
|
||||
## Security notes
|
||||
|
||||
- `/metrics` is firewalled to internal networks by Caddy. If you need
|
||||
external scraping, add proper auth (Caddy `basicauth` or mTLS).
|
||||
- All public endpoints are rate-limited per-IP via the node itself — see
|
||||
`api_guards.go`. Adjust limits before releasing to the open internet.
|
||||
- Each node runs as non-root inside a read-only rootfs container with all
|
||||
capabilities dropped. If you need to exec into one, `docker compose exec
|
||||
--user root nodeN sh`.
|
||||
- The Ed25519 key files mounted at `/keys/node.json` are your validator
|
||||
identities. Losing them means losing your ability to produce blocks; get
|
||||
them onto the host via your normal secret-management (Vault, sealed-
|
||||
secrets, encrypted tarball at deploy time). **Never commit them to git.**
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Symptom | Check |
|
||||
|---------|-------|
|
||||
| Caddy keeps issuing `failed to get certificate` | Is port 80 open? DNS A-record pointing here? `docker compose logs caddy` |
|
||||
| New node can't sync: `FATAL: genesis hash mismatch` | The `--db` volume has data from a different chain. `docker volume rm nodeN_data` and re-up |
|
||||
| Chain stops producing blocks | `docker compose logs nodeN \| tail -100`; look for `SLOW AddBlock` or validator silence |
|
||||
| `/api/ws` returns 429 | Client opened > `WSMaxConnectionsPerIP` (default 10). Check `ws.go` for per-IP cap |
|
||||
| Disk usage growing | Background vlog GC runs every 5 min. Manual: `docker compose exec nodeN /bin/sh -c 'kill -USR1 1'` (see `StartValueLogGC`) |
|
||||
88
deploy/prod/caddy/Caddyfile
Normal file
88
deploy/prod/caddy/Caddyfile
Normal file
@@ -0,0 +1,88 @@
|
||||
# Caddy configuration for DChain prod.
|
||||
#
|
||||
# What this does:
|
||||
# 1. Auto-HTTPS via Let's Encrypt (requires the DOMAIN envvar and
|
||||
# a DNS A-record pointing at this host).
|
||||
# 2. Round-robins HTTP /api/* across the three node backends. GETs are
|
||||
# idempotent so round-robin is safe; POST /api/tx is accepted by any
|
||||
# validator and gossiped to the rest — no stickiness needed.
|
||||
# 3. Routes /api/ws (WebSocket upgrade) through with header
|
||||
# preservation. Uses ip_hash (lb_policy client_ip) so one client
|
||||
# sticks to one node — avoids re-doing the auth handshake on every
|
||||
# subscribe.
|
||||
# 4. Serves /metrics ONLY from localhost IPs so the Prometheus inside
|
||||
# the stack can scrape it; public scrapers are refused.
|
||||
#
|
||||
# To use:
|
||||
# - Set environment var DOMAIN before `docker compose up`:
|
||||
# DOMAIN=dchain.example.com docker compose up -d
|
||||
# - DNS must resolve DOMAIN → this host's public IP.
|
||||
# - Port 80 must be reachable for ACME HTTP-01 challenge.
|
||||
{
|
||||
# Global options. `auto_https` is on by default — leave it alone.
|
||||
email {$ACME_EMAIL:admin@example.com}
|
||||
servers {
|
||||
# Enable HTTP/3 for mobile clients.
|
||||
protocols h1 h2 h3
|
||||
}
|
||||
}
|
||||
|
||||
# ── Public endpoint ────────────────────────────────────────────────────────
|
||||
{$DOMAIN:localhost} {
|
||||
# Compression for JSON / HTML responses.
|
||||
encode zstd gzip
|
||||
|
||||
# ── WebSocket ──────────────────────────────────────────────────────
|
||||
# Client-IP stickiness so reconnects land on the same node. This keeps
|
||||
# per-subscription state local and avoids replaying every auth+subscribe
|
||||
# to a cold node.
|
||||
@ws path /api/ws
|
||||
handle @ws {
|
||||
reverse_proxy node1:8080 node2:8080 node3:8080 {
|
||||
lb_policy ip_hash
|
||||
# Health-check filters dead nodes out of the pool automatically.
|
||||
health_uri /api/netstats
|
||||
health_interval 15s
|
||||
# Upgrade headers preserved by Caddy by default for WS path; no
|
||||
# extra config needed.
|
||||
}
|
||||
}
|
||||
|
||||
# ── REST API ──────────────────────────────────────────────────────
|
||||
handle /api/* {
|
||||
reverse_proxy node1:8080 node2:8080 node3:8080 {
|
||||
lb_policy least_conn
|
||||
health_uri /api/netstats
|
||||
health_interval 15s
|
||||
# Soft fail open: if no node is healthy, return a clear 503.
|
||||
fail_duration 30s
|
||||
}
|
||||
}
|
||||
|
||||
# ── /metrics — internal only ──────────────────────────────────────
|
||||
# Refuse external scraping of Prometheus metrics. Inside the Docker
|
||||
# network Prometheus hits node1:8080/metrics directly, bypassing Caddy.
|
||||
@metricsPublic {
|
||||
path /metrics
|
||||
not remote_ip 127.0.0.1 ::1 172.16.0.0/12 192.168.0.0/16 10.0.0.0/8
|
||||
}
|
||||
handle @metricsPublic {
|
||||
respond "forbidden" 403
|
||||
}
|
||||
|
||||
# ── Everything else → explorer HTML ───────────────────────────────
|
||||
handle {
|
||||
reverse_proxy node1:8080 {
|
||||
health_uri /api/netstats
|
||||
health_interval 15s
|
||||
}
|
||||
}
|
||||
|
||||
# Server-side logging; write JSON for easy log aggregation.
|
||||
log {
|
||||
output stdout
|
||||
format json
|
||||
level INFO
|
||||
}
|
||||
}
|
||||
}
|
||||
175
deploy/prod/docker-compose.yml
Normal file
175
deploy/prod/docker-compose.yml
Normal file
@@ -0,0 +1,175 @@
|
||||
name: dchain-prod
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════════
|
||||
# DChain production stack.
|
||||
#
|
||||
# Layout:
|
||||
# - 3 validator nodes, each with its own persistent volume and key file
|
||||
# - Caddy reverse proxy on the edge: auto-HTTPS from Let's Encrypt,
|
||||
# rewrites ws upgrades, round-robins /api/* across nodes
|
||||
# - Prometheus + Grafana for observability (optional, profile=monitor)
|
||||
#
|
||||
# Quick start (1-host single-server):
|
||||
# cp node.env.example node1.env # edit domain / pubkeys
|
||||
# cp node.env.example node2.env
|
||||
# cp node.env.example node3.env
|
||||
# docker compose up -d # runs nodes + Caddy
|
||||
# docker compose --profile monitor up -d # adds Prometheus + Grafana
|
||||
#
|
||||
# For multi-host (the realistic case), copy this file per VPS and remove
|
||||
# the two nodes that aren't yours; Caddy can still live on one of them or
|
||||
# on a dedicated edge box. Operators are expected to edit this file —
|
||||
# it's a reference, not a magic turnkey.
|
||||
#
|
||||
# Key files:
|
||||
# ./keys/node{1,2,3}.json — Ed25519 identity, bake in via bind mount
|
||||
# ./caddy/Caddyfile — auto-HTTPS config
|
||||
# ./node.env.example — ENV template
|
||||
# ./prometheus.yml — scrape config
|
||||
# ══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
networks:
|
||||
internet:
|
||||
name: dchain_internet
|
||||
driver: bridge
|
||||
|
||||
volumes:
|
||||
node1_data:
|
||||
node2_data:
|
||||
node3_data:
|
||||
caddy_data:
|
||||
caddy_config:
|
||||
prom_data:
|
||||
grafana_data:
|
||||
|
||||
x-node-base: &node-base
|
||||
build:
|
||||
context: ../..
|
||||
dockerfile: deploy/prod/Dockerfile.slim
|
||||
restart: unless-stopped
|
||||
networks: [internet]
|
||||
# Drop all Linux capabilities — the node binary needs none.
|
||||
cap_drop: [ALL]
|
||||
# Read-only root FS; only /data is writable (volume-mounted).
|
||||
read_only: true
|
||||
tmpfs: [/tmp]
|
||||
security_opt: [no-new-privileges:true]
|
||||
# Health check hits /api/netstats through the local HTTP server.
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:8080/api/netstats >/dev/null || exit 1"]
|
||||
interval: 10s
|
||||
timeout: 3s
|
||||
retries: 6
|
||||
start_period: 15s
|
||||
|
||||
services:
|
||||
node1:
|
||||
<<: *node-base
|
||||
container_name: dchain_node1
|
||||
hostname: node1
|
||||
env_file: ./node1.env
|
||||
volumes:
|
||||
- node1_data:/data
|
||||
- ./keys/node1.json:/keys/node.json:ro
|
||||
command:
|
||||
- "--genesis" # drop --genesis after first boot
|
||||
- "--db=/data/chain"
|
||||
- "--mailbox-db=/data/mailbox"
|
||||
- "--key=/keys/node.json"
|
||||
- "--relay-key=/data/relay.json"
|
||||
- "--listen=/ip4/0.0.0.0/tcp/4001"
|
||||
- "--stats-addr=:8080"
|
||||
- "--heartbeat=true"
|
||||
- "--register-relay"
|
||||
|
||||
node2:
|
||||
<<: *node-base
|
||||
container_name: dchain_node2
|
||||
hostname: node2
|
||||
env_file: ./node2.env
|
||||
depends_on:
|
||||
node1: { condition: service_healthy }
|
||||
volumes:
|
||||
- node2_data:/data
|
||||
- ./keys/node2.json:/keys/node.json:ro
|
||||
command:
|
||||
- "--db=/data/chain"
|
||||
- "--mailbox-db=/data/mailbox"
|
||||
- "--key=/keys/node.json"
|
||||
- "--relay-key=/data/relay.json"
|
||||
- "--listen=/ip4/0.0.0.0/tcp/4001"
|
||||
- "--stats-addr=:8080"
|
||||
- "--join=http://node1:8080" # bootstrap from node1
|
||||
- "--register-relay"
|
||||
|
||||
node3:
|
||||
<<: *node-base
|
||||
container_name: dchain_node3
|
||||
hostname: node3
|
||||
env_file: ./node3.env
|
||||
depends_on:
|
||||
node1: { condition: service_healthy }
|
||||
volumes:
|
||||
- node3_data:/data
|
||||
- ./keys/node3.json:/keys/node.json:ro
|
||||
command:
|
||||
- "--db=/data/chain"
|
||||
- "--mailbox-db=/data/mailbox"
|
||||
- "--key=/keys/node.json"
|
||||
- "--relay-key=/data/relay.json"
|
||||
- "--listen=/ip4/0.0.0.0/tcp/4001"
|
||||
- "--stats-addr=:8080"
|
||||
- "--join=http://node1:8080"
|
||||
- "--register-relay"
|
||||
|
||||
# ── Edge: Caddy with auto-HTTPS + WS upgrade + load-balancing ────────────
|
||||
caddy:
|
||||
image: caddy:2.8-alpine
|
||||
container_name: dchain_caddy
|
||||
restart: unless-stopped
|
||||
networks: [internet]
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
- "443:443/udp" # HTTP/3 / QUIC
|
||||
volumes:
|
||||
- ./caddy/Caddyfile:/etc/caddy/Caddyfile:ro
|
||||
- caddy_data:/data
|
||||
- caddy_config:/config
|
||||
depends_on:
|
||||
node1: { condition: service_healthy }
|
||||
|
||||
# ── Observability ────────────────────────────────────────────────────────
|
||||
# Start these only when needed: `docker compose --profile monitor up -d`
|
||||
|
||||
prometheus:
|
||||
profiles: [monitor]
|
||||
image: prom/prometheus:v2.53.0
|
||||
container_name: dchain_prometheus
|
||||
restart: unless-stopped
|
||||
networks: [internet]
|
||||
volumes:
|
||||
- ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
|
||||
- prom_data:/prometheus
|
||||
command:
|
||||
- "--config.file=/etc/prometheus/prometheus.yml"
|
||||
- "--storage.tsdb.retention.time=30d"
|
||||
# No external port — exposed only to Grafana via internal network.
|
||||
|
||||
grafana:
|
||||
profiles: [monitor]
|
||||
image: grafana/grafana:11.1.0
|
||||
container_name: dchain_grafana
|
||||
restart: unless-stopped
|
||||
networks: [internet]
|
||||
ports:
|
||||
- "3000:3000"
|
||||
depends_on: [prometheus]
|
||||
environment:
|
||||
GF_SECURITY_ADMIN_USER: admin
|
||||
GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_ADMIN_PW:-change-me}
|
||||
GF_USERS_ALLOW_SIGN_UP: "false"
|
||||
volumes:
|
||||
- grafana_data:/var/lib/grafana
|
||||
- ./grafana/datasources:/etc/grafana/provisioning/datasources:ro
|
||||
- ./grafana/dashboards:/etc/grafana/provisioning/dashboards:ro
|
||||
36
deploy/prod/node.env.example
Normal file
36
deploy/prod/node.env.example
Normal file
@@ -0,0 +1,36 @@
|
||||
# DChain node environment — copy to node1.env / node2.env / node3.env and
|
||||
# customise per-host. These values are read by the node binary via ENV
|
||||
# fallback (flags still override).
|
||||
#
|
||||
# Required:
|
||||
# DCHAIN_VALIDATORS Comma-separated Ed25519 pubkeys of the initial
|
||||
# validator set. All three nodes must agree on
|
||||
# this list at genesis; later additions happen
|
||||
# on-chain via ADD_VALIDATOR.
|
||||
# DCHAIN_ANNOUNCE Public libp2p multiaddr peers use to dial this
|
||||
# node from the internet. e.g.
|
||||
# /ip4/203.0.113.10/tcp/4001
|
||||
#
|
||||
# Optional:
|
||||
# DCHAIN_PEERS Bootstrap peer multiaddrs. Auto-filled by
|
||||
# --join if omitted.
|
||||
# DCHAIN_GOVERNANCE_CONTRACT Deployed governance contract ID (hex).
|
||||
# DCHAIN_RELAY_FEE µT per message when registering as a relay.
|
||||
# ACME_EMAIL Email for Let's Encrypt (TLS expiry reminders).
|
||||
# DOMAIN Public hostname — Caddy issues cert for this.
|
||||
#
|
||||
# Security:
|
||||
# Key files are bind-mounted at runtime; do NOT put private keys in this
|
||||
# file. Each node needs its own identity — generate with
|
||||
# docker compose run --rm node1 /usr/local/bin/client keygen --out /keys/node.json
|
||||
# and copy out with `docker cp`.
|
||||
|
||||
DCHAIN_VALIDATORS=PUT_FIRST_PUBKEY_HERE,PUT_SECOND_PUBKEY_HERE,PUT_THIRD_PUBKEY_HERE
|
||||
DCHAIN_ANNOUNCE=/ip4/0.0.0.0/tcp/4001
|
||||
|
||||
# DCHAIN_PEERS=/ip4/203.0.113.10/tcp/4001/p2p/12D3Koo...
|
||||
# DCHAIN_GOVERNANCE_CONTRACT=
|
||||
# DCHAIN_RELAY_FEE=1000
|
||||
# ACME_EMAIL=admin@example.com
|
||||
# DOMAIN=dchain.example.com
|
||||
# GRAFANA_ADMIN_PW=change-me-to-something-long
|
||||
17
deploy/prod/prometheus.yml
Normal file
17
deploy/prod/prometheus.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
# Prometheus scrape config for DChain prod.
|
||||
# Mounted read-only into the prometheus container.
|
||||
|
||||
global:
|
||||
scrape_interval: 15s
|
||||
scrape_timeout: 5s
|
||||
evaluation_interval: 30s
|
||||
external_labels:
|
||||
network: dchain-prod
|
||||
|
||||
scrape_configs:
|
||||
- job_name: dchain-node
|
||||
metrics_path: /metrics
|
||||
static_configs:
|
||||
- targets: [node1:8080, node2:8080, node3:8080]
|
||||
labels:
|
||||
group: validators
|
||||
Reference in New Issue
Block a user