Compare commits

..

29 Commits

Author SHA1 Message Date
d47d230c25 fix: restore corrupted variable names (${OPENROUTER_API_KEY}, ${OPENCLAW_GATEWAY_TOKEN})
Some checks failed
Build Hermes agent / build (pull_request) Has been cancelled
Build ollama (gfx906) / build (pull_request) Has been cancelled
2026-05-21 00:29:16 -04:00
3596ac5219 fix: restore command: gateway run and OPENROUTER_API_KEY variable
Some checks failed
Build Hermes agent / build (pull_request) Has been cancelled
Build ollama (gfx906) / build (pull_request) Has been cancelled
2026-05-21 00:27:01 -04:00
802c71cf4e chore: remove Hermes config files from repo — lives on persistent volume
Some checks failed
Build Hermes agent / build (pull_request) Has been cancelled
Build ollama (gfx906) / build (pull_request) Has been cancelled
2026-05-20 23:45:32 -04:00
8ce9f7189f chore: remove .env.example
Some checks failed
Build Hermes agent / build (pull_request) Has been cancelled
Build ollama (gfx906) / build (pull_request) Has been cancelled
2026-05-20 23:38:15 -04:00
e3f47cac6b feat: keep only Honcho, remove OpenViking from memory providers 2026-05-20 23:38:15 -04:00
04dcca1aa7 Merge remote-tracking branch 'origin/master' into feat/memory-providers
Some checks failed
Build Hermes agent / build (pull_request) Has been cancelled
Build ollama (gfx906) / build (pull_request) Has been cancelled
2026-05-20 23:08:44 -04:00
35aa466e87 fix: Matrix bridge ModuleNotFoundError - install deps to venv with persistence
Some checks failed
Build Hermes agent / build (push) Has been cancelled
Build ollama (gfx906) / build (push) Has been cancelled
fix: Matrix bridge ModuleNotFoundError - install deps to venv with persistence
2026-05-20 18:47:28 +00:00
2ff99b1f57 Merge master into fix/matrix-bridge-dependencies to resolve conflicts
Some checks failed
Build Hermes agent / build (pull_request) Has been cancelled
Build ollama (gfx906) / build (pull_request) Has been cancelled
2026-05-20 14:42:02 -04:00
dea1429a5d Merge pull request #2: fix: Matrix bridge ModuleNotFoundError - install deps to venv with persistence
# Conflicts:
#	ai/compose.yml
2026-05-20 14:41:06 -04:00
d3f2e3b7b9 Merge pull request 'feat: add Syncthing service for Hermes org-file sync' (#30) from feat/syncthing-org-sync into master
Some checks failed
Build Hermes agent / build (push) Has been cancelled
Build ollama (gfx906) / build (push) Has been cancelled
Reviewed-on: #30
2026-05-19 00:28:59 +00:00
6a44120b1a Fixed syncthing dir path
Some checks failed
Build Hermes agent / build (pull_request) Has been cancelled
Build ollama (gfx906) / build (pull_request) Has been cancelled
2026-05-18 20:25:18 -04:00
01fbf2ab62 feat: add self-hosted memory providers (OpenViking, Honcho, Holographic)
Some checks failed
Build Hermes agent / build (pull_request) Has been cancelled
Build ollama (gfx906) / build (pull_request) Has been cancelled
- Add OpenViking service (knowledge graph) using official GHCR image
- Add Honcho stack (user modeling): API + PostgreSQL pgvector + Redis
- Add Holographic config to Hermes (local SQLite, no server needed)
- Hermes: install httpx for OpenViking client
- Hermes: auto-generate config.yaml + honcho.json on first boot
- All data 100% local, zero cloud dependencies
2026-05-17 17:10:04 -04:00
38a1451689 Merge branch 'master' into feat/syncthing-org-sync 2026-05-14 22:24:19 -04:00
f9fb28d560 fix: route Syncthing web UI through Traefik with HTTPS
Some checks failed
Build Hermes agent / build (pull_request) Has been cancelled
Build ollama (gfx906) / build (pull_request) Has been cancelled
2026-05-14 21:40:00 -04:00
bcc4b6d157 feat: add Syncthing service for Hermes org-file sync
Some checks failed
Build Hermes agent / build (pull_request) Has been cancelled
Build ollama (gfx906) / build (pull_request) Has been cancelled
2026-05-14 21:35:31 -04:00
8d1ae7e632 Remove the unsuported gitea action off 2026-05-13 13:11:11 -04:00
29ae32a1c5 Merge pull request 'fix: use ln -sf instead of update-alternatives --set for iptables-nft' (#28) from fix/vpn-iptables-nft-v3 into master
Reviewed-on: #28
2026-05-13 16:59:50 +00:00
8dff094768 fix: use ln -sf instead of update-alternatives --set
update-alternatives --set fails because the base image only registers
iptables-legacy as an alternative. The iptables-nft binary (/usr/sbin/iptables-nft)
exists but isn't in the alternatives database. Direct ln -sf bypasses this.
2026-05-13 12:58:43 -04:00
ec08f5eb5d Merge pull request 'fix: remove apk add iptables-nft — built-in on Alpine 3.18+' (#27) from fix/vpn-iptables-nft-v2 into master
Reviewed-on: #27
2026-05-13 16:49:23 +00:00
611e96b306 fix: remove apk add iptables-nft — built-in on Alpine 3.18+
In Alpine 3.18+, the 'iptables' package IS the nftables variant.
iptables-nft is not a separate package. The binary is already in
the base image — only need to flip update-alternatives.
2026-05-13 12:48:51 -04:00
f184ed957c Merge pull request 'fix: update wg-easy to official ghcr image with iptables-nft' (#26) from fix/vpn-iptables-nft-upstream into master
Reviewed-on: #26
2026-05-13 16:37:35 +00:00
2bf31c7ccc fix: update wg-easy to official ghcr image with iptables-nft
- Switch FROM weejewel/wg-easy:latest (4yr old, Alpine 3.11) to
  ghcr.io/wg-easy/wg-easy:latest (actively maintained, Alpine krypton)
- Use update-alternatives instead of raw ln -sf to flip iptables
  from legacy to nftables backend
- Fix compose build context: ./vpn -> . (Dockerfile was at same level)

The weejewel/wg-easy image lacked iptables-nft package in Alpine 3.11.
The new official image has it available, we just flip the alternatives.
The old ln -sf approach was fragile across Alpine versions.
2026-05-13 12:30:15 -04:00
f44f93e35a Merge pull request 'fix: add Himalaya email CLI to Hermes Docker image' (#25) from fix/himalaya-email-cli into master
Some checks failed
Build Hermes agent / build (push) Has been cancelled
Reviewed-on: #25
2026-05-13 15:03:40 +00:00
4cdd157e3f Merge pull request 'fix: add iptables-nft to wg-easy for nftables-only kernels' (#24) from fix/wg-easy-iptables-nft into master
Reviewed-on: #24
2026-05-13 15:03:25 +00:00
27571ddb3f feat: add Himalaya email CLI to Hermes Docker image
Some checks failed
Build Hermes agent / build (pull_request) Failing after 2s
2026-05-12 18:09:40 -04:00
5e242eb946 fix: add iptables-nft to wg-easy for nftables-only kernels
wg-easy's Alpine wg-quick uses legacy iptables which requires the
iptable_nat kernel module. On NixOS kernels compiled without legacy
netfilter modules, the container crashes in a restart loop:

  iptables v1.8.3 (legacy): can't initialize iptables table 'nat'
  Table does not exist (do you need to insmod?)

Fix: build a custom image that installs Alpine's iptables-nft package
and symlinks iptables -> iptables-nft (nftables backend).
2026-05-12 14:52:33 -04:00
Thierry Pouplier
2aab06cc1a fix: use mautrix[encryption] instead of matrix-nio for Matrix bridge
The Hermes Matrix gateway uses the mautrix SDK, not matrix-nio.
This fixes E2EE support by installing the correct library.

Refs: PR #2
2026-04-29 03:34:15 +00:00
Thierry Pouplier
a404f5e2c4 fix: Correct OPENROUTER_API_KEY variable name 2026-04-29 02:43:35 +00:00
Thierry Pouplier
f9afd79f3e fix: Add openai and matrix-nio dependencies for Hermes Matrix bridge 2026-04-29 02:19:24 +00:00
9 changed files with 370 additions and 5 deletions

99
ai/compose.yml Normal file → Executable file
View File

@@ -32,7 +32,7 @@ services:
- default
container_name: hermes
entrypoint: ["/bin/bash", "-c",
"bash /opt/data/hermes-tools/install.sh && exec /usr/bin/tini -g -- /opt/hermes/docker/entrypoint.sh \"$@\"",
"bash /opt/data/hermes-tools/install.sh && . /opt/hermes/.venv/bin/activate && uv pip install openai 'mautrix[encryption]' -q && exec /usr/bin/tini -g -- /opt/hermes/docker/entrypoint.sh \"$@\"",
"hermes-entrypoint"]
restart: always
# Gateway run enables the internal API server on port 8642
@@ -54,6 +54,12 @@ services:
- TZ=America/Montreal
volumes:
- /mnt/HoardingCow_docker_data/Hermes/data:/opt/data
# Syncthing-shared org files — read-only view of user's agenda
- /mnt/HoardingCow_docker_data/Syncthing/telos-ro:/opt/data/telos-ro:ro
# Syncthing-shared inbox — write tasks here, they sync to user's laptop
- /mnt/HoardingCow_docker_data/Syncthing/telos-rw:/opt/data/telos-rw:rw
# Persist Python venv across container recreation (Matrix bridge deps, etc.)
- /mnt/HoardingCow_docker_data/Hermes/venv:/opt/hermes/.venv
devices:
- /dev/kfd:/dev/kfd
- /dev/dri:/dev/dri
@@ -62,6 +68,37 @@ services:
- "26"
networks:
- ai_backend
depends_on:
- honcho
syncthing:
image: syncthing/syncthing:latest
container_name: syncthing
hostname: syncthing
restart: always
ports:
- "8384:8384"
- "22000:22000"
- "21027:21027/udp"
environment:
- TZ=America/Montreal
volumes:
- /mnt/HoardingCow_docker_data/Syncthing/config:/var/syncthing/config
- /mnt/HoardingCow_docker_data/Syncthing/telos-ro:/telos-ro
- /mnt/HoardingCow_docker_data/Syncthing/telos-rw:/telos-rw
networks:
- ai_backend
- ai_net
labels:
- "traefik.enable=true"
- "traefik.http.routers.syncthing-http.rule=Host(`syncthing.lazyworkhorse.net`)"
- "traefik.http.routers.syncthing-http.entrypoints=web"
- "traefik.http.routers.syncthing-http.middlewares=redirect-to-https"
- "traefik.http.routers.syncthing-https.rule=Host(`syncthing.lazyworkhorse.net`)"
- "traefik.http.routers.syncthing-https.entrypoints=websecure"
- "traefik.http.routers.syncthing-https.tls=true"
- "traefik.http.routers.syncthing-https.tls.certresolver=njalla"
- "traefik.http.services.syncthing.loadbalancer.server.port=8384"
ollama:
build:
@@ -96,6 +133,66 @@ services:
- "303"
- "26"
# --- Honcho: AI-native user modeling ---
honcho:
build: ./honcho
container_name: honcho
restart: unless-stopped
ports:
- "127.0.0.1:8000:8000"
environment:
- DB_CONNECTION_URI=postgresql+psycopg://honcho:honcho_pass@honcho-db:5432/honcho
- CACHE_URL=redis://honcho-redis:6379/0
- CACHE_ENABLED=true
volumes:
- /mnt/HoardingCow_docker_data/Honcho/data:/app/data
networks:
- ai_backend
depends_on:
honcho-db:
condition: service_healthy
honcho-redis:
condition: service_healthy
honcho-db:
image: pgvector/pgvector:pg15
container_name: honcho-db
restart: unless-stopped
ports:
- "127.0.0.1:5432:5432"
command: ["postgres", "-c", "max_connections=200"]
environment:
- POSTGRES_DB=honcho
- POSTGRES_USER=honcho
- POSTGRES_PASSWORD=honcho_pass
- PGDATA=/var/lib/postgresql/data/pgdata
volumes:
- /mnt/HoardingCow_docker_data/Honcho/postgres:/var/lib/postgresql/data
- ./honcho/init-db.sql:/docker-entrypoint-initdb.d/init.sql:ro
networks:
- ai_backend
healthcheck:
test: ["CMD-SHELL", "pg_isready -U honcho -d honcho"]
interval: 5s
timeout: 5s
retries: 5
honcho-redis:
image: redis:8
container_name: honcho-redis
restart: unless-stopped
ports:
- "127.0.0.1:6379:6379"
volumes:
- /mnt/HoardingCow_docker_data/Honcho/redis:/data
networks:
- ai_backend
healthcheck:
test: ["CMD-SHELL", "redis-cli ping"]
interval: 5s
timeout: 5s
retries: 5
networks:
ai_net:
external: true

View File

@@ -48,6 +48,14 @@ RUN apt-get update && \
# ---------- UV ----------
COPY --chmod=0755 --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/
WORKDIR /opt/hermes
# ---------- Memory provider dependencies ----------
# httpx: HTTP client for OpenViking plugin
# honcho-ai: already installed in upstream image (v2.1.1+)
RUN . /opt/hermes/.venv/bin/activate && \
uv pip install --no-cache-dir httpx
# ---------- Piper TTS ----------
RUN . /opt/hermes/.venv/bin/activate && \
uv pip install --no-cache-dir piper-tts sounddevice numpy && \
@@ -61,6 +69,24 @@ urllib.request.urlretrieve(url, base + '/en_US-ryan-high.onnx')
urllib.request.urlretrieve(url + '.json', base + '/en_US-ryan-high.onnx.json')
PYEOF
# ---------- Install Himalaya email CLI ----------
RUN /opt/hermes/.venv/bin/python3 /dev/stdin << 'PYEOF'
import urllib.request, tarfile, os, shutil
url = 'https://github.com/pimalaya/himalaya/releases/download/v1.2.0/himalaya.x86_64-linux.tgz'
tgz = '/tmp/himalaya.tgz'
urllib.request.urlretrieve(url, tgz)
with tarfile.open(tgz) as t:
t.extractall('/tmp')
shutil.move('/tmp/himalaya', '/usr/local/bin/himalaya')
os.chmod('/usr/local/bin/himalaya', 0o755)
os.remove(tgz)
print('himalaya v1.2.0 installed')
PYEOF
# ---------- Install himalaya-ro wrapper ----------
COPY --chmod=0755 himalaya-ro.sh /usr/local/bin/himalaya-ro
# ---------- Runtime ----------
USER hermes
ENV HERMES_HOME=/opt/data

73
ai/hermes/himalaya-ro.sh Normal file
View File

@@ -0,0 +1,73 @@
#!/usr/bin/env bash
# ─────────────────────────────────────────────────────────────
# himalaya-ro — Read-only wrapper for himalaya
#
# Blocks destructive commands and logs audit trail.
# Pass-through for read-only commands (list, read, search).
#
# Usage: himalaya-ro [options] <command> [args...]
#
# Install: place in PATH before the real himalaya, or use
# `ln -sf himalaya-ro /usr/local/bin/himalaya`
# ─────────────────────────────────────────────────────────────
set -o pipefail
# ── Configuration ───────────────────────────────────────────
HIMALAYA_BIN="${HIMALAYA_BIN:-/usr/local/bin/himalaya}"
AUDIT_LOG="${HIMALAYA_AUDIT_LOG:-/var/log/himalaya-audit.log}"
# ── Destructive commands we block ──────────────────────────
BLOCKED_CMDS=(
"message move"
"message delete"
"message copy"
"flag add"
"flag remove"
"folder create"
"folder delete"
"folder rename"
"template send"
"account configure"
"account delete"
)
# ── Determine the subcommand being invoked ─────────────────
# Strip leading options (--account, --output, etc.) to find the verb
ARGS=()
SKIP_NEXT=false
for arg in "$@"; do
if $SKIP_NEXT; then
SKIP_NEXT=false
continue
fi
if [[ "$arg" == --* ]]; then
case "$arg" in
--account|--output|--page|--page-size|--folder|--color|--format)
SKIP_NEXT=true ;;
esac
continue
fi
ARGS+=("$arg")
done
# Build subcommand string and check against blocklist
CMD_STR=""
for ((i=0; i<${#ARGS[@]}; i++)); do
if [ -z "$CMD_STR" ]; then
CMD_STR="${ARGS[$i]}"
else
CMD_STR="$CMD_STR ${ARGS[$i]}"
fi
for blocked in "${BLOCKED_CMDS[@]}"; do
if [[ "$CMD_STR" == "$blocked" ]]; then
TS=$(date '+%Y-%m-%d %H:%M:%S')
echo "[AUDIT] $TS BLOCKED: himalaya $*" >> "$AUDIT_LOG"
echo "ERROR: Command 'himalaya $CMD_STR ...' is blocked by read-only policy." >&2
echo " Audit log: $AUDIT_LOG" >&2
exit 100
fi
done
done
# ── Allow pass-through ─────────────────────────────────────
exec "$HIMALAYA_BIN" "$@"

42
ai/honcho/Dockerfile Normal file
View File

@@ -0,0 +1,42 @@
# build stage — fetches and builds Honcho from source
# Using buildkit cache mounts for speed across rebuilds
FROM python:3.13-slim-bookworm AS builder
RUN apt-get update && \
apt-get install -y --no-install-recommends git && \
rm -rf /var/lib/apt/lists/*
COPY --from=ghcr.io/astral-sh/uv:0.9.24 /uv /bin/uv
# Clone Honcho at a pinned commit for reproducibility
ARG HONCHO_REPO=https://github.com/plastic-labs/honcho
ARG HONCHO_REF=main
RUN git clone --depth 1 --branch ${HONCHO_REF} ${HONCHO_REPO} /app
WORKDIR /app
ENV UV_COMPILE_BYTECODE=1
ENV UV_LINK_MODE=copy
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --frozen --no-group dev
# --- runtime stage ---
FROM python:3.13-slim-bookworm
RUN groupadd --system app && \
useradd --system --gid app --create-home app
COPY --from=builder /app /app
COPY --from=builder /root/.cache/uv /root/.cache/uv
WORKDIR /app
ENV PATH="/app/.venv/bin:$PATH"
ENV HOME=/app
COPY --chown=app:app config.toml /app/config.toml
USER app
EXPOSE 8000
CMD ["fastapi", "run", "--host", "0.0.0.0", "src/main.py"]

117
ai/honcho/config.toml Normal file
View File

@@ -0,0 +1,117 @@
[app]
LOG_LEVEL = "INFO"
MAX_MESSAGE_SIZE = 25000
EMBED_MESSAGES = true
NAMESPACE = "honcho"
[db]
CONNECTION_URI = "postgresql+psycopg://honcho:honcho_pass@honcho-db:5432/honcho"
SCHEMA = "public"
POOL_SIZE = 10
MAX_OVERFLOW = 20
[auth]
USE_AUTH = false
[sentry]
ENABLED = false
[telemetry]
ENABLED = false
[webhook]
ENABLED = false
[cache]
ENABLED = true
URL = "redis://honcho-redis:6379/0"
[llm]
DEFAULT_MAX_TOKENS = 4096
# Embeddings via Ollama (nomic-embed-text recommended on this system)
[embedding]
VECTOR_DIMENSIONS = 768
MAX_INPUT_TOKENS = 8192
[embedding.model_config]
transport = "openai"
model = "nomic-embed-text"
base_url = "http://ollama:11434/v1"
# --- Deriver (user representation builder) ---
[deriver]
ENABLED = true
WORKERS = 1
POLLING_SLEEP_INTERVAL_SECONDS = 5.0
FLUSH_ENABLED = true
[deriver.model_config]
transport = "openai"
model = "hermes-3"
base_url = "http://ollama:11434/v1"
# --- Dialectic ---
[dialectic]
MAX_INPUT_TOKENS = 4096
SESSION_HISTORY_MAX_TOKENS = 8192
[dialectic.levels.minimal]
MAX_TOOL_ITERATIONS = 1
MAX_OUTPUT_TOKENS = 512
[dialectic.levels.minimal.model_config]
transport = "openai"
model = "hermes-3"
base_url = "http://ollama:11434/v1"
[dialectic.levels.low]
MAX_TOOL_ITERATIONS = 3
[dialectic.levels.low.model_config]
transport = "openai"
model = "hermes-3"
base_url = "http://ollama:11434/v1"
[dialectic.levels.medium]
MAX_TOOL_ITERATIONS = 2
[dialectic.levels.medium.model_config]
transport = "openai"
model = "hermes-3"
base_url = "http://ollama:11434/v1"
[dialectic.levels.high]
MAX_TOOL_ITERATIONS = 4
[dialectic.levels.high.model_config]
transport = "openai"
model = "hermes-3"
base_url = "http://ollama:11434/v1"
[dialectic.levels.max]
MAX_TOOL_ITERATIONS = 10
[dialectic.levels.max.model_config]
transport = "openai"
model = "hermes-3"
base_url = "http://ollama:11434/v1"
# --- Summary ---
[summary]
ENABLED = true
MESSAGES_PER_SHORT_SUMMARY = 20
MESSAGES_PER_LONG_SUMMARY = 60
[summary.model_config]
transport = "openai"
model = "hermes-3"
base_url = "http://ollama:11434/v1"
# --- Dream ---
[dream]
ENABLED = false
# --- Peer Card ---
[peer_card]
ENABLED = true
# --- Vector Store ---
[vector_store]
TYPE = "pgvector"
DIMENSIONS = 768

1
ai/honcho/init-db.sql Normal file
View File

@@ -0,0 +1 @@
CREATE EXTENSION IF NOT EXISTS vector;

View File

@@ -8,13 +8,10 @@ services:
- USER_GID=1000
- GITEA__server__ROOT_URL=https://code.lazyworkhorse.net
- GITEA__actions__ENABLED=true
- GITEA__actions__DEFAULT_ACTIONS_URL=off
- SSH_PORT=2222
- SSH_LISTEN_PORT=2222
# Enable Gitea Actions (act_runner required on host)
- GITEA__actions__ENABLED=true
# Don't fetch actions from GitHub (offline mode + local only)
- GITEA__actions__DEFAULT_ACTIONS_URL=off
volumes:
- /mnt/HoardingCow_docker_data/Gitea:/data
networks:

9
vpn/Dockerfile Normal file
View File

@@ -0,0 +1,9 @@
# Custom wg-easy with iptables-nft (nftables-backed iptables)
# Fixes crash-loop when host kernel lacks legacy iptable_nat module.
FROM ghcr.io/wg-easy/wg-easy:latest
# The upstream image registers only iptables-legacy with update-alternatives.
# iptables-nft binary exists but isn't registered as an alternative key.
# Override the alternatives-managed symlinks directly.
RUN ln -sf /usr/sbin/iptables-nft /usr/sbin/iptables && \
ln -sf /usr/sbin/ip6tables-nft /usr/sbin/ip6tables

View File

@@ -2,7 +2,10 @@ version: "3.8"
services:
wireguard:
image: weejewel/wg-easy:latest
build:
context: .
dockerfile: Dockerfile
image: wg-easy-iptables-nft:latest
container_name: wireguard
cap_add:
- NET_ADMIN