| android | ||
| public | ||
| screenshots | ||
| scripts | ||
| src | ||
| tests | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| docker-compose.coolify.yml | ||
| docker-compose.yml | ||
| Dockerfile | ||
| next.config.mjs | ||
| package-lock.json | ||
| package.json | ||
| postcss.config.js | ||
| README.md | ||
| tailwind.config.js | ||
| tsconfig.json | ||
| vitest.config.mts | ||
🤖 JodaBot
Dein eigener KI-Assistent: Chat mit Verlauf und Gedächtnis, umschaltbare Modelle (Cloud & lokal), Websuche, Code-Sandbox, Routinen nach Zeitplan, GitHub- und MCP-Anbindung und mehrere Bots mit eigenen Rollen. Phase 1: Web-App + Server. Die Android-App (Phase 2) nutzt dieselbe API (siehe unten).
Öffentliche Landingpage unter /, die App selbst unter /chat (angemeldete Nutzer werden von / direkt weitergeleitet).
Funktionen
| Bereich | Was geht |
|---|---|
| Bots | Beliebig viele Bots mit Name, Beschreibung, System-Prompt, Modell und aktivierten Werkzeugen (Seitenleiste). |
| Chat | Mehrere Unterhaltungen pro Bot, Verlauf in SQLite, Streaming-Antworten (SSE). |
| Gedächtnis | Dauerhafte Fakten pro Bot & Nutzer; das Modell speichert/sucht/löscht sie per Tool, sie stehen automatisch im System-Prompt. Auch manuell pflegbar. |
| Modelle | OpenAI, xAI (Grok), Anthropic (Claude), Ollama, beliebige OpenAI-kompatible lokale Server (llama.cpp, LM Studio, vLLM …) und ein Mock-Modell für Tests. Auswahl pro Bot. Tool-Calling bei allen. |
| Websuche | Tavily → Brave → SearXNG (je nachdem, welcher Key/URL gesetzt ist), sonst DuckDuckGo-HTML-Fallback. Plus fetch_url (Text einer Webseite; interne Adressen gesperrt). |
| Code & Dateien | run_code (Python/Node/Bash) in einer Docker-Sandbox pro Aufruf: kein Netzwerk, Speicher-/CPU-/PID-Limit, Zeitlimit, read-only Root-FS, nur der Bot-Workspace ist gemountet. Ohne Docker deaktiviert mit klarer Meldung. write_file/read_file/list_files im Workspace, Dateien im Chat & im Tab „Dateien“ herunterladbar. |
| Routinen | Cron-Prompts pro Bot (Zeitzone Europe/Berlin), Ergebnis landet in der Unterhaltung „⏰ Name“. Auch manuell auslösbar. |
| GitHub | Token-basiert: Repos, Issues, PRs lesen; Issue anlegen (mit Bestätigung). |
| MCP | Generische Anbindung von MCP-Servern per Streamable HTTP oder stdio; deren Tools erscheinen automatisch beim Bot (standardmäßig mit Bestätigung). |
| Sicherheit | Admin-Login (scrypt-Hash, HttpOnly-Session-Cookie bzw. Bearer-Token). API-Keys nur in .env oder AES-256-GCM-verschlüsselt in der DB; das Frontend sieht nie Werte, nur „gesetzt ja/nein“. Riskante Aktionen (Code, GitHub-Schreiben, MCP) brauchen eine Bestätigung im UI. |
Architektur
Browser / Android ──REST + SSE──▶ Next.js 15 (App Router, TypeScript, Tailwind)
├─ src/app/App.tsx UI (deutsch, mobil-tauglich)
├─ src/app/api/** REST-API (Route Handler)
└─ src/lib/
├─ agent.ts Agenten-Schleife: Modell ⇄ Tools, Bestätigungen, SSE
├─ models.ts Modell-Schicht (OpenAI-kompatibel, Anthropic, Mock)
├─ tools.ts Tool-Registry (Gedächtnis, Web, Code, GitHub, MCP)
├─ sandbox.ts Docker-Sandbox + Workspaces
├─ scheduler.ts Routinen (node-cron, startet in instrumentation.ts)
├─ mcp.ts MCP-Client (HTTP/stdio)
├─ db.ts/schema.ts SQLite via better-sqlite3 + Drizzle ORM
└─ crypto.ts/secrets.ts/auth.ts
Warum ein einzelnes Next.js-Projekt statt Monorepo mit separatem Fastify-Server? Für Phase 1 hält das
Deployment und Typen an einem Ort; die gesamte Logik liegt framework-unabhängig in src/lib, die API ist sauber
unter /api versioniert dokumentiert. Wenn die Android-App oder Skalierung es erfordern, lässt sich src/lib ohne
Umbau in ein eigenes packages/core + Fastify-Server auslagern. Drizzle erlaubt später den Wechsel auf Postgres.
Streaming: Server-Sent Events über POST (statt WebSocket) – funktioniert durch Proxies, einfach in OkHttp/Ktor
auf Android zu lesen, und braucht keinen separaten Socket-Server.
Schnellstart (lokal)
Voraussetzung: Node.js 20+, optional Docker (für Code-Ausführung).
cp .env.example .env # JODABOT_ADMIN_PASSWORD und JODABOT_SECRET setzen!
npm install
npm run build && npm start # oder: npm run dev
# → http://localhost:3000, Login mit JODABOT_ADMIN_USER / JODABOT_ADMIN_PASSWORD
JODABOT_SECRET erzeugen: openssl rand -hex 32. Ohne API-Key kannst du sofort mit dem Anbieter Mock testen
(probiere „merke dir: …“, „was weißt du über mich?“, „suche …“, „rechne 6*7“, „datei …“).
Start mit Docker Compose
cp .env.example .env # Werte setzen
docker compose up -d --build # nur JodaBot
docker compose --profile ollama up -d --build # inkl. lokalem Ollama
docker compose exec ollama ollama pull llama3.1 # Modell laden, dann im Bot „Ollama (lokal)“ wählen
Für die Code-Sandbox wird der Docker-Socket des Hosts gemountet (entspricht Root-Rechten auf dem Host – nur auf
eigenen Servern verwenden, Zeile in docker-compose.yml sonst entfernen). Die Sandbox-Images
(python:3.12-slim, node:20-slim, bash:5) werden beim ersten Lauf gezogen; vorab: docker pull python:3.12-slim.
Konfiguration
Alle Variablen stehen in .env.example.
Modell-Server (xAI, OpenAI, Anthropic, Ollama, eigener OpenAI-kompatibler Endpunkt) verwaltet der Admin unter
Einstellungen → Modell-Server – in der Web-App und in der Android-App: aktivieren/deaktivieren, API-Schlüssel bzw.
Server-URL eintragen, Verbindung testen (lädt die Modellliste) und Standardmodell wählen. Schlüssel werden mit
AES-256-GCM (JODABOT_SECRET) verschlüsselt in der DB gespeichert und nie im Klartext ausgeliefert (nur •••• + letzte 4 Zeichen).
In der UI gespeicherte Werte haben Vorrang vor der .env; OPENAI_API_KEY, XAI_API_KEY, ANTHROPIC_API_KEY,
OLLAMA_BASE_URL, LOCAL_OPENAI_BASE_URL/LOCAL_OPENAI_API_KEY bleiben als Fallback. Ein nur per .env konfigurierter
Anbieter gilt als aktiv, bis er in den Einstellungen deaktiviert wird. Bots können nur aktive, konfigurierte Anbieter
(plus „Mock“) wählen. Weitere Schlüssel (Websuche, GitHub) unter Einstellungen → Weitere Schlüssel (dort hat .env Vorrang).
Tests
npm run typecheck
npm test # Vitest: Verschlüsselung, Pfadschutz, DuckDuckGo-Parser, Agenten-Schleife mit Mock-Modell
npm run build && npm run smoke # Headless-UI-Smoke-Test (Chrome/Chromium nötig, CHROME_PATH optional) → screenshots/
API-Dokumentation (für Web & Android)
Basis-URL: https://<server>/api. Alle Bodies sind JSON. Authentifizierung entweder per Cookie jodabot_session
(Web) oder Header Authorization: Bearer <token> (Android). Fehler: HTTP-Status + { "error": "Text" }.
Auth
| Methode | Pfad | Body | Antwort |
|---|---|---|---|
| POST | /auth/login |
{username, password} |
{token, user:{id,username}} (+ Cookie) |
| POST | /auth/logout |
– | {ok:true} |
| GET | /me |
– | {user:{id,username,isAdmin}} |
| GET | /meta |
– | {providers[{id,label,defaultModel,models[]}], toolGroups[], docker:boolean, dockerMessage} – providers enthält nur „mock“ + aktive, konfigurierte Anbieter |
Bots
| Methode | Pfad | Body / Antwort |
|---|---|---|
| GET | /bots |
Bot[] |
| POST | /bots |
{name, description?, systemPrompt?, provider?, model?, tools?} → Bot |
| GET/PATCH/DELETE | /bots/{botId} |
PATCH mit beliebigen Feldern von oben |
Bot = {id, name, description, systemPrompt, provider: "mock"|"openai"|"xai"|"anthropic"|"ollama"|"local-openai", model, tools: ("memory"|"web"|"code"|"github"|"mcp")[], createdAt}
Unterhaltungen & Chat
| Methode | Pfad | Body / Antwort |
|---|---|---|
| GET | /bots/{botId}/conversations |
Conversation[] (neueste zuerst) |
| POST | /bots/{botId}/conversations |
{title?} → Conversation |
| GET | /conversations/{id} |
{conversation, messages: Message[], pending: PendingAction[]} |
| PATCH | /conversations/{id} |
{title} |
| DELETE | /conversations/{id} |
– |
| POST | /conversations/{id}/messages |
{content} → SSE-Stream |
| POST | /actions/{actionId} |
{approve: true|false} → SSE-Stream (Fortsetzung nach Bestätigung) |
Message = {id, role: "user"|"assistant"|"tool", content, toolCalls: {id,name,args}[]|null, toolCallId, toolName, createdAt}
SSE-Ereignisse (event: <type> / data: <json>):
| type | Daten |
|---|---|
text |
{delta} – Textstück der Antwort |
tool_call |
{call:{id,name,args}, risky} |
tool_result |
{toolCallId, name, result, error?} |
confirm |
{action:{id, toolName, args}} – Nutzer muss über /actions/{id} bestätigen/ablehnen |
error |
{message} |
done |
Stream endet |
Schickt der Nutzer bei offener Bestätigung eine neue Nachricht, gilt die Aktion als abgelehnt.
Gedächtnis, Dateien, Routinen
| Methode | Pfad | Body / Antwort |
|---|---|---|
| GET/POST | /bots/{botId}/memories |
POST {content} |
| DELETE | /memories/{id} |
– |
| GET | /bots/{botId}/files |
{path,size}[] |
| GET | /bots/{botId}/files/{pfad} |
Datei-Download |
| GET/POST | /bots/{botId}/routines |
POST {name, cron, prompt, enabled?} |
| PATCH/DELETE | /routines/{id} |
PATCH {name?, cron?, prompt?, enabled?} |
| POST | /routines/{id}/run |
sofort ausführen |
Einstellungen
| Methode | Pfad | Body / Antwort |
|---|---|---|
| GET | /settings/secrets |
{key, source: "env"|"db"|null}[] (nie Werte) |
| PUT | /settings/secrets |
{TAVILY_API_KEY: "…", …} (leerer String = löschen) |
| GET | /settings/providers |
Admin. ProviderSettings[] (s. u.) |
| GET | /settings/providers/{id} |
Admin. ProviderSettings |
| PUT | /settings/providers/{id} |
Admin. {enabled?, apiKey?, baseUrl?, defaultModel?} – fehlendes Feld = unverändert, apiKey: "" löscht den gespeicherten Schlüssel (env greift wieder), baseUrl: "" setzt die URL zurück → ProviderSettings |
| POST | /settings/providers/{id}/test |
Admin. Optional {apiKey?, baseUrl?} zum Testen ungespeicherter Werte → {ok, models[], error?}; ohne Overrides wird die Modellliste gespeichert |
{id} ∈ xai, openai, anthropic, ollama, local-openai. Admin = Benutzer JODABOT_ADMIN_USER; andere erhalten 403, ohne Login 401 (Cookie oder Bearer).
ProviderSettings = {id, label, needsKey, needsUrl, enabled, configured, keyMasked: "••••abcd"|null, keySource: "db"|"env"|null, baseUrl, urlSource: "db"|"env"|"default", defaultModel, models[]}
POST/PATCH /bots lehnt Anbieter ab, die nicht aktiv und konfiguriert sind (400).
| GET/POST | /mcp | POST {name, transport:"http"\|"stdio", url?, command?, args?, requireConfirm?} |
| DELETE | /mcp/{id} | – |
Beispiel (curl):
TOKEN=$(curl -s -X POST localhost:3000/api/auth/login -H 'content-type: application/json' \
-d '{"username":"admin","password":"…"}' | jq -r .token)
curl -N -X POST localhost:3000/api/conversations/<id>/messages -H "Authorization: Bearer $TOKEN" \
-H 'content-type: application/json' -d '{"content":"Hallo!"}'
Screenshots
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
Roadmap
- Android-App (Kotlin + Jetpack Compose, OkHttp-SSE) gegen diese API; Push für Routinen-Ergebnisse (FCM).
- Mehrbenutzer & Rollen, Rate-Limits, Postgres-Option, Datei-Upload in den Chat, Vektor-Gedächtnis (Embeddings), GitHub-Schreibzugriffe (Kommentare, PRs), OAuth für Verbindungen.
Android-App (android/)
Native App (Kotlin, Jetpack Compose, Material 3, dunkles Theme, deutsche Oberfläche), spricht dieselbe API wie die Web-App (Bearer-Token, Chat-Streaming per SSE).
Funktionen: Login mit Server-URL + Passwort oder Token · Bot-Liste und Umschalten · Unterhaltungen · Chat mit gestreamten Antworten (Markdown) · Bestätigungskarten für Tools (Erlauben/Ablehnen) · Routinen ansehen, ein-/ausschalten, sofort ausführen · Modell-Server verwalten (nur Admin: aktivieren, Schlüssel/URL, Verbindung testen, Standardmodell) · Einstellungen/Abmelden.
Bauen (JDK 17 + Android SDK, Plattform 36/37):
cd android
echo "sdk.dir=$ANDROID_HOME" > local.properties
./gradlew assembleDebug testDebugUnitTest
# APK: android/app/build/outputs/apk/debug/app-debug.apk
Verbinden: Im Emulator ist der Rechner unter http://10.0.2.2:3000 erreichbar, auf einem Gerät im WLAN die LAN-IP des Servers. Die App prüft die URL über GET /api/health (öffentlich). Klartext-HTTP ist für lokale Tests erlaubt – im Internet nur HTTPS verwenden.
Sicherheit: Server-URL und Token liegen in EncryptedSharedPreferences (AES-256, Schlüssel im Android Keystore); das Passwort wird nicht gespeichert.
Hosting auf Render (Free-Plan)
JodaBot läuft als Docker-Web-Service auf Render (HTTPS automatisch, PORT wird vom Dockerfile übernommen). Prüfen: GET https://<dienst>.onrender.com/api/health.
Umgebungsvariablen: JODABOT_SECRET (zufällig, ≥ 16 Zeichen), JODABOT_ADMIN_USER, JODABOT_ADMIN_PASSWORD, JODABOT_DISABLE_SANDBOX=1.
Einschränkungen des Free-Plans:
- Keine persistente Disk → die SQLite-Datenbank (Bots, Chats, Gedächtnis, gespeicherte Schlüssel) liegt im Container und geht bei jedem Neustart/Deploy verloren. Dauerhaft speichern: bezahlter Plan (z. B. Starter) + Persistent Disk unter
/app/data, oder JodaBot auf eigenem Server/VPS mit Docker betreiben. - Der Dienst schläft nach ~15 Min. ohne Anfragen ein (erster Aufruf dauert dann ~1 Min.); Routinen laufen in dieser Zeit nicht.
- Code-Ausführung ist abgeschaltet (kein Docker auf Render).
run_codewird dem Modell nicht angeboten; die UI zeigt einen Hinweis. Abschalten auch lokal perJODABOT_DISABLE_SANDBOX=1(auf Render automatisch überRENDER=true).
GitHub-Werkzeuge
Mit GITHUB_TOKEN (Einstellungen → Weitere Schlüssel; Fine-grained Token mit Contents, Issues, Pull requests: Read & Write) kann ein Bot mit Werkzeug „GitHub“:
| Tool | Bestätigung |
|---|---|
github_list_repos, github_list_issues, github_get_issue, github_list_pulls, github_read_file |
nein (nur lesen) |
github_write_file (Datei erstellen/ändern per Commit), github_create_branch, github_create_pull, github_comment (Issue/PR), github_create_issue |
ja – Karte „Erlauben/Ablehnen“ im Chat |
E-Mail & Kalender
Werkzeuge „E-Mail“ und „Kalender“ (pro Bot aktivierbar):
| Tool | Bestätigung |
|---|---|
mail_search, mail_read, calendar_list |
nein |
mail_draft (Entwurf, wird nicht gesendet) |
nein |
mail_send, calendar_create |
ja |
Google (Gmail + Kalender, auch Drive): In der Google Cloud Console einen OAuth-Client „Webanwendung“ anlegen, Gmail-, Calendar- und Drive-API aktivieren, Weiterleitungs-URI https://<host>/api/oauth/google/callback eintragen (wird in den Einstellungen angezeigt; hinter Proxys ggf. JODABOT_PUBLIC_URL setzen). GOOGLE_CLIENT_ID/GOOGLE_CLIENT_SECRET unter Einstellungen → Weitere Schlüssel eintragen, dann Einstellungen → Verbindungen → Mit Google verbinden. Tokens werden pro Nutzer verschlüsselt gespeichert und automatisch erneuert. Solange die App bei Google im Testmodus ist, müssen Konten als Testnutzer eingetragen sein.
IMAP/SMTP (optional, ohne Google): IMAP_HOST, IMAP_PORT (993), IMAP_USER, IMAP_PASSWORD, SMTP_HOST, SMTP_PORT (465), SMTP_USER, SMTP_PASSWORD, MAIL_FROM. Entwürfe landen im Entwürfe-Ordner. Kalender nur über Google.
API: GET /api/connections (Status, nie Tokens), GET /api/oauth/google/start (Weiterleitung zu Google, ?json=1 liefert {url} – für die Android-App), GET /api/oauth/google/callback, DELETE /api/connections/{provider}.
Push-Benachrichtigungen
Auslöser: Routine fertig und Aktion wartet auf Bestätigung.
- Web-Push (Browser): Einstellungen → Benachrichtigungen → „In diesem Browser aktivieren“. Die VAPID-Schlüssel erzeugt der Server beim ersten Aufruf selbst und speichert sie verschlüsselt (
VAPID_PUBLIC_KEY/VAPID_PRIVATE_KEY, optionalVAPID_SUBJECT). Abschalten mitJODABOT_DISABLE_WEBPUSH=1. Service Worker:public/sw.js. Funktioniert nur über HTTPS (oder localhost). - Android (Firebase Cloud Messaging, HTTP v1): Der alte FCM-„Legacy-Server-Key“ wurde von Google abgeschaltet; als Server-Schlüssel dient der Service-Account-JSON (Firebase-Konsole → Projekteinstellungen → Dienstkonten → „Neuen privaten Schlüssel generieren“). In den Einstellungen eintragen:
FCM_SERVICE_ACCOUNT(kompletter JSON-Inhalt),FCM_PROJECT_ID,FCM_ANDROID_APP_ID(z. B.1:123:android:abc, App mit Paketnamede.crypto.jodabotin Firebase anlegen),FCM_API_KEY,FCM_SENDER_ID(alle aus dergoogle-services.json). Die App braucht keine eigenegoogle-services.json: Sie holt die Firebase-Optionen vonGET /api/pushund registriert ihr Token beiPOST /api/push/register. Ohne FCM-Konfiguration bleibt Firebase komplett aus (Status in den App-Einstellungen). - API:
GET /api/push(Konfiguration + eigene Geräte),POST|DELETE /api/push/register({kind:"web",subscription}bzw.{kind:"fcm",token}),POST /api/push/test. Ungültige Geräte (404/410) werden automatisch entfernt.
Chat-Kanäle (Telegram, Discord), Google Drive, Notion
Eingehende Nachrichten gehen an einen Ziel-Bot (Einstellungen → Chat-Kanäle, nur Admin). Jeder Chat bekommt eine eigene Unterhaltung (sichtbar in der Web-App). Befehle: /neu (neue Unterhaltung), /ja / /nein (wartende riskante Aktion bestätigen/ablehnen). Nur freigegebene IDs dürfen schreiben; Unbekannte bekommen ihre ID genannt.
- Telegram: Bot bei @BotFather anlegen →
TELEGRAM_BOT_TOKEN; Chat-IDs inTELEGRAM_ALLOWED_CHATS(kommagetrennt,*= alle). Danach „Webhook setzen“ (POST /api/channels/telegram/setup) – benötigt eine öffentliche HTTPS-URL (z. B. Render). Der WebhookPOST /api/channels/telegramprüftX-Telegram-Bot-Api-Secret-Token(ausJODABOT_SECRETabgeleitet). - Discord: App im Developer Portal anlegen →
DISCORD_APP_ID,DISCORD_PUBLIC_KEY,DISCORD_BOT_TOKEN; „Interactions Endpoint URL“ =https://<host>/api/channels/discord(Ed25519-Signaturprüfung). „Slash-Befehle registrieren“ legt/ask,/ja,/nein,/neuan. Erlaubte Discord-User-IDs inDISCORD_ALLOWED_USERS. (Kein Gateway/Websocket nötig – passt zum schlafenden Free-Plan; die erste Antwort nach dem Aufwachen kann länger dauern.) - Google Drive (Werkzeug „Google Drive“, nutzt die Google-Verbindung):
drive_search,drive_read(Docs/Sheets werden als Text/CSV exportiert),drive_upload(Workspace-Datei oder Text, mit Bestätigung). - Notion (Werkzeug „Notion“):
NOTION_TOKEN= Token einer internen Integration (notion.so/my-integrations; Seiten mit der Integration teilen).notion_search,notion_read_page,notion_create_page(mit Bestätigung). Pro Nutzer kann alternativ eine Verbindungnotionmit eigenem Token hinterlegt werden.
Medien: Bilder, Sprache, Bilderzeugung
- Bild-Upload mit Vision (Web: 📎, Android: 📎 Bildauswahl, verkleinert auf 1600 px): bis zu 4 Bilder (PNG/JPEG/WebP/GIF, je ≤ 5 MB) pro Nachricht.
POST /api/conversations/{id}/messagesakzeptiert{ content, images: ["data:image/…;base64,…"] }. Die Bilder liegen im Bot-Workspace unteruploads/und werden bei OpenAI, xAI, Ollama und lokalen OpenAI-kompatiblen Servern alsimage_url, bei Anthropic alsimage-Block übergeben. Das Modell muss Bilder können (z. B.gpt-4o,grok-4,claude-*,llava). - Spracheingabe (🎙️):
POST /api/media/transcribe(multipart, Feldaudio) → Whisper-kompatibles/audio/transcriptions. Einstellungen:STT_PROVIDER(Standardopenai, auchlocal-openaiz. B. faster-whisper-server),STT_MODEL(Standardwhisper-1). - Vorlesen (🔊 an jeder Antwort):
POST /api/media/speak {text}→ MP3 über/audio/speech.TTS_PROVIDER(Standardopenai),TTS_MODEL(Standardgpt-4o-mini-tts),TTS_VOICE(Standardalloy). - Bilderzeugung (Werkzeug „Bilderzeugung“,
image_generate; oderPOST /api/media/image {prompt, botId}):IMAGE_PROVIDERxai(Standard,grok-2-image) oderopenai(gpt-image-1),IMAGE_MODELüberschreibt. Bilder landen unterimages/im Workspace und werden im Chat angezeigt. - Schlüssel kommen aus den Modell-Server-Einstellungen des jeweiligen Anbieters.
GET /api/mediazeigt, was verfügbar ist (ohne Schlüssel: verständliche Fehlermeldung statt Absturz).
Mehrbenutzerbetrieb
- Rollen:
adminunduser. Das Konto ausJODABOT_ADMIN_USER(Standardadmin) ist immer Admin und kann weder gesperrt, herabgestuft noch gelöscht werden (Notzugang). - Registrierung (
/login→ „Registrieren“, Android: Chip „Registrieren“): Modusoff(aus),invite(Standard: nur mit Einladungscode) oderopen. Der Admin stellt das unter Einstellungen → Nutzerverwaltung um;JODABOT_REGISTRATION=off|invite|openin der Umgebung überschreibt die Einstellung. - Einladungscodes: Admin erstellt Codes mit Rolle, Anzahl Nutzungen und Ablauf (Tage) und kopiert einen Link
…/login?invite=CODE. Codes lassen sich widerrufen. - Nutzerverwaltung (Admin): Nutzer auflisten, Rolle ändern, sperren/entsperren (beendet Sitzungen), Passwort zurücksetzen, löschen (inkl. Bots, Chats, Gedächtnis, Routinen, Verbindungen, Workspace-Dateien).
- Daten pro Nutzer: Bots (und damit Chats, Gedächtnis, Routinen, Dateien), Konto-Verbindungen (Google, Notion) und Push-Geräte gehören dem jeweiligen Nutzer.
- Nur Admin: Modell-Server, Schlüssel/Secrets, MCP-Server, Chat-Kanäle, Nutzerverwaltung. Werkzeuge mit server-weiten Zugangsdaten (GitHub über
GITHUB_TOKEN, IMAP/SMTP, globalerNOTION_TOKEN, MCP-Server) funktionieren nur in Bots von Admins; normale Nutzer verbinden ihr eigenes Google- bzw. Notion-Konto. - Migration: Beim Start werden vorhandene Daten ohne gültigen Besitzer (Einzelnutzer-Installation) automatisch dem Admin zugeordnet; der bestehende Admin erhält die Rolle
admin. - API:
GET/POST /api/auth/register,GET /api/admin/users,PATCH/DELETE /api/admin/users/{id},GET/POST /api/admin/invites,DELETE /api/admin/invites/{code},GET/PUT /api/admin/registration.
Browser-Werkzeuge (Playwright)
Werkzeuggruppe „Browser (Playwright)“ im Bot-Editor: browser_open (URL öffnen), browser_click (Element per CSS-Selektor oder sichtbarem Text anklicken), browser_type (Text in ein Feld tippen, ohne abzusenden), browser_submit (Formular absenden), browser_text (sichtbaren Text lesen), browser_screenshot (PNG im Workspace unter screenshots/, im Chat angezeigt), browser_close.
- Bestätigung:
browser_clickundbrowser_submitsind riskant und warten auf deine Freigabe (Web, Android, Telegram/Discord/ja). Öffnen, Tippen, Lesen und Screenshots laufen ohne Rückfrage. - Sicherheit: Jede Anfrage des Browsers (inkl. Weiterleitungen und Unterressourcen) wird gegen interne Adressen geprüft (localhost, 10.x, 192.168.x, 169.254.x, ::1 …). Für Tests im eigenen Netz:
JODABOT_BROWSER_ALLOW_PRIVATE=1. Downloads sind aus. Pro Bot eine Seite, nach 5 Minuten Leerlauf geschlossen (BROWSER_IDLE_MS). - Voraussetzung: Chromium oder Chrome auf dem Server (gesucht:
CHROMIUM_PATH,/usr/bin/chromium,/usr/bin/google-chrome…). Docker-Image:docker build --build-arg INSTALL_CHROMIUM=1 .(bzw. indocker-compose.ymlunterbuild.args). Ohne Chromium oder mitJODABOT_DISABLE_BROWSER=1werden die Werkzeuge dem Modell nicht angeboten und der Bot-Editor zeigt einen Hinweis. - Render (Free): Das Image wird ohne Chromium gebaut (
INSTALL_CHROMIUM=0), die Browser-Werkzeuge sind dort also aus. Chromium würde die 512 MB RAM des Free-Plans sprengen; wer es trotzdem will, setzt im Render-Dashboard die Docker-Build-VariableINSTALL_CHROMIUM=1(sinnvoll erst ab einem größeren, kostenpflichtigen Plan).






