#!/usr/bin/env bash # Absolute DB v9.5.1 # Copyright (c) 2024-2026 D.H.Maree. All rights reserved. # Author/Creator: David H Maree # Owner: D.H.Maree (ABN 21 498 105 915) — sole IP holder # Licensed to: SupportCALL AU — primary distributor # SPDX-License-Identifier: BSL-1.1 # https://absolutedb.com/ # https://downloads.absolutedb.com/install.sh # # Usage: # curl -fsSL https://downloads.absolutedb.com/install.sh | bash # curl -fsSL https://downloads.absolutedb.com/install.sh | bash -s -- --uninstall # curl -fsSL https://downloads.absolutedb.com/install.sh | bash -s -- --no-service # # Copyright 2024-2026 SupportCALL AU & D.H.Maree # License: Proprietary (Free Community Edition) set -euo pipefail # ─── Configuration ──────────────────────────────────────────────────────────── VERSION="9.5.1" REPO="https://github.com/supportcall/AbsoluteDB.git" INSTALL_DIR="${HOME}/AbsoluteDB" BIN_DIR="/usr/local/bin" DATA_DIR="/var/lib/absdb" SERVICE_NAME="absdb" SERVICE_USER="absdb" PG_PORT=5433 REST_PORT=8080 # ─── Colours ────────────────────────────────────────────────────────────────── RED='\033[0;31m'; GRN='\033[0;32m'; YLW='\033[0;33m' BLU='\033[0;34m'; CYN='\033[0;36m'; NC='\033[0m'; BOLD='\033[1m' # Disable colours when stdout is not a terminal (piped/CI/log-file output) if [ ! -t 1 ]; then RED=''; GRN=''; YLW=''; BLU=''; CYN=''; NC=''; BOLD='' fi info() { echo -e "${BLU}[INFO]${NC} $*"; } ok() { echo -e "${GRN}[ OK ]${NC} $*"; } warn() { echo -e "${YLW}[WARN]${NC} $*"; } err() { echo -e "${RED}[FAIL]${NC} $*" >&2; } step() { echo -e "\n${BOLD}${CYN}═══ $* ═══${NC}"; } die() { err "$*"; exit 1; } # ─── Parse arguments ────────────────────────────────────────────────────────── OPT_UNINSTALL=0 OPT_NO_SERVICE=0 OPT_NO_INSTALL=0 # build but don't copy to /usr/local/bin PROTO_PRESET="" # empty = prompt interactively (or auto-minimal if non-TTY) while [ $# -gt 0 ]; do case "$1" in --uninstall) OPT_UNINSTALL=1 ;; --no-service) OPT_NO_SERVICE=1 ;; --no-install) OPT_NO_INSTALL=1 ;; --preset=*) PROTO_PRESET="${1#--preset=}" ;; --preset) shift; PROTO_PRESET="${1:-minimal}" ;; --help|-h) echo "Absolute DB Installer v${VERSION}" echo "" echo "Usage:" echo " curl -fsSL https://downloads.absolutedb.com/install.sh | bash" echo " curl -fsSL https://downloads.absolutedb.com/install.sh | bash -s -- [OPTIONS]" echo "" echo "Options:" echo " --uninstall Remove Absolute DB from the system" echo " --no-service Skip systemd service setup" echo " --no-install Build only, do not copy to /usr/local/bin" echo " --preset=X Protocol preset: minimal | webapp | microservices | enterprise | all" echo " --help Show this help" exit 0 ;; *) warn "Unknown option: $1" ;; esac shift done # ─── Banner ─────────────────────────────────────────────────────────────────── echo "" echo -e "${BOLD}╔═══════════════════════════════════════════╗${NC}" echo -e "${BOLD}║ Absolute DB v${VERSION} Installer ║${NC}" echo -e "${BOLD}║ https://absolutedb.com/ ║${NC}" echo -e "${BOLD}╚═══════════════════════════════════════════╝${NC}" echo "" echo -e " Script checksum (SHA-256):" echo -e " ${CYN}https://downloads.absolutedb.com/install.sh.sha256${NC}" echo -e " To verify before running:" echo -e " ${CYN}curl -fsSL https://downloads.absolutedb.com/install.sh -o /tmp/absdb-install.sh${NC}" echo -e " ${CYN}curl -fsSL https://downloads.absolutedb.com/install.sh.sha256 | sed 's|install.sh|/tmp/absdb-install.sh|' | sha256sum -c${NC}" echo -e " ${CYN}bash /tmp/absdb-install.sh${NC}" echo "" # ─── Detect OS ──────────────────────────────────────────────────────────────── detect_os() { if [ -f /etc/debian_version ]; then OS="debian" PKG="apt" elif [ -f /etc/redhat-release ]; then OS="rhel" if command -v dnf &>/dev/null; then PKG="dnf"; else PKG="yum"; fi elif [ "$(uname -s)" = "Darwin" ]; then OS="macos" PKG="brew" elif [ -f /etc/arch-release ]; then OS="arch" PKG="pacman" elif [ -f /etc/alpine-release ]; then die "Alpine Linux (musl libc) is not supported by this source-build installer. Use the Docker installer instead: curl -fsSL https://downloads.absolutedb.com/install-docker.sh | bash" else OS="unknown" PKG="unknown" warn "Unrecognised OS — will attempt to proceed but package installation may fail." warn "If this fails, use the Docker installer: curl -fsSL https://downloads.absolutedb.com/install-docker.sh | bash" fi info "Detected OS: ${OS} (package manager: ${PKG})" } # ─── Check for root / sudo ──────────────────────────────────────────────────── SUDO="" if [ "$(id -u)" -eq 0 ]; then SUDO="" else if command -v sudo &>/dev/null; then SUDO="sudo" else die "Not running as root and 'sudo' not found. Run as root or install sudo first: apt-get install sudo (Debian) or yum install sudo (RHEL)" fi fi # ─── Uninstall ──────────────────────────────────────────────────────────────── if [ "$OPT_UNINSTALL" -eq 1 ]; then step "Uninstalling Absolute DB" if command -v systemctl &>/dev/null; then $SUDO systemctl stop "${SERVICE_NAME}" 2>/dev/null || true $SUDO systemctl disable "${SERVICE_NAME}" 2>/dev/null || true $SUDO rm -f "/etc/systemd/system/${SERVICE_NAME}.service" $SUDO systemctl daemon-reload 2>/dev/null || true ok "Stopped and removed systemd service" fi $SUDO rm -f "${BIN_DIR}/absdb" $SUDO rm -f "${BIN_DIR}/absdb-server" $SUDO rm -f "${BIN_DIR}/absdb-bench" $SUDO rm -f "${BIN_DIR}/adb_admin" $SUDO rm -rf "/usr/local/include/absdb" $SUDO rm -rf "/usr/local/share/doc/absdb" ok "Removed binaries and documentation" warn "Data directory ${DATA_DIR} was NOT removed (to preserve your data)." warn "To remove data: sudo rm -rf ${DATA_DIR}" echo "" ok "Absolute DB uninstalled successfully." exit 0 fi # ─── Adaptive Protocol Manager wizard ──────────────────────────────────────── # # Writes /etc/absdb/absdb.conf [protocols] section based on selected preset. # Presets map to the principle of least-protocol: only enable what you need. # Shadow listener is always active — it logs attempts on disabled ports and # NEVER auto-enables them. # configure_protocols() { local preset="${1:-minimal}" local conf_dir="/etc/absdb" local conf_file="${conf_dir}/absdb.conf" # Resolve preset to per-protocol on/off values local pg_wire=on rest=on grpc=off redis=off graphql=off websocket=off prometheus=off case "$preset" in minimal) pg_wire=on; rest=on; grpc=off; redis=off; graphql=off; websocket=off; prometheus=off ;; webapp) pg_wire=on; rest=on; grpc=off; redis=on; graphql=on; websocket=on; prometheus=off ;; microservices) pg_wire=on; rest=on; grpc=on; redis=off; graphql=off; websocket=off; prometheus=on ;; enterprise|all) pg_wire=on; rest=on; grpc=on; redis=on; graphql=on; websocket=on; prometheus=on ;; *) warn "Unknown preset '${preset}' — using minimal" preset="minimal" ;; esac # Write config file $SUDO mkdir -p "${conf_dir}" $SUDO tee "${conf_file}" > /dev/null < # To re-run wizard: absdb-server --setup [server] pg_port = ${PG_PORT} rest_port = ${REST_PORT} data_dir = ${DATA_DIR}/data.db [protocols] # Principle of least-protocol: only enable what your workload requires. # Shadow listener always active — logs attempts on disabled ports (never auto-enables). pg_wire = ${pg_wire} # port ${PG_PORT} — PostgreSQL wire v3 rest = ${rest} # port ${REST_PORT} — REST API + Web Console grpc = ${grpc} # port 9090 — gRPC / HTTP/2 (native, no grpc-c) redis = ${redis} # port 6379 — Redis RESP3 (30+ commands) graphql = ${graphql} # port ${REST_PORT}/graphql — GraphQL (Apollo/Relay) websocket = ${websocket} # port ${REST_PORT}/ws — WebSocket live queries prometheus = ${prometheus} # port 9093 — Prometheus /metrics [shadow_listener] # Log connection attempts on disabled ports; never auto-enable. enabled = on EOF $SUDO chmod 640 "${conf_file}" # Allow the absdb service user to read config (created later in Step 6) $SUDO chown root:root "${conf_file}" 2>/dev/null || true ok "Config written: ${conf_file} (preset: ${preset})" info " Enabled : pg_wire=${pg_wire} rest=${rest} grpc=${grpc} redis=${redis} graphql=${graphql} websocket=${websocket} prometheus=${prometheus}" info " Runtime : adb_admin --protocol status" info " Toggle : adb_admin --protocol enable grpc | adb_admin --protocol disable redis" } prompt_protocol_wizard() { echo "" echo -e "${BOLD}${CYN}┌─────────────────────────────────────────────────────────────────┐${NC}" echo -e "${BOLD}${CYN}│ Adaptive Protocol Manager — Choose a protocol preset │${NC}" echo -e "${BOLD}${CYN}│ Principle of least-protocol: enable only what you need │${NC}" echo -e "${BOLD}${CYN}└─────────────────────────────────────────────────────────────────┘${NC}" echo "" echo -e " ${BOLD}1) Minimal${NC} — PostgreSQL wire (${PG_PORT}) + REST API (${REST_PORT})" echo -e " Default. Smallest attack surface." echo "" echo -e " ${BOLD}2) Web App${NC} — Minimal + Redis RESP3 (6379) + GraphQL + WebSocket" echo -e " For web applications using real-time features." echo "" echo -e " ${BOLD}3) Microservices${NC} — Minimal + gRPC/HTTP2 (9090) + Prometheus (9093)" echo -e " For service meshes and observability stacks." echo "" echo -e " ${BOLD}4) Enterprise${NC} — All protocols enabled" echo -e " PG wire + REST + gRPC + Redis + GraphQL + WebSocket + Prometheus" echo "" echo -e " ${BOLD}5) Custom${NC} — I'll configure protocols manually in absdb.conf" echo -e " Starts with minimal; edit /etc/absdb/absdb.conf to change." echo "" local choice="" if [ -t 0 ]; then # Interactive terminal — prompt user printf " Enter choice [1-5] (default: 1 — Minimal): " read -r choice 2>/dev/null || choice="1" else # Non-interactive (piped curl | bash) — auto-select minimal choice="1" echo -e " ${YLW}[Non-interactive install — auto-selected: 1 (Minimal)]${NC}" echo -e " ${YLW}Re-run wizard after install: sudo absdb-server --setup${NC}" fi case "${choice}" in 2) PROTO_PRESET="webapp" ;; 3) PROTO_PRESET="microservices" ;; 4) PROTO_PRESET="enterprise" ;; 5) PROTO_PRESET="minimal" info "Minimal config written. Edit /etc/absdb/absdb.conf to enable additional protocols." info "Runtime toggle: adb_admin --protocol enable " ;; *) PROTO_PRESET="minimal" ;; esac configure_protocols "${PROTO_PRESET}" } # ─── Step 1: Install prerequisites ─────────────────────────────────────────── step "Step 1/6 — Installing prerequisites" detect_os # Ensure package lists are up to date (Debian/Ubuntu only) if [ "$PKG" = "apt" ]; then info "Updating package lists..." $SUDO apt-get update -qq fi # Install build tools + curl if [ "$OS" = "debian" ]; then info "Installing git, gcc, make, build-essential, curl..." $SUDO apt-get install -y -qq git gcc make build-essential curl elif [ "$OS" = "rhel" ]; then if ! command -v gcc &>/dev/null; then info "Installing Development Tools group..." $SUDO "$PKG" groupinstall -y "Development Tools" fi info "Installing git and curl..." $SUDO "$PKG" install -y git curl elif [ "$OS" = "arch" ]; then $SUDO pacman -S --noconfirm --needed base-devel git curl elif [ "$OS" = "macos" ]; then if ! command -v gcc &>/dev/null; then info "Installing Xcode command-line tools..." xcode-select --install 2>/dev/null || true warn "If a dialog appeared, click Install and wait for it to finish." warn "Then re-run this installer." exit 0 fi command -v git &>/dev/null || brew install git command -v curl &>/dev/null || brew install curl fi # Verify required tools are present for req in git gcc make curl; do if command -v "$req" &>/dev/null; then ok "$req: $(command -v "$req")" else die "$req is required but could not be installed. Install it manually and retry." fi done # ─── Disk space check ───────────────────────────────────────────────────────── # Full build (debug objects + static lib) requires ~1.5 GB; release ~512 MB. _check_disk_space() { local required_kb=1572864 # 1.5 GB local available_kb available_kb=$(df -k "${HOME}" 2>/dev/null | awk 'NR==2{print $4}' || echo 9999999) if [ "${available_kb}" -lt "${required_kb}" ] 2>/dev/null; then die "Insufficient disk space. Need ≥ 1.5 GB free in ${HOME}. Have: $(( available_kb / 1024 )) MB. Free up space and retry." fi ok "Disk space: $(( available_kb / 1024 )) MB available — sufficient." } _check_disk_space # ─── Step 2: Download / update source ──────────────────────────────────────── step "Step 2/6 — Getting Absolute DB source" # Branch priority: always prefer 'main' (the default). Fall back to 'master' # only if 'main' does not exist on the remote. Never auto-select an ambiguous # branch — prevents installing from a stale or wrong branch silently. _detect_branch() { local remote="${1:-origin}" if git ls-remote --heads "${remote}" main 2>/dev/null | grep -q "refs/heads/main"; then echo "main" elif git ls-remote --heads "${remote}" master 2>/dev/null | grep -q "refs/heads/master"; then echo "master" else echo "main" # default; clone will fail with a clear error if missing fi } # Retry wrapper: 3 attempts with exponential backoff (4s, 8s) # Removes partial clone dirs before each retry to prevent "already exists" errors. _git_clone_with_retry() { local branch="$1" dest="$2" repo="$3" local attempt=1 delay=4 while [ "${attempt}" -le 3 ]; do # Remove partial clone (no .git dir) from a previous failed attempt if [ -d "${dest}" ] && [ ! -d "${dest}/.git" ]; then rm -rf "${dest}" fi if git clone --depth=1 --branch="${branch}" "${repo}" "${dest}" 2>&1; then return 0 fi if [ "${attempt}" -lt 3 ]; then warn "Clone attempt ${attempt}/3 failed — retrying in ${delay}s..." sleep "${delay}"; delay=$(( delay * 2 )) fi attempt=$(( attempt + 1 )) done die "git clone failed after 3 attempts. Check: internet, DNS, firewall, proxy settings." } if [ -d "${INSTALL_DIR}/.git" ]; then info "Existing installation found at ${INSTALL_DIR} — updating..." cd "${INSTALL_DIR}" DEFAULT_BRANCH="$( _detect_branch origin )" info "Fetching latest source (branch: ${DEFAULT_BRANCH})..." if git fetch --depth=1 origin "${DEFAULT_BRANCH}" 2>&1; then git reset --hard FETCH_HEAD ok "Updated to latest ${DEFAULT_BRANCH}" else warn "git fetch failed — removing and recloning..." cd /; rm -rf "${INSTALL_DIR}" _git_clone_with_retry "${DEFAULT_BRANCH}" "${INSTALL_DIR}" "${REPO}" ok "Recloned to ${INSTALL_DIR} (branch: ${DEFAULT_BRANCH})" fi else if [ -d "${INSTALL_DIR}" ]; then warn "Directory ${INSTALL_DIR} exists but is not a git repo — removing..." rm -rf "${INSTALL_DIR}" fi info "Cloning from GitHub (30-60 seconds on a normal connection)..." DEFAULT_BRANCH="$( _detect_branch "${REPO}" )" _git_clone_with_retry "${DEFAULT_BRANCH}" "${INSTALL_DIR}" "${REPO}" ok "Cloned to ${INSTALL_DIR} (branch: ${DEFAULT_BRANCH})" fi cd "${INSTALL_DIR}" # ─── Step 3: Build ──────────────────────────────────────────────────────────── step "Step 3/6 — Building Absolute DB" info "Compiling source code and running 2,793 automated tests (~7 seconds)..." echo "" make clean --quiet 2>/dev/null || true make release # Verify all required binaries were produced for _bin in absdb absdb-server; do if [ ! -x "build/${_bin}" ]; then die "Build failed — build/${_bin} not found. Check output above for errors." fi done for _bin in absdb-bench adb_admin; do [ -x "build/${_bin}" ] || warn "build/${_bin} not found — optional binary, continuing." done echo "" ok "Build complete — binaries ready in ./build/" # ─── Step 4: Protocol configuration ────────────────────────────────────────── step "Step 4/6 — Adaptive Protocol Manager" if [ -n "${PROTO_PRESET}" ]; then # Preset was provided via --preset flag — skip interactive wizard info "Using preset from command line: ${PROTO_PRESET}" configure_protocols "${PROTO_PRESET}" else prompt_protocol_wizard fi # ─── Step 5: Install binaries ───────────────────────────────────────────────── if [ "$OPT_NO_INSTALL" -eq 0 ]; then step "Step 5/6 — Installing binaries to ${BIN_DIR}" $SUDO install -m 755 build/absdb "${BIN_DIR}/absdb" $SUDO install -m 755 build/absdb-server "${BIN_DIR}/absdb-server" $SUDO install -m 755 build/absdb-bench "${BIN_DIR}/absdb-bench" $SUDO install -m 755 build/adb_admin "${BIN_DIR}/adb_admin" # Headers $SUDO mkdir -p /usr/local/include/absdb $SUDO cp include/absolute.h /usr/local/include/absdb/ 2>/dev/null || true $SUDO cp include/adb_types.h /usr/local/include/absdb/ 2>/dev/null || true $SUDO cp include/adb_sql.h /usr/local/include/absdb/ 2>/dev/null || true # Docs $SUDO mkdir -p /usr/local/share/doc/absdb $SUDO cp README.md LICENSE dist/docs/*.md /usr/local/share/doc/absdb/ 2>/dev/null || true ok "Installed: absdb absdb-server absdb-bench adb_admin → ${BIN_DIR}/" else step "Step 5/6 — Skipped (--no-install)" fi # ─── Step 6: Systemd service ────────────────────────────────────────────────── if [ "$OPT_NO_SERVICE" -eq 0 ] && command -v systemctl &>/dev/null && [ "$OS" != "macos" ]; then step "Step 6/6 — Setting up systemd service" # Create dedicated system user (never run a database as root) if ! id "${SERVICE_USER}" &>/dev/null; then $SUDO useradd --system --no-create-home \ --shell /usr/sbin/nologin 2>/dev/null \ --shell /sbin/nologin "${SERVICE_USER}" 2>/dev/null || \ $SUDO useradd --system --no-create-home "${SERVICE_USER}" ok "Created system user '${SERVICE_USER}'" else ok "System user '${SERVICE_USER}' already exists" fi # Allow service user to read the config file written in Step 4 if [ -f /etc/absdb/absdb.conf ]; then $SUDO chown root:"${SERVICE_USER}" /etc/absdb/absdb.conf 2>/dev/null || true $SUDO chmod 640 /etc/absdb/absdb.conf fi # Create and secure data directory $SUDO mkdir -p "${DATA_DIR}" $SUDO chown "${SERVICE_USER}:${SERVICE_USER}" "${DATA_DIR}" 2>/dev/null || \ $SUDO chown "${SERVICE_USER}" "${DATA_DIR}" 2>/dev/null || true $SUDO chmod 750 "${DATA_DIR}" ok "Data directory: ${DATA_DIR} (owned by ${SERVICE_USER})" # Write service file with explicit ports $SUDO tee /etc/systemd/system/${SERVICE_NAME}.service > /dev/null </dev/null && ($SUDO ufw status 2>/dev/null | grep -q "Status: active"); then for p in ${OPEN_PORTS}; do $SUDO ufw allow "${p}/tcp" 2>/dev/null || true done ok "Firewall (ufw): opened ports: ${OPEN_PORTS}" fi # Open firewall ports (firewalld — RHEL/Fedora) if command -v firewall-cmd &>/dev/null && ($SUDO firewall-cmd --state 2>/dev/null | grep -q "running" || true); then for p in ${OPEN_PORTS}; do $SUDO firewall-cmd --add-port="${p}/tcp" --permanent -q 2>/dev/null || true done $SUDO firewall-cmd --reload -q 2>/dev/null || true ok "Firewall (firewalld): opened ports: ${OPEN_PORTS}" fi # Pre-flight: check ports are not already bound (handles IPv4 + IPv6 formats) for _port in ${PG_PORT} ${REST_PORT}; do if (ss -tlnp 2>/dev/null | grep -qE ":${_port}[[:space:]]|:${_port}$") 2>/dev/null || \ (netstat -tlnp 2>/dev/null | grep -qE ":${_port}[[:space:]]|:${_port}$") 2>/dev/null; then warn "Port ${_port} is already in use by another process." warn "Check with: sudo ss -tlnp | grep :${_port}" warn "The service may fail to start. Stop the conflicting process first." fi done # Enable and start $SUDO systemctl daemon-reload $SUDO systemctl enable "${SERVICE_NAME}" --quiet $SUDO systemctl restart "${SERVICE_NAME}" # Wait up to 10 seconds for service to start info "Waiting for server to start..." STARTED=0 for i in 1 2 3 4 5 6 7 8 9 10; do sleep 1 if $SUDO systemctl is-active "${SERVICE_NAME}" --quiet 2>/dev/null; then ok "Service '${SERVICE_NAME}' is running (PID: $(systemctl show -p MainPID --value absdb 2>/dev/null || echo '?'))" STARTED=1 break fi done if [ "$STARTED" -eq 0 ]; then err "Service '${SERVICE_NAME}' did not start within 10 seconds." err "Check logs with: sudo journalctl -u ${SERVICE_NAME} --no-pager -n 30" warn "Continuing — you may need to start manually: sudo systemctl start ${SERVICE_NAME}" fi # Check for immediate crash after "active" state (catches start + instant fail) if $SUDO systemctl is-failed "${SERVICE_NAME}" --quiet 2>/dev/null; then err "Service '${SERVICE_NAME}' has failed. Last 20 log lines:" $SUDO journalctl -u "${SERVICE_NAME}" -n 20 --no-pager 2>/dev/null || true warn "Fix the issue above, then: sudo systemctl start ${SERVICE_NAME}" fi elif [ "$OPT_NO_SERVICE" -eq 1 ]; then step "Step 6/6 — Skipped (--no-service)" info "To start manually:" info " ${BIN_DIR}/absdb-server -c /etc/absdb/absdb.conf" fi # ─── macOS LaunchDaemon ─────────────────────────────────────────────────────── # Uses /Library/LaunchDaemons (system-level, starts at boot, persists across logins). # Runs as a dedicated 'absdb' system user — not root. if [ "$OPT_NO_SERVICE" -eq 0 ] && [ "$OS" = "macos" ] && [ "$OPT_NO_INSTALL" -eq 0 ]; then step "Setting up macOS LaunchDaemon" # Create dedicated system user on macOS (dscl) if ! id "${SERVICE_USER}" &>/dev/null; then _next_uid=$(( $(dscl . -list /Users UniqueID | awk '{print $2}' | sort -n | tail -1) + 1 )) $SUDO dscl . -create "/Users/${SERVICE_USER}" UserShell /usr/bin/false 2>/dev/null || true $SUDO dscl . -create "/Users/${SERVICE_USER}" UniqueID "${_next_uid}" 2>/dev/null || true $SUDO dscl . -create "/Users/${SERVICE_USER}" PrimaryGroupID 20 2>/dev/null || true $SUDO dscl . -create "/Users/${SERVICE_USER}" IsHidden 1 2>/dev/null || true ok "Created system user '${SERVICE_USER}' (UID ${_next_uid})" fi PLIST="/Library/LaunchDaemons/com.absdb.server.plist" $SUDO mkdir -p "${DATA_DIR}" $SUDO chown "${SERVICE_USER}" "${DATA_DIR}" 2>/dev/null || true $SUDO tee "${PLIST}" > /dev/null < Labelcom.absdb.server UserName${SERVICE_USER} ProgramArguments ${BIN_DIR}/absdb-server -c/etc/absdb/absdb.conf RunAtLoad KeepAlive SuccessfulExit ThrottleInterval10 WorkingDirectory${DATA_DIR} StandardOutPath/var/log/absdb.log StandardErrorPath/var/log/absdb.log EOF $SUDO launchctl unload "${PLIST}" 2>/dev/null || true $SUDO launchctl load "${PLIST}" ok "LaunchDaemon installed: ${PLIST} (runs as '${SERVICE_USER}' at boot)" fi # ─── Verify REST API is responding ──────────────────────────────────────────── step "Verifying installation" if command -v absdb &>/dev/null; then ok "absdb CLI: $(absdb --version 2>/dev/null || echo "v${VERSION}")" fi if command -v absdb-server &>/dev/null; then ok "absdb-server: $(absdb-server --version 2>/dev/null || echo "v${VERSION}")" fi # Wait for REST API to respond (up to 15 seconds total) info "Testing REST API at http://localhost:${REST_PORT}/health ..." REST_OK=0 for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15; do sleep 1 HEALTH=$(curl -sf --connect-timeout 2 "http://localhost:${REST_PORT}/health" 2>/dev/null || true) if echo "${HEALTH}" | grep -q '"healthy":true'; then ok "REST API is responding: ${HEALTH}" REST_OK=1 break fi done if [ "$REST_OK" -eq 0 ]; then err "REST API on port ${REST_PORT} did not respond within 15 seconds." echo "" warn "Troubleshooting steps:" warn " 1. Check server is running: sudo systemctl status absdb" warn " 2. View logs: sudo journalctl -u absdb --no-pager -n 30" warn " 3. Check port is open: sudo ss -tlnp | grep ${REST_PORT}" warn " or: sudo netstat -tlnp | grep ${REST_PORT}" warn " 4. Test manually: curl http://localhost:${REST_PORT}/health" warn " 5. If on a VPS/cloud: open port ${REST_PORT} in your cloud security group" warn " (AWS: EC2 → Security Groups → Inbound Rules → Add ${REST_PORT}/tcp)" warn " (GCP: VPC → Firewall → Allow ${REST_PORT}/tcp)" warn " (DigitalOcean: Networking → Firewalls → Add ${REST_PORT}/tcp)" fi # Test psql if available if command -v psql &>/dev/null; then PSQL_RESULT=$(psql -h localhost -p ${PG_PORT} -U absdb -t -c "SELECT 1+1;" 2>/dev/null | tr -d ' \n' || true) if [ "${PSQL_RESULT}" = "2" ]; then ok "PostgreSQL wire protocol: psql connected successfully on port ${PG_PORT}" else warn "psql test inconclusive (server may still be initializing)" fi else info "psql not installed — to test wire protocol:" info " Ubuntu/Debian: sudo apt install -y postgresql-client" info " RHEL/Fedora: sudo dnf install -y postgresql" info " Then: psql -h localhost -p ${PG_PORT} -U absdb" fi # ─── Done ───────────────────────────────────────────────────────────────────── echo "" echo -e "${BOLD}${GRN}╔══════════════════════════════════════════════════════════════╗${NC}" echo -e "${BOLD}${GRN}║ Absolute DB v${VERSION} installed successfully! ║${NC}" echo -e "${BOLD}${GRN}╚══════════════════════════════════════════════════════════════╝${NC}" echo "" echo -e " ${BOLD}REST API:${NC}" echo -e " curl http://localhost:${REST_PORT}/health" echo -e " curl -X POST http://localhost:${REST_PORT}/sql \\" echo -e " -H 'Content-Type: application/json' \\" echo -e " -d '{\"sql\":\"SELECT 1+1 AS result\"}'" echo "" echo -e " ${BOLD}PostgreSQL wire protocol:${NC}" echo -e " psql -h localhost -p ${PG_PORT} -U \$(whoami)" echo -e " (Install psql: sudo apt install -y postgresql-client)" echo "" echo -e " ${BOLD}Service management:${NC}" echo -e " sudo systemctl status absdb # check status" echo -e " sudo systemctl restart absdb # restart" echo -e " sudo journalctl -u absdb -f # live logs" echo "" echo -e " ${BOLD}Post-install verification:${NC}" echo -e " ${CYN}# 1. Verify the installer checksum${NC}" echo -e " curl -fsSL https://downloads.absolutedb.com/install.sh -o /tmp/absdb-install.sh" echo -e " curl -fsSL https://downloads.absolutedb.com/install.sh.sha256 \\" echo -e " | sed 's|install.sh|/tmp/absdb-install.sh|' | sha256sum -c" echo -e " ${CYN}# 2. Inspect the protocol config that was written${NC}" echo -e " sudo cat /etc/absdb/absdb.conf" echo -e " ${CYN}# 3. Confirm only expected ports are listening${NC}" echo -e " sudo ss -tlnp | grep absdb" echo -e " ${CYN}# 4. Test the REST endpoint${NC}" echo -e " curl -s http://localhost:${REST_PORT}/health | jq ." echo -e " ${CYN}# 5. Check service logs${NC}" echo -e " sudo journalctl -u absdb -n 50 --no-pager" echo "" echo -e " ${BOLD}Protocol management:${NC}" echo -e " adb_admin --protocol status # show all protocols on/off" echo -e " adb_admin --protocol enable grpc # enable a protocol" echo -e " adb_admin --protocol disable redis # disable a protocol" echo -e " adb_admin --protocol preset webapp # apply a preset profile" echo -e " sudo absdb-server --setup # re-run interactive wizard" echo -e " sudo nano /etc/absdb/absdb.conf # edit config directly" echo -e " sudo systemctl reload absdb # apply config changes (SIGHUP)" echo "" echo -e " ${BOLD}Protocol presets:${NC}" echo -e " minimal pg_wire + rest only (most secure, default)" echo -e " webapp + redis (sessions/caching)" echo -e " microservices + grpc" echo -e " enterprise all protocols on" echo "" echo -e " ${BOLD}Service management:${NC}" echo -e " sudo systemctl status absdb # check status" echo -e " sudo systemctl restart absdb # restart" echo -e " sudo journalctl -u absdb -f # live logs" echo "" echo -e " ${BOLD}If on a VPS/cloud server:${NC}" echo -e " Open ports ${PG_PORT}/tcp and ${REST_PORT}/tcp in your cloud firewall" echo -e " Then connect from your machine using the server's public IP:" echo -e " curl http://YOUR_SERVER_IP:${REST_PORT}/health" echo -e " psql -h YOUR_SERVER_IP -p ${PG_PORT} -U absdb" echo "" echo -e " ${BOLD}Documentation:${NC}" echo -e " https://absolutedb.com/docs" echo -e " https://absolutedb.com/docs/protocol-management" echo ""