ByteClaw – selbst gehosteter KI-Assistent (Go-Backend + Android-App)
Find a file
2026-10-25 04:29:42 +01:00
android fix: track Android data package (was hidden by root .gitignore 'data/') 2026-10-25 04:29:42 +01:00
cmd/byteclaw chore: rename project to ByteClaw (module, binary, env prefix BYTECLAW_, Android package/app/theme) 2026-10-25 04:28:22 +01:00
internal chore: rename project to ByteClaw (module, binary, env prefix BYTECLAW_, Android package/app/theme) 2026-10-25 04:28:22 +01:00
.dockerignore build: Makefile with cross-builds (linux/termux arm64, armv7), non-root distroless Dockerfile, example config 2026-10-25 03:15:32 +01:00
.gitignore fix: track Android data package (was hidden by root .gitignore 'data/') 2026-10-25 04:29:42 +01:00
config.example.yaml chore: rename project to ByteClaw (module, binary, env prefix BYTECLAW_, Android package/app/theme) 2026-10-25 04:28:22 +01:00
Dockerfile chore: rename project to ByteClaw (module, binary, env prefix BYTECLAW_, Android package/app/theme) 2026-10-25 04:28:22 +01:00
go.mod chore: rename project to ByteClaw (module, binary, env prefix BYTECLAW_, Android package/app/theme) 2026-10-25 04:28:22 +01:00
go.sum feat: HTTP API, WebSocket streaming with token auth and origin check, server entrypoint 2026-10-25 03:14:02 +01:00
Makefile chore: rename project to ByteClaw (module, binary, env prefix BYTECLAW_, Android package/app/theme) 2026-10-25 04:28:22 +01:00
README.md chore: rename project to ByteClaw (module, binary, env prefix BYTECLAW_, Android package/app/theme) 2026-10-25 04:28:22 +01:00

ByteClaw

Schlanker, selbst gehosteter KI-Assistent: Go-Backend (ein statisches Binary, kein CGO) + nativer Android-Client (Kotlin, Jetpack Compose).

Architektur

Android-App (Compose)                     Go-Backend (cmd/byteclaw)
 ├─ Einstellungen (EncryptedSharedPrefs)   ├─ internal/server  HTTP-API + WebSocket, Bearer-Token, Origin-Check
 ├─ Chat  ──── WebSocket /api/ws ───────▶  ├─ internal/agent   Agent-Loop mit Tool-Calling, Lock pro Session
 └─ Sessions ─ REST /api/sessions ──────▶  ├─ internal/llm     Provider-Interface + OpenAI-kompatibler Streaming-Provider
                                           ├─ internal/tools   time_now, web_fetch, file_read/_write/_list
                                           ├─ internal/store   SQLite (modernc.org/sqlite, pure Go)
                                           └─ internal/config  YAML/JSON + Umgebungsvariablen

Ablauf einer Nachricht: Client → /api/ws → Agent speichert User-Nachricht → LLM (Streaming) → bei Tool-Calls werden Tools ausgeführt, Ergebnisse gespeichert und das LLM erneut gefragt (max. agent.max_iterations) → Tokens werden live als delta-Events gestreamt, am Ende done.

Abhängigkeiten des Backends: gopkg.in/yaml.v3, modernc.org/sqlite, github.com/coder/websocket.

Sichere Defaults

  • Start verweigert ohne auth_token (mind. 16 Zeichen); Vergleich in konstanter Zeit.
  • Lauscht standardmäßig nur auf 127.0.0.1:8787.
  • Browser-Origins: nur gleicher Host oder explizit in allowed_origins; * muss bewusst gesetzt werden. Native Clients (ohne Origin-Header) brauchen trotzdem das Token.
  • TLS-Zertifikatsprüfung ist überall an (LLM-Provider, web_fetch); es gibt keinen Schalter zum Abschalten.
  • web_fetch: nur http(s), Timeout, Größenlimit, max. 5 Redirects, blockiert Loopback/Private/Link-Local/CGNAT-Adressen beim Verbindungsaufbau (schützt auch gegen DNS-Rebinding und Redirect-Tricks). Abschaltbar per allow_private.
  • Datei-Tools sind per os.Root auf den Workspace beschränkt (kein .., keine absoluten Pfade, keine Symlink-Ausbrüche) und größenbegrenzt.
  • Kein Shell-Tool.
  • Docker: distroless, User 65532 (nonroot), Go-Version fixiert.
  • Android: Release-Builds erlauben nur HTTPS/WSS; Klartext (http/ws) nur im Debug-Build für lokale Entwicklung. Token/URL verschlüsselt per Android Keystore.

Quickstart Backend

cp config.example.yaml config.yaml
export BYTECLAW_AUTH_TOKEN=$(openssl rand -hex 32)
export BYTECLAW_LLM_API_KEY=sk-...           # OpenAI / OpenRouter; für Ollama leer lassen
make run                                     # = go build + ./bin/byteclaw -config config.yaml

Provider-Beispiele (llm.base_url / llm.model):

Provider base_url model (Beispiel)
OpenAI https://api.openai.com/v1 gpt-4o-mini
OpenRouter https://openrouter.ai/api/v1 openai/gpt-4o-mini
Ollama http://localhost:11434/v1 llama3.1 (Tool-fähig)

Test:

curl localhost:8787/healthz
curl -H "Authorization: Bearer $BYTECLAW_AUTH_TOKEN" -d '{"text":"Wie spät ist es in Berlin?"}' localhost:8787/api/chat

API

Methode Pfad Zweck
GET /healthz Health (ohne Auth)
GET /api/sessions Sessions (neueste zuerst)
POST /api/sessions Session anlegen {"title":""}
GET/PATCH/DELETE /api/sessions/{id} Lesen / umbenennen / löschen
GET /api/sessions/{id}/messages Verlauf
POST /api/chat Nicht-streamend {"session_id"?, "text"}
GET /api/ws WebSocket-Streaming

Alle /api/* brauchen Authorization: Bearer <token>. WebSocket: Client sendet {"type":"message","session_id":"<optional>","text":"..."}; Server sendet Events session, delta, tool_call, tool_result, done oder error ({"type","text","tool","args","call_id","session_id"}). {"type":"ping"} → pong.

Build, Tests, Cross-Builds

make            # vet + test + build
make race       # Tests mit Race-Detector (benötigt CGO)
make cross      # dist/: linux-amd64, linux-arm64, linux-armv7, termux-arm64, termux-armv7
make docker

Termux: byteclaw-termux-arm64 ist ein GOOS=android-Build; für armv7 wird ein statisches linux/arm-Binary gebaut, da android/arm ohne NDK/CGO nicht linkbar ist. In Termux z. B. nach $PREFIX/bin kopieren, chmod +x und starten.

Docker

docker build -t byteclaw .
docker run -d -p 127.0.0.1:8787:8787 -v byteclaw-data:/data \
  -e BYTECLAW_AUTH_TOKEN=... -e BYTECLAW_LLM_API_KEY=... byteclaw

Im Container lauscht der Server auf 0.0.0.0:8787; nach außen nur hinter einem TLS-Reverse-Proxy (Caddy, Traefik) oder in einem VPN (z. B. Tailscale) freigeben.

Android-Client

android/
 ├─ app/src/main/java/dev/byteclaw/client/
 │   ├─ MainActivity.kt          Navigation (Chat, Sessions, Einstellungen)
 │   ├─ data/ByteClawApi.kt      OkHttp REST + WebSocket-Flow
 │   ├─ data/SettingsStore.kt    EncryptedSharedPreferences
 │   ├─ data/Models.kt           kotlinx.serialization
 │   └─ ui/                      ChatViewModel, Compose-Screens
 └─ gradle/libs.versions.toml    Versionskatalog (AGP 8.7.3, Kotlin 2.0.21, compileSdk 35, minSdk 26)

Build (JDK 17 + Android SDK, ANDROID_HOME oder local.properties mit sdk.dir):

cd android
./gradlew assembleDebug        # app/build/outputs/apk/debug/app-debug.apk
./gradlew testDebugUnitTest
./gradlew assembleRelease      # unsigniert; Signing-Config fehlt noch

In der App unter Einstellungen Server-URL (https://…; im Debug-Build auch http://10.0.2.2:8787 für den Emulator) und Token eintragen, „Verbindung testen", speichern.

Nächste Schritte

  • Release-Signing, App-Icon, CI (GitHub Actions) für Go + Android
  • Session-Kontext zusammenfassen statt nur Fenster der letzten N Nachrichten
  • Weitere Provider (Anthropic nativ), Abbrechen laufender Antworten über WebSocket
  • Optional: Einrichtung per QR-Code (URL + Token)