diff --git a/.env.example b/.env.example index 294f28f..3b4492e 100644 --- a/.env.example +++ b/.env.example @@ -9,7 +9,12 @@ LLM_HOST=localhost # Additional LLM hosts, comma-separated (for model discovery) -# LLM_HOSTS=llm-host.local:8000,backup-llm.local:8001 +# Use hostnames/IPs only; Odysseus scans common serve ports, including Ollama's 11434. +# LLM_HOSTS=llm-host.local,backup-llm.local + +# Optional Ollama base URL. In Docker, host Ollama is usually reachable here +# when started with OLLAMA_HOST=0.0.0.0:11434. +# OLLAMA_BASE_URL=http://host.docker.internal:11434/v1 # OpenAI API key (only needed if using OpenAI models). # Do not commit real keys. Keep this commented until needed. @@ -61,6 +66,16 @@ SEARXNG_INSTANCE=http://localhost:8080 # CHROMADB_HOST=localhost # CHROMADB_PORT=8100 +# Docker Compose host-port bind addresses for bundled services. +# Defaults are loopback-only for safety. To expose ntfy only on Tailscale, +# set NTFY_BIND to your host's Tailscale IP and update NTFY_BASE_URL. +# CHROMADB_BIND=127.0.0.1 +# NTFY_BIND=127.0.0.1 +# NTFY_BASE_URL=http://localhost:8091 +# Example: +# NTFY_BIND=100.x.y.z +# NTFY_BASE_URL=http://100.x.y.z:8091 + # ============================================================ # RAG / Embeddings # ============================================================ diff --git a/README.md b/README.md index beedd55..56590ed 100644 --- a/README.md +++ b/README.md @@ -172,15 +172,39 @@ Key settings: | `EMBEDDING_URL` | -- | OpenAI-compatible embeddings endpoint | ### Bundled services -Docker Compose includes these by default: +Docker Compose includes these by default. The bundled service ports bind to `127.0.0.1` unless you opt in to a different bind address in `.env`, so they are reachable from the host machine but not from your LAN or the public internet by default: - - **ChromaDB** → vector store for semantic memory. In Docker, Odysseus connects to `chromadb:8000`; from the host it is exposed as `localhost:8100`. - - **SearXNG** → meta search for web search. In Docker, Odysseus connects to `searxng:8080`; from the host it is exposed only on `127.0.0.1:8080`. - - **ntfy** → local notification service, exposed as `localhost:8091`. + - **ChromaDB** → vector store for semantic memory. In Docker, Odysseus connects to `chromadb:8000`; from the host it is exposed as `${CHROMADB_BIND:-127.0.0.1}:8100`. + - **SearXNG** → meta search for web search. In Docker, Odysseus connects to `searxng:8080`; from the host it is exposed as `127.0.0.1:8080`. + - **ntfy** → local notification service, exposed as `${NTFY_BIND:-127.0.0.1}:8091`. + +**Phone push notifications via ntfy:** A phone cannot subscribe to `127.0.0.1` on your server. To expose ntfy safely without opening it on every interface: + + - **Tailscale (recommended)** — set `NTFY_BIND=` and `NTFY_BASE_URL=http://:8091` in `.env`, recreate ntfy, then point the ntfy Android/iOS app at `http://:8091/`. + - **Enable ntfy auth and bind to LAN** — add `NTFY_AUTH_FILE` + `NTFY_AUTH_DEFAULT_ACCESS=deny-all` to the `ntfy` service, create a user with `docker compose exec ntfy ntfy user add ...`, then set `NTFY_BIND` to your LAN IP. See the [ntfy docs](https://docs.ntfy.sh/config/#access-control). ### Optional external services - **Ollama** → local LLM server -- [ollama.ai](https://ollama.ai) +### Ollama with Docker +If Odysseus is running in Docker and Ollama is running on the host, add the endpoint in Settings as: + +`http://host.docker.internal:11434/v1` + +The default Compose file already maps `host.docker.internal` on Linux. Ollama also needs to listen outside its own loopback interface: + +```bash +OLLAMA_HOST=0.0.0.0:11434 ollama serve +``` + +For a systemd Ollama install, set that in the Ollama service override. If Odysseus can see Ollama but requests hang or fail, check that your host firewall allows Docker bridge traffic to port `11434`. + +First-token latency is usually Ollama/model/hardware, not Odysseus. To compare, test Ollama directly: + +```bash +curl http://127.0.0.1:11434/v1/models +``` + ## Architecture ``` app.py # FastAPI entry point diff --git a/app.py b/app.py index af53f9c..48ad3cf 100644 --- a/app.py +++ b/app.py @@ -600,7 +600,7 @@ app.include_router(setup_contacts_routes()) def _serve_html_with_nonce(request: Request, file_path: str) -> HTMLResponse: """Read an HTML file and inject the CSP nonce into inline