From d9c9758c76db309278e16177658a6404fc207961 Mon Sep 17 00:00:00 2001 From: albertfj114 Date: Sun, 29 Mar 2026 22:02:22 -0400 Subject: [PATCH] chore: initial commit --- .env.example | 58 ++++++++ .gitignore | 2 + config/searxng/limiter.toml | 11 ++ config/searxng/settings.yml | 50 +++++++ docker-compose.yml | 203 +++++++++++++++++++++++++++ pull-models.sh | 54 +++++++ scripts/pull-models.sh | 54 +++++++ stacks/automation/docker-compose.yml | 62 ++++++++ stacks/extras/docker-compose.yml | 53 +++++++ stacks/llm/docker-compose.yml | 52 +++++++ stacks/rag/docker-compose.yml | 53 +++++++ stacks/search/docker-compose.yml | 33 +++++ 12 files changed, 685 insertions(+) create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 config/searxng/limiter.toml create mode 100644 config/searxng/settings.yml create mode 100644 docker-compose.yml create mode 100755 pull-models.sh create mode 100755 scripts/pull-models.sh create mode 100644 stacks/automation/docker-compose.yml create mode 100644 stacks/extras/docker-compose.yml create mode 100644 stacks/llm/docker-compose.yml create mode 100644 stacks/rag/docker-compose.yml create mode 100644 stacks/search/docker-compose.yml diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..8c0e6b5 --- /dev/null +++ b/.env.example @@ -0,0 +1,58 @@ +# ╔═══════════════════════════════════════════════════════════╗ +# ║ HomeAI Kit — Configuration ║ +# ║ ║ +# ║ Copy this file to .env and edit the values below. ║ +# ║ At minimum, change POSTGRES_PASSWORD. ║ +# ║ ║ +# ║ Usage: ║ +# ║ cp .env.example .env ║ +# ║ nano .env # edit passwords ║ +# ║ docker compose up -d # start core services ║ +# ╚═══════════════════════════════════════════════════════════╝ + +# === Ollama === +# No auto-pull env var exists. After startup, pull a model manually: +# docker exec homeai-ollama ollama pull llama3.2:3b +# Or use the helper script: +# ./scripts/pull-models.sh llama3.2:3b +OLLAMA_PORT=11434 + +# === Open WebUI === +# Chat interface — visit http://localhost:3000 after startup. +# You'll create an admin account on first visit. +WEBUI_PORT=3000 + +# === PostgreSQL (n8n backend) === +POSTGRES_USER=homeai +POSTGRES_PASSWORD=CHANGE-ME-use-a-real-password +POSTGRES_DB=n8n +POSTGRES_PORT=5432 + +# === n8n (Workflow Automation) === +# Visit http://localhost:5678 after startup to create your owner account. +# n8n uses interactive signup — no env-var auth. +N8N_PORT=5678 +N8N_HOST=localhost + +# === ChromaDB (Vector Database) === +CHROMA_PORT=8000 +# Token auth (recommended — prevents other devices on your LAN from accessing your embeddings) +# Remove or leave empty to disable auth. +CHROMA_SERVER_AUTHN_CREDENTIALS=CHANGE-ME-use-a-random-token +CHROMA_SERVER_AUTHN_PROVIDER=chromadb.auth.token_authn.TokenAuthenticationServerProvider + +# === SearXNG (Private Search) === +SEARXNG_PORT=8080 + +# === Optional: LibreTranslate === +# Only started with: docker compose --profile extras up -d +LIBRETRANSLATE_PORT=5000 +LT_LOAD_ONLY=en,es,fr,de + +# === Optional: Redis === +# Only started with: docker compose --profile extras up -d +REDIS_PORT=6379 + +# === Optional: ChromaDB Admin === +# Only started with: docker compose --profile extras up -d +CHROMADB_ADMIN_PORT=3002 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..78b3df5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.env +*.env diff --git a/config/searxng/limiter.toml b/config/searxng/limiter.toml new file mode 100644 index 0000000..75d2419 --- /dev/null +++ b/config/searxng/limiter.toml @@ -0,0 +1,11 @@ +# SearXNG rate limiter — disabled for private use +# This file disables rate limiting since this is a personal instance, +# not a public-facing search engine. + +[botdetection.ip_limit] +# No rate limiting for private use +link_token = false + +[botdetection.ip_lists] +# No IP blocking +pass_searxng_org = false diff --git a/config/searxng/settings.yml b/config/searxng/settings.yml new file mode 100644 index 0000000..fa1117b --- /dev/null +++ b/config/searxng/settings.yml @@ -0,0 +1,50 @@ +# SearXNG settings — tuned for private, self-hosted use +# Full docs: https://docs.searxng.org/admin/settings/ + +use_default_settings: true + +general: + instance_name: "HomeAI Search" + debug: false + privacypolicy_url: false + contact_url: false + enable_metrics: false + +search: + safe_search: 0 + autocomplete: "google" + default_lang: "en" + +server: + secret_key: "change-me-to-a-random-string" + bind_address: "0.0.0.0" + port: 8080 + limiter: false + image_proxy: true + public_instance: false + +ui: + static_use_hash: true + default_theme: simple + results_on_new_tab: false + +# JSON API — required for n8n workflow integration +search: + formats: + - html + - json + +# Engine overrides — disable slow/unreliable engines +engines: + - name: bing + disabled: false + - name: duckduckgo + disabled: false + - name: google + disabled: false + - name: wikipedia + disabled: false + - name: wikidata + disabled: true + - name: currency + disabled: true diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..32087a2 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,203 @@ +# HomeAI Kit — Full Stack +# Usage: +# docker compose up -d # Core services only +# docker compose --profile extras up -d # Core + optional services +# docker compose down # Stop everything + +services: + # ── Ollama (Local LLM Inference) ───────────────────────── + ollama: + image: ollama/ollama:latest + container_name: homeai-ollama + ports: + - "${OLLAMA_PORT:-11434}:11434" + volumes: + - ollama_data:/root/.ollama + healthcheck: + test: ["CMD-SHELL", "bash -c 'echo > /dev/tcp/localhost/11434'"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 15s + restart: unless-stopped + networks: + - homeai-net + + # ── Open WebUI (Chat Interface) ────────────────────────── + open-webui: + image: ghcr.io/open-webui/open-webui:main + container_name: homeai-open-webui + ports: + - "${WEBUI_PORT:-3000}:8080" + environment: + OLLAMA_BASE_URL: http://ollama:11434 + WEBUI_AUTH: "true" + volumes: + - openwebui_data:/app/backend/data + depends_on: + ollama: + condition: service_healthy + restart: unless-stopped + networks: + - homeai-net + + # ── ChromaDB (Vector Database) ─────────────────────────── + chromadb: + image: chromadb/chroma:latest + container_name: homeai-chromadb + ports: + - "127.0.0.1:${CHROMA_PORT:-8000}:8000" + environment: + IS_PERSISTENT: "true" + ANONYMIZED_TELEMETRY: "false" + CHROMA_SERVER_AUTHN_CREDENTIALS: ${CHROMA_SERVER_AUTHN_CREDENTIALS:-} + CHROMA_SERVER_AUTHN_PROVIDER: ${CHROMA_SERVER_AUTHN_PROVIDER:-} + volumes: + - chromadb_data:/chroma/chroma + healthcheck: + test: ["CMD-SHELL", "bash -c 'echo > /dev/tcp/localhost/8000'"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 15s + restart: unless-stopped + networks: + - homeai-net + + # ── SearXNG (Private Meta-Search) ─────────────────────── + searxng: + image: searxng/searxng:latest + container_name: homeai-searxng + ports: + - "${SEARXNG_PORT:-8080}:8080" + volumes: + - ./config/searxng/settings.yml:/etc/searxng/settings.yml:ro + - ./config/searxng/limiter.toml:/etc/searxng/limiter.toml:ro + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://localhost:8080/healthz"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 15s + restart: unless-stopped + networks: + - homeai-net + + # ── PostgreSQL (n8n Backend) ───────────────────────────── + postgres: + image: postgres:16-alpine + container_name: homeai-postgres + ports: + - "${POSTGRES_PORT:-5432}:5432" + environment: + POSTGRES_USER: ${POSTGRES_USER:-homeai} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env} + POSTGRES_DB: ${POSTGRES_DB:-n8n} + volumes: + - postgres_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-homeai} -d ${POSTGRES_DB:-n8n}"] + interval: 10s + timeout: 5s + retries: 10 + start_period: 30s + restart: unless-stopped + networks: + - homeai-net + + # ── n8n (Workflow Automation) ──────────────────────────── + n8n: + image: n8nio/n8n:latest + container_name: homeai-n8n + ports: + - "${N8N_PORT:-5678}:5678" + environment: + DB_TYPE: postgresdb + DB_POSTGRESDB_HOST: postgres + DB_POSTGRESDB_PORT: 5432 + DB_POSTGRESDB_DATABASE: ${POSTGRES_DB:-n8n} + DB_POSTGRESDB_USER: ${POSTGRES_USER:-homeai} + DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env} + N8N_HOST: ${N8N_HOST:-localhost} + N8N_PROTOCOL: http + WEBHOOK_URL: http://${N8N_HOST:-localhost}:${N8N_PORT:-5678}/ + volumes: + - n8n_data:/home/node/.n8n + depends_on: + postgres: + condition: service_healthy + restart: unless-stopped + networks: + - homeai-net + + # ── Optional: LibreTranslate (ML Translation) ─────────── + libretranslate: + image: libretranslate/libretranslate:latest + container_name: homeai-libretranslate + profiles: + - extras + ports: + - "${LIBRETRANSLATE_PORT:-5000}:5000" + environment: + LT_LOAD_ONLY: ${LT_LOAD_ONLY:-en,es,fr,de} + volumes: + - libretranslate_data:/home/libretranslate/.local + healthcheck: + test: ["CMD-SHELL", "python3 -c \"import urllib.request; urllib.request.urlopen('http://localhost:5000/languages')\""] + interval: 30s + timeout: 10s + retries: 5 + start_period: 120s + restart: unless-stopped + networks: + - homeai-net + + # ── Optional: Redis (Caching) ─────────────────────────── + redis: + image: redis:7-alpine + container_name: homeai-redis + profiles: + - extras + ports: + - "${REDIS_PORT:-6379}:6379" + volumes: + - redis_data:/data + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 3 + restart: unless-stopped + networks: + - homeai-net + + # ── Optional: ChromaDB Admin UI ───────────────────────── + chromadb-admin: + image: ghcr.io/flanker/chromadb-admin:latest + container_name: homeai-chromadb-admin + profiles: + - extras + ports: + - "${CHROMADB_ADMIN_PORT:-3002}:3000" + environment: + CHROMADB_URL: http://chromadb:8000 + depends_on: + chromadb: + condition: service_healthy + restart: unless-stopped + networks: + - homeai-net + +volumes: + ollama_data: + openwebui_data: + chromadb_data: + postgres_data: + n8n_data: + libretranslate_data: + redis_data: + +networks: + homeai-net: + name: homeai-net + driver: bridge diff --git a/pull-models.sh b/pull-models.sh new file mode 100755 index 0000000..f326357 --- /dev/null +++ b/pull-models.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash +# pull-models.sh — Pull Ollama models into the HomeAI stack +# +# Usage: +# ./scripts/pull-models.sh # pulls llama3.2:3b (default) +# ./scripts/pull-models.sh mistral:7b # pulls a specific model +# ./scripts/pull-models.sh llama3.2:3b mistral:7b # pulls multiple models + +set -euo pipefail + +CONTAINER="homeai-ollama" +DEFAULT_MODEL="llama3.2:3b" + +# Use provided models or fall back to default +MODELS=("${@:-$DEFAULT_MODEL}") + +# Check that the Ollama container is running +if ! docker inspect "$CONTAINER" --format '{{.State.Running}}' 2>/dev/null | grep -q true; then + echo "Error: Container '$CONTAINER' is not running." + echo "Start the stack first: docker compose up -d" + exit 1 +fi + +# Wait for Ollama to be healthy +echo "Waiting for Ollama to be ready..." +for i in $(seq 1 30); do + if docker exec "$CONTAINER" bash -c 'echo > /dev/tcp/localhost/11434' 2>/dev/null; then + break + fi + if [ "$i" -eq 30 ]; then + echo "Error: Ollama did not become healthy after 30 seconds." + exit 1 + fi + sleep 1 +done +echo "Ollama is ready." + +# Pull each model +for MODEL in "${MODELS[@]}"; do + echo "" + echo "Pulling $MODEL ..." + echo "(This may take 10-40 minutes depending on model size and your connection)" + echo "" + docker exec "$CONTAINER" ollama pull "$MODEL" + echo "" + echo "$MODEL pulled successfully." +done + +# List installed models +echo "" +echo "=== Installed Models ===" +docker exec "$CONTAINER" ollama list +echo "" +echo "Done. Open WebUI at http://localhost:${WEBUI_PORT:-3000} should now show your models." diff --git a/scripts/pull-models.sh b/scripts/pull-models.sh new file mode 100755 index 0000000..f326357 --- /dev/null +++ b/scripts/pull-models.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash +# pull-models.sh — Pull Ollama models into the HomeAI stack +# +# Usage: +# ./scripts/pull-models.sh # pulls llama3.2:3b (default) +# ./scripts/pull-models.sh mistral:7b # pulls a specific model +# ./scripts/pull-models.sh llama3.2:3b mistral:7b # pulls multiple models + +set -euo pipefail + +CONTAINER="homeai-ollama" +DEFAULT_MODEL="llama3.2:3b" + +# Use provided models or fall back to default +MODELS=("${@:-$DEFAULT_MODEL}") + +# Check that the Ollama container is running +if ! docker inspect "$CONTAINER" --format '{{.State.Running}}' 2>/dev/null | grep -q true; then + echo "Error: Container '$CONTAINER' is not running." + echo "Start the stack first: docker compose up -d" + exit 1 +fi + +# Wait for Ollama to be healthy +echo "Waiting for Ollama to be ready..." +for i in $(seq 1 30); do + if docker exec "$CONTAINER" bash -c 'echo > /dev/tcp/localhost/11434' 2>/dev/null; then + break + fi + if [ "$i" -eq 30 ]; then + echo "Error: Ollama did not become healthy after 30 seconds." + exit 1 + fi + sleep 1 +done +echo "Ollama is ready." + +# Pull each model +for MODEL in "${MODELS[@]}"; do + echo "" + echo "Pulling $MODEL ..." + echo "(This may take 10-40 minutes depending on model size and your connection)" + echo "" + docker exec "$CONTAINER" ollama pull "$MODEL" + echo "" + echo "$MODEL pulled successfully." +done + +# List installed models +echo "" +echo "=== Installed Models ===" +docker exec "$CONTAINER" ollama list +echo "" +echo "Done. Open WebUI at http://localhost:${WEBUI_PORT:-3000} should now show your models." diff --git a/stacks/automation/docker-compose.yml b/stacks/automation/docker-compose.yml new file mode 100644 index 0000000..e34c0cb --- /dev/null +++ b/stacks/automation/docker-compose.yml @@ -0,0 +1,62 @@ +# HomeAI Kit — Automation Stack (n8n + PostgreSQL) +# Workflow automation that connects all other services. +# +# Setup: +# docker network create homeai-net # once, if not already created +# docker compose up -d +# +# n8n UI: http://localhost:5678 +# Create your admin account on first visit. + +services: + postgres: + image: postgres:16-alpine + container_name: homeai-postgres + ports: + - "${POSTGRES_PORT:-5432}:5432" + environment: + POSTGRES_USER: ${POSTGRES_USER:-homeai} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env} + POSTGRES_DB: ${POSTGRES_DB:-n8n} + volumes: + - postgres_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-homeai} -d ${POSTGRES_DB:-n8n}"] + interval: 10s + timeout: 5s + retries: 5 + restart: unless-stopped + networks: + - homeai-net + + n8n: + image: n8nio/n8n:latest + container_name: homeai-n8n + ports: + - "${N8N_PORT:-5678}:5678" + environment: + DB_TYPE: postgresdb + DB_POSTGRESDB_HOST: postgres + DB_POSTGRESDB_PORT: 5432 + DB_POSTGRESDB_DATABASE: ${POSTGRES_DB:-n8n} + DB_POSTGRESDB_USER: ${POSTGRES_USER:-homeai} + DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env} + N8N_HOST: ${N8N_HOST:-localhost} + N8N_PROTOCOL: http + WEBHOOK_URL: http://${N8N_HOST:-localhost}:${N8N_PORT:-5678}/ + volumes: + - n8n_data:/home/node/.n8n + depends_on: + postgres: + condition: service_healthy + restart: unless-stopped + networks: + - homeai-net + +volumes: + postgres_data: + n8n_data: + +networks: + homeai-net: + external: true diff --git a/stacks/extras/docker-compose.yml b/stacks/extras/docker-compose.yml new file mode 100644 index 0000000..98aecf4 --- /dev/null +++ b/stacks/extras/docker-compose.yml @@ -0,0 +1,53 @@ +# HomeAI Kit — Extras Stack (LibreTranslate + Redis) +# Optional services for translation and caching. +# +# Setup: +# docker network create homeai-net # once, if not already created +# docker compose up -d +# +# LibreTranslate API: http://localhost:5000 +# Redis: localhost:6379 + +services: + libretranslate: + image: libretranslate/libretranslate:latest + container_name: homeai-libretranslate + ports: + - "${LIBRETRANSLATE_PORT:-5000}:5000" + environment: + LT_LOAD_ONLY: ${LT_LOAD_ONLY:-en,es,fr,de} + volumes: + - libretranslate_data:/home/libretranslate/.local + healthcheck: + test: ["CMD-SHELL", "python3 -c \"import urllib.request; urllib.request.urlopen('http://localhost:5000/languages')\""] + interval: 30s + timeout: 10s + retries: 5 + start_period: 120s + restart: unless-stopped + networks: + - homeai-net + + redis: + image: redis:7-alpine + container_name: homeai-redis + ports: + - "${REDIS_PORT:-6379}:6379" + volumes: + - redis_data:/data + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 3 + restart: unless-stopped + networks: + - homeai-net + +volumes: + libretranslate_data: + redis_data: + +networks: + homeai-net: + external: true diff --git a/stacks/llm/docker-compose.yml b/stacks/llm/docker-compose.yml new file mode 100644 index 0000000..ec91f97 --- /dev/null +++ b/stacks/llm/docker-compose.yml @@ -0,0 +1,52 @@ +# HomeAI Kit — LLM Stack (Ollama + Open WebUI) +# Standalone local ChatGPT alternative. +# +# Setup: +# docker network create homeai-net # once, if not already created +# docker compose up -d +# docker exec homeai-ollama ollama pull llama3.2:3b +# +# Then visit http://localhost:3000 + +services: + ollama: + image: ollama/ollama:latest + container_name: homeai-ollama + ports: + - "${OLLAMA_PORT:-11434}:11434" + volumes: + - ollama_data:/root/.ollama + healthcheck: + test: ["CMD-SHELL", "curl -sf http://localhost:11434/api/tags || exit 1"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 30s + restart: unless-stopped + networks: + - homeai-net + + open-webui: + image: ghcr.io/open-webui/open-webui:main + container_name: homeai-open-webui + ports: + - "${WEBUI_PORT:-3000}:8080" + environment: + OLLAMA_BASE_URL: http://ollama:11434 + WEBUI_AUTH: "true" + volumes: + - openwebui_data:/app/backend/data + depends_on: + ollama: + condition: service_healthy + restart: unless-stopped + networks: + - homeai-net + +volumes: + ollama_data: + openwebui_data: + +networks: + homeai-net: + external: true diff --git a/stacks/rag/docker-compose.yml b/stacks/rag/docker-compose.yml new file mode 100644 index 0000000..667d0fd --- /dev/null +++ b/stacks/rag/docker-compose.yml @@ -0,0 +1,53 @@ +# HomeAI Kit — RAG Stack (ChromaDB + Admin UI) +# Vector database for document embeddings and similarity search. +# +# Setup: +# docker network create homeai-net # once, if not already created +# docker compose up -d +# +# ChromaDB API: http://localhost:8000 +# Admin UI: http://localhost:3002 + +services: + chromadb: + image: chromadb/chroma:latest + container_name: homeai-chromadb + ports: + - "127.0.0.1:${CHROMA_PORT:-8000}:8000" + environment: + IS_PERSISTENT: "true" + ANONYMIZED_TELEMETRY: "false" + CHROMA_SERVER_AUTHN_CREDENTIALS: ${CHROMA_SERVER_AUTHN_CREDENTIALS:-} + CHROMA_SERVER_AUTHN_PROVIDER: ${CHROMA_SERVER_AUTHN_PROVIDER:-} + volumes: + - chromadb_data:/chroma/chroma + healthcheck: + test: ["CMD-SHELL", "curl -sf http://localhost:8000/api/v2/heartbeat || exit 1"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 15s + restart: unless-stopped + networks: + - homeai-net + + chromadb-admin: + image: ghcr.io/flanker/chromadb-admin:latest + container_name: homeai-chromadb-admin + ports: + - "${CHROMADB_ADMIN_PORT:-3002}:3000" + environment: + CHROMADB_URL: http://chromadb:8000 + depends_on: + chromadb: + condition: service_healthy + restart: unless-stopped + networks: + - homeai-net + +volumes: + chromadb_data: + +networks: + homeai-net: + external: true diff --git a/stacks/search/docker-compose.yml b/stacks/search/docker-compose.yml new file mode 100644 index 0000000..9e4d69d --- /dev/null +++ b/stacks/search/docker-compose.yml @@ -0,0 +1,33 @@ +# HomeAI Kit — Search Stack (SearXNG) +# Private meta-search engine — searches Google, Bing, DuckDuckGo +# without tracking or ads. +# +# Setup: +# docker network create homeai-net # once, if not already created +# docker compose up -d +# +# Search UI: http://localhost:8080 +# JSON API: http://localhost:8080/search?q=test&format=json + +services: + searxng: + image: searxng/searxng:latest + container_name: homeai-searxng + ports: + - "${SEARXNG_PORT:-8080}:8080" + volumes: + - ../../config/searxng/settings.yml:/etc/searxng/settings.yml:ro + - ../../config/searxng/limiter.toml:/etc/searxng/limiter.toml:ro + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://localhost:8080/healthz"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 15s + restart: unless-stopped + networks: + - homeai-net + +networks: + homeai-net: + external: true