Some checks failed
CI / Frontend Lint (pull_request) Failing after 7s
CI / Frontend Type Check (pull_request) Failing after 7s
CI / Frontend Build (pull_request) Failing after 6s
CI / Frontend E2E Tests (pull_request) Failing after 7s
CI / Orchestrator Build (pull_request) Failing after 7s
CI / Orchestrator Unit Tests (pull_request) Failing after 6s
CI / Orchestrator E2E (Testcontainers) (pull_request) Has been skipped
CI / Contracts Compile (pull_request) Failing after 5s
CI / Contracts Test (pull_request) Failing after 6s
Code Quality / SonarQube Analysis (pull_request) Failing after 20s
Code Quality / Code Quality Checks (pull_request) Failing after 7s
Security Scan / Dependency Vulnerability Scan (pull_request) Failing after 4s
Security Scan / OWASP ZAP Scan (pull_request) Failing after 4s
- deploy-currencicombo-8604.sh: on readiness timeout, print loud failure summary (journalctl tails + exact --rollback command with specific backup path) instead of silently exiting. Deliberately does NOT auto-rollback; first cutovers often fail because of env/migration mistakes and auto-restore hides the failure state ops needs. - install.sh: on first run, write the three API keys + EVENT_SIGNING_SECRET to /root/currencicombo-first-keys.txt (0600, root:root) as a handoff copy. Canonical values still live in /etc/currencicombo/orchestrator.env. Log one pointer line (not the secrets themselves) to journald. Handoff file is NOT regenerated if orchestrator.env already exists. - install-prune-cron.sh (new, opt-in): installs /etc/cron.daily/ currencicombo-prune-backups that deletes entries older than 30 days from /var/lib/currencicombo/backups/ WHILE always keeping the newest 5 regardless of age. Enforced via newest-first sort + i<KEEP_MIN skip. - webapp-nginx.conf: drop the misleading /events/* 421 guard-rail. The orchestrator's SSE endpoint is /api/plans/:id/events/stream (under /api/), so one /api/* guard-rail covers both normal REST and SSE. - README.md: corrected NPMplus rule table to TWO rules (/api/* with SSE-friendly proxy_buffering=off + 24h read_timeout + Connection "" + http/1.1, and /); added post-cutover smoke checks section with a concrete SSE streaming test that catches silent proxy_buffering=on misconfig; documented the /root/currencicombo-first-keys.txt handoff and the install-prune-cron.sh workflow; replaced stale 'not auto-pruned' note. Verification: - shellcheck --severity=warning: clean on all 3 scripts. - bash -n: clean on install-prune-cron.sh. - install-prune-cron.sh --dry-run: prints the pruner body with resolved env values as expected. - install.sh --dry-run: walks through user/dirs/nginx-apt steps, then fails fast on missing psql (expected on a build box without Postgres). Co-Authored-By: Nakamoto, S <defi@defi-oracle.io>
239 lines
10 KiB
Bash
Executable File
239 lines
10 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# install.sh — idempotent first-time setup for CurrenciCombo on a systemd host.
|
|
#
|
|
# Intended to run ONCE per host as root (or with sudo). Running it again is
|
|
# safe: it will skip already-present artifacts and warn on conflicts.
|
|
#
|
|
# What this does:
|
|
# 1. Creates the `currencicombo` system user and /opt/currencicombo tree.
|
|
# 2. Installs nginx (Debian/Ubuntu or Alpine) if not present.
|
|
# 3. Ensures a local Postgres is running and creates a fresh
|
|
# `currencicombo` role + DB (refuses to touch an existing one unless
|
|
# --force-recreate is passed).
|
|
# 4. Ensures a local Redis is running.
|
|
# 5. Writes /etc/currencicombo/orchestrator.env from .env.prod.example,
|
|
# auto-populating EVENT_SIGNING_SECRET and ORCHESTRATOR_API_KEYS with
|
|
# fresh randoms the first time.
|
|
# 6. Installs /etc/currencicombo/webapp-nginx.conf.
|
|
# 7. Installs the two systemd units and runs `systemctl daemon-reload`.
|
|
# 8. Enables (does NOT start) both units. First start happens via
|
|
# scripts/deployment/deploy-currencicombo-8604.sh after the first
|
|
# successful build.
|
|
#
|
|
# This script is target-agnostic. It has no hardcoded IP / hostname /
|
|
# VLAN. The NPMplus ingress in front of it is configured separately —
|
|
# see scripts/deployment/README.md.
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
|
APP_USER="currencicombo"
|
|
APP_HOME="/opt/currencicombo"
|
|
ETC_DIR="/etc/currencicombo"
|
|
LOG_DIR="/var/log/currencicombo"
|
|
REPO_DIR="/var/lib/currencicombo/repo"
|
|
ENV_FILE="${ETC_DIR}/orchestrator.env"
|
|
NGINX_FILE="${ETC_DIR}/webapp-nginx.conf"
|
|
SYSTEMD_DIR="/etc/systemd/system"
|
|
|
|
FORCE_RECREATE_DB=0
|
|
DRY_RUN=0
|
|
SKIP_NGINX_INSTALL=0
|
|
|
|
log() { printf '[install] %s\n' "$*" >&2; }
|
|
warn() { printf '[install][WARN] %s\n' "$*" >&2; }
|
|
die() { printf '[install][FATAL] %s\n' "$*" >&2; exit 1; }
|
|
run() { if [[ "${DRY_RUN}" -eq 1 ]]; then printf '[install][dry-run] %s\n' "$*" >&2; else eval "$*"; fi; }
|
|
|
|
usage() {
|
|
cat <<'USAGE'
|
|
Usage: sudo ./install.sh [--force-recreate-db] [--skip-nginx-install] [--dry-run]
|
|
|
|
--force-recreate-db DROP and recreate the currencicombo Postgres role
|
|
and DB even if they already exist. DESTRUCTIVE.
|
|
--skip-nginx-install Do not apt/apk install nginx (use if you already
|
|
have a custom nginx build in place).
|
|
--dry-run Print the commands that would run, don't run them.
|
|
USAGE
|
|
}
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--force-recreate-db) FORCE_RECREATE_DB=1; shift ;;
|
|
--skip-nginx-install) SKIP_NGINX_INSTALL=1; shift ;;
|
|
--dry-run) DRY_RUN=1; shift ;;
|
|
-h|--help) usage; exit 0 ;;
|
|
*) die "unknown arg: $1" ;;
|
|
esac
|
|
done
|
|
|
|
[[ "$EUID" -eq 0 ]] || die "must run as root (sudo)"
|
|
|
|
# ----------------------------------------------------------------------
|
|
# 1. User + tree
|
|
# ----------------------------------------------------------------------
|
|
if id "${APP_USER}" >/dev/null 2>&1; then
|
|
log "user ${APP_USER} already exists"
|
|
else
|
|
log "creating system user ${APP_USER}"
|
|
run useradd --system --home-dir "${APP_HOME}" --shell /usr/sbin/nologin --user-group "${APP_USER}"
|
|
fi
|
|
|
|
for d in "${APP_HOME}" "${APP_HOME}/orchestrator" "${APP_HOME}/webapp" \
|
|
"${APP_HOME}/webapp/dist" "${ETC_DIR}" "${LOG_DIR}" "${REPO_DIR}"; do
|
|
run install -d -o "${APP_USER}" -g "${APP_USER}" -m 0755 "$d"
|
|
done
|
|
run chown "${APP_USER}:${APP_USER}" "${APP_HOME}" "${LOG_DIR}" "${REPO_DIR}"
|
|
run chmod 0750 "${ETC_DIR}"
|
|
|
|
# ----------------------------------------------------------------------
|
|
# 2. nginx (required by currencicombo-webapp.service)
|
|
# ----------------------------------------------------------------------
|
|
if [[ "${SKIP_NGINX_INSTALL}" -eq 0 ]]; then
|
|
if command -v nginx >/dev/null 2>&1; then
|
|
log "nginx already installed ($(nginx -v 2>&1 | head -1))"
|
|
elif command -v apt-get >/dev/null 2>&1; then
|
|
log "installing nginx via apt"
|
|
run 'DEBIAN_FRONTEND=noninteractive apt-get update -q'
|
|
run 'DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends nginx-light'
|
|
# We use our own nginx.conf via -c, so disable the distro site.
|
|
run systemctl disable --now nginx 2>/dev/null || true
|
|
elif command -v apk >/dev/null 2>&1; then
|
|
log "installing nginx via apk"
|
|
run apk add --no-cache nginx
|
|
run rc-update del nginx 2>/dev/null || true
|
|
else
|
|
die "no apt or apk available — install nginx manually or re-run with --skip-nginx-install"
|
|
fi
|
|
fi
|
|
[[ -f /etc/nginx/mime.types ]] || warn "/etc/nginx/mime.types missing; webapp-nginx.conf may fail"
|
|
|
|
# ----------------------------------------------------------------------
|
|
# 3. Postgres role + DB
|
|
# ----------------------------------------------------------------------
|
|
if ! command -v psql >/dev/null 2>&1; then
|
|
die "psql not on PATH — install Postgres on this host (e.g. apt install postgresql) before running install.sh"
|
|
fi
|
|
|
|
# Use the OS `postgres` superuser for DDL.
|
|
pg_role_exists() {
|
|
sudo -u postgres psql -tAc "SELECT 1 FROM pg_roles WHERE rolname='${APP_USER}';" 2>/dev/null | grep -q 1
|
|
}
|
|
pg_db_exists() {
|
|
sudo -u postgres psql -tAc "SELECT 1 FROM pg_database WHERE datname='${APP_USER}';" 2>/dev/null | grep -q 1
|
|
}
|
|
|
|
if pg_role_exists; then
|
|
if [[ "${FORCE_RECREATE_DB}" -eq 1 ]]; then
|
|
log "dropping existing role/DB (--force-recreate-db)"
|
|
run "sudo -u postgres psql -c 'DROP DATABASE IF EXISTS ${APP_USER};'"
|
|
run "sudo -u postgres psql -c 'DROP ROLE IF EXISTS ${APP_USER};'"
|
|
else
|
|
warn "Postgres role ${APP_USER} already exists — skipping role/DB creation. Re-run with --force-recreate-db to wipe."
|
|
fi
|
|
fi
|
|
|
|
if ! pg_role_exists; then
|
|
log "creating Postgres role ${APP_USER}"
|
|
run "sudo -u postgres psql -c \"CREATE ROLE ${APP_USER} LOGIN;\""
|
|
fi
|
|
if ! pg_db_exists; then
|
|
log "creating Postgres database ${APP_USER}"
|
|
run "sudo -u postgres psql -c \"CREATE DATABASE ${APP_USER} OWNER ${APP_USER};\""
|
|
fi
|
|
# Peer auth from the currencicombo OS user → currencicombo DB role "just works"
|
|
# on Debian-style pg_hba (local all all peer). No password needed.
|
|
|
|
# ----------------------------------------------------------------------
|
|
# 4. Redis
|
|
# ----------------------------------------------------------------------
|
|
if systemctl list-unit-files | grep -q '^redis-server\.service'; then
|
|
run systemctl enable --now redis-server
|
|
elif systemctl list-unit-files | grep -q '^redis\.service'; then
|
|
run systemctl enable --now redis
|
|
elif command -v redis-cli >/dev/null 2>&1; then
|
|
warn "redis-cli present but no redis-server.service / redis.service unit — assuming external Redis"
|
|
else
|
|
warn "redis not detected; orchestrator will fall back to in-process event bus. Install redis for multi-replica support."
|
|
fi
|
|
|
|
# ----------------------------------------------------------------------
|
|
# 5. orchestrator.env
|
|
# ----------------------------------------------------------------------
|
|
FIRST_KEYS_FILE="/root/currencicombo-first-keys.txt"
|
|
if [[ -f "${ENV_FILE}" ]]; then
|
|
log "${ENV_FILE} already exists — leaving alone (no new keys generated)"
|
|
else
|
|
log "writing ${ENV_FILE}"
|
|
install -o "${APP_USER}" -g "${APP_USER}" -m 0640 "${SCRIPT_DIR}/.env.prod.example" "${ENV_FILE}"
|
|
# Auto-fill the two REQUIRED secrets so first boot doesn't crash.
|
|
SECRET="$(openssl rand -hex 32)"
|
|
INIT_KEY="$(openssl rand -hex 24)"
|
|
SETT_KEY="$(openssl rand -hex 24)"
|
|
AUD_KEY="$(openssl rand -hex 24)"
|
|
run "sed -i 's|^EVENT_SIGNING_SECRET=.*|EVENT_SIGNING_SECRET=${SECRET}|' '${ENV_FILE}'"
|
|
run "sed -i 's|^ORCHESTRATOR_API_KEYS=.*|ORCHESTRATOR_API_KEYS=${INIT_KEY}:initiator,${SETT_KEY}:settler,${AUD_KEY}:auditor|' '${ENV_FILE}'"
|
|
# Write a root-only handoff file so ops can grab the keys without
|
|
# scraping journald or reading the env file. The canonical copy lives
|
|
# in ${ENV_FILE}; delete this file once the keys are in your password
|
|
# manager.
|
|
if [[ "${DRY_RUN}" -eq 0 ]]; then
|
|
umask 077
|
|
cat > "${FIRST_KEYS_FILE}" <<EOF
|
|
# CurrenciCombo first-deploy secrets — generated $(date -Iseconds) by install.sh
|
|
#
|
|
# This file contains the initial API keys and event-signing secret for the
|
|
# orchestrator. The canonical live values live in ${ENV_FILE} and are what
|
|
# systemd actually loads. This file is a root-only handoff copy — record
|
|
# these values in your password manager, then:
|
|
#
|
|
# shred -u ${FIRST_KEYS_FILE}
|
|
#
|
|
# Re-running install.sh does NOT regenerate these values if ${ENV_FILE}
|
|
# already exists. Losing both ${FIRST_KEYS_FILE} and ${ENV_FILE} means
|
|
# rotating all three API keys and the signing secret.
|
|
|
|
EVENT_SIGNING_SECRET=${SECRET}
|
|
ORCHESTRATOR_API_KEY_INITIATOR=${INIT_KEY}
|
|
ORCHESTRATOR_API_KEY_SETTLER=${SETT_KEY}
|
|
ORCHESTRATOR_API_KEY_AUDITOR=${AUD_KEY}
|
|
|
|
# As it appears in ${ENV_FILE}:
|
|
ORCHESTRATOR_API_KEYS=${INIT_KEY}:initiator,${SETT_KEY}:settler,${AUD_KEY}:auditor
|
|
EOF
|
|
chmod 0600 "${FIRST_KEYS_FILE}"
|
|
chown root:root "${FIRST_KEYS_FILE}"
|
|
else
|
|
log "[dry-run] would write ${FIRST_KEYS_FILE} (0600, root:root)"
|
|
fi
|
|
log " generated EVENT_SIGNING_SECRET (64 hex)"
|
|
log " generated 3 API keys (initiator/settler/auditor)"
|
|
log " initial secrets written to ${FIRST_KEYS_FILE} (0600) — record in password manager, then 'shred -u ${FIRST_KEYS_FILE}'"
|
|
fi
|
|
|
|
# ----------------------------------------------------------------------
|
|
# 6. webapp-nginx.conf
|
|
# ----------------------------------------------------------------------
|
|
run install -o "${APP_USER}" -g "${APP_USER}" -m 0644 \
|
|
"${SCRIPT_DIR}/webapp-nginx.conf" "${NGINX_FILE}"
|
|
|
|
# ----------------------------------------------------------------------
|
|
# 7. systemd units
|
|
# ----------------------------------------------------------------------
|
|
run install -o root -g root -m 0644 \
|
|
"${SCRIPT_DIR}/systemd/currencicombo-orchestrator.service" \
|
|
"${SYSTEMD_DIR}/currencicombo-orchestrator.service"
|
|
run install -o root -g root -m 0644 \
|
|
"${SCRIPT_DIR}/systemd/currencicombo-webapp.service" \
|
|
"${SYSTEMD_DIR}/currencicombo-webapp.service"
|
|
run systemctl daemon-reload
|
|
|
|
# ----------------------------------------------------------------------
|
|
# 8. Enable (but do NOT start yet — no build exists)
|
|
# ----------------------------------------------------------------------
|
|
run systemctl enable currencicombo-orchestrator.service
|
|
run systemctl enable currencicombo-webapp.service
|
|
|
|
log "install complete."
|
|
log " next: run scripts/deployment/deploy-currencicombo-8604.sh as root to build + start."
|