Files
passepartout/literate/setup.org

11 KiB

Setup & Onboarding (setup.org)

Overview: The Zero-to-One Experience

The Setup & Onboarding process ensures that users can boot the org-agent Lisp Machine with zero friction. We follow the Appliance Paradigm for standard users (Docker-first) and provide a Power User Path (Baremetal) for those wanting deep native integration.

This file is a Literate Devops document. Tangling it generates the Docker configuration, the one-liner installation script, and the baremetal onboarding script.

1. The Appliance Paradigm (Docker First)

The easiest way to run the agent is via Docker. This prevents the user from having to manually manage SBCL, Quicklisp, Python virtual environments, Playwright binaries, and Java (for Signal).

The Dockerfile

The container wraps all messy OS dependencies and pre-caches the Lisp environment for rapid booting.

# ORG-AGENT v1.0 Production Environment
FROM debian:bookworm-slim

# Prevent interactive prompts during build
ENV DEBIAN_FRONTEND=noninteractive

# 1. Install System Dependencies
# - sbcl: The Lisp Runtime
# - curl/git/unzip: Standard tools for Quicklisp and binaries
# - default-jre: Required by signal-cli
# - python3/pip: Required for Playwright bridge
RUN apt-get update && apt-get install -y \
    sbcl \
    curl \
    git \
    unzip \
    default-jre \
    libsqlite3-0 \
    python3 \
    python3-pip \
    python3-venv \
    && rm -rf /var/lib/apt/lists/*

# 2. Setup Playwright (High-Fidelity Browsing)
RUN python3 -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
RUN pip install playwright \
    && playwright install --with-deps chromium

# 3. Install signal-cli (v0.14.0)
ENV SIGNAL_CLI_VERSION=0.14.0
RUN curl -L https://github.com/AsamK/signal-cli/releases/download/v${SIGNAL_CLI_VERSION}/signal-cli-${SIGNAL_CLI_VERSION}-Linux.tar.gz | tar xz -C /opt \
    && ln -s /opt/signal-cli-${SIGNAL_CLI_VERSION}/bin/signal-cli /usr/local/bin/signal-cli

# 4. Install Quicklisp & Pin Distribution
# Pinned to 2026-04-01 for bit-rot resistance.
WORKDIR /root
RUN curl -O https://beta.quicklisp.org/quicklisp.lisp \
    && sbcl --non-interactive \
        --load quicklisp.lisp \
        --eval '(quicklisp-quickstart:install)' \
        --eval '(ql-dist:install-dist "http://beta.quicklisp.org/dist/quicklisp/2026-04-01/distinfo.txt" :prompt nil :replace t)'

# 5. Configure SBCL to load Quicklisp on startup
RUN echo '(let ((quicklisp-init (merge-pathnames "quicklisp/setup.lisp" (user-homedir-pathname)))) (when (probe-file quicklisp-init) (load quicklisp-init)))' > /root/.sbclrc

# 6. Setup Application Directory
WORKDIR /app
COPY . /app/projects/org-agent

# 7. Pre-cache Lisp Dependencies
RUN sbcl --non-interactive \
    --eval '(push #p"/app/projects/org-agent/" asdf:*central-registry*)' \
    --eval '(ql:quickload :org-agent)'

# 8. Environment & Volumes
# The host's memex root should be mounted to /memex
ENV MEMEX_DIR=/memex
VOLUME ["/memex"]

# Default Ports
EXPOSE 9105 8080

# Entrypoint
CMD ["sbcl", "--non-interactive", \
     "--eval", "(push #p\"/app/projects/org-agent/\" asdf:*central-registry*)", \
     "--eval", "(ql:quickload :org-agent)", \
     "--eval", "(org-agent:main)"]

Docker Compose

services:
  org-agent:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: org-agent
    env_file: .env
    volumes:
      # Mount the entire memex directory (2 levels up from projects/org-agent)
      - ../..:/memex
      # Ensure signal-cli state is preserved
      - signal-state:/root/.local/share/signal-cli
    ports:
      - "${ORG_AGENT_DAEMON_PORT:-9105}:9105"
      - "${ORG_AGENT_WEB_PORT:-8080}:8080"
    restart: unless-stopped

volumes:
  signal-state:

2. The Interactive One-Liner (install.sh)

This script is what the user pipes to bash from curl. It detects the OS, installs Docker if missing (after asking permission), clones the repo, interactively generates the .env, and launches the container.

set -e

RED='\033[0;31m'
GREEN='\033[0;32m'
BLUE='\033[0;34m'
YELLOW='\033[0;33m'
NC='\033[0m' # No Color

echo -e "${BLUE}==================================================${NC}"
echo -e "${BLUE} org-agent: Sovereign Intelligence Onboarding     ${NC}"
echo -e "${BLUE}==================================================${NC}"

# --- OS & Docker Detection ---
echo -e "\n${BLUE}[1/4] Verifying Environment...${NC}"

command_exists() { command -v "$1" >/dev/null 2>&1; }

install_docker() {
    echo -e "${YELLOW}Docker is required to run org-agent natively without messy dependencies.${NC}"
    read -p "Would you like me to attempt to install Docker? [Y/n]: " install_choice
    install_choice=${install_choice:-Y}
    if [[ "$install_choice" =~ ^[Yy]$ ]]; then
        if [[ "$OSTYPE" == "linux-gnu"* ]]; then
            if command_exists apt-get; then
                echo "Installing Docker via apt..."
                sudo apt-get update
                sudo apt-get install -y docker.io docker-compose
            elif command_exists dnf; then
                echo "Installing Docker via dnf..."
                sudo dnf install -y docker docker-compose
                sudo systemctl start docker
                sudo systemctl enable docker
            else
                echo -e "${RED}Unsupported package manager. Please install Docker manually.${NC}"
                exit 1
            fi
        elif [[ "$OSTYPE" == "darwin"* ]]; then
            if command_exists brew; then
                echo "Installing Docker Desktop via Homebrew..."
                brew install --cask docker
                echo -e "${YELLOW}Please start Docker Desktop from your Applications folder, then re-run this script.${NC}"
                exit 0
            else
                echo -e "${RED}Homebrew not found. Please install Docker Desktop for Mac manually.${NC}"
                exit 1
            fi
        else
            echo -e "${RED}Unsupported OS for automated Docker installation. Please install manually.${NC}"
            exit 1
        fi
    else
        echo -e "${RED}Docker is required. Aborting.${NC}"
        exit 1
    fi
}

if ! command_exists docker || ! command_exists docker-compose; then
    install_docker
else
    echo -e "${GREEN}✓ Docker and docker-compose detected.${NC}"
fi

# --- Repository Setup ---
echo -e "\n${BLUE}[2/4] Downloading Kernel...${NC}"
MEMEX_DEFAULT="$HOME/memex"
read -p "Where is your Memex located? (default: $MEMEX_DEFAULT): " MEMEX_TARGET
MEMEX_TARGET=${MEMEX_TARGET:-$MEMEX_DEFAULT}

mkdir -p "$MEMEX_TARGET/projects"
cd "$MEMEX_TARGET/projects"

if [ ! -d "org-agent" ]; then
    echo "Cloning org-agent..."
    git clone https://github.com/gharbeia/org-agent.git
    cd org-agent
else
    echo -e "${GREEN}✓ Repository already exists.${NC}"
    cd org-agent
    git pull origin main
fi

# --- Interactive Configuration ---
echo -e "\n${BLUE}[3/4] Neural & Identity Calibration...${NC}"
if [ ! -f .env ]; then
    cp .env.example .env
fi

# Ask for Name
read -p "What is your name? (default: User): " USER_NAME
USER_NAME=${USER_NAME:-User}
sed -i "s/MEMEX_USER=.*/MEMEX_USER=\"$USER_NAME\"/g" .env

# Ask for Assistant Name
read -p "What shall we name your Assistant? (default: Agent): " AGENT_NAME
AGENT_NAME=${AGENT_NAME:-Agent}
sed -i "s/MEMEX_ASSISTANT=.*/MEMEX_ASSISTANT=\"$AGENT_NAME\"/g" .env

# Ask for LLM
echo -e "\nSelect your primary neural provider:"
echo "1) Google Gemini (Free Tier / Official)"
echo "2) OpenRouter (Unified / Paid)"
echo "3) Anthropic (Claude / API Key)"
echo "4) OpenAI (GPT / API Key)"
read -p "Choice [1-4]: " LLM_CHOICE

case $LLM_CHOICE in
    2) read -p "Enter OpenRouter API Key: " INPUT; sed -i "s/OPENROUTER_API_KEY=.*/OPENROUTER_API_KEY=\"$INPUT\"/g" .env ;;
    3) read -p "Enter Anthropic API Key: " INPUT; sed -i "s/ANTHROPIC_API_KEY=.*/ANTHROPIC_API_KEY=\"$INPUT\"/g" .env ;;
    4) read -p "Enter OpenAI API Key: " INPUT; sed -i "s/OPENAI_API_KEY=.*/OPENAI_API_KEY=\"$INPUT\"/g" .env ;;
    *) read -p "Enter Gemini API Key: " INPUT; sed -i "s/GEMINI_API_KEY=.*/GEMINI_API_KEY=\"$INPUT\"/g" .env ;;
esac

# Seed Core Skills
echo -e "\n${BLUE}[4/4] Seeding Skills...${NC}"
# In Docker, the host's memex maps to /memex. The skills should be saved in the host's memex notes folder.
SKILLS_DIR="$MEMEX_TARGET/notes"
mkdir -p "$SKILLS_DIR"
cp -n skills/*.org "$SKILLS_DIR/" 2>/dev/null || true
echo -e "${GREEN}✓ Core skills seeded to $SKILLS_DIR.${NC}"

# Ensure proper ownership if sudo was used for apt
if [ -n "$SUDO_USER" ]; then
    chown -R "$SUDO_USER" "$MEMEX_TARGET/projects/org-agent"
fi

echo -e "\n${GREEN}==================================================${NC}"
echo -e "${GREEN} Onboarding Complete!                             ${NC}"
echo -e "${GREEN} Booting your sovereign brain in the background...${NC}"
echo -e "${GREEN}==================================================${NC}"

# Launch
docker-compose up -d --build
echo -e "\n${YELLOW}To view logs, run: cd $MEMEX_TARGET/projects/org-agent && docker-compose logs -f${NC}"

3. The Power-User Path (Baremetal Onboarding)

For users who want to run the Lisp Machine natively on their host OS (typically Emacs users who want the agent to directly manipulate their local .emacs.d), we provide the baremetal setup script. This script verifies the host has SBCL and Quicklisp installed, and configures the paths natively.

set -e
RED='\033[0;31m'; GREEN='\033[0;32m'; BLUE='\033[0;34m'; NC='\033[0m'

echo -e "${BLUE}=== org-agent: Baremetal Power-User Setup ===${NC}"

if ! command -v sbcl >/dev/null 2>&1; then
    echo -e "${RED}✗ SBCL not found. Please install it first.${NC}"
    exit 1
fi

if [ ! -d "$HOME/quicklisp" ] && [ ! -d "$HOME/.quicklisp" ]; then
    echo -e "${RED}✗ Quicklisp not found. Please install Quicklisp.${NC}"
    exit 1
fi

if [ ! -f .env ]; then cp .env.example .env; fi

read -p "What is your name? (default: User): " USER_NAME
USER_NAME=${USER_NAME:-User}
sed -i "s/MEMEX_USER=.*/MEMEX_USER=\"$USER_NAME\"/g" .env

read -p "What shall we name your Assistant? (default: Agent): " AGENT_NAME
AGENT_NAME=${AGENT_NAME:-Agent}
sed -i "s/MEMEX_ASSISTANT=.*/MEMEX_ASSISTANT=\"$AGENT_NAME\"/g" .env

echo "Select primary neural provider:"
echo "1) Gemini"; echo "2) OpenRouter"; echo "3) Anthropic"; echo "4) OpenAI"
read -p "Choice [1-4]: " LLM_CHOICE
case $LLM_CHOICE in
    2) read -p "Enter OpenRouter Key: " INPUT; sed -i "s/OPENROUTER_API_KEY=.*/OPENROUTER_API_KEY=\"$INPUT\"/g" .env ;;
    3) read -p "Enter Anthropic Key: " INPUT; sed -i "s/ANTHROPIC_API_KEY=.*/ANTHROPIC_API_KEY=\"$INPUT\"/g" .env ;;
    4) read -p "Enter OpenAI Key: " INPUT; sed -i "s/OPENAI_API_KEY=.*/OPENAI_API_KEY=\"$INPUT\"/g" .env ;;
    *) read -p "Enter Gemini Key: " INPUT; sed -i "s/GEMINI_API_KEY=.*/GEMINI_API_KEY=\"$INPUT\"/g" .env ;;
esac

# Update baremetal paths based on current directory structure
PROJECT_ROOT=$(pwd)
PARENT_DIR=$(dirname "$PROJECT_ROOT")
sed -i "s|MEMEX_DIR=.*|MEMEX_DIR=\"$PARENT_DIR\"|g" .env
sed -i "s|ZETTELKASTEN_DIR=.*|ZETTELKASTEN_DIR=\"$PARENT_DIR/notes\"|g" .env
sed -i "s|SKILLS_DIR=.*|SKILLS_DIR=\"$PARENT_DIR/notes\"|g" .env

mkdir -p "$PARENT_DIR/notes"
cp -n skills/*.org "$PARENT_DIR/notes/" 2>/dev/null || true

echo -e "${GREEN}Baremetal setup complete. Run 'make run' to start.${NC}"