merge: sync local master with origin/main

This commit is contained in:
2026-04-07 16:07:16 +08:00
63 changed files with 7417 additions and 215 deletions

View File

@@ -0,0 +1,204 @@
#!/usr/bin/env bash
# Upgrade the public Chain 138 RPC node (VMID 2201) to a Besu version that supports
# eth_maxPriorityFeePerGas. Default target is the current fleet baseline.
#
# Usage:
# bash scripts/besu/upgrade-public-rpc-vmid2201.sh
# bash scripts/besu/upgrade-public-rpc-vmid2201.sh --dry-run
# BESU_VERSION=25.12.0 bash scripts/besu/upgrade-public-rpc-vmid2201.sh
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
source "${PROJECT_ROOT}/config/ip-addresses.conf" 2>/dev/null || true
VMID="${RPC_VMID_2201:-2201}"
RPC_HOST="${RPC_VM_2201_HOST:-root@${PROXMOX_R630_02:-192.168.11.12}}"
[[ "$RPC_HOST" != *"@"* ]] && RPC_HOST="root@$RPC_HOST"
BESU_VERSION="${BESU_VERSION:-25.12.0}"
BESU_TAR="besu-${BESU_VERSION}.tar.gz"
BESU_DIR="/opt/besu-${BESU_VERSION}"
DOWNLOAD_URL="${BESU_DOWNLOAD_URL:-https://github.com/hyperledger/besu/releases/download/${BESU_VERSION}/${BESU_TAR}}"
JAVA21_FALLBACK_URL="${JAVA21_FALLBACK_URL:-https://api.adoptium.net/v3/binary/latest/21/ga/linux/x64/jre/hotspot/normal/eclipse}"
RPC_HTTP_MAX_ACTIVE_CONNECTIONS="${RPC_HTTP_MAX_ACTIVE_CONNECTIONS:-256}"
RPC_WS_MAX_ACTIVE_CONNECTIONS="${RPC_WS_MAX_ACTIVE_CONNECTIONS:-256}"
LOCAL_CACHE="${LOCAL_CACHE:-/tmp}"
DRY_RUN=false
[[ "${1:-}" == "--dry-run" ]] && DRY_RUN=true
SSH_OPTS=(-o StrictHostKeyChecking=accept-new -o ConnectTimeout=15)
RPC_IP="${RPC_PUBLIC_1:-192.168.11.221}"
run_on_host() {
ssh "${SSH_OPTS[@]}" "$RPC_HOST" "$@"
}
run_in_vmid() {
local cmd="$1"
if command -v pct >/dev/null 2>&1 && pct list 2>/dev/null | grep -q "^${VMID} "; then
pct exec "$VMID" -- bash -lc "$cmd"
else
run_on_host "pct exec ${VMID} -- bash -lc $(printf '%q' "$cmd")"
fi
}
push_to_vmid() {
local src="$1"
local dest="$2"
if command -v pct >/dev/null 2>&1 && pct list 2>/dev/null | grep -q "^${VMID} "; then
pct push "$VMID" "$src" "$dest"
else
local host_tmp="/tmp/$(basename "$src")"
scp "${SSH_OPTS[@]}" "$src" "${RPC_HOST}:${host_tmp}"
run_on_host "pct push ${VMID} $(printf '%q' "$host_tmp") $(printf '%q' "$dest") && rm -f $(printf '%q' "$host_tmp")"
fi
}
rpc_request() {
local method="$1"
local params="${2:-[]}"
curl -sS --max-time 20 -X POST "http://${RPC_IP}:8545" \
-H "Content-Type: application/json" \
-d "{\"jsonrpc\":\"2.0\",\"method\":\"${method}\",\"params\":${params},\"id\":1}"
}
echo "=============================================="
echo "Upgrade public Chain 138 RPC (VMID ${VMID})"
echo "Host: ${RPC_HOST}"
echo "Target Besu version: ${BESU_VERSION}"
echo "=============================================="
if $DRY_RUN; then
echo "[dry-run] No changes will be made."
fi
run_on_host "echo connected >/dev/null"
run_in_vmid "
set -euo pipefail
if [[ ! -e /opt/besu ]]; then
fallback=\$(find /opt -maxdepth 1 -type d -name 'besu-*' | sort -V | tail -1)
if [[ -n \"\${fallback:-}\" ]]; then
ln -sfn \"\$fallback\" /opt/besu
chown -h besu:besu /opt/besu 2>/dev/null || true
fi
fi
"
CURRENT_VERSION="$(run_in_vmid '/opt/besu/bin/besu --version 2>/dev/null || besu --version 2>/dev/null || true' | head -1 || true)"
JAVA_VERSION_RAW="$(run_in_vmid 'java -version 2>&1 | head -1' || true)"
echo "Current version: ${CURRENT_VERSION:-unknown}"
echo "Current Java: ${JAVA_VERSION_RAW:-unknown}"
if $DRY_RUN; then
echo "[dry-run] Would download ${DOWNLOAD_URL}"
echo "[dry-run] Would stage ${BESU_TAR} in VMID ${VMID}, extract to ${BESU_DIR}, switch /opt/besu, restart besu-rpc."
exit 0
fi
mkdir -p "$LOCAL_CACHE"
if [[ ! -f "${LOCAL_CACHE}/${BESU_TAR}" ]]; then
echo "Downloading ${DOWNLOAD_URL} ..."
curl -fsSL -o "${LOCAL_CACHE}/${BESU_TAR}" "${DOWNLOAD_URL}"
fi
echo "Pushing tarball into VMID ${VMID} ..."
push_to_vmid "${LOCAL_CACHE}/${BESU_TAR}" "/tmp/${BESU_TAR}"
echo "Ensuring Java 21 runtime is present ..."
run_in_vmid "
set -euo pipefail
java_major=\$(java -version 2>&1 | sed -n '1s/.*version \"\\([0-9][0-9]*\\).*/\\1/p')
if [[ -z \"\${java_major:-}\" || \"\$java_major\" -lt 21 ]]; then
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y -qq openjdk-21-jre-headless || true
java_major=\$(java -version 2>&1 | sed -n '1s/.*version \"\\([0-9][0-9]*\\).*/\\1/p')
if [[ -z \"\${java_major:-}\" || \"\$java_major\" -lt 21 ]]; then
command -v curl >/dev/null 2>&1 || apt-get install -y -qq curl ca-certificates
tmp_jre=/tmp/java21-jre.tar.gz
curl -fsSL -o \"\$tmp_jre\" '${JAVA21_FALLBACK_URL}'
tar -tzf \"\$tmp_jre\" > /tmp/java21-jre.list
extracted_dir=\$(head -1 /tmp/java21-jre.list | cut -d/ -f1)
rm -f /tmp/java21-jre.list
tar -xzf \"\$tmp_jre\" -C /opt
rm -f \"\$tmp_jre\"
ln -sfn \"/opt/\${extracted_dir}\" /opt/java-21
update-alternatives --install /usr/bin/java java /opt/java-21/bin/java 2100
fi
fi
config_file=\$(systemctl cat besu-rpc.service | sed -n 's/.*--config-file=\\([^ ]*\\).*/\\1/p' | tail -1)
if [[ -n \"\${config_file:-}\" && -f \"\$config_file\" ]]; then
find /etc/besu -maxdepth 1 -type f -name '*.toml' -print0 2>/dev/null | while IFS= read -r -d '' toml; do
sed -i \
-e '/^[[:space:]]*miner-enabled[[:space:]]*=.*/d' \
-e '/^[[:space:]]*privacy-enabled[[:space:]]*=.*/d' \
\"\$toml\"
if grep -q '^rpc-http-enabled=true' \"\$toml\" && ! grep -q '^rpc-http-max-active-connections=' \"\$toml\"; then
tmp=\$(mktemp)
awk '1; /^rpc-http-port=/{print \"rpc-http-max-active-connections=${RPC_HTTP_MAX_ACTIVE_CONNECTIONS}\"}' \"\$toml\" > \"\$tmp\"
cat \"\$tmp\" > \"\$toml\"
rm -f \"\$tmp\"
fi
if grep -q '^rpc-ws-enabled=true' \"\$toml\" && ! grep -q '^rpc-ws-max-active-connections=' \"\$toml\"; then
tmp=\$(mktemp)
awk '1; /^rpc-ws-port=/{print \"rpc-ws-max-active-connections=${RPC_WS_MAX_ACTIVE_CONNECTIONS}\"}' \"\$toml\" > \"\$tmp\"
cat \"\$tmp\" > \"\$toml\"
rm -f \"\$tmp\"
fi
done
if ! grep -q '^data-storage-format=' \"\$config_file\"; then
tmp=\$(mktemp)
awk '1; /^sync-mode=/{print \"data-storage-format=\\\"FOREST\\\"\"}' \"\$config_file\" > \"\$tmp\"
cat \"\$tmp\" > \"\$config_file\"
rm -f \"\$tmp\"
fi
fi
"
echo "Installing Besu ${BESU_VERSION} inside VMID ${VMID} ..."
run_in_vmid "
set -euo pipefail
cd /opt
if [[ -L /opt/besu ]]; then
current_target=\$(readlink -f /opt/besu)
current_version=\$(basename \"\$current_target\")
else
current_version=\$(/opt/besu/bin/besu --version 2>/dev/null | head -1 | sed -E 's#^.*/(v)?([0-9.]+).*\$#besu-\\2#')
[[ -z \"\$current_version\" ]] && current_version=besu-backup-pre-${BESU_VERSION}
mv /opt/besu \"/opt/\${current_version}\"
fi
rm -rf '${BESU_DIR}'
tar -xzf '/tmp/${BESU_TAR}' -C /opt
rm -f '/tmp/${BESU_TAR}'
ln -sfn '${BESU_DIR}' /opt/besu
chown -h besu:besu /opt/besu
chown -R besu:besu '${BESU_DIR}' /opt/besu-* 2>/dev/null || true
"
echo "Restarting besu-rpc.service ..."
run_in_vmid "systemctl restart besu-rpc.service"
for _ in $(seq 1 24); do
ACTIVE_STATE="$(run_in_vmid 'systemctl is-active besu-rpc.service' || true)"
[[ "$ACTIVE_STATE" == "active" ]] && break
sleep 5
done
NEW_VERSION="$(run_in_vmid '/opt/besu/bin/besu --version 2>/dev/null | grep -m1 "besu/" || true' | head -1 || true)"
echo "Service state: ${ACTIVE_STATE:-unknown}"
echo "New version: ${NEW_VERSION:-unknown}"
echo "Verifying live RPC methods ..."
CHAIN_ID="$(rpc_request eth_chainId | jq -r '.result // empty' 2>/dev/null || true)"
PRIORITY_FEE="$(curl -sS --max-time 20 -X POST 'https://rpc-http-pub.d-bis.org' -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"method\":\"eth_maxPriorityFeePerGas\",\"params\":[],\"id\":1}' | jq -r '.result // empty' 2>/dev/null || true)"
TRACE_OK="$(rpc_request trace_block '[\"0x1\"]' | jq -r 'has(\"result\")' 2>/dev/null || true)"
if [[ "$ACTIVE_STATE" != "active" || -z "$CHAIN_ID" || "$TRACE_OK" != "true" || -z "$PRIORITY_FEE" ]]; then
echo "ERROR: post-upgrade verification failed."
echo " eth_chainId result: ${CHAIN_ID:-missing}"
echo " trace_block result present: ${TRACE_OK:-false}"
echo " eth_maxPriorityFeePerGas result: ${PRIORITY_FEE:-missing}"
exit 1
fi
echo "OK: VMID ${VMID} upgraded successfully and public RPC now exposes eth_maxPriorityFeePerGas."

View File

@@ -0,0 +1,54 @@
#!/usr/bin/env bash
set -euo pipefail
# Create placeholder LXCs for the DBIS RTGS control plane.
# Usage:
# ./scripts/deployment/create-dbis-rtgs-control-plane-lxcs.sh [--dry-run]
HOST="${PROXMOX_HOST_R630_02:-192.168.11.12}"
SSH_OPTS="-o BatchMode=yes -o ConnectTimeout=15 -o StrictHostKeyChecking=accept-new"
TEMPLATE="${PVE_LXC_TEMPLATE:-local:vztmpl/debian-12-standard_12.7-1_amd64.tar.zst}"
STORAGE="${PVE_STORAGE:-local-lvm}"
BRIDGE="${PVE_BRIDGE:-vmbr0}"
GATEWAY="${PVE_GATEWAY:-192.168.11.1}"
DRY_RUN=false
if [[ "${1:-}" == "--dry-run" ]]; then
DRY_RUN=true
fi
LXCS=(
"${RTGS_ORCH_VMID:-5805} ${RTGS_ORCH_HOSTNAME:-rtgs-orchestrator-1} ${RTGS_ORCH_IP:-192.168.11.93} 4096 2 24"
"${RTGS_FX_VMID:-5806} ${RTGS_FX_HOSTNAME:-rtgs-fx-1} ${RTGS_FX_IP:-192.168.11.94} 4096 2 24"
"${RTGS_LIQ_VMID:-5807} ${RTGS_LIQ_HOSTNAME:-rtgs-liquidity-1} ${RTGS_LIQ_IP:-192.168.11.95} 4096 2 24"
)
run_remote() {
local cmd="$1"
if $DRY_RUN; then
echo "[DRY-RUN] $cmd"
else
ssh $SSH_OPTS "root@$HOST" "$cmd"
fi
}
echo "=== DBIS RTGS control-plane LXCs ==="
echo "Host: $HOST"
echo "Template: $TEMPLATE"
echo
for spec in "${LXCS[@]}"; do
read -r vmid hostname ip memory cores disk <<<"$spec"
cmd="pct create $vmid $TEMPLATE \
--hostname $hostname \
--cores $cores \
--memory $memory \
--rootfs ${STORAGE}:${disk} \
--net0 name=eth0,bridge=${BRIDGE},gw=${GATEWAY},ip=${ip}/24 \
--onboot 1 \
--unprivileged 1 \
--features nesting=1 \
--password \$(openssl rand -base64 18) \
--description 'DBIS RTGS control-plane LXC ($hostname)'"
run_remote "$cmd"
done

View File

@@ -0,0 +1,54 @@
#!/usr/bin/env bash
set -euo pipefail
# Create placeholder LXCs for later-phase DBIS RTGS sidecars.
# Usage:
# ./scripts/deployment/create-dbis-rtgs-later-phase-sidecar-lxcs.sh [--dry-run]
HOST="${PROXMOX_HOST_R630_02:-192.168.11.12}"
SSH_OPTS="-o BatchMode=yes -o ConnectTimeout=15 -o StrictHostKeyChecking=accept-new"
TEMPLATE="${PVE_LXC_TEMPLATE:-local:vztmpl/debian-12-standard_12.7-1_amd64.tar.zst}"
STORAGE="${PVE_STORAGE:-local-lvm}"
BRIDGE="${PVE_BRIDGE:-vmbr0}"
GATEWAY="${PVE_GATEWAY:-192.168.11.1}"
DRY_RUN=false
if [[ "${1:-}" == "--dry-run" ]]; then
DRY_RUN=true
fi
LXCS=(
"${RTGS_SECURITIES_VMID:-5808} ${RTGS_SECURITIES_HOSTNAME:-rtgs-securities-1} ${RTGS_SECURITIES_IP:-192.168.11.96} 4096 2 24"
"${RTGS_CARDNET_VMID:-5809} ${RTGS_CARDNET_HOSTNAME:-rtgs-cardnet-1} ${RTGS_CARDNET_IP:-192.168.11.97} 4096 2 24"
"${RTGS_MT103_VMID:-5810} ${RTGS_MT103_HOSTNAME:-rtgs-mt103-1} ${RTGS_MT103_IP:-192.168.11.98} 4096 2 24"
)
run_remote() {
local cmd="$1"
if $DRY_RUN; then
echo "[DRY-RUN] $cmd"
else
ssh $SSH_OPTS "root@$HOST" "$cmd"
fi
}
echo "=== DBIS RTGS later-phase sidecar LXCs ==="
echo "Host: $HOST"
echo "Template: $TEMPLATE"
echo
for spec in "${LXCS[@]}"; do
read -r vmid hostname ip memory cores disk <<<"$spec"
cmd="pct create $vmid $TEMPLATE \
--hostname $hostname \
--cores $cores \
--memory $memory \
--rootfs ${STORAGE}:${disk} \
--net0 name=eth0,bridge=${BRIDGE},gw=${GATEWAY},ip=${ip}/24 \
--onboot 1 \
--unprivileged 1 \
--features nesting=1 \
--password \$(openssl rand -base64 18) \
--description 'DBIS RTGS later-phase sidecar LXC ($hostname)'"
run_remote "$cmd"
done

View File

@@ -0,0 +1,79 @@
#!/usr/bin/env bash
set -euo pipefail
# Create the three DBIS RTGS first-slice sidecar LXCs on r630-02.
# Usage:
# ./scripts/deployment/create-dbis-rtgs-sidecar-lxcs.sh [--dry-run]
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
source "${PROJECT_ROOT}/config/ip-addresses.conf" 2>/dev/null || true
HOST="${PROXMOX_HOST_R630_02:-${PROXMOX_R630_02:-192.168.11.12}}"
NETWORK="${NETWORK:-vmbr0}"
GATEWAY="${NETWORK_GATEWAY:-192.168.11.1}"
DNS="${DNS_PRIMARY:-1.1.1.1}"
STORAGE="${RTGS_SIDECAR_STORAGE:-thin3}"
TEMPLATE="${TEMPLATE_UBUNTU_24:-local:vztmpl/ubuntu-24.04-standard_24.04-1_amd64.tar.zst}"
SSH_OPTS="-o BatchMode=yes -o ConnectTimeout=15 -o StrictHostKeyChecking=accept-new"
DRY_RUN=false
if [[ "${1:-}" == "--dry-run" ]]; then
DRY_RUN=true
fi
SIDEcars=(
"5802 rtgs-scsm-1 192.168.11.89 4096 2 24"
"5803 rtgs-funds-1 192.168.11.90 4096 2 24"
"5804 rtgs-xau-1 192.168.11.92 4096 2 24"
)
resolve_template() {
if ssh $SSH_OPTS "root@$HOST" "pveam list local 2>/dev/null | grep -q 'ubuntu-24.04-standard'" 2>/dev/null; then
echo "local:vztmpl/ubuntu-24.04-standard_24.04-1_amd64.tar.zst"
elif ssh $SSH_OPTS "root@$HOST" "pveam list local 2>/dev/null | grep -q 'ubuntu-22.04-standard'" 2>/dev/null; then
echo "local:vztmpl/ubuntu-22.04-standard_22.04-1_amd64.tar.zst"
else
echo "$TEMPLATE"
fi
}
TEMPLATE="$(resolve_template)"
echo "=== DBIS RTGS first-slice sidecar LXCs ==="
echo "Host: $HOST"
echo "Storage: $STORAGE"
echo "Template: $TEMPLATE"
echo
for spec in "${SIDEcars[@]}"; do
read -r VMID HOSTNAME IP MEMORY CORES ROOTFS_GB <<<"$spec"
if ssh $SSH_OPTS "root@$HOST" "pct status $VMID >/dev/null 2>&1"; then
echo "CT $VMID already exists on $HOST; skipping create."
continue
fi
CREATE_CMD="pct create $VMID $TEMPLATE \
--hostname $HOSTNAME \
--memory $MEMORY \
--cores $CORES \
--rootfs $STORAGE:${ROOTFS_GB} \
--net0 name=eth0,bridge=$NETWORK,ip=$IP/24,gw=$GATEWAY \
--features nesting=1,keyctl=1 \
--nameserver $DNS \
--onboot 1 \
--start 1 \
--unprivileged 0 \
--description 'DBIS RTGS first-slice sidecar LXC ($HOSTNAME)'"
if $DRY_RUN; then
echo "[DRY-RUN] $CREATE_CMD"
echo
continue
fi
echo "Creating CT $VMID ($HOSTNAME, $IP)..."
ssh $SSH_OPTS "root@$HOST" "$CREATE_CMD"
done
echo "Done."

View File

@@ -0,0 +1,153 @@
#!/usr/bin/env bash
set -euo pipefail
# Deploy the DBIS RTGS control-plane services when artifacts are available.
# Usage:
# ./scripts/deployment/deploy-dbis-rtgs-control-plane.sh [--dry-run]
HOST="${PROXMOX_HOST_R630_02:-192.168.11.12}"
SSH_OPTS="-o BatchMode=yes -o ConnectTimeout=15 -o StrictHostKeyChecking=accept-new"
ORCH_VMID="${RTGS_ORCH_VMID:-5805}"
FX_VMID="${RTGS_FX_VMID:-5806}"
LIQ_VMID="${RTGS_LIQ_VMID:-5807}"
ORCH_JAR="${RTGS_ORCH_JAR:-}"
FX_JAR="${RTGS_FX_JAR:-}"
LIQ_JAR="${RTGS_LIQ_JAR:-}"
OMNL_BASE_URL="${OMNL_FINERACT_BASE_URL:-http://192.168.11.85:8080/fineract-provider/api/v1}"
OMNL_TENANT="${OMNL_FINERACT_TENANT:-omnl}"
OMNL_USER="${OMNL_FINERACT_USER:-}"
OMNL_PASSWORD="${OMNL_FINERACT_PASSWORD:-}"
DRY_RUN=false
if [[ "${1:-}" == "--dry-run" ]]; then
DRY_RUN=true
fi
run_remote() {
local vmid="$1"
local cmd="$2"
if $DRY_RUN; then
echo "[DRY-RUN][CT $vmid] $cmd"
else
ssh $SSH_OPTS "root@$HOST" "pct exec $vmid -- bash -lc $(printf '%q' "$cmd")"
fi
}
push_file() {
local vmid="$1"
local src="$2"
local dest="$3"
if $DRY_RUN; then
echo "[DRY-RUN][CT $vmid] copy $src -> $dest"
else
ssh $SSH_OPTS "root@$HOST" "pct exec $vmid -- mkdir -p $(dirname "$dest")"
ssh $SSH_OPTS "root@$HOST" "cat > /tmp/$(basename "$dest")" < "$src"
ssh $SSH_OPTS "root@$HOST" "pct push $vmid /tmp/$(basename "$dest") $dest >/dev/null && rm -f /tmp/$(basename "$dest")"
fi
}
setup_base_runtime() {
local vmid="$1"
run_remote "$vmid" "export DEBIAN_FRONTEND=noninteractive && apt-get update && apt-get install -y openjdk-21-jre-headless redis-server curl ca-certificates"
run_remote "$vmid" "systemctl enable redis-server --now"
}
require_artifact() {
local label="$1"
local path="$2"
if [[ -z "$path" ]]; then
echo "Missing ${label}: set the corresponding RTGS_*_JAR env var." >&2
exit 1
fi
if [[ ! -f "$path" ]]; then
echo "Missing ${label} artifact: $path" >&2
exit 1
fi
}
deploy_service() {
local vmid="$1"
local service_name="$2"
local jar_path="$3"
local env_path="$4"
local env_content="$5"
local workdir="/opt/dbis-rtgs/${service_name}"
local unitfile
setup_base_runtime "$vmid"
push_file "$vmid" "$jar_path" "${workdir}/${service_name}.jar"
local env_tmp
env_tmp="$(mktemp)"
cat > "$env_tmp" <<<"$env_content"
push_file "$vmid" "$env_tmp" "$env_path"
rm -f "$env_tmp"
unitfile="$(mktemp)"
cat > "$unitfile" <<EOF
[Unit]
Description=DBIS RTGS ${service_name}
After=network-online.target redis-server.service
Wants=network-online.target
[Service]
User=root
WorkingDirectory=${workdir}
EnvironmentFile=${env_path}
ExecStart=/usr/bin/java -jar ${workdir}/${service_name}.jar
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
push_file "$vmid" "$unitfile" "/etc/systemd/system/dbis-rtgs-${service_name}.service"
rm -f "$unitfile"
run_remote "$vmid" "mkdir -p ${workdir} /etc/dbis-rtgs /var/lib/dbis-rtgs/${service_name} && systemctl daemon-reload && systemctl enable dbis-rtgs-${service_name} && systemctl restart dbis-rtgs-${service_name}"
}
require_artifact "orchestrator JAR" "$ORCH_JAR"
require_artifact "FX engine JAR" "$FX_JAR"
require_artifact "liquidity engine JAR" "$LIQ_JAR"
deploy_service "$ORCH_VMID" "orchestrator" "$ORCH_JAR" "/etc/dbis-rtgs/orchestrator.env" "$(cat <<EOF
SERVER_PORT=8080
DB_URL=jdbc:h2:file:/var/lib/dbis-rtgs/orchestrator/orchestrator;DB_CLOSE_ON_EXIT=FALSE
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
OMNL_BASE_URL=${OMNL_BASE_URL}
OMNL_TENANT=${OMNL_TENANT}
OMNL_USER=${OMNL_USER}
OMNL_PASSWORD=${OMNL_PASSWORD}
EOF
)"
deploy_service "$FX_VMID" "fx-engine" "$FX_JAR" "/etc/dbis-rtgs/fx-engine.env" "$(cat <<EOF
SERVER_PORT=8080
DB_URL=jdbc:h2:file:/var/lib/dbis-rtgs/fx-engine/fx-engine;DB_CLOSE_ON_EXIT=FALSE
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
OMNL_BASE_URL=${OMNL_BASE_URL}
OMNL_TENANT=${OMNL_TENANT}
OMNL_USER=${OMNL_USER}
OMNL_PASSWORD=${OMNL_PASSWORD}
EOF
)"
deploy_service "$LIQ_VMID" "liquidity-engine" "$LIQ_JAR" "/etc/dbis-rtgs/liquidity-engine.env" "$(cat <<EOF
SERVER_PORT=8080
DB_URL=jdbc:h2:file:/var/lib/dbis-rtgs/liquidity-engine/liquidity-engine;DB_CLOSE_ON_EXIT=FALSE
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
OMNL_BASE_URL=${OMNL_BASE_URL}
OMNL_TENANT=${OMNL_TENANT}
OMNL_USER=${OMNL_USER}
OMNL_PASSWORD=${OMNL_PASSWORD}
EOF
)"
echo "DBIS RTGS control-plane deployment complete."

View File

@@ -0,0 +1,291 @@
#!/usr/bin/env bash
set -euo pipefail
# Deploy the three selected DBIS RTGS first-slice sidecars to their LXC targets.
# Usage:
# ./scripts/deployment/deploy-dbis-rtgs-first-slice-sidecars.sh [--dry-run]
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
if [[ -f "$PROJECT_ROOT/omnl-fineract/.env" ]]; then
set +u
# shellcheck source=/dev/null
source "$PROJECT_ROOT/omnl-fineract/.env"
set -u
fi
HOST="${PROXMOX_HOST_R630_02:-192.168.11.12}"
SSH_OPTS="-o BatchMode=yes -o ConnectTimeout=15 -o StrictHostKeyChecking=accept-new"
SCSM_JAR="/home/intlc/projects/HYBX_Sidecars/mifos-fineract-sidecar/scsm-app/target/scsm-app-1.0.0-SNAPSHOT.jar"
FUNDS_JAR="/home/intlc/projects/HYBX_Sidecars/server-funds-sidecar/funds-app/target/funds-app-1.0.0-SNAPSHOT.jar"
XAU_JAR="/home/intlc/projects/HYBX_Sidecars/off-ledger-2-on-ledger-sidecar/target/off-ledger-2-on-ledger-sidecar-0.1.0-SNAPSHOT.jar"
SCSM_FINERACT_BASE_URL="${SCSM_FINERACT_BASE_URL:-${OMNL_FINERACT_BASE_URL:-http://192.168.11.85:8080/fineract-provider/api/v1}}"
SCSM_FINERACT_TENANT="${SCSM_FINERACT_TENANT:-${OMNL_FINERACT_TENANT:-omnl}}"
SCSM_FINERACT_USERNAME="${SCSM_FINERACT_USERNAME:-${OMNL_FINERACT_USER:-}}"
SCSM_FINERACT_PASSWORD="${SCSM_FINERACT_PASSWORD:-${OMNL_FINERACT_PASSWORD:-}}"
FUNDS_FINERACT_BASE_URL="${FUNDS_FINERACT_BASE_URL:-${OMNL_FINERACT_BASE_URL:-http://192.168.11.85:8080/fineract-provider/api/v1}}"
FUNDS_FINERACT_TENANT="${FUNDS_FINERACT_TENANT:-${OMNL_FINERACT_TENANT:-omnl}}"
FUNDS_FINERACT_USERNAME="${FUNDS_FINERACT_USERNAME:-${OMNL_FINERACT_USER:-}}"
FUNDS_FINERACT_PASSWORD="${FUNDS_FINERACT_PASSWORD:-${OMNL_FINERACT_PASSWORD:-}}"
FUNDS_FINERACT_OFFICE_ID="${FUNDS_FINERACT_OFFICE_ID:-1}"
FUNDS_DEFAULT_DEBIT_GL_ID="${FUNDS_DEFAULT_DEBIT_GL_ID:-24}"
FUNDS_DEFAULT_CREDIT_GL_ID="${FUNDS_DEFAULT_CREDIT_GL_ID:-3}"
XAU_FINERACT_BASE_URL="${XAU_FINERACT_BASE_URL:-${OMNL_FINERACT_BASE_URL:-http://192.168.11.85:8080/fineract-provider/api/v1}}"
XAU_FINERACT_TENANT="${XAU_FINERACT_TENANT:-${OMNL_FINERACT_TENANT:-omnl}}"
XAU_FINERACT_USERNAME="${XAU_FINERACT_USERNAME:-${OMNL_FINERACT_USER:-}}"
XAU_FINERACT_PASSWORD="${XAU_FINERACT_PASSWORD:-${OMNL_FINERACT_PASSWORD:-}}"
XAU_FINERACT_OFFICE_ID="${XAU_FINERACT_OFFICE_ID:-1}"
XAU_FEED_URL="${XAU_FEED_URL:-}"
XAU_STUB_PRICE="${XAU_STUB_PRICE:-2000}"
XAU_GL_GOLD_COLLATERAL_ENCUMBERED="${XAU_GL_GOLD_COLLATERAL_ENCUMBERED:-7}"
XAU_GL_GOLD_COLLATERAL_FREE="${XAU_GL_GOLD_COLLATERAL_FREE:-1}"
XAU_GL_OFF_LEDGER_SETTLEMENT_CLEARING="${XAU_GL_OFF_LEDGER_SETTLEMENT_CLEARING:-14}"
XAU_GL_BORROWER_RECEIVABLE="${XAU_GL_BORROWER_RECEIVABLE:-24}"
XAU_GL_TEMPORARY_CREDIT_ISSUED="${XAU_GL_TEMPORARY_CREDIT_ISSUED:-3}"
XAU_GL_TEMPORARY_DEBT_PAYABLE="${XAU_GL_TEMPORARY_DEBT_PAYABLE:-25}"
XAU_GL_CONVERSION_CONTROL_SUSPENSE="${XAU_GL_CONVERSION_CONTROL_SUSPENSE:-5}"
CHAIN138_SETTLEMENT_PRIVATE_KEY="${CHAIN138_SETTLEMENT_PRIVATE_KEY:-${PRIVATE_KEY:-}}"
CHAIN138_SETTLEMENT_WAIT_FOR_RECEIPT="${CHAIN138_SETTLEMENT_WAIT_FOR_RECEIPT:-false}"
CHAIN138_SETTLEMENT_MERCHANT_ADDRESS="${CHAIN138_SETTLEMENT_MERCHANT_ADDRESS:-}"
if [[ -z "$CHAIN138_SETTLEMENT_MERCHANT_ADDRESS" && -n "$CHAIN138_SETTLEMENT_PRIVATE_KEY" ]] && command -v cast >/dev/null 2>&1; then
CHAIN138_SETTLEMENT_MERCHANT_ADDRESS="$(cast wallet address "$CHAIN138_SETTLEMENT_PRIVATE_KEY" 2>/dev/null || true)"
fi
DRY_RUN=false
if [[ "${1:-}" == "--dry-run" ]]; then
DRY_RUN=true
fi
TARGETS="${TARGETS:-scsm,funds,xau}"
require_file() {
local path="$1"
if [[ ! -f "$path" ]]; then
echo "Missing required artifact: $path" >&2
exit 1
fi
}
require_file "$SCSM_JAR"
require_file "$FUNDS_JAR"
require_file "$XAU_JAR"
run_remote() {
local vmid="$1"
local cmd="$2"
if $DRY_RUN; then
echo "[DRY-RUN][CT $vmid] $cmd"
else
ssh $SSH_OPTS "root@$HOST" "pct exec $vmid -- bash -lc $(printf '%q' "$cmd")"
fi
}
target_enabled() {
local want="$1"
[[ ",$TARGETS," == *",$want,"* ]]
}
wait_for_health() {
local vmid="$1"
local url="$2"
local out_file="$3"
local attempts="${4:-20}"
local sleep_seconds="${5:-2}"
local cmd="for i in \$(seq 1 $attempts); do if curl -sf \"$url\" > \"$out_file\"; then cat \"$out_file\"; exit 0; fi; sleep $sleep_seconds; done; exit 7"
run_remote "$vmid" "$cmd"
}
push_file() {
local vmid="$1"
local src="$2"
local dest="$3"
if $DRY_RUN; then
echo "[DRY-RUN][CT $vmid] copy $src -> $dest"
else
ssh $SSH_OPTS "root@$HOST" "pct exec $vmid -- mkdir -p $(dirname "$dest")"
ssh $SSH_OPTS "root@$HOST" "cat > /tmp/$(basename "$dest")" < "$src"
ssh $SSH_OPTS "root@$HOST" "pct push $vmid /tmp/$(basename "$dest") $dest >/dev/null && rm -f /tmp/$(basename "$dest")"
fi
}
setup_base_runtime() {
local vmid="$1"
run_remote "$vmid" "export DEBIAN_FRONTEND=noninteractive && apt-get update && apt-get install -y openjdk-21-jre-headless redis-server curl ca-certificates"
run_remote "$vmid" "systemctl enable redis-server --now"
}
deploy_scsm() {
local vmid=5802
setup_base_runtime "$vmid"
push_file "$vmid" "$SCSM_JAR" "/opt/dbis-rtgs/scsm/scsm-app.jar"
local envfile unit
envfile="$(mktemp)"
cat > "$envfile" <<EOF
SERVER_PORT=8080
DB_URL=jdbc:h2:file:/var/lib/dbis-rtgs/scsm/scsm;DB_CLOSE_ON_EXIT=FALSE
DB_USER=sa
DB_PASSWORD=
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
KAFKA_BOOTSTRAP_SERVERS=
SCSM_KAFKA_ENABLED=false
FINERACT_BASE_URL=${SCSM_FINERACT_BASE_URL}
FINERACT_TENANT=${SCSM_FINERACT_TENANT}
FINERACT_USERNAME=${SCSM_FINERACT_USERNAME}
FINERACT_PASSWORD=${SCSM_FINERACT_PASSWORD}
FINERACT_OFFICE_ID=1
EOF
push_file "$vmid" "$envfile" "/etc/dbis-rtgs/scsm.env"
rm -f "$envfile"
unit="$(mktemp)"
cat > "$unit" <<'EOF'
[Unit]
Description=DBIS RTGS SCSM sidecar
After=network-online.target redis-server.service
Wants=network-online.target
[Service]
User=root
WorkingDirectory=/opt/dbis-rtgs/scsm
EnvironmentFile=/etc/dbis-rtgs/scsm.env
ExecStart=/usr/bin/java -jar /opt/dbis-rtgs/scsm/scsm-app.jar
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
push_file "$vmid" "$unit" "/etc/systemd/system/dbis-rtgs-scsm.service"
rm -f "$unit"
run_remote "$vmid" "mkdir -p /var/lib/dbis-rtgs/scsm /opt/dbis-rtgs/scsm /etc/dbis-rtgs && systemctl daemon-reload && systemctl enable dbis-rtgs-scsm && systemctl restart dbis-rtgs-scsm"
wait_for_health "$vmid" "http://127.0.0.1:8080/actuator/health" "/tmp/scsm-health.json"
}
deploy_funds() {
local vmid=5803
setup_base_runtime "$vmid"
push_file "$vmid" "$FUNDS_JAR" "/opt/dbis-rtgs/funds/funds-app.jar"
local envfile unit
envfile="$(mktemp)"
cat > "$envfile" <<EOF
SERVER_PORT=8080
DB_URL=jdbc:h2:file:/var/lib/dbis-rtgs/funds/funds;DB_CLOSE_ON_EXIT=FALSE
DB_USER=sa
DB_PASSWORD=
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
KAFKA_BOOTSTRAP_SERVERS=
SPRING_AUTOCONFIGURE_EXCLUDE=org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration
FINERACT_BASE_URL=${FUNDS_FINERACT_BASE_URL}
FINERACT_TENANT=${FUNDS_FINERACT_TENANT}
FINERACT_USERNAME=${FUNDS_FINERACT_USERNAME}
FINERACT_PASSWORD=${FUNDS_FINERACT_PASSWORD}
FINERACT_OFFICE_ID=${FUNDS_FINERACT_OFFICE_ID}
FINERACT_DEFAULT_DEBIT_GL_ID=${FUNDS_DEFAULT_DEBIT_GL_ID}
FINERACT_DEFAULT_CREDIT_GL_ID=${FUNDS_DEFAULT_CREDIT_GL_ID}
FUNDS_KAFKA_ENABLED=false
EOF
push_file "$vmid" "$envfile" "/etc/dbis-rtgs/funds.env"
rm -f "$envfile"
unit="$(mktemp)"
cat > "$unit" <<'EOF'
[Unit]
Description=DBIS RTGS server-funds sidecar
After=network-online.target redis-server.service
Wants=network-online.target
[Service]
User=root
WorkingDirectory=/opt/dbis-rtgs/funds
EnvironmentFile=/etc/dbis-rtgs/funds.env
ExecStart=/usr/bin/java -jar /opt/dbis-rtgs/funds/funds-app.jar
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
push_file "$vmid" "$unit" "/etc/systemd/system/dbis-rtgs-funds.service"
rm -f "$unit"
run_remote "$vmid" "mkdir -p /var/lib/dbis-rtgs/funds /opt/dbis-rtgs/funds /etc/dbis-rtgs && systemctl daemon-reload && systemctl enable dbis-rtgs-funds && systemctl restart dbis-rtgs-funds"
wait_for_health "$vmid" "http://127.0.0.1:8080/actuator/health" "/tmp/funds-health.json"
}
deploy_xau() {
local vmid=5804
setup_base_runtime "$vmid"
push_file "$vmid" "$XAU_JAR" "/opt/dbis-rtgs/xau/off-ledger-2-on-ledger-sidecar.jar"
local envfile unit
envfile="$(mktemp)"
cat > "$envfile" <<EOF
SERVER_PORT=8080
FINERACT_BASE_URL=${XAU_FINERACT_BASE_URL}
FINERACT_TENANT=${XAU_FINERACT_TENANT}
FINERACT_USERNAME=${XAU_FINERACT_USERNAME}
FINERACT_PASSWORD=${XAU_FINERACT_PASSWORD}
FINERACT_OFFICE_ID=${XAU_FINERACT_OFFICE_ID}
XAU_FINERACT_ENABLED=true
XAU_FEED_URL=${XAU_FEED_URL}
XAU_STUB_PRICE=${XAU_STUB_PRICE}
GL_ASSETS_GOLD_COLLATERAL_ENCUMBERED=${XAU_GL_GOLD_COLLATERAL_ENCUMBERED}
GL_ASSETS_GOLD_COLLATERAL_FREE=${XAU_GL_GOLD_COLLATERAL_FREE}
GL_ASSETS_OFF_LEDGER_SETTLEMENT_CLEARING=${XAU_GL_OFF_LEDGER_SETTLEMENT_CLEARING}
GL_ASSETS_BORROWER_RECEIVABLE=${XAU_GL_BORROWER_RECEIVABLE}
GL_LIABILITIES_TEMPORARY_CREDIT_ISSUED=${XAU_GL_TEMPORARY_CREDIT_ISSUED}
GL_LIABILITIES_TEMPORARY_DEBT_PAYABLE=${XAU_GL_TEMPORARY_DEBT_PAYABLE}
GL_CONTROL_CONVERSION_CONTROL_SUSPENSE=${XAU_GL_CONVERSION_CONTROL_SUSPENSE}
JAVA_TOOL_OPTIONS=-Dgl.assets.goldCollateralEncumbered=${XAU_GL_GOLD_COLLATERAL_ENCUMBERED}\ -Dgl.assets.goldCollateralFree=${XAU_GL_GOLD_COLLATERAL_FREE}\ -Dgl.assets.offLedgerSettlementClearing=${XAU_GL_OFF_LEDGER_SETTLEMENT_CLEARING}\ -Dgl.assets.borrowerReceivable=${XAU_GL_BORROWER_RECEIVABLE}\ -Dgl.liabilities.temporaryCreditIssued=${XAU_GL_TEMPORARY_CREDIT_ISSUED}\ -Dgl.liabilities.temporaryDebtPayable=${XAU_GL_TEMPORARY_DEBT_PAYABLE}\ -Dgl.control.conversionControlSuspense=${XAU_GL_CONVERSION_CONTROL_SUSPENSE}
CHAIN138_SETTLEMENT_ENABLED=true
CHAIN138_RPC_URL=${CHAIN138_RPC_URL:-${RPC_URL_138:-}}
CHAIN138_SETTLEMENT_PRIVATE_KEY=${CHAIN138_SETTLEMENT_PRIVATE_KEY}
CHAIN138_SETTLEMENT_WAIT_FOR_RECEIPT=${CHAIN138_SETTLEMENT_WAIT_FOR_RECEIPT}
SETTLEMENT_REGISTRY_ADDRESS=${SETTLEMENT_REGISTRY_ADDRESS:-${MERCHANT_SETTLEMENT_REGISTRY:-}}
CHAIN138_SETTLEMENT_MERCHANT_ADDRESS=${CHAIN138_SETTLEMENT_MERCHANT_ADDRESS}
EOF
push_file "$vmid" "$envfile" "/etc/dbis-rtgs/xau.env"
rm -f "$envfile"
unit="$(mktemp)"
cat > "$unit" <<'EOF'
[Unit]
Description=DBIS RTGS XAU conversion sidecar
After=network-online.target
Wants=network-online.target
[Service]
User=root
WorkingDirectory=/opt/dbis-rtgs/xau
EnvironmentFile=/etc/dbis-rtgs/xau.env
ExecStart=/usr/bin/java -jar /opt/dbis-rtgs/xau/off-ledger-2-on-ledger-sidecar.jar
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
push_file "$vmid" "$unit" "/etc/systemd/system/dbis-rtgs-xau.service"
rm -f "$unit"
run_remote "$vmid" "mkdir -p /opt/dbis-rtgs/xau /etc/dbis-rtgs && systemctl daemon-reload && systemctl enable dbis-rtgs-xau && systemctl restart dbis-rtgs-xau"
wait_for_health "$vmid" "http://127.0.0.1:8080/actuator/health" "/tmp/xau-health.json"
}
echo "=== Deploy DBIS RTGS first-slice sidecars ==="
echo "Host: $HOST"
echo
if target_enabled scsm; then
deploy_scsm
fi
if target_enabled funds; then
deploy_funds
fi
if target_enabled xau; then
deploy_xau
fi
echo
echo "Done."

View File

@@ -0,0 +1,178 @@
#!/usr/bin/env bash
set -euo pipefail
# Deploy later-phase DBIS RTGS sidecars when artifacts are available.
# Usage:
# ./scripts/deployment/deploy-dbis-rtgs-later-phase-sidecars.sh [--dry-run]
HOST="${PROXMOX_HOST_R630_02:-192.168.11.12}"
SSH_OPTS="-o BatchMode=yes -o ConnectTimeout=15 -o StrictHostKeyChecking=accept-new"
SECT_VMID="${RTGS_SECURITIES_VMID:-5808}"
CARD_VMID="${RTGS_CARDNET_VMID:-5809}"
MT103_VMID="${RTGS_MT103_VMID:-5810}"
SECT_JAR="${RTGS_SECURITIES_JAR:-/home/intlc/projects/HYBX_Sidecars/securities-sidecar/securities-app/target/securities-app-1.0.0-SNAPSHOT.jar}"
CARD_JAR="${RTGS_CARDNET_JAR:-/home/intlc/projects/HYBX_Sidecars/card-networks-sidecar/cardnet-app/target/cardnet-app-1.0.0-SNAPSHOT.jar}"
MT103_BIN="${RTGS_MT103_BIN:-/home/intlc/projects/HYBX_Sidecars/mt103-hardcopy-sidecar/server}"
OMNL_BASE_URL="${OMNL_FINERACT_BASE_URL:-http://192.168.11.85:8080/fineract-provider/api/v1}"
OMNL_TENANT="${OMNL_FINERACT_TENANT:-omnl}"
OMNL_USER="${OMNL_FINERACT_USER:-}"
OMNL_PASSWORD="${OMNL_FINERACT_PASSWORD:-}"
MT103_DATABASE_URL="${MT103_DATABASE_URL:-postgres://localhost/mt103_sidecar?sslmode=disable}"
DRY_RUN=false
if [[ "${1:-}" == "--dry-run" ]]; then
DRY_RUN=true
fi
run_remote() {
local vmid="$1"
local cmd="$2"
if $DRY_RUN; then
echo "[DRY-RUN][CT $vmid] $cmd"
else
ssh $SSH_OPTS "root@$HOST" "pct exec $vmid -- bash -lc $(printf '%q' "$cmd")"
fi
}
push_file() {
local vmid="$1"
local src="$2"
local dest="$3"
if $DRY_RUN; then
echo "[DRY-RUN][CT $vmid] copy $src -> $dest"
else
ssh $SSH_OPTS "root@$HOST" "pct exec $vmid -- mkdir -p $(dirname "$dest")"
ssh $SSH_OPTS "root@$HOST" "cat > /tmp/$(basename "$dest")" < "$src"
ssh $SSH_OPTS "root@$HOST" "pct push $vmid /tmp/$(basename "$dest") $dest >/dev/null && rm -f /tmp/$(basename "$dest")"
fi
}
require_file() {
local path="$1"
if [[ ! -f "$path" ]]; then
echo "Missing required artifact: $path" >&2
exit 1
fi
}
setup_java_runtime() {
local vmid="$1"
run_remote "$vmid" "export DEBIAN_FRONTEND=noninteractive && apt-get update && apt-get install -y openjdk-21-jre-headless redis-server curl ca-certificates"
run_remote "$vmid" "systemctl enable redis-server --now"
}
setup_go_runtime() {
local vmid="$1"
run_remote "$vmid" "export DEBIAN_FRONTEND=noninteractive && apt-get update && apt-get install -y curl ca-certificates"
}
deploy_java_sidecar() {
local vmid="$1"
local svc="$2"
local jar="$3"
local env_content="$4"
require_file "$jar"
setup_java_runtime "$vmid"
push_file "$vmid" "$jar" "/opt/dbis-rtgs/${svc}/${svc}.jar"
local envfile unit
envfile="$(mktemp)"
cat > "$envfile" <<<"$env_content"
push_file "$vmid" "$envfile" "/etc/dbis-rtgs/${svc}.env"
rm -f "$envfile"
unit="$(mktemp)"
cat > "$unit" <<EOF
[Unit]
Description=DBIS RTGS ${svc}
After=network-online.target redis-server.service
Wants=network-online.target
[Service]
User=root
WorkingDirectory=/opt/dbis-rtgs/${svc}
EnvironmentFile=/etc/dbis-rtgs/${svc}.env
ExecStart=/usr/bin/java -jar /opt/dbis-rtgs/${svc}/${svc}.jar
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
push_file "$vmid" "$unit" "/etc/systemd/system/dbis-rtgs-${svc}.service"
rm -f "$unit"
run_remote "$vmid" "mkdir -p /opt/dbis-rtgs/${svc} /etc/dbis-rtgs /var/lib/dbis-rtgs/${svc} && systemctl daemon-reload && systemctl enable dbis-rtgs-${svc} && systemctl restart dbis-rtgs-${svc}"
}
deploy_mt103() {
require_file "$MT103_BIN"
setup_go_runtime "$MT103_VMID"
push_file "$MT103_VMID" "$MT103_BIN" "/opt/dbis-rtgs/mt103/server"
run_remote "$MT103_VMID" "chmod +x /opt/dbis-rtgs/mt103/server"
local envfile unit
envfile="$(mktemp)"
cat > "$envfile" <<EOF
HTTP_PORT=8080
DATABASE_URL=${MT103_DATABASE_URL}
STORAGE_BASE_PATH=/var/lib/dbis-rtgs/mt103/storage
AUTH_MODE=none
EOF
push_file "$MT103_VMID" "$envfile" "/etc/dbis-rtgs/mt103.env"
rm -f "$envfile"
unit="$(mktemp)"
cat > "$unit" <<'EOF'
[Unit]
Description=DBIS RTGS mt103-hardcopy-sidecar
After=network-online.target
Wants=network-online.target
[Service]
User=root
WorkingDirectory=/opt/dbis-rtgs/mt103
EnvironmentFile=/etc/dbis-rtgs/mt103.env
ExecStart=/opt/dbis-rtgs/mt103/server
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
push_file "$MT103_VMID" "$unit" "/etc/systemd/system/dbis-rtgs-mt103.service"
rm -f "$unit"
run_remote "$MT103_VMID" "mkdir -p /opt/dbis-rtgs/mt103 /etc/dbis-rtgs /var/lib/dbis-rtgs/mt103/storage && systemctl daemon-reload && systemctl enable dbis-rtgs-mt103 && systemctl restart dbis-rtgs-mt103"
}
deploy_java_sidecar "$SECT_VMID" "securities" "$SECT_JAR" "$(cat <<EOF
SERVER_PORT=8080
DB_URL=jdbc:h2:file:/var/lib/dbis-rtgs/securities/securities;DB_CLOSE_ON_EXIT=FALSE
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
KAFKA_BOOTSTRAP_SERVERS=
FINERACT_BASE_URL=${OMNL_BASE_URL}
FINERACT_TENANT=${OMNL_TENANT}
FINERACT_USERNAME=${OMNL_USER}
FINERACT_PASSWORD=${OMNL_PASSWORD}
EOF
)"
deploy_java_sidecar "$CARD_VMID" "cardnet" "$CARD_JAR" "$(cat <<EOF
SERVER_PORT=8080
DB_URL=jdbc:h2:file:/var/lib/dbis-rtgs/cardnet/cardnet;DB_CLOSE_ON_EXIT=FALSE
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
KAFKA_BOOTSTRAP_SERVERS=
FINERACT_BASE_URL=${OMNL_BASE_URL}
FINERACT_TENANT=${OMNL_TENANT}
FINERACT_USERNAME=${OMNL_USER}
FINERACT_PASSWORD=${OMNL_PASSWORD}
EOF
)"
deploy_mt103
echo "DBIS RTGS later-phase sidecar deployment complete."

View File

@@ -0,0 +1,70 @@
#!/usr/bin/env bash
# Site-wide Gitea Actions runner: use admin GITEA_TOKEN from root .env to fetch the
# instance registration token, then register act_runner on dev-vm (5700) with ubuntu-latest.
#
# Requires: SSH to Proxmox (BatchMode), CT 5700 running Gitea + act_runner under /opt/act_runner.
# Env (from .env via load-project-env): GITEA_TOKEN, optional GITEA_URL, RUNNER_LABELS,
# RUNNER_FORCE_REREGISTER=1 to drop .runner and re-register, DEV_VM_VMID (default 5700).
#
# Usage (repo root):
# bash scripts/dev-vm/bootstrap-gitea-act-runner-site-wide.sh
# RUNNER_FORCE_REREGISTER=1 bash scripts/dev-vm/bootstrap-gitea-act-runner-site-wide.sh
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
# Load only root .env + IPs (avoid full load-project-env if another dotenv exits non-zero under set -e).
[[ -f "${PROJECT_ROOT}/.env" ]] && set -a && source "${PROJECT_ROOT}/.env" && set +a
[[ -f "${PROJECT_ROOT}/config/ip-addresses.conf" ]] && source "${PROJECT_ROOT}/config/ip-addresses.conf"
PROXMOX_HOST_R630_01="${PROXMOX_R630_01:-${PROXMOX_HOST_R630_01:-192.168.11.11}}"
PROXMOX_HOST_R630_02="${PROXMOX_R630_02:-${PROXMOX_HOST_R630_02:-192.168.11.12}}"
PROXMOX_HOST_ML110="${PROXMOX_ML110:-${PROXMOX_HOST_ML110:-192.168.11.10}}"
get_host_for_vmid() {
case "$1" in
5000|5700|7810|2201|2303|2401|6200|6201|10234|10237|5800|5801) echo "${PROXMOX_HOST_R630_02}";;
5400|5401|5402|5403|5410|5411|5412|5413|5414|5415|5416|5417|5418|5419|5420|5421|5422|5423|5424|5425|5440|5441|5442|5443|5444|5445|5446|5447|5448|5449|5450|5451|5452|5453|5454|5455|5470|5471|5472|5473|5474|5475|5476) echo "${PROXMOX_HOST_R630_02}";;
2101|10130|10150|10151|106|107|108|10000|10001|10020|10100|10101|10120|10233|10235) echo "${PROXMOX_HOST_R630_01}";;
2301|2400|1504|2503|2504|2505) echo "${PROXMOX_HOST_ML110}";;
*) echo "${PROXMOX_HOST_R630_01}";;
esac
}
GITEA_URL="${GITEA_URL:-https://gitea.d-bis.org}"
GITEA_URL="${GITEA_URL%/}"
VMID="${DEV_VM_VMID:-5700}"
RUNNER_LABELS="${RUNNER_LABELS:-ubuntu-latest}"
if [[ -z "${GITEA_TOKEN:-}" ]]; then
echo "ERROR: GITEA_TOKEN not set (root .env)." >&2
exit 1
fi
REG_JSON="$(curl -sS -H "Authorization: token ${GITEA_TOKEN}" \
"${GITEA_URL}/api/v1/admin/runners/registration-token")"
REG_TOKEN="$(printf '%s' "$REG_JSON" | sed -n 's/.*"token"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p')"
if [[ -z "$REG_TOKEN" || "$REG_TOKEN" == "null" ]]; then
echo "ERROR: Could not get admin registration token. Response:" >&2
printf '%s\n' "$REG_JSON" >&2
echo "Ensure GITEA_TOKEN is an admin token with access to GET /api/v1/admin/runners/registration-token" >&2
exit 1
fi
PROXMOX_HOST="$(get_host_for_vmid "$VMID")"
echo "Using Proxmox host ${PROXMOX_HOST} for VMID ${VMID}."
if [[ "${RUNNER_FORCE_REREGISTER:-0}" == "1" ]]; then
ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new "root@${PROXMOX_HOST}" \
"pct exec ${VMID} -- bash -lc 'rm -f /opt/act_runner/.runner; systemctl stop act-runner 2>/dev/null || true'"
fi
# Pass registration token into the container without embedding raw secret in ssh argv (still reversible from b64).
TB64="$(printf '%s' "$REG_TOKEN" | base64 | tr -d '\n')"
ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new "root@${PROXMOX_HOST}" \
"pct exec ${VMID} -- bash -c 'export GITEA_RUNNER_REGISTRATION_TOKEN=\$(printf %s \"${TB64}\" | base64 -d); export RUNNER_LABELS=\"${RUNNER_LABELS}\"; bash -s'" \
< "${SCRIPT_DIR}/setup-act-runner.sh"
ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new "root@${PROXMOX_HOST}" \
"pct exec ${VMID} -- bash -s" < "${SCRIPT_DIR}/install-act-runner-systemd.sh"
echo "Done. Check Gitea Admin → Actions → Runners for an online runner with labels including: ${RUNNER_LABELS}"

View File

@@ -0,0 +1,45 @@
#!/usr/bin/env bash
# Install systemd unit for Gitea act_runner on the Gitea host (e.g. dev-vm 5700).
# Run inside the container, or: ssh root@<proxmox> "pct exec 5700 -- bash -s" < scripts/dev-vm/install-act-runner-systemd.sh
#
# Optional env:
# WORK_DIR default /opt/act_runner
# GITEA_ACTION_URL default http://127.0.0.1:3000 (same host as Gitea)
set -euo pipefail
WORK_DIR="${WORK_DIR:-/opt/act_runner}"
GITEA_ACTION_URL="${GITEA_ACTION_URL:-http://127.0.0.1:3000}"
if [ ! -x "${WORK_DIR}/act_runner" ]; then
echo "Missing ${WORK_DIR}/act_runner — run setup-act-runner.sh with GITEA_RUNNER_REGISTRATION_TOKEN first."
exit 1
fi
if [ ! -f "${WORK_DIR}/.runner" ]; then
echo "Missing ${WORK_DIR}/.runner — register first: GITEA_RUNNER_REGISTRATION_TOKEN=... bash setup-act-runner.sh"
exit 1
fi
cat > /etc/systemd/system/act-runner.service << EOF
[Unit]
Description=Gitea act_runner
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=${WORK_DIR}
ExecStart=${WORK_DIR}/act_runner daemon
Restart=on-failure
RestartSec=10
Environment=GITEA_ACTION_URL=${GITEA_ACTION_URL}
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable act-runner
systemctl restart act-runner
systemctl --no-pager status act-runner

View File

@@ -6,9 +6,12 @@
set -euo pipefail
ACT_RUNNER_VERSION="${ACT_RUNNER_VERSION:-0.2.13}"
INSTANCE="${INSTANCE:-http://192.168.11.59:3000}"
# Gitea root URL as seen from this host (same LXC as Gitea → 127.0.0.1)
INSTANCE="${INSTANCE:-http://127.0.0.1:3000}"
WORK_DIR="${WORK_DIR:-/opt/act_runner}"
TOKEN="${GITEA_RUNNER_REGISTRATION_TOKEN:-}"
# Workflows commonly use runs-on: ubuntu-latest; labels must match.
RUNNER_LABELS="${RUNNER_LABELS:-ubuntu-latest}"
if [ -z "$TOKEN" ]; then
echo "Set GITEA_RUNNER_REGISTRATION_TOKEN"
@@ -29,6 +32,6 @@ fi
chmod +x ./act_runner
if [ ! -f .runner ]; then
./act_runner register --no-interactive --instance "$INSTANCE" --token "$TOKEN"
./act_runner register --no-interactive --instance "$INSTANCE" --token "$TOKEN" --labels "$RUNNER_LABELS"
fi
echo "Ready. Run: ./act_runner daemon"

View File

@@ -0,0 +1,149 @@
#!/usr/bin/env bash
# Regenerate docs/02-architecture/DBIS_NODE_ROLE_MATRIX.md body tables from
# config/proxmox-operational-template.json (run from repo root).
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
JSON="$ROOT/config/proxmox-operational-template.json"
OUT="$ROOT/docs/02-architecture/DBIS_NODE_ROLE_MATRIX.md"
if ! command -v jq &>/dev/null; then
echo "jq required" >&2
exit 1
fi
TMP="$(mktemp)"
trap 'rm -f "$TMP"' EXIT
jq -r '
def vstatus:
if .category == "besu_validator" then "QBFT signer"
elif .category == "besu_sentry" then "Sentry (no signer)"
elif (.category | test("^rpc")) then "RPC only"
else "N/A"
end;
def ntype:
if .category == "besu_validator" then "Besu validator"
elif .category == "besu_sentry" then "Besu sentry"
elif .category == "rpc_core" or .category == "rpc_public" or .category == "rpc_private" or .category == "rpc_named" or .category == "rpc_thirdweb" or .category == "rpc_alltra_hybx" then "Besu RPC (\(.category))"
elif .category == "dlt" and (.hostname | test("fabric")) then "Fabric"
elif .category == "dlt" and (.hostname | test("indy")) then "Indy"
elif .category == "firefly" then "FireFly"
elif .category == "explorer" then "Blockscout"
elif .category == "npmplus" then "NPMplus ingress"
elif .category == "infra" then "Infra LXC"
elif .category == "monitoring" and (.hostname | test("cacti")) then "Cacti"
elif .category == "monitoring" then "Monitoring"
elif .category == "oracle" then "Oracle publisher"
elif .category == "ccip" then "CCIP monitor"
elif .category == "tunnel" then "Cloudflare tunnel"
elif .category == "ml" then "ML node"
elif .category == "vault" then "HashiCorp Vault"
elif .category == "order" then "The Order service"
elif .category == "sankofa_phoenix" then "Sankofa / Phoenix"
elif .category == "mim4u" then "MIM4U"
elif .category == "dbis" then "DBIS stack"
elif .category == "mifos" then "Mifos"
elif .category == "dapp" then "DApp"
elif .category == "dev" then "Dev"
elif .category == "ai_infra" then "AI infra"
elif .category == "defi" then "DeFi"
elif .category == "general" then "General CT"
elif .category == "legacy_proxy" then "Legacy NPM"
else .category
end;
def stier:
if .category == "besu_validator" or .category == "besu_sentry" then "validator-tier"
elif (.category | test("^rpc")) then "DMZ / RPC exposure"
elif .category == "npmplus" or .category == "tunnel" then "edge ingress"
elif .category == "dlt" or .category == "firefly" then "identity / workflow DLT"
elif .category == "vault" or .category == "infra" then "management / secrets"
elif .category == "order" or .category == "sankofa_phoenix" or .category == "dbis" then "application"
else "standard internal"
end;
([.services[] | select(.ipv4 != null) | .ipv4] | group_by(.) | map(select(length > 1) | .[0])) as $dup_ips
| .services[]
| (.ipv4) as $ip
| [(.vmid // "—"), .hostname, ($ip // "—"), (if ($ip != null and ($dup_ips | index($ip))) then "shared / non-concurrent mapping — verify live owner" else "unique in template" end), ntype, (.runtime_state // "unspecified"), "TBD", "TBD", (.preferred_node // "—"), vstatus, stier]
| @tsv
' "$JSON" | sort -t$'\t' -k1,1n > "$TMP"
UPDATED="$(date -u +%Y-%m-%d)"
{
cat <<EOF
# DBIS Node Role Matrix
**Last updated:** ${UPDATED} (UTC) — regenerate machine-derived rows: \`bash scripts/docs/generate-dbis-node-role-matrix-md.sh\`
**Status:** Active — infrastructure constitution for DBIS Chain 138 and colocated workloads.
## Purpose
This matrix assigns **node type**, **preferred host placement**, **validator/signing role** (for Besu), and **security tier** per workload. It implements the entity-placement model in [dbis_chain_138_technical_master_plan.md](../../dbis_chain_138_technical_master_plan.md) (Sections 67) in a form operators can maintain.
**Canonical pairs (keep in sync):**
- Human detail and status: [ALL_VMIDS_ENDPOINTS.md](../04-configuration/ALL_VMIDS_ENDPOINTS.md)
- Machine-readable services: [config/proxmox-operational-template.json](../../config/proxmox-operational-template.json)
When you change VMID, IP, hostname, or placement, update **ALL_VMIDS** and **operational-template.json** first, then regenerate the table below with this script (or edit the static sections manually).
## Columns
| Column | Meaning |
|--------|---------|
| **Entity owner** | DBIS Core, Central Bank, IFI, Regional Operator, etc. — use **TBD** until governance assigns. |
| **Region** | Geographic or site label — **TBD** until multi-site is formalized. |
| **IP note** | Flags duplicate IPv4 entries in the planning template. A duplicate means **shared or historical mapping**, not concurrent ownership — verify live owner in ALL_VMIDS or on-cluster. |
| **Runtime state** | Current disposition from the planning template, e.g. active, placeholder CT only, or retired standby. |
| **Preferred host** | Preferred Proxmox node (\`r630-01\`, \`r630-02\`, \`ml110\`, \`any\`). This is a planning target, not an assertion of current placement. |
| **Validator / signing** | For Chain 138 Besu: QBFT signer, sentry (no signer), RPC-only, or N/A. |
| **Security tier** | High-level zone: validator-tier, DMZ/RPC, edge ingress, identity/DLT, application, etc. |
## Proxmox hypervisor nodes
| Hostname | MGMT IP | Cluster | Role (summary) |
|----------|---------|---------|------------------|
EOF
jq -r '.proxmox_nodes[] | "| \(.hostname) | \(.mgmt_ipv4) | \(.cluster_name // "h — verify") | \(.role) |"' "$JSON"
cat <<'MID'
## Workloads (from operational template)
Machine-derived rows below come from `services[]` in `config/proxmox-operational-template.json`. Duplicate IPv4 notes are warnings that the planning template still contains alternative or legacy ownership for the same address; they must not be read as concurrent live allocations.
| VMID | Hostname | IPv4 | IP note | Node type | Runtime state | Entity owner | Region | Preferred host | Validator / signing | Security tier |
|------|----------|------|---------|-----------|---------------|--------------|--------|----------------|---------------------|---------------|
MID
while IFS=$'\t' read -r vmid host ip ipnote ntype rstate ent reg hw vst stier; do
echo "| $vmid | $host | $ip | $ipnote | $ntype | $rstate | $ent | $reg | $hw | $vst | $stier |"
done < "$TMP"
cat <<'FOOT'
## Supplementary rows (not in template JSON)
These appear in [ALL_VMIDS_ENDPOINTS.md](../04-configuration/ALL_VMIDS_ENDPOINTS.md) but are not modeled as `services[]` entries in `proxmox-operational-template.json`. They are **manual supplements**, not generator-backed source of truth.
| VMID | Hostname | IPv4 | IP note | Node type | Runtime state | Entity owner | Region | Preferred host | Validator / signing | Security tier |
|------|----------|------|---------|-----------|---------------|--------------|--------|----------------|---------------------|---------------|
| 106 | redis-rpc-translator | 192.168.11.110 | manual supplement | RPC translator (Redis) | manual supplement | TBD | TBD | r630-01 (per ALL_VMIDS) | N/A | DMZ / RPC exposure |
| 107 | web3signer-rpc-translator | 192.168.11.111 | manual supplement | RPC translator (Web3Signer) | manual supplement | TBD | TBD | r630-01 | N/A | DMZ / RPC exposure |
| 108 | vault-rpc-translator | 192.168.11.112 | manual supplement | RPC translator (Vault) | manual supplement | TBD | TBD | r630-01 | N/A | management / secrets |
## Host-level services (no VMID)
| Name | Location | Node type | Notes |
|------|----------|-----------|-------|
| CCIP relay | r630-01 host `/opt/smom-dbis-138/services/relay` | Cross-chain relay | Uses RPC (e.g. VMID 2201); see [NETWORK_CONFIGURATION_MASTER.md](../11-references/NETWORK_CONFIGURATION_MASTER.md), [docs/07-ccip/](../07-ccip/). |
## Related
- [dbis_chain_138_technical_master_plan.md](../../dbis_chain_138_technical_master_plan.md)
- [CHAIN138_CANONICAL_NETWORK_ROLES_VALIDATORS_SENTRY_AND_RPC.md](CHAIN138_CANONICAL_NETWORK_ROLES_VALIDATORS_SENTRY_AND_RPC.md)
- [VMID_ALLOCATION_FINAL.md](VMID_ALLOCATION_FINAL.md)
FOOT
} > "$OUT"
echo "Wrote $OUT"

View File

@@ -75,7 +75,7 @@ get_host_for_vmid() {
case "$vmid" in
10130|10150|10151|106|107|108|10000|10001|10020|10100|10101|10120|10233|10235) echo "${PROXMOX_HOST_R630_01}";;
2101) echo "${PROXMOX_HOST_R630_01}";;
5000|7810|2201|2303|2401|6200|6201|10234|10237|5800|5801) echo "${PROXMOX_HOST_R630_02}";;
5000|5700|7810|2201|2303|2401|6200|6201|10234|10237|5800|5801) echo "${PROXMOX_HOST_R630_02}";;
2301|2400|1504|2503|2504|2505) echo "${PROXMOX_HOST_ML110}";;
5400|5401|5402|5403|5410|5411|5412|5413|5414|5415|5416|5417|5418|5419|5420|5421|5422|5423|5424|5425|5440|5441|5442|5443|5444|5445|5446|5447|5448|5449|5450|5451|5452|5453|5454|5455|5470|5471|5472|5473|5474|5475|5476) echo "${PROXMOX_HOST_R630_02}";;
*) echo "${PROXMOX_HOST_R630_01:-${PROXMOX_R630_02}}";;

View File

@@ -48,8 +48,8 @@ NODES[r630-02]="${PROXMOX_HOST_R630_02:-192.168.11.12}:password"
NODES[r630-03]="${IP_SERVICE_13:-${IP_SERVICE_13:-${IP_SERVICE_13:-${IP_SERVICE_13:-${IP_SERVICE_13:-${IP_SERVICE_13:-192.168.11.13}}}}}}:L@kers2010"
NODES[r630-04]="${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-192.168.11.14}}}}}}:L@kers2010"
# Alert tracking
declare -a ALERTS
# Alert tracking (must stay in main shell — no pipe-|while subshell)
ALERTS=()
# SSH helper function
ssh_node() {
@@ -166,22 +166,22 @@ monitor_node() {
return 1
fi
# Process each storage line (skip header)
echo "$storage_status" | tail -n +2 | while IFS= read -r line; do
# Process each storage line (skip header) — process substitution keeps ALERTS in this shell
while IFS= read -r line; do
if [ -n "$line" ]; then
check_storage_usage "$hostname" "$line"
fi
done
done < <(echo "$storage_status" | tail -n +2)
# Check volume groups
local vgs_info=$(ssh_node "$hostname" 'vgs --units g --noheadings -o vg_name,vg_size,vg_free 2>/dev/null' || echo "")
if [ -n "$vgs_info" ]; then
echo "$vgs_info" | while IFS= read -r line; do
while IFS= read -r line; do
if [ -n "$line" ]; then
check_vg_free_space "$hostname" "$line"
fi
done
done < <(echo "$vgs_info")
fi
# Log storage status
@@ -199,7 +199,7 @@ monitor_node() {
# Send alerts (can be extended to email, Slack, etc.)
send_alerts() {
if [ ${#ALERTS[@]} -eq 0 ]; then
if [[ ${#ALERTS[@]} -eq 0 ]]; then
log_success "No storage alerts"
return 0
fi
@@ -244,7 +244,8 @@ generate_summary() {
echo "=== Proxmox Storage Summary $(date) ==="
echo ""
echo "Nodes Monitored:"
for hostname in "${!NODES[@]}"; do
for hostname in ml110 r630-01 r630-02 r630-03 r630-04; do
[[ -n "${NODES[$hostname]:-}" ]] || continue
if check_node "$hostname"; then
echo "$hostname"
else
@@ -280,9 +281,10 @@ main() {
echo "Date: $(date)"
echo ""
# Monitor all nodes
for hostname in "${!NODES[@]}"; do
monitor_node "$hostname"
# Monitor all nodes (fixed order for readable logs; optional nodes may be unreachable)
for hostname in ml110 r630-01 r630-02 r630-03 r630-04; do
[[ -n "${NODES[$hostname]:-}" ]] || continue
monitor_node "$hostname" || true
done
# Send alerts
@@ -297,7 +299,8 @@ main() {
status)
# Show current status
echo "=== Current Storage Status ==="
for hostname in "${!NODES[@]}"; do
for hostname in ml110 r630-01 r630-02 r630-03 r630-04; do
[[ -n "${NODES[$hostname]:-}" ]] || continue
if check_node "$hostname"; then
echo ""
echo "--- $hostname ---"

View File

@@ -1,52 +1,31 @@
#!/usr/bin/env bash
# Upgrade all Besu nodes to the latest (or specified) version.
# Requires: SSH to Proxmox host, curl/wget, enough disk in containers.
# Upgrade all running Besu containers to the requested version.
# Installs Java 21 where needed, preserves the previous /opt/besu-* directory for rollback,
# and restarts the detected Besu systemd unit in each container.
#
# Usage:
# ./scripts/upgrade-besu-all-nodes.sh # upgrade to latest (25.12.0)
# ./scripts/upgrade-besu-all-nodes.sh --dry-run # show what would be done
# BESU_VERSION=25.11.0 ./scripts/upgrade-besu-all-nodes.sh
# Optional: pre-download to avoid long run (script uses $LOCAL_CACHE/besu-${BESU_VERSION}.tar.gz):
# curl -sSL -o /tmp/besu-25.12.0.tar.gz https://github.com/hyperledger/besu/releases/download/25.12.0/besu-25.12.0.tar.gz
# bash scripts/upgrade-besu-all-nodes.sh
# bash scripts/upgrade-besu-all-nodes.sh --dry-run
# BESU_VERSION=25.12.0 bash scripts/upgrade-besu-all-nodes.sh
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
source "${PROJECT_ROOT}/config/ip-addresses.conf" 2>/dev/null || true
if [ -f "$PROJECT_ROOT/config/ip-addresses.conf" ]; then
# shellcheck source=../config/ip-addresses.conf
source "$PROJECT_ROOT/config/ip-addresses.conf"
fi
PROXMOX_HOST="${PROXMOX_HOST:-${PROXMOX_HOST_ML110:-192.168.11.10}}"
# Latest stable as of 2025-12; EIP-7702 requires >= 24.1.0
BESU_VERSION="${BESU_VERSION:-25.12.0}"
BESU_TAR="besu-${BESU_VERSION}.tar.gz"
BESU_DIR="besu-${BESU_VERSION}"
BESU_DIR="/opt/besu-${BESU_VERSION}"
DOWNLOAD_URL="${BESU_DOWNLOAD_URL:-https://github.com/hyperledger/besu/releases/download/${BESU_VERSION}/${BESU_TAR}}"
JAVA21_FALLBACK_URL="${JAVA21_FALLBACK_URL:-https://api.adoptium.net/v3/binary/latest/21/ga/linux/x64/jre/hotspot/normal/eclipse}"
RPC_HTTP_MAX_ACTIVE_CONNECTIONS="${RPC_HTTP_MAX_ACTIVE_CONNECTIONS:-256}"
RPC_WS_MAX_ACTIVE_CONNECTIONS="${RPC_WS_MAX_ACTIVE_CONNECTIONS:-256}"
LOCAL_CACHE="${LOCAL_CACHE:-/tmp}"
DRY_RUN=false
for arg in "$@"; do
[ "$arg" = "--dry-run" ] && DRY_RUN=true
done
[[ "${1:-}" == "--dry-run" ]] && DRY_RUN=true
# Same node list and services as restart-all-besu-services.sh
declare -A NODE_SERVICES=(
["1000"]="besu-validator"
["1001"]="besu-validator"
["1002"]="besu-validator"
["1003"]="besu-validator"
["1004"]="besu-validator"
["1500"]="besu-sentry"
["1501"]="besu-sentry"
["1502"]="besu-sentry"
["1503"]="besu-sentry"
["2101"]="besu-rpc"
["2400"]="besu-rpc"
["2401"]="besu-rpc"
["2402"]="besu-rpc"
)
SSH_OPTS=(-o ConnectTimeout=20 -o ServerAliveInterval=15 -o ServerAliveCountMax=3 -o StrictHostKeyChecking=accept-new)
RED='\033[0;31m'
GREEN='\033[0;32m'
@@ -54,120 +33,245 @@ YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
log_ok() { echo -e "${GREEN}[OK]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_err() { echo -e "${RED}[ERROR]${NC} $1"; }
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
log_ok() { echo -e "${GREEN}[OK]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_err() { echo -e "${RED}[ERROR]${NC} $1"; }
is_running() {
local vmid=$1
ssh -o ConnectTimeout=3 -o StrictHostKeyChecking=accept-new root@"$PROXMOX_HOST" \
"pct status $vmid 2>/dev/null" | grep -q running
declare -A HOST_BY_VMID
for v in 1000 1001 1002 1500 1501 1502 2101; do HOST_BY_VMID[$v]="${PROXMOX_R630_01:-${PROXMOX_HOST_R630_01:-192.168.11.11}}"; done
for v in 2201 2303 2401; do HOST_BY_VMID[$v]="${PROXMOX_R630_02:-${PROXMOX_HOST_R630_02:-192.168.11.12}}"; done
for v in 1003 1004 1503 1504 1505 1506 1507 1508 2102 2301 2304 2305 2306 2307 2308 2400 2402 2403; do HOST_BY_VMID[$v]="${PROXMOX_ML110:-${PROXMOX_HOST_ML110:-192.168.11.10}}"; done
BESU_VMIDS=(
1000 1001 1002 1003 1004
1500 1501 1502 1503 1504 1505 1506 1507 1508
2101 2102 2201 2301 2303 2304 2305 2306 2307 2308
2400 2401 2402 2403
)
host_ssh() {
local host="$1"
shift
ssh "${SSH_OPTS[@]}" "root@${host}" "$@"
}
# Ensure tarball exists (download to host or use cache)
ensure_tarball() {
local path="$LOCAL_CACHE/$BESU_TAR"
if [ -f "$path" ]; then
log_ok "Using existing $path"
echo "$path"
return
fi
log_info "Downloading $DOWNLOAD_URL ..."
if $DRY_RUN; then
echo ""
return
fi
(cd "$LOCAL_CACHE" && curl -sSfL -o "$BESU_TAR" "$DOWNLOAD_URL") || {
log_err "Download failed"
return 1
}
log_ok "Downloaded $path"
echo "$path"
local path="${LOCAL_CACHE}/${BESU_TAR}"
mkdir -p "$LOCAL_CACHE"
if [[ -f "$path" ]]; then
log_ok "Using existing $path" >&2
printf '%s\n' "$path"
return 0
fi
if $DRY_RUN; then
printf '%s\n' "$path"
return 0
fi
log_info "Downloading ${DOWNLOAD_URL}" >&2
curl -fsSL -o "$path" "$DOWNLOAD_URL"
log_ok "Downloaded $path" >&2
printf '%s\n' "$path"
}
detect_service() {
local host="$1"
local vmid="$2"
host_ssh "$host" "pct exec ${vmid} -- bash -lc 'systemctl list-units --type=service --no-legend 2>/dev/null | awk \"{print \\\$1}\" | grep -iE \"^besu-(validator|sentry|rpc|rpc-core)\\.service$|^besu\\.service$\" | head -1'" 2>/dev/null || true
}
is_running() {
local host="$1"
local vmid="$2"
host_ssh "$host" "pct status ${vmid} 2>/dev/null | awk '{print \$2}'" 2>/dev/null | grep -q '^running$'
}
prepare_host_tarball() {
local host="$1"
local local_path="$2"
local host_tmp="/tmp/${BESU_TAR}"
if $DRY_RUN; then
log_info " [dry-run] would copy ${BESU_TAR} to ${host}:${host_tmp}"
return 0
fi
scp "${SSH_OPTS[@]}" "$local_path" "root@${host}:${host_tmp}" >/dev/null
}
upgrade_node() {
local vmid=$1
local service="${NODE_SERVICES[$vmid]:-besu-rpc}"
local tarball_path="$2"
local host="$1"
local vmid="$2"
local service="$3"
if ! is_running "$vmid"; then
log_warn "VMID $vmid not running skip"
return 0
if ! is_running "$host" "$vmid"; then
log_warn "VMID ${vmid} @ ${host}: not running, skipping"
return 0
fi
if [[ -z "$service" ]]; then
log_warn "VMID ${vmid} @ ${host}: no Besu service detected, skipping"
return 0
fi
log_info "VMID ${vmid} @ ${host}: upgrading ${service} to Besu ${BESU_VERSION}"
if $DRY_RUN; then
log_info " [dry-run] would install Java 21, extract ${BESU_TAR}, switch /opt/besu, restart ${service}"
return 0
fi
host_ssh "$host" "pct push ${vmid} /tmp/${BESU_TAR} /tmp/${BESU_TAR}" >/dev/null
host_ssh "$host" "pct exec ${vmid} -- bash -lc '
set -euo pipefail
if [[ ! -e /opt/besu ]]; then
fallback=\$(find /opt -maxdepth 1 -type d -name \"besu-*\" | sort -V | tail -1)
if [[ -n \"\${fallback:-}\" ]]; then
ln -sfn \"\$fallback\" /opt/besu
chown -h besu:besu /opt/besu 2>/dev/null || true
fi
elif [[ ! -L /opt/besu ]]; then
current_semver=\$(/opt/besu/bin/besu --version 2>/dev/null | grep -Eo \"[0-9]+\\.[0-9]+\\.[0-9]+\" | head -1)
current_version=\"besu-\${current_semver:-}\"
[[ -z \"\${current_version:-}\" ]] && current_version=besu-backup-pre-${BESU_VERSION}
if [[ ! -d \"/opt/\${current_version}\" ]]; then
mv /opt/besu \"/opt/\${current_version}\"
else
rm -rf /opt/besu
fi
ln -sfn \"/opt/\${current_version}\" /opt/besu
chown -h besu:besu /opt/besu 2>/dev/null || true
fi
log_info "VMID $vmid: upgrade to Besu $BESU_VERSION ($service) ..."
if $DRY_RUN; then
log_info " [dry-run] would push $BESU_TAR and extract, switch /opt/besu, restart $service"
return 0
java_major=\$(java -version 2>&1 | sed -n \"1s/.*version \\\"\\([0-9][0-9]*\\).*/\\1/p\")
if [[ -z \"\${java_major:-}\" || \"\$java_major\" -lt 21 ]]; then
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y -qq openjdk-21-jre-headless || true
java_major=\$(java -version 2>&1 | sed -n \"1s/.*version \\\"\\([0-9][0-9]*\\).*/\\1/p\")
if [[ -z \"\${java_major:-}\" || \"\$java_major\" -lt 21 ]]; then
command -v curl >/dev/null 2>&1 || apt-get install -y -qq curl ca-certificates
tmp_jre=/tmp/java21-jre.tar.gz
curl -fsSL -o \"\$tmp_jre\" '${JAVA21_FALLBACK_URL}'
tar -tzf \"\$tmp_jre\" > /tmp/java21-jre.list
extracted_dir=\$(head -1 /tmp/java21-jre.list | cut -d/ -f1)
rm -f /tmp/java21-jre.list
tar -xzf \"\$tmp_jre\" -C /opt
rm -f \"\$tmp_jre\"
ln -sfn \"/opt/\${extracted_dir}\" /opt/java-21
update-alternatives --install /usr/bin/java java /opt/java-21/bin/java 2100
fi
fi
# Copy tarball into container
if ! ssh -o ConnectTimeout=10 -o StrictHostKeyChecking=accept-new root@"$PROXMOX_HOST" \
"pct push $vmid $tarball_path /tmp/$BESU_TAR" 2>/dev/null; then
log_err " Failed to push tarball to $vmid"
return 1
config_file=\$(systemctl cat ${service} | sed -n \"s/.*--config-file=\\\\([^ ]*\\\\).*/\\\\1/p\" | tail -1)
if [[ -n \"\${config_file:-}\" && -f \"\$config_file\" ]]; then
find /etc/besu -maxdepth 1 -type f -name \"*.toml\" -print0 2>/dev/null | while IFS= read -r -d \"\" toml; do
sed -i \
-e \"/^[[:space:]]*miner-enabled[[:space:]]*=.*/d\" \
-e \"/^[[:space:]]*privacy-enabled[[:space:]]*=.*/d\" \
\"\$toml\"
if grep -q \"^rpc-http-enabled=true\" \"\$toml\" && ! grep -q \"^rpc-http-max-active-connections=\" \"\$toml\"; then
tmp=\$(mktemp)
awk \"1; /^rpc-http-port=/{print \\\"rpc-http-max-active-connections=${RPC_HTTP_MAX_ACTIVE_CONNECTIONS}\\\"}\" \"\$toml\" > \"\$tmp\"
cat \"\$tmp\" > \"\$toml\"
rm -f \"\$tmp\"
fi
if grep -q \"^rpc-ws-enabled=true\" \"\$toml\" && ! grep -q \"^rpc-ws-max-active-connections=\" \"\$toml\"; then
tmp=\$(mktemp)
awk \"1; /^rpc-ws-port=/{print \\\"rpc-ws-max-active-connections=${RPC_WS_MAX_ACTIVE_CONNECTIONS}\\\"}\" \"\$toml\" > \"\$tmp\"
cat \"\$tmp\" > \"\$toml\"
rm -f \"\$tmp\"
fi
done
if ! grep -q \"^data-storage-format=\" \"\$config_file\"; then
tmp=\$(mktemp)
awk \"1; /^sync-mode=/{print \\\"data-storage-format=\\\\\\\"FOREST\\\\\\\"\\\"}\" \"\$config_file\" > \"\$tmp\"
cat \"\$tmp\" > \"\$config_file\"
rm -f \"\$tmp\"
fi
fi
# Extract, switch symlink, fix ownership, restart (each step via pct exec to avoid quoting issues)
ssh -o ConnectTimeout=60 -o StrictHostKeyChecking=accept-new root@"$PROXMOX_HOST" \
"pct exec $vmid -- bash -c 'cd /opt && tar -xzf /tmp/$BESU_TAR && rm -f /tmp/$BESU_TAR'" || {
log_err " VMID $vmid: extract failed"
return 1
}
ssh -o ConnectTimeout=10 root@"$PROXMOX_HOST" \
"pct exec $vmid -- bash -c 'cd /opt && rm -f besu && ln -sf $BESU_DIR besu && chown -R besu:besu $BESU_DIR besu 2>/dev/null || true'" || true
ssh -o ConnectTimeout=15 root@"$PROXMOX_HOST" \
"pct exec $vmid -- systemctl restart ${service}.service" || {
log_err " VMID $vmid: restart failed"
return 1
}
sleep 3
local active
active=$(ssh -o ConnectTimeout=5 root@"$PROXMOX_HOST" "pct exec $vmid -- systemctl is-active ${service}.service 2>/dev/null" || echo "unknown")
if [ "$active" = "active" ]; then
log_ok " VMID $vmid upgraded and $service active"
return 0
cd /opt
if [[ ! -d ${BESU_DIR} ]]; then
tar -xzf /tmp/${BESU_TAR} -C /opt
fi
log_err " VMID $vmid: service status after restart: $active"
return 1
rm -f /tmp/${BESU_TAR}
ln -sfn ${BESU_DIR} /opt/besu
chown -h besu:besu /opt/besu 2>/dev/null || true
chown -R besu:besu ${BESU_DIR} /opt/besu-* 2>/dev/null || true
systemctl restart ${service}
'" || return 1
local active version
active=""
for _ in $(seq 1 24); do
active="$(host_ssh "$host" "pct exec ${vmid} -- systemctl is-active ${service}" 2>/dev/null || true)"
[[ "$active" == "active" ]] && break
sleep 5
done
version="$(host_ssh "$host" "pct exec ${vmid} -- bash -lc '/opt/besu/bin/besu --version 2>/dev/null | grep -m1 \"besu/\" || true'" 2>/dev/null || true)"
if [[ "$active" == "active" ]]; then
log_ok " VMID ${vmid}: ${service} active (${version:-version unavailable})"
return 0
fi
log_err " VMID ${vmid}: ${service} state=${active:-unknown}"
host_ssh "$host" "pct exec ${vmid} -- journalctl -u ${service} -n 30 --no-pager" 2>/dev/null || true
return 1
}
# --- main ---
log_info "Upgrade Besu on all nodes to $BESU_VERSION (host: $PROXMOX_HOST)"
[ "$DRY_RUN" = true ] && log_warn "DRY RUN — no changes will be made"
echo ""
log_info "Upgrade Besu fleet to ${BESU_VERSION}"
$DRY_RUN && log_warn "DRY RUN: no changes will be made"
echo
# Check SSH
if ! ssh -o ConnectTimeout=5 -o StrictHostKeyChecking=accept-new root@"$PROXMOX_HOST" "echo OK" &>/dev/null; then
log_err "Cannot SSH to $PROXMOX_HOST"
exit 1
fi
TARBALL_PATH="$(ensure_tarball)"
tarball_path=""
if ! $DRY_RUN; then
tarball_path=$(ensure_tarball) || exit 1
[ -z "$tarball_path" ] && exit 1
fi
declare -A VMIDS_ON_HOST
for vmid in "${BESU_VMIDS[@]}"; do
host="${HOST_BY_VMID[$vmid]:-}"
[[ -n "$host" ]] || continue
VMIDS_ON_HOST[$host]+=" ${vmid}"
done
PASS=0
FAIL=0
VMIDS_SORTED=$(echo "${!NODE_SERVICES[@]}" | tr ' ' '\n' | sort -n)
for vmid in $VMIDS_SORTED; do
if upgrade_node "$vmid" "$tarball_path"; then
((PASS++)) || true
else
((FAIL++)) || true
SKIP=0
for host in "${!VMIDS_ON_HOST[@]}"; do
log_info "Host ${host}"
if ! host_ssh "$host" "echo OK" >/dev/null 2>&1; then
log_err " Cannot SSH to ${host}"
((FAIL++)) || true
continue
fi
prepare_host_tarball "$host" "$TARBALL_PATH"
for vmid in ${VMIDS_ON_HOST[$host]}; do
service="$(detect_service "$host" "$vmid")"
if ! is_running "$host" "$vmid"; then
log_warn "VMID ${vmid} @ ${host}: not running, skipping"
((SKIP++)) || true
continue
fi
echo ""
if [[ -z "$service" ]]; then
log_warn "VMID ${vmid} @ ${host}: no Besu unit found, skipping"
((SKIP++)) || true
continue
fi
if upgrade_node "$host" "$vmid" "$service"; then
((PASS++)) || true
else
((FAIL++)) || true
fi
echo
done
if ! $DRY_RUN; then
host_ssh "$host" "rm -f /tmp/${BESU_TAR}" >/dev/null 2>&1 || true
fi
done
echo "────────────────────────────────────────────────────────────"
log_info "Upgrade summary: $PASS succeeded, $FAIL failed"
echo "────────────────────────────────────────────────────────────"
echo "------------------------------------------------------------"
log_info "Upgrade summary: passed=${PASS} skipped=${SKIP} failed=${FAIL}"
echo "------------------------------------------------------------"
if [ "$FAIL" -gt 0 ]; then
exit 1
if [[ "$FAIL" -gt 0 ]]; then
exit 1
fi
exit 0

View File

@@ -122,28 +122,12 @@ check_supported_method() {
return 1
}
check_expected_missing_method() {
local method="$1"
local params="${2:-[]}"
local response code message
response="$(rpc_request "$method" "$params" || printf '%s' '{"error":"curl"}')"
code="$(printf '%s' "$response" | jq -r '.error.code // empty' 2>/dev/null || true)"
message="$(printf '%s' "$response" | jq -r '.error.message // empty' 2>/dev/null || true)"
if [[ "$code" == "-32601" || "$message" == "Method not found" ]]; then
printf ' %-32s %s\n' "$method" "EXPECTED_MISSING"
return 0
fi
printf ' %-32s %s\n' "$method" "UNEXPECTED"
((fail++)) || true
return 1
}
check_supported_method "eth_chainId"
check_supported_method "eth_gasPrice"
check_supported_method "eth_maxPriorityFeePerGas"
check_supported_method "eth_feeHistory" "[\"0x1\", \"latest\", []]"
check_supported_method "trace_block" "[\"0x1\"]"
check_supported_method "trace_replayBlockTransactions" "[\"0x1\", [\"trace\"]]"
check_expected_missing_method "eth_maxPriorityFeePerGas"
if [[ "$fail" -eq 0 ]]; then
echo "OK: node health and public RPC capability checks passed"

View File

@@ -0,0 +1,35 @@
#!/usr/bin/env bash
set -euo pipefail
# Verify the DBIS RTGS control-plane services once deployed.
HOST="${PROXMOX_HOST_R630_02:-192.168.11.12}"
SSH_OPTS="-o BatchMode=yes -o ConnectTimeout=15 -o StrictHostKeyChecking=accept-new"
check_ct() {
local vmid="$1"
local hostname="$2"
local service="$3"
echo "=== CT $vmid ($hostname) ==="
ssh $SSH_OPTS "root@$HOST" "pct status $vmid"
ssh $SSH_OPTS "root@$HOST" "pct exec $vmid -- bash -lc 'systemctl is-active redis-server'"
ssh $SSH_OPTS "root@$HOST" "pct exec $vmid -- bash -lc 'systemctl is-active $service'"
ssh $SSH_OPTS "root@$HOST" "pct exec $vmid -- bash -lc 'curl -sf http://127.0.0.1:8080/actuator/health'"
echo
}
echo "=== DBIS RTGS control-plane runtime check ==="
echo "Host: $HOST"
echo
check_ct "${RTGS_ORCH_VMID:-5805}" "${RTGS_ORCH_HOSTNAME:-rtgs-orchestrator-1}" dbis-rtgs-orchestrator
check_ct "${RTGS_FX_VMID:-5806}" "${RTGS_FX_HOSTNAME:-rtgs-fx-1}" dbis-rtgs-fx-engine
check_ct "${RTGS_LIQ_VMID:-5807}" "${RTGS_LIQ_HOSTNAME:-rtgs-liquidity-1}" dbis-rtgs-liquidity-engine
echo "=== OMNL reachability from control plane ==="
for vmid in "${RTGS_ORCH_VMID:-5805}" "${RTGS_FX_VMID:-5806}" "${RTGS_LIQ_VMID:-5807}"; do
printf 'CT %s -> ' "$vmid"
ssh $SSH_OPTS "root@$HOST" "pct exec $vmid -- bash -lc 'curl -s -o /tmp/fineract.out -w \"%{http_code}\" http://192.168.11.85:8080/fineract-provider/api/v1/offices'"
echo
done

View File

@@ -0,0 +1,40 @@
#!/usr/bin/env bash
set -euo pipefail
# Verify the deployed DBIS RTGS first-slice sidecars on Proxmox VE.
HOST="${PROXMOX_HOST_R630_02:-192.168.11.12}"
SSH_OPTS="-o BatchMode=yes -o ConnectTimeout=15 -o StrictHostKeyChecking=accept-new"
check_ct() {
local vmid="$1"
local hostname="$2"
local service="$3"
echo "=== CT $vmid ($hostname) ==="
ssh $SSH_OPTS "root@$HOST" "pct status $vmid"
ssh $SSH_OPTS "root@$HOST" "pct exec $vmid -- bash -lc 'systemctl is-active redis-server'"
ssh $SSH_OPTS "root@$HOST" "pct exec $vmid -- bash -lc 'systemctl is-active $service'"
ssh $SSH_OPTS "root@$HOST" "pct exec $vmid -- bash -lc 'curl -sf http://127.0.0.1:8080/actuator/health'"
echo
}
echo "=== DBIS RTGS first-slice runtime check ==="
echo "Host: $HOST"
echo
check_ct 5802 rtgs-scsm-1 dbis-rtgs-scsm
check_ct 5803 rtgs-funds-1 dbis-rtgs-funds
check_ct 5804 rtgs-xau-1 dbis-rtgs-xau
echo "=== Fineract reachability from sidecars ==="
for vmid in 5802 5803 5804; do
printf 'CT %s -> ' "$vmid"
ssh $SSH_OPTS "root@$HOST" "pct exec $vmid -- bash -lc 'curl -s -o /tmp/fineract.out -w \"%{http_code}\" http://192.168.11.85:8080/fineract-provider/api/v1/offices'"
echo
done
echo
echo "Interpretation:"
echo "- 200 means unauthenticated route unexpectedly open or credentials baked into proxy"
echo "- 400/401 means HTTP reachability exists, but authenticated tenant flow is not yet frozen"

View File

@@ -0,0 +1,38 @@
#!/usr/bin/env bash
set -euo pipefail
# Verify the later-phase DBIS RTGS sidecars once deployed.
HOST="${PROXMOX_HOST_R630_02:-192.168.11.12}"
SSH_OPTS="-o BatchMode=yes -o ConnectTimeout=15 -o StrictHostKeyChecking=accept-new"
check_java_ct() {
local vmid="$1"
local hostname="$2"
local service="$3"
echo "=== CT $vmid ($hostname) ==="
ssh $SSH_OPTS "root@$HOST" "pct status $vmid"
ssh $SSH_OPTS "root@$HOST" "pct exec $vmid -- bash -lc 'systemctl is-active redis-server'"
ssh $SSH_OPTS "root@$HOST" "pct exec $vmid -- bash -lc 'systemctl is-active $service'"
ssh $SSH_OPTS "root@$HOST" "pct exec $vmid -- bash -lc 'curl -sf http://127.0.0.1:8080/actuator/health'"
echo
}
check_go_ct() {
local vmid="$1"
local hostname="$2"
local service="$3"
echo "=== CT $vmid ($hostname) ==="
ssh $SSH_OPTS "root@$HOST" "pct status $vmid"
ssh $SSH_OPTS "root@$HOST" "pct exec $vmid -- bash -lc 'systemctl is-active $service'"
ssh $SSH_OPTS "root@$HOST" "pct exec $vmid -- bash -lc 'curl -sf http://127.0.0.1:8080/health'"
echo
}
echo "=== DBIS RTGS later-phase sidecar runtime check ==="
echo "Host: $HOST"
echo
check_java_ct "${RTGS_SECURITIES_VMID:-5808}" "${RTGS_SECURITIES_HOSTNAME:-rtgs-securities-1}" dbis-rtgs-securities
check_java_ct "${RTGS_CARDNET_VMID:-5809}" "${RTGS_CARDNET_HOSTNAME:-rtgs-cardnet-1}" dbis-rtgs-cardnet
check_go_ct "${RTGS_MT103_VMID:-5810}" "${RTGS_MT103_HOSTNAME:-rtgs-mt103-1}" dbis-rtgs-mt103

View File

@@ -0,0 +1,11 @@
#!/usr/bin/env bash
# Print Caliper integration hints for Chain 138 (no network I/O).
# See docs/03-deployment/CALIPER_CHAIN138_PERF_HOOK.md
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
echo "Caliper is not bundled in this repo."
echo "Read: $ROOT/docs/03-deployment/CALIPER_CHAIN138_PERF_HOOK.md"
echo ""
echo "Suggested SUT URL for benchmarks (lab): \${RPC_URL_138:-http://192.168.11.211:8545}"
echo "Chain ID: 138 (verify with eth_chainId)."

View File

@@ -0,0 +1,16 @@
#!/usr/bin/env bash
# Print Gitea Actions UI URLs (no token). Use after pushing complete-credential / cc-* repos.
# Gitea REST "actions runs" APIs vary by version; the web UI is the reliable check.
set -euo pipefail
GITEA_URL="${GITEA_URL:-https://gitea.d-bis.org}"
ORG="${GITEA_ORG:-DBIS}"
REPOS=(
complete-credential
cc-shared-authz
cc-audit-ledger
cc-eidas-connector
)
echo "Open in browser (Actions tab):"
for r in "${REPOS[@]}"; do
echo " ${GITEA_URL}/${ORG}/${r}/actions"
done

View File

@@ -0,0 +1,72 @@
#!/usr/bin/env bash
# DBIS Phase 3 — liveness / availability wrapper: Besu RPC liveness + optional FireFly HTTP + optional full RPC health.
# This does NOT execute Indy issuance, Aries verification, Fabric chaincode, or cross-chain business workflow steps.
#
# Usage: bash scripts/verify/run-dbis-phase3-e2e-simulation.sh
# Env: RPC_URL_138 (default http://192.168.11.211:8545)
# FIREFLY_URL (default http://192.168.11.35:5000)
# RUN_CHAIN138_RPC_HEALTH=1 to run check-chain138-rpc-health.sh (slower)
set -uo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
# shellcheck source=/dev/null
source "$PROJECT_ROOT/config/ip-addresses.conf" 2>/dev/null || true
RPC_URL="${RPC_URL_138:-http://${IP_BESU_RPC_CORE_1:-192.168.11.211}:8545}"
FIREFLY_URL="${FIREFLY_URL:-http://192.168.11.35:5000}"
fail=0
echo "=== DBIS Phase 3 liveness wrapper (partial) ==="
echo "RPC: $RPC_URL"
echo ""
if command -v curl &>/dev/null; then
echo "--- Besu eth_chainId / eth_blockNumber ---"
if ! out=$(curl -sS --connect-timeout 5 -X POST -H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}' "$RPC_URL"); then
echo "[FAIL] curl chainId"
fail=1
else
echo "$out"
fi
if ! out=$(curl -sS --connect-timeout 5 -X POST -H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' "$RPC_URL"); then
echo "[FAIL] curl blockNumber"
fail=1
else
echo "$out"
fi
else
echo "[SKIP] curl not installed"
fail=1
fi
echo ""
echo "--- FireFly HTTP (optional) ---"
if command -v curl &>/dev/null; then
code=$(curl -sS -o /dev/null -w '%{http_code}' --connect-timeout 4 "$FIREFLY_URL/api/v1/status" || true)
if [[ "$code" =~ ^(200|401|403)$ ]]; then
echo "[OK] $FIREFLY_URL/api/v1/status HTTP $code"
else
echo "[WARN] $FIREFLY_URL/api/v1/status HTTP ${code:-000} (FireFly may be down or path differs)"
fi
else
echo "[SKIP] curl not installed"
fi
if [[ "${RUN_CHAIN138_RPC_HEALTH:-}" == "1" ]]; then
echo ""
echo "--- check-chain138-rpc-health.sh ---"
bash "$PROJECT_ROOT/scripts/verify/check-chain138-rpc-health.sh" || fail=1
fi
echo ""
echo "--- Manual follow-ups (Section 18) ---"
echo "This script proves only liveness / availability for the automated checks above."
echo "Indy 6400 / Fabric 6000 / CCIP relay on r630-01: see docs/03-deployment/DBIS_PHASE3_E2E_PRODUCTION_SIMULATION_RUNBOOK.md"
echo "Caliper: docs/03-deployment/CALIPER_CHAIN138_PERF_HOOK.md"
echo ""
exit "$fail"

View File

@@ -0,0 +1,218 @@
#!/usr/bin/env bash
# Phase 1 — Reality mapping (read-only): compose Proxmox/Besu audits and optional
# Hyperledger CT probes into a timestamped report under reports/phase1-discovery/.
#
# Usage (repo root, LAN + SSH to Proxmox recommended):
# bash scripts/verify/run-phase1-discovery.sh
# HYPERLEDGER_PROBE=1 bash scripts/verify/run-phase1-discovery.sh # SSH pct exec smoke checks on r630-02
#
# Env: PROXMOX_HOSTS, SSH_USER, SSH_OPTS (same as audit-proxmox-operational-template.sh)
# HYPERLEDGER_PROBE=1 to run optional Fabric/Indy/FireFly container checks (requires SSH to r630-02)
set -uo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
# shellcheck source=/dev/null
source "$PROJECT_ROOT/config/ip-addresses.conf" 2>/dev/null || true
REPORT_DIR="${REPORT_DIR:-$PROJECT_ROOT/reports/phase1-discovery}"
STAMP="$(date -u +%Y%m%d_%H%M%S)"
MD="$REPORT_DIR/phase1-discovery-${STAMP}.md"
LOG="$REPORT_DIR/phase1-discovery-${STAMP}.log"
mkdir -p "$REPORT_DIR"
SSH_USER="${SSH_USER:-root}"
SSH_OPTS="${SSH_OPTS:--o BatchMode=yes -o ConnectTimeout=6 -o StrictHostKeyChecking=accept-new}"
R630_02="${PROXMOX_HOST_R630_02:-192.168.11.12}"
append_cmd() {
local title="$1"
local severity="${2:-info}"
shift 2 || true
local rc=0
local tmp
tmp="$(mktemp)"
"$@" >"$tmp" 2>&1
rc=$?
{
echo ""
echo "## $title"
echo ""
echo '```text'
cat "$tmp"
if (( rc != 0 )); then
echo "[exit $rc]"
fi
echo '```'
} | tee -a "$MD" >>"$LOG"
rm -f "$tmp"
if (( rc != 0 )) && [[ "$severity" == "critical" ]]; then
PHASE1_CRITICAL_FAILURES+=("$title (exit $rc)")
fi
}
PHASE1_CRITICAL_FAILURES=()
{
echo "# Phase 1 discovery report"
echo ""
echo "**Generated (UTC):** $(date -u +%Y-%m-%dT%H:%M:%SZ)"
echo ""
echo "**Runbook:** [docs/03-deployment/PHASE1_DISCOVERY_RUNBOOK.md](../../docs/03-deployment/PHASE1_DISCOVERY_RUNBOOK.md)"
echo ""
echo "**Doctrine:** [dbis_chain_138_technical_master_plan.md](../../dbis_chain_138_technical_master_plan.md) (Sections 3, 1920)"
echo ""
echo "## Dependency graph (logical)"
echo ""
echo "Same diagram as the runbook; edges reflect documented traffic flow, not live packet capture."
echo ""
cat <<'MERMAID'
```mermaid
flowchart TB
subgraph edge [EdgeIngress]
CF[Cloudflare_DNS]
NPM[NPMplus_LXC]
end
subgraph besu [Chain138_Besu]
RPCpub[RPC_public_2201]
RPCcore[RPC_core_2101]
Val[Validators_1000_1004]
Sen[Sentries_1500_1508]
end
subgraph observe [Observability]
BS[Blockscout_5000]
end
subgraph relay [CrossChain]
CCIP[CCIP_relay_r63001_host]
end
subgraph dlt [Hyperledger_optional]
FF[FireFly_6200_6201]
Fab[Fabric_6000_plus]
Indy[Indy_6400_plus]
end
CF --> NPM
NPM --> RPCpub
NPM --> RPCcore
NPM --> BS
RPCpub --> Sen
RPCcore --> Sen
Sen --> Val
CCIP --> RPCpub
FF --> Fab
FF --> Indy
```
MERMAID
} >"$MD"
touch "$LOG"
append_cmd "Proxmox template vs live VMID audit" critical bash "$PROJECT_ROOT/scripts/verify/audit-proxmox-operational-template.sh"
PROXMOX_HOSTS="${PROXMOX_HOSTS:-${PROXMOX_HOST_ML110:-192.168.11.10} ${PROXMOX_HOST_R630_01:-192.168.11.11} $R630_02}"
append_cmd "Proxmox cluster status (pvecm) per host" critical bash -c "
fail=0
for h in $PROXMOX_HOSTS; do
echo '=== '"\$h"' ==='
ssh $SSH_OPTS ${SSH_USER}@\"\$h\" 'pvecm status 2>&1' || fail=1
echo ''
done
exit \$fail
"
append_cmd "Proxmox storage (pvesm status) per host" critical bash -c "
fail=0
for h in $PROXMOX_HOSTS; do
echo '=== '"\$h"' ==='
ssh $SSH_OPTS ${SSH_USER}@\"\$h\" 'pvesm status 2>&1 | head -80' || fail=1
echo ''
done
exit \$fail
"
append_cmd "Live pct/qm lists per host" critical bash -c "
fail=0
for h in $PROXMOX_HOSTS; do
echo '=== '"\$h"' ==='
ssh $SSH_OPTS ${SSH_USER}@\"\$h\" 'echo PCT:; pct list 2>&1; echo VM:; qm list 2>&1' || fail=1
echo ''
done
exit \$fail
"
if command -v curl &>/dev/null; then
append_cmd "Chain 138 RPC quick probe (core, LAN)" critical bash -c "
curl -sS --connect-timeout 4 -X POST -H 'Content-Type: application/json' \
--data '{\"jsonrpc\":\"2.0\",\"method\":\"eth_chainId\",\"params\":[],\"id\":1}' \
\"http://${IP_BESU_RPC_CORE_1:-192.168.11.211}:8545\" || echo 'curl failed'
"
fi
append_cmd "Besu RPC health script (may fail off-LAN)" critical bash -c "
bash \"$PROJECT_ROOT/scripts/verify/check-chain138-rpc-health.sh\"
"
append_cmd "Besu enodes / IPs verify (may fail off-LAN)" critical bash -c "
bash \"$PROJECT_ROOT/scripts/verify/verify-besu-enodes-and-ips.sh\"
"
if [[ "${HYPERLEDGER_PROBE:-}" == "1" ]]; then
append_cmd "Hyperledger CT smoke (r630-02; pct exec)" critical bash -c "
ssh $SSH_OPTS ${SSH_USER}@$R630_02 '
for id in 6200 6201 6000 6001 6002 6400 6401 6402; do
echo \"=== VMID \$id status ===\"
pct status \$id 2>&1 || true
if pct status \$id 2>/dev/null | grep -q running; then
pct exec \$id -- bash -lc \"command -v docker >/dev/null && docker ps --format 'table {{.Names}}\t{{.Status}}' 2>/dev/null | head -10 || true; command -v systemctl >/dev/null && systemctl list-units --type=service --state=running --no-pager 2>/dev/null | head -20 || true; ss -ltnp 2>/dev/null | head -20 || true\" 2>&1 || echo \"[exec failed]\"
fi
echo \"\"
done
'
"
else
{
echo ""
echo "## Hyperledger CT smoke (skipped)"
echo ""
echo "Set \`HYPERLEDGER_PROBE=1\` to SSH to r630-02 and run \`pct status/exec\` on 6200, 6201, 6000, 6001, 6002, 6400, 6401, 6402."
echo ""
} >>"$MD"
fi
{
echo ""
echo "## Configuration snapshot pointers (no secrets in repo)"
echo ""
echo "- \`config/proxmox-operational-template.json\`"
echo "- \`config/ip-addresses.conf\`"
echo "- \`docs/04-configuration/ALL_VMIDS_ENDPOINTS.md\`"
echo ""
echo "## Next steps"
echo ""
echo "1. Reconcile **Entity owner** / **Region** in [DBIS_NODE_ROLE_MATRIX.md](../../docs/02-architecture/DBIS_NODE_ROLE_MATRIX.md)."
echo "2. If ML110 row shows Proxmox + workloads, update [PHYSICAL_HARDWARE_INVENTORY.md](../../docs/02-architecture/PHYSICAL_HARDWARE_INVENTORY.md) vs [NETWORK_CONFIGURATION_MASTER.md](../../docs/11-references/NETWORK_CONFIGURATION_MASTER.md)."
echo ""
if ((${#PHASE1_CRITICAL_FAILURES[@]} > 0)); then
echo "## Critical failure summary"
echo ""
for failure in "${PHASE1_CRITICAL_FAILURES[@]}"; do
echo "- $failure"
done
echo ""
echo "This report is complete as evidence capture, but the discovery run is **not** a pass. Re-run from LAN with working SSH/RPC access until the critical failures clear."
else
echo "## Critical failure summary"
echo ""
echo "- none"
echo ""
echo "All critical discovery checks completed successfully for this run."
fi
echo ""
} >>"$MD"
echo "Wrote $MD"
echo "Full log mirror: $LOG"
ls -la "$MD" "$LOG"
if ((${#PHASE1_CRITICAL_FAILURES[@]} > 0)); then
exit 1
fi

View File

@@ -83,6 +83,14 @@ declare -A DOMAIN_TYPES_ALL=(
["the-order.sankofa.nexus"]="web" # OSJ portal (secure auth); app: ~/projects/the_order
["www.the-order.sankofa.nexus"]="web" # 301 → https://the-order.sankofa.nexus
["studio.sankofa.nexus"]="web"
# Client SSO / IdP / operator dash (FQDN_EXPECTED_CONTENT + EXPECTED_WEB_CONTENT Deployment Status)
["keycloak.sankofa.nexus"]="web"
["admin.sankofa.nexus"]="web"
["portal.sankofa.nexus"]="web"
["dash.sankofa.nexus"]="web"
# d-bis.org docs on explorer nginx where configured; generic Blockscout hostname (VMID 5000 when proxied)
["docs.d-bis.org"]="web"
["blockscout.defi-oracle.io"]="web"
["rpc.public-0138.defi-oracle.io"]="rpc-http"
["rpc.defi-oracle.io"]="rpc-http"
["wss.defi-oracle.io"]="rpc-ws"
@@ -166,7 +174,7 @@ else
fi
# Domains that are optional when any test fails (off-LAN, 502, unreachable); fail → skip so run passes.
_PUB_OPTIONAL_WHEN_FAIL="dapp.d-bis.org mifos.d-bis.org explorer.d-bis.org dbis-admin.d-bis.org dbis-api.d-bis.org dbis-api-2.d-bis.org secure.d-bis.org sankofa.nexus www.sankofa.nexus phoenix.sankofa.nexus www.phoenix.sankofa.nexus the-order.sankofa.nexus www.the-order.sankofa.nexus studio.sankofa.nexus mim4u.org www.mim4u.org secure.mim4u.org training.mim4u.org rpc-http-pub.d-bis.org rpc.d-bis.org rpc2.d-bis.org rpc.public-0138.defi-oracle.io rpc.defi-oracle.io ws.rpc.d-bis.org ws.rpc2.d-bis.org"
_PUB_OPTIONAL_WHEN_FAIL="dapp.d-bis.org mifos.d-bis.org explorer.d-bis.org dbis-admin.d-bis.org dbis-api.d-bis.org dbis-api-2.d-bis.org secure.d-bis.org sankofa.nexus www.sankofa.nexus phoenix.sankofa.nexus www.phoenix.sankofa.nexus the-order.sankofa.nexus www.the-order.sankofa.nexus studio.sankofa.nexus keycloak.sankofa.nexus admin.sankofa.nexus portal.sankofa.nexus dash.sankofa.nexus docs.d-bis.org blockscout.defi-oracle.io mim4u.org www.mim4u.org secure.mim4u.org training.mim4u.org rpc-http-pub.d-bis.org rpc.d-bis.org rpc2.d-bis.org rpc.public-0138.defi-oracle.io rpc.defi-oracle.io ws.rpc.d-bis.org ws.rpc2.d-bis.org"
_PRIV_OPTIONAL_WHEN_FAIL="rpc-http-prv.d-bis.org rpc-ws-prv.d-bis.org rpc-fireblocks.d-bis.org ws.rpc-fireblocks.d-bis.org"
if [[ -z "${E2E_OPTIONAL_WHEN_FAIL:-}" ]]; then
if [[ "$PROFILE" == "private" ]]; then
@@ -410,15 +418,16 @@ test_domain() {
result=$(echo "$result" | jq --arg time "$time_total" '.tests.https = {"status": "fail", "response_time_seconds": ($time | tonumber)}')
fi
# Optional: Blockscout API check for explorer.d-bis.org (does not affect E2E pass/fail)
if [ "$domain" = "explorer.d-bis.org" ] && [ "${SKIP_BLOCKSCOUT_API:-0}" != "1" ]; then
if { [ "$domain" = "explorer.d-bis.org" ] || [ "$domain" = "blockscout.defi-oracle.io" ]; } && [ "${SKIP_BLOCKSCOUT_API:-0}" != "1" ]; then
log_info "Test 3b: Blockscout API (optional)"
api_body_file="$OUTPUT_DIR/explorer_d-bis_org_blockscout_api.txt"
api_safe="${domain//./_}"
api_body_file="$OUTPUT_DIR/${api_safe}_blockscout_api.txt"
api_code=$(curl -s -o "$api_body_file" -w "%{http_code}" -k --connect-timeout 10 "https://$domain/api/v2/stats" 2>/dev/null || echo "000")
if [ "$api_code" = "200" ] && [ -s "$api_body_file" ] && (grep -qE '"total_blocks"|"total_transactions"' "$api_body_file" 2>/dev/null); then
log_success "Blockscout API: /api/v2/stats returned 200 with stats"
log_success "Blockscout API: $domain /api/v2/stats returned 200 with stats"
result=$(echo "$result" | jq '.tests.blockscout_api = {"status": "pass", "http_code": 200}')
else
log_warn "Blockscout API: HTTP $api_code or invalid response (optional; run from LAN if backend unreachable)"
log_warn "Blockscout API: $domain HTTP $api_code or invalid response (optional; run from LAN if backend unreachable)"
result=$(echo "$result" | jq --arg code "$api_code" '.tests.blockscout_api = {"status": "skip", "http_code": $code}')
fi
fi