From 27571ddb3fde7e49d68afc41c028411ebc2e51a9 Mon Sep 17 00:00:00 2001 From: Hermes Date: Tue, 12 May 2026 18:02:51 -0400 Subject: [PATCH] feat: add Himalaya email CLI to Hermes Docker image --- ai/hermes/Dockerfile | 18 ++++++++++ ai/hermes/himalaya-ro.sh | 73 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 ai/hermes/himalaya-ro.sh diff --git a/ai/hermes/Dockerfile b/ai/hermes/Dockerfile index 042e1db..238b3bc 100644 --- a/ai/hermes/Dockerfile +++ b/ai/hermes/Dockerfile @@ -61,6 +61,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 diff --git a/ai/hermes/himalaya-ro.sh b/ai/hermes/himalaya-ro.sh new file mode 100644 index 0000000..212f1ae --- /dev/null +++ b/ai/hermes/himalaya-ro.sh @@ -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] [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" "$@"