Replace Clay with n8n, Ollama, SearXNG and browserless; add Telegram assistant

Remove the Clay role (Claude Code web UI) and its Anthropic API key.

New roles:
- ollama: local CPU LLM (qwen3:4b-instruct), internal network only
- browserless: headless Chromium for JavaScript-heavy pages
- searxng: metasearch backend for the chat web_search tool
- n8n at agent.ladkau.de: web chat (/chat), scheduled workflows,
  Telegram bot; workflows and credentials are imported from the repo

Telegram bot: Gemini 3.8 -> Gemini 3.5 -> local Qwen fallback chain,
/qwen to force the local model, /stocks and a weekday schedule
(07:30, 13:00, 17:40) for the MTE.DE / PTX.F stock report.

Also: dashboard and check-services entries for the chat, vault checks
for the new secrets (n8n, SearXNG, Gemini, Telegram, SMTP), updated
CLAUDE.md, runbooks and README.
This commit is contained in:
ml
2026-10-03 17:10:33 +02:00
parent b3cf4e250a
commit 974486bc53
37 changed files with 2805 additions and 290 deletions
+106
View File
@@ -0,0 +1,106 @@
---
- name: Create n8n directories
ansible.builtin.file:
path: "{{ item.path }}"
state: directory
owner: "{{ item.owner }}"
group: "{{ item.owner }}"
mode: "{{ item.mode }}"
loop:
- { path: "{{ n8n_data_dir }}", owner: root, mode: "0755" }
# n8n runs as uid 1000 (node) inside the container
- { path: "{{ n8n_data_dir }}/data", owner: "1000", mode: "0700" }
- { path: "{{ n8n_data_dir }}/import", owner: "1000", mode: "0700" }
- { path: "{{ n8n_data_dir }}/import/workflows", owner: "1000", mode: "0700" }
- { path: "{{ n8n_data_dir }}/import/credentials", owner: "1000", mode: "0700" }
tags: n8n
- name: Deploy Docker Compose file
ansible.builtin.template:
src: docker-compose.yml.j2
dest: "{{ n8n_data_dir }}/docker-compose.yml"
owner: root
group: root
mode: "0644"
notify: Restart n8n
tags: n8n
# __TELEGRAM_USER_ID__ and __OLLAMA_MODEL__ in the workflow files are replaced by telegram_user_id and ollama_model
- name: Deploy workflows
ansible.builtin.copy:
content: "{{ lookup('ansible.builtin.file', item) | replace('__TELEGRAM_USER_ID__', telegram_user_id | string) | replace('__OLLAMA_MODEL__', ollama_model) }}"
dest: "{{ n8n_data_dir }}/import/workflows/{{ item | basename }}"
owner: "1000"
group: "1000"
mode: "0600"
with_fileglob:
- "workflows/*.json"
notify: Import n8n content
tags: n8n
- name: Deploy Ollama credential
ansible.builtin.copy:
src: credentials/ollama.json
dest: "{{ n8n_data_dir }}/import/credentials/ollama.json"
owner: "1000"
group: "1000"
mode: "0600"
notify: Import n8n content
tags: n8n
- name: Deploy SMTP credential
ansible.builtin.template:
src: smtp-credential.json.j2
dest: "{{ n8n_data_dir }}/import/credentials/smtp.json"
owner: "1000"
group: "1000"
mode: "0600"
no_log: true
notify: Import n8n content
tags: n8n
# Workflows with "active": true in their JSON are published after import
- name: Collect workflow ids to publish
ansible.builtin.set_fact:
n8n_publish_ids: >-
{%- set ns = namespace(ids=[]) -%}
{%- for f in query('fileglob', role_path ~ '/files/workflows/*.json') -%}
{%- set wf = lookup('file', f) | from_json -%}
{%- if wf.active | default(false) -%}{%- set ns.ids = ns.ids + [wf.id] -%}{%- endif -%}
{%- endfor -%}
{{ ns.ids | to_json }}
tags: n8n
- name: Deploy Telegram credential
ansible.builtin.template:
src: telegram-credential.json.j2
dest: "{{ n8n_data_dir }}/import/credentials/telegram.json"
owner: "1000"
group: "1000"
mode: "0600"
no_log: true
notify: Import n8n content
tags: n8n
- name: Deploy Gemini credential
ansible.builtin.template:
src: gemini-credential.json.j2
dest: "{{ n8n_data_dir }}/import/credentials/gemini.json"
owner: "1000"
group: "1000"
mode: "0600"
no_log: true
notify: Import n8n content
tags: n8n
- name: Start n8n
community.docker.docker_compose_v2:
project_src: "{{ n8n_data_dir }}"
state: present
pull: missing
tags: n8n
# Handlers run at the end of the play; flush so the import happens before the role finishes
- name: Apply pending handlers
ansible.builtin.meta: flush_handlers
tags: n8n