#!/usr/bin/env bash # Provision the OP Stack operator landing zone on Proxmox r630-02. # # Creates: # 5751 op-stack-deployer-1 (192.168.11.69) # 5752 op-stack-ops-1 (192.168.11.70) # # Installs baseline tooling, enables SSH, seeds repo OP Stack scaffolding, # and prepares /etc/op-stack and /opt/op-stack-bootstrap inside each CT. # # Usage: # bash scripts/deployment/provision-op-stack-operator-lxcs.sh [--dry-run] set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" cd "$PROJECT_ROOT" source "$PROJECT_ROOT/.env" 2>/dev/null || true source "$PROJECT_ROOT/config/ip-addresses.conf" 2>/dev/null || true CT_PROXMOX_HOST="${OP_STACK_PROXMOX_HOST:-${PROXMOX_HOST_R630_02:-192.168.11.12}}" CT_STORAGE="${OP_STACK_PROXMOX_STORAGE:-thin5}" CT_TEMPLATE="${OP_STACK_PROXMOX_TEMPLATE:-local:vztmpl/debian-12-standard_12.12-1_amd64.tar.zst}" CT_NETWORK="${OP_STACK_PROXMOX_NETWORK:-vmbr0}" CT_GATEWAY="${OP_STACK_PROXMOX_GATEWAY:-${NETWORK_GATEWAY:-192.168.11.1}}" CT_NAMESERVER="${OP_STACK_PROXMOX_NAMESERVER:-${DNS_PRIMARY:-1.1.1.1}}" SSH_OPTS="-o BatchMode=yes -o ConnectTimeout=15 -o StrictHostKeyChecking=accept-new" DRY_RUN=false [[ "${1:-}" == "--dry-run" ]] && DRY_RUN=true SSH_PUBKEY_PATH="${SSH_PUBKEY_PATH:-$HOME/.ssh/id_ed25519_proxmox.pub}" if [[ ! -f "$SSH_PUBKEY_PATH" ]]; then SSH_PUBKEY_PATH="${HOME}/.ssh/id_ed25519.pub" fi if [[ ! -f "$SSH_PUBKEY_PATH" ]]; then echo "ERROR: No SSH public key found at ~/.ssh/id_ed25519_proxmox.pub or ~/.ssh/id_ed25519.pub" exit 1 fi SSH_KEY_PATH="${SSH_PUBKEY_PATH%.pub}" DIRECT_CT_SSH_OPTS="-o BatchMode=yes -o ConnectTimeout=15 -o StrictHostKeyChecking=accept-new" if [[ -f "$SSH_KEY_PATH" ]]; then DIRECT_CT_SSH_OPTS="-i $SSH_KEY_PATH $DIRECT_CT_SSH_OPTS" fi BOOTSTRAP_TAR="$(mktemp /tmp/op-stack-bootstrap.XXXXXX.tgz)" REMOTE_BOOTSTRAP_TAR="/root/op-stack-bootstrap.tgz" REMOTE_PUBKEY="/root/op-stack-authorized_key.pub" trap 'rm -f "$BOOTSTRAP_TAR"' EXIT CTS=( "${OP_STACK_DEPLOYER_VMID:-5751}|op-stack-deployer-1|${IP_OP_STACK_DEPLOYER_CT:-192.168.11.69}|8192|4|32|op-stack-deployer-operator-workspace" "${OP_STACK_OPS_VMID:-5752}|op-stack-ops-1|${IP_OP_STACK_OPS_CT:-192.168.11.70}|12288|6|48|op-stack-runtime-service-staging" ) tar -C "$PROJECT_ROOT" -czf "$BOOTSTRAP_TAR" \ config/op-stack-superchain \ config/wormhole \ scripts/op-stack \ scripts/wormhole \ docs/03-deployment/OP_STACK_STANDARD_ROLLUP_SUPERCHAIN_RUNBOOK.md \ docs/03-deployment/OP_STACK_L2_AND_BESU138_BRIDGE_NOTES.md \ docs/03-deployment/WORMHOLE_NTT_EXECUTOR_OPERATOR_RUNBOOK.md \ config/systemd/op-stack-batcher.example.service \ config/systemd/op-stack-challenger.example.service \ config/systemd/op-stack-op-node.example.service \ config/systemd/op-stack-op-reth.example.service \ config/systemd/op-stack-proposer.example.service \ config/systemd/op-stack-sequencer.example.service echo "=== OP Stack operator landing zone ===" echo "Proxmox host: $CT_PROXMOX_HOST" echo "Storage: $CT_STORAGE | Template: $CT_TEMPLATE | Network: $CT_NETWORK" echo "SSH public key: $SSH_PUBKEY_PATH" echo for spec in "${CTS[@]}"; do IFS="|" read -r vmid hostname ip memory cores disk description <<<"$spec" echo " - CT $vmid $hostname @ $ip (${memory}MB RAM, ${cores} cores, ${disk}G disk)" done echo if $DRY_RUN; then echo "[DRY-RUN] Would create/bootstrap the CTs above on $CT_PROXMOX_HOST." exit 0 fi scp $SSH_OPTS "$SSH_PUBKEY_PATH" "root@${CT_PROXMOX_HOST}:${REMOTE_PUBKEY}" scp $SSH_OPTS "$BOOTSTRAP_TAR" "root@${CT_PROXMOX_HOST}:${REMOTE_BOOTSTRAP_TAR}" for spec in "${CTS[@]}"; do IFS="|" read -r vmid hostname ip memory cores disk description <<<"$spec" echo "=== Provisioning CT ${vmid} (${hostname}) ===" if ssh $SSH_OPTS "root@${CT_PROXMOX_HOST}" "pct list 2>/dev/null | grep -q '^${vmid} '"; then echo "CT ${vmid} already exists — skipping create" else ssh $SSH_OPTS "root@${CT_PROXMOX_HOST}" bash -s -- \ "$vmid" "$hostname" "$memory" "$cores" "$disk" "$ip" "$description" \ "$CT_TEMPLATE" "$CT_STORAGE" "$CT_NETWORK" "$CT_GATEWAY" "$CT_NAMESERVER" <<'REMOTE_CREATE' set -euo pipefail vmid="$1" hostname="$2" memory="$3" cores="$4" disk="$5" ip="$6" description="$7" template="$8" storage="$9" network="${10}" gateway="${11}" nameserver="${12}" pct create "$vmid" "$template" \ --hostname "$hostname" \ --memory "$memory" \ --cores "$cores" \ --rootfs "${storage}:${disk}" \ --net0 "name=eth0,bridge=${network},ip=${ip}/24,gw=${gateway}" \ --nameserver "$nameserver" \ --description "$description" \ --start 1 \ --onboot 1 \ --unprivileged 0 \ --features nesting=1,keyctl=1 REMOTE_CREATE echo "Waiting for CT ${vmid} to boot..." sleep 20 fi ssh $SSH_OPTS "root@${CT_PROXMOX_HOST}" "pct start ${vmid} >/dev/null 2>&1 || true" ssh $SSH_OPTS "root@${CT_PROXMOX_HOST}" "pct push ${vmid} ${REMOTE_PUBKEY} /root/op-stack-authorized_key.pub >/dev/null" ssh $SSH_OPTS "root@${CT_PROXMOX_HOST}" "pct push ${vmid} ${REMOTE_BOOTSTRAP_TAR} /root/op-stack-bootstrap.tgz >/dev/null" ssh $SSH_OPTS "root@${CT_PROXMOX_HOST}" bash -s -- "$vmid" "$hostname" <<'REMOTE_BOOTSTRAP' set -euo pipefail vmid="$1" hostname="$2" pct exec "$vmid" -- bash -lc ' set -euo pipefail export DEBIAN_FRONTEND=noninteractive apt-get update -qq apt-get install -y -qq \ sudo openssh-server ca-certificates curl git jq rsync unzip zip \ tar gzip xz-utils make build-essential tmux htop python3 python3-pip golang-go systemctl enable ssh >/dev/null 2>&1 || systemctl enable ssh.service >/dev/null 2>&1 || true systemctl restart ssh >/dev/null 2>&1 || systemctl restart ssh.service >/dev/null 2>&1 || true id -u opuser >/dev/null 2>&1 || useradd -m -s /bin/bash -G sudo opuser install -d -m 700 /root/.ssh /home/opuser/.ssh install -d -m 755 /opt/op-stack /etc/op-stack /etc/op-stack/systemd-examples /opt/op-stack-bootstrap if ! grep -qxF "$(cat /root/op-stack-authorized_key.pub)" /root/.ssh/authorized_keys 2>/dev/null; then cat /root/op-stack-authorized_key.pub >> /root/.ssh/authorized_keys fi if ! grep -qxF "$(cat /root/op-stack-authorized_key.pub)" /home/opuser/.ssh/authorized_keys 2>/dev/null; then cat /root/op-stack-authorized_key.pub >> /home/opuser/.ssh/authorized_keys fi chown -R opuser:opuser /home/opuser/.ssh /opt/op-stack /opt/op-stack-bootstrap chmod 600 /root/.ssh/authorized_keys /home/opuser/.ssh/authorized_keys echo "opuser ALL=(ALL) NOPASSWD:ALL" >/etc/sudoers.d/90-opuser chmod 440 /etc/sudoers.d/90-opuser rm -rf /opt/op-stack-bootstrap/* tar -xzf /root/op-stack-bootstrap.tgz -C /opt/op-stack-bootstrap cp /opt/op-stack-bootstrap/config/op-stack-superchain/op-stack-l2.example.env /etc/op-stack/op-stack-l2.example.env cp /opt/op-stack-bootstrap/config/systemd/op-stack-*.example.service /etc/op-stack/systemd-examples/ 2>/dev/null || true case "'"${hostname}"'" in op-stack-deployer-1) bash /opt/op-stack-bootstrap/scripts/op-stack/prepare-operator-ct.sh deployer ;; op-stack-ops-1) bash /opt/op-stack-bootstrap/scripts/op-stack/prepare-operator-ct.sh ops ;; esac echo "'"${hostname}"'" >/etc/hostname hostname "'"${hostname}"'" ' REMOTE_BOOTSTRAP ssh $DIRECT_CT_SSH_OPTS "opuser@${ip}" "hostname && id" done echo "Refreshing governance TOMLs inside deployer CT if cache is missing..." ssh $DIRECT_CT_SSH_OPTS "opuser@${IP_OP_STACK_DEPLOYER_CT:-192.168.11.69}" ' set -euo pipefail cache_dir=/opt/op-stack-bootstrap/config/op-stack-superchain/cache required=( standard-config-params-mainnet.toml standard-config-roles-mainnet.toml standard-versions-mainnet.toml ) missing=0 for file in "${required[@]}"; do if [[ ! -s "$cache_dir/$file" ]]; then missing=1 fi done if [[ "${OP_STACK_REFRESH_TOMLS:-0}" == "1" || "$missing" == "1" ]]; then cd /opt/op-stack-bootstrap bash scripts/op-stack/fetch-standard-mainnet-toml.sh else echo "Using seeded governance TOML cache in $cache_dir" fi ' echo echo "✅ OP Stack operator landing zone ready:" echo " - 5751 op-stack-deployer-1 @ ${IP_OP_STACK_DEPLOYER_CT:-192.168.11.69}" echo " - 5752 op-stack-ops-1 @ ${IP_OP_STACK_OPS_CT:-192.168.11.70}" echo echo "Next:" echo " 1. SSH as opuser to the CTs" echo " 2. Copy live secrets/env into /etc/op-stack/" echo " 3. Install pinned Optimism binaries or packages" echo " 4. Run Sepolia rehearsal from 5751, then stage runtime services from 5752"