Simplify Athena runtime and document current architecture

This commit is contained in:
Mikei386
2026-08-30 22:11:26 +02:00
parent 9bc7d9803a
commit 0b927d47b9
56 changed files with 1276 additions and 4712 deletions
+3 -193
View File
@@ -12,147 +12,8 @@ x-tool-common: &tool-common
max-file: "3"
services:
mcp-web:
<<: *tool-common
# Historical site-specific facade. Kept only for rollback while the
# default portable endpoint points directly at TinySearch's broad MCP.
profiles: [legacy-web]
build:
context: ..
dockerfile: mcp/Dockerfile.web
image: mike-ai/mcp-web:local
container_name: mike-ai-mcp-web
# The relay fetches and validates public result pages itself. It therefore
# needs both the private tool network and the explicitly separated egress
# network; keeping it on `tools` only makes search discovery work while
# every page fetch fails.
networks: [tools, tools-egress]
dns: ["${AI_DNS:-1.1.1.1}"]
environment:
TINYSEARCH_MCP_URL: http://tinysearch:8000/mcp
SEARXNG_URL: http://searxng:8080
WEB_SEARCH_BUDGET_MAX_RELATED: "3"
YOUTUBE_TIMEOUT: "45"
depends_on:
tinysearch:
condition: service_started
healthcheck:
test: ["CMD", "python", "-c", "import socket; s=socket.create_connection(('127.0.0.1', 8000), 2); s.close()"]
interval: 30s
timeout: 5s
retries: 5
start_period: 15s
searxng:
<<: *tool-common
image: searxng/searxng@sha256:e45d5894bfaa0bf8773b9f283795ae57f1c15ddb29c8cecb70b3665b0ce9ec60
container_name: mike-ai-tools-searxng
dns: ["${AI_DNS:-1.1.1.1}"]
volumes:
- ${SEARXNG_SETTINGS_FILE:-../web-search/searxng-settings.example.yml}:/etc/searxng/settings.yml:ro
networks: [tools, tools-egress]
tinysearch:
<<: *tool-common
# TinySearch v0.6.1. This release fixes the crawler behavior observed with
# v0.5.1 and adds the current search -> scrape_urls workflow.
image: marcellm01/tinysearch@sha256:7a7d0585f5000f462e699e42b97409715826a9e2edcd09a166afa93a4b7cba31
container_name: mike-ai-tools-tinysearch
dns: ["${AI_DNS:-1.1.1.1}"]
# Crawl4AI keeps transient browser/session state here. The container stays
# read-only; only this disposable runtime directory (and /tmp from the
# common hardening block) is writable.
tmpfs:
- /tmp:rw,noexec,nosuid,nodev,size=64m
- /home/tinysearch/.crawl4ai:rw,nosuid,nodev,size=256m,mode=1777
shm_size: 1gb
volumes:
- tinysearch-models:/data/models
- ../web-search/tinysearch_config.json:/config/tinysearch_config.json:ro
environment:
MCP_TRANSPORT: streamable-http
MCP_HOST: 0.0.0.0
MCP_PORT: "8000"
TINYSEARCH_CONFIG_PATH: /config/tinysearch_config.json
TINYSEARCH_SEARCH_BACKEND: searxng
SEARXNG_URL: http://searxng:8080/search
depends_on: [searxng]
cap_add: [SETUID, SETGID, CHOWN]
networks: [tools, tools-egress]
# The image's built-in `tinysearch doctor` also requires a writable
# configuration directory, although normal server operation does not.
# Check the service socket instead so read-only hardening remains intact.
healthcheck:
test: ["CMD", "python", "-c", "import socket; s=socket.create_connection(('127.0.0.1', 8000), 2); s.close()"]
interval: 30s
timeout: 5s
retries: 5
start_period: 20s
mcp-homeassistant:
<<: *tool-common
build:
context: ../..
dockerfile: platform/mcp/Dockerfile.homeassistant-relay
image: mike-ai/mcp-homeassistant-relay:local
container_name: mike-ai-mcp-homeassistant
profiles: [homeassistant]
# Keep the public TLS hostname for SNI/certificate validation, but route it
# to the private reverse proxy through WireGuard. Public DNS may otherwise
# resolve to the Fritzbox WAN address, which is unreachable/hairpinned from
# Athena's remote-site containers.
extra_hosts:
- "ha.casaderoll.de:${HOME_LAN_PROXY_IP:-192.168.1.2}"
volumes:
- ${HA_ENV_FILE:-/etc/mike-ai/homeassistant-admin-mcp.env}:/run/secrets/homeassistant.env:ro
cap_add: [CHOWN, SETUID, SETGID]
networks: [tools, tools-egress]
mcp-arr:
<<: *tool-common
build:
context: .
dockerfile: Dockerfile.arr
image: mike-ai/mcp-arr:1.0.1-patched
container_name: mike-ai-mcp-arr
profiles: [arr]
env_file:
- ${ARR_ENV_FILE:-/etc/mike-ai/arr-mcp.env}
volumes:
# The local fork adds bounded read-only Sonarr pseudo-actions. Keep the
# patch explicit until upstream publishes a self-contained 2.x image.
- ${ARR_SONARR_PATCH:-./patches/mcp_sonarr.py}:/usr/local/lib/python3.13/site-packages/arr_mcp/mcp/mcp_sonarr.py:ro
# Upstream's generic "Execute any Radarr API action" text gives small
# models no routing boundary. This overlay changes guidance only.
- ${ARR_RADARR_PATCH:-./patches/mcp_radarr.py}:/usr/local/lib/python3.13/site-packages/arr_mcp/mcp/mcp_radarr.py:ro
networks: [tools, tools-egress]
mcp-navidrome:
<<: *tool-common
# Version and amd64 manifest are pinned. The image contains no mpv, so it
# cannot play audio on the headless AI host and does not expose playback
# controls. It talks to Navidrome only through its authenticated API.
build:
context: .
dockerfile: Dockerfile.navidrome
image: mike-ai/mcp-navidrome:2.2.0-schemafix1
container_name: mike-ai-mcp-navidrome
profiles: [navidrome]
env_file:
- ${NAVIDROME_MCP_ENV_FILE:-/etc/mike-ai/navidrome-mcp.env}
environment:
MCP_TRANSPORT: http
MCP_HTTP_EXPOSE: "true"
MCP_HTTP_PORT: "3000"
# OpenWebUI uses the Docker name; Pi/Hermes may reach the same endpoint
# directly through Athena's WireGuard address and VPN port 8207.
MCP_HTTP_ALLOWED_HOSTS: "mike-ai-mcp-navidrome:3000,mike-ai-mcp-navidrome,${VPN_SERVICE_IP:-192.168.1.212}:8207,${VPN_SERVICE_IP:-192.168.1.212}"
WEBUI_ENABLED: "false"
tmpfs:
- /tmp:rw,noexec,nosuid,nodev,size=64m
- /config:rw,noexec,nosuid,nodev,size=4m,mode=0700
networks: [tools, tools-egress]
# Einziger MCP auf Athena: die schmale Fassade zum root-eigenen Operator.
# Alle portablen Fach-MCPs laufen als eigene Container auf Unraid.
mcp-athena-operator:
<<: *tool-common
build:
@@ -163,61 +24,10 @@ services:
environment:
ATHENA_OPERATOR_SOCKET: /operator/operator.sock
volumes:
# The unprivileged MCP facade sees only the root-owned executor socket.
# Docker, source, models, Git credentials and host paths remain on the
# executor side and are reachable only through structured operations.
- /run/mike-ai-operator:/operator:ro
networks: [tools]
healthcheck:
test: ["CMD", "python", "-c", "import socket; s=socket.create_connection(('127.0.0.1',8000),2); s.close()"]
test: [CMD, python, -c, "import socket; s=socket.create_connection(('127.0.0.1',8000),2); s.close()"]
interval: 30s
timeout: 5s
retries: 5
start_period: 10s
mcp-github:
<<: *tool-common
build:
context: .
dockerfile: Dockerfile.github
image: mike-ai/mcp-github:github-v1.10.1-mcp-proxy-v0.12.0
container_name: mike-ai-mcp-github
profiles: [github]
env_file:
- ${GITHUB_MCP_ENV_FILE:-/etc/mike-ai/github-mcp.env}
environment:
# These server-side limits remain authoritative even if a client asks
# for broader toolsets. The token itself must also remain read-only.
GITHUB_TOOLS: search_repositories,get_file_contents,search_code
GITHUB_READ_ONLY: "1"
networks: [tools, tools-egress]
healthcheck:
test: ["CMD", "python", "-c", "import socket; s=socket.create_connection(('127.0.0.1',8000),2); s.close()"]
interval: 30s
timeout: 5s
retries: 5
start_period: 15s
mcp-unraid-ssh:
<<: *tool-common
profiles: [extended]
build:
context: .
dockerfile: Dockerfile.unraid-ssh
image: mike-ai/mcp-unraid-ssh:local
container_name: mike-ai-mcp-unraid-ssh
environment:
UNRAID_MCP_CONFIG: /run/config/unraid-mcp.json
volumes:
- ${UNRAID_MCP_SOURCE:-/opt/mike-ai/unraid-agent/unraid_mcp.py}:/app/unraid_mcp.py:ro
- ${UNRAID_MCP_CONFIG:-/etc/mike-ai/unraid-mcp.json}:/run/config/unraid-mcp.json:ro
- ${UNRAID_SSH_KEY:-/etc/mike-ai/keys/unraid_root}:/etc/mike-ai/keys/unraid_root:ro
- ${UNRAID_KNOWN_HOSTS:-/etc/mike-ai/ssh/known_hosts_unraid_ai}:/etc/mike-ai/ssh/known_hosts_unraid_ai:ro
- unraid-audit:/var/log/mike-ai
networks: [tools, tools-egress]
volumes:
tinysearch-models:
name: mike-ai-tools_tinysearch-models
external: true
unraid-audit: