CS

chinghauchu/snorlax-bot

Developer tools
0 stars Quality 48 Trend 8

Open-source local Grok Bot that runs on NVIDIA DGX Spark. Desktop + iOS, local inference.

Overview

An open-source, local Grok Bot-like desktop assistant. Named teammates, shared computer, skills and routines — running entirely on an NVIDIA DGX Spark (GB10 Grace Blackwell superchip, 128 GB coherent unified memory). Inference stays on the desk. There is no cloud LLM. Snorlax-Bot is an independent recreation from the public product shape of Grok Bot (named bots, a persistent computer, skills/routines, MCP connectors, question widgets, desktop + iOS). It does not include, and is not derived from, any Grok Bot source. Grok Bot is a team of always-on agents with a computer of their own in the cloud. Snorlax-Bot is that product idea turned inward: the DGX Spark is the computer. Bots live on your LAN, talk through a thin local runtime, and (in later versions) browse, click, schedule, and call tools without sending tokens off-box. v0.31 is still small: . Header click opens agent identity (PATCH) or the channel member list.

README

Snorlax-Bot

An open-source, local Grok Bot-like desktop assistant. Named teammates, shared computer, skills and routines — running entirely on an NVIDIA DGX Spark (GB10 Grace Blackwell superchip, 128 GB coherent unified memory). Inference stays on the desk. There is no cloud LLM.

Founder: Chinghau Chu. License: Apache-2.0.

Snorlax-Bot is an independent recreation from the public product shape of Grok Bot (named bots, a persistent computer, skills/routines, MCP connectors, question widgets, desktop + iOS). It does not include, and is not derived from, any Grok Bot source.

Why this exists

Grok Bot is a team of always-on agents with a computer of their own in the cloud. Snorlax-Bot is that product idea turned inward: the DGX Spark is the computer. Bots live on your LAN, talk through a thin local runtime, and (in later versions) browse, click, schedule, and call tools without sending tokens off-box.

v0.31 is still small: **named teammates + identity pane + group channel threads

  • chat with @mentions + report-back + extra channels + built-in tools + a thin desktop Computer pane + a runtime MCP client + question widgets + skills and cron/webhook/Slack/GitHub routines (Add / Remove + enable/pause + Copy webhook URL; Slack/GitHub listeners when that plugin is connected) + MCP connect chrome (Settings plugins list + kind=connect card) + assistant markdown + MCP Add custom (Settings POST / DELETE; no separate disconnect) + curated plugin catalog (Settings Catalog Slack/GitHub Add; not a store) + chat attachments (composer paperclip / drop; user-right image + file chips; attachmentIds in that turn) + agent-sent attachments (LEFT kind=message reuses that chrome; runtime binds write_file / screenshot) + video attachments (composer drop/pick; 220×160 player; kind=video; 50MB; not fed to the model) + watch-video tool (watch_video { attachmentId }; Watched {name} on the existing kind=tool line; no auto-inject; desktop/iOS idle) + create agent/channel tools (create_agent / create_channel wrap POST /v1/agents; Created {name} tool line; 项目 / 员工 seed skill) + IME-safe composer Enter (does not send while composing) + composer clipboard paste (Cmd-V / Ctrl-V / paste event of image/file/video → same pending chips; text-only paste stays in the field) + Copy / Regenerate / Speak on assistant LEFT kind=message (Copy 1:1 and channel; Regenerate 1:1 latest only; { regenerate: true }; Speak is local piper via POST /v1/speak, no autoplay) + mermaid diagrams on completed LEFT kind=message (fenced mermaid; invalid falls back to fence chrome) + TeX math on completed LEFT kind=message (inline \( \) / block $$; KaTeX; invalid falls back to monospace source) + stick-to-bottom while streaming (follow within ~64px; freeze on scroll-up; Send / Regenerates re-arm; 12px muted Jump to latest) + short multi-bubbles on completed LEFT kind=message (split on blank lines after the stream completes; Copy / Speak / Regenerates on the last bubble) + same-turn consecutive LEFT bubbles 6px/6pt (different turns / after tool-widget-approve-connect stay 12px/12pt) + Stop generating mid-stream (12px/12pt muted Stop at the chat-column bottom; client abort; keep partial LEFT as completed; composer stays focused) + waiting ··· until first token (LEFT 12px/12pt muted pulsing ··· after Send until the first assistant token; Reduce Motion static) + streaming caret on the growing LEFT bubble (12px/12pt muted blinking caret after the first token until complete / Stop) + compact tool traces (2+ consecutive kind=tool → 12px/12pt muted N tools + chevron; default collapsed) + identity-pane Box computer preview (Bearer PNG) + desktop Box takeover (Open / Done; pointer/key in 1280×800) + teach-a-task (Record / Stop / Save as skill inside takeover) + skill markdown editor (identity-pane Skills list + Edit sheet) + iOS Open/Done takeover (same v0.15 session protocol) + iOS Record (same v0.16 record protocol on the takeover bar) + composer / skill autocomplete (1:1 only; channel / is plain text) + blank New skill (identity-pane Add → POST { name, body })**. Header click opens agent identity (PATCH) or the channel member list. Agent identity lists routines with a live enable/pause switch, Add, Remove, and Copy for webhook URLs, then skills with Add / Edit / Remove. 1:1s are user ↔ that agent only; a user @chip (or agent DM) opens a handoff thread in a channel (seed snorlax-bot-group by default). B answers in the thread; A reports back in A’s 1:1 as A. Seed snorlax-bot can be deleted (no auto-reseed). Users can create more channels. Agents can list/read/write files, run a workspace shell (no extra network), search or fetch the web, and call MCP tools the runtime loaded from ~/.snorlax-bot/mcp.json — the runtime owns that loop and auto-runs tools; clients never call the model, the tools, or MCP. Channel work lives in a sandbox under ~/.snorlax-bot when the channel’s shared-project toggle is on (default off); otherwise each agent uses its own workspace. Desktop shows that sandbox as a 320px right pane (file tree + text preview; collapsible — 12px seam chevron, default collapsed). The agent identity pane shows a live 16:10 computer preview (Bearer PNG of the runtime-owned 1280×800 display). Desktop Open takes over that display (overlay on chat + info pane; Esc / Done). While driving, desktop Record captures pointer/key and Save as skill writes SKILL.md (v0.9 list). iOS matches the preview in the agent sheet and Open is a full-screen takeover (tap the 16:10 shot or 12pt Open; Keyboard + Done). While driving, iOS Record captures pointer/key and Save as skill writes SKILL.md (same v0.16 HTTP). No file browser. No VNC / separate Box window. That slice is meant to actually run on a laptop or a Spark, with a mocked model backend when a 70B-class checkpoint is not present.

Hardware target

DGX Spark (intended)
Superchip GB10 Grace Blackwell
CPU 20-core Arm (aarch64)
GPU Blackwell, compute capability 12.1 (sm_121)
Memory 128 GB LPDDR5x, coherent unified CPU+GPU pool
Intended models ~70B FP8 by default; ~200B-class locally; two Sparks linked for ~405B

The 128 GB pool is shared by the OS, vLLM weights, KV cache, the FastAPI runtime, and (later) the sandbox computer. Serving flags must leave headroom — see docs/architecture.md.

Repository layout

desktop/     Tauri + TypeScript chat client
ios/         Swift/SwiftUI companion (list, chat, settings)
runtime/     FastAPI agent runtime (in front of vLLM)
protocol/    OpenAPI contract for /v1
docs/        Architecture, Mac-local oMLX recipe, next tickets

Architecture (one paragraph)

Clients never talk to the model server. A thin FastAPI runtime owns agents, transcripts, and LAN bearer-token auth. It sits in front of oMLX (Mac-local OpenAI-compat) or vLLM (Spark); TensorRT-LLM is a later swap behind the same interface. SQLite on disk holds agents and messages. The seeded agent id is snorlax-bot (PATCH-able; DELETE 204, no auto-reseed). Seeded channel snorlax-bot-group DELETE is 204, no auto-reseed. Bind is 127.0.0.1 until a token exists, then 0.0.0.0 so a phone on the same LAN can reach the host. Details: docs/architecture.md. Mac-local recipe: docs/mac-local.md. Locked HTTP contract: protocol/openapi.yaml (copy: runtime/openapi.yaml).

How to run locally (v0 vertical slice)

You do not need a DGX Spark or a cloud LLM. The default inference backend is mock (CI and first boot). On a Mac, point the runtime at local oMLX. On a Spark, use vLLM. Clients always talk to :8787, never :8000.

1. Runtime

Python 3.11+.

cd runtime
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
snorlax-runtime

On first start the process:

  1. Creates a data directory (default ~/.snorlax-bot, override with SNORLAX_DATA_DIR)
  2. Writes ~/.snorlax-bot/token and ~/.snorlax-bot/snorlax.db
  3. Seeds agent snorlax-bot (name Snorlax, title Assistant) and channel snorlax-bot-group (name Snorlax-Bot) on a first empty DB. Deleting the seed agent or the seed channel does not reseed (never recreate snorlax-bot-group). After seed channel delete, clients select an agent first if any remain, else a remaining channel. Tool workspaces are created lazily under ~/.snorlax-bot/workspaces/.
  4. Listens on 127.0.0.1:8787 until that token file exists, then 0.0.0.0:8787 on later launches. SNORLAX_TOKEN overrides the file. Clients use SNORLAX_URL + SNORLAX_TOKEN; they never read the Spark disk.
export SNORLAX_TOKEN=''
curl -sS -H "Authorization: Bearer $SNORLAX_TOKEN" http://127.0.0.1:8787/v1/agents

Chat (SSE):

curl -N -H "Authorization: Bearer $SNORLAX_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"content":"Hello from the LAN"}' \
  http://127.0.0.1:8787/v1/agents/snorlax-bot/messages

Tests:

cd runtime && pytest

2. Desktop (web, then Tauri)

Node 20+.

cd desktop
npm install
npm run dev

Open http://127.0.0.1:1420, paste the runtime URL (http://127.0.0.1:8787 or http://localhost:8787) and the bearer token, then chat with Snorlax. Loopback URLs persist across reload.

The same UI is wrapped by Tauri for a native window:

cd desktop
npm run tauri dev

3. On a Mac (oMLX)

oMLX is the first-class local OpenAI-compat backend. It is not vLLM. Recipe: docs/mac-local.md.

curl -sS http://127.0.0.1:8000/v1/models   # copy data[0].id
export SNORLAX_INFERENCE_BACKEND=omlx
export SNORLAX_OMLX_BASE_URL=http://127.0.0.1:8000/v1
export SNORLAX_MODEL=''
# No API key: runtime does not send Authorization to localhost inference.
snorlax-runtime

Turn API-key auth off in oMLX admin. Desktop Settings: Runtime URL http://127.0.0.1:8787. Clients never call :8000.

4. On a real DGX Spark

Point the runtime at local vLLM instead of mock or oMLX:

export SNORLAX_INFERENCE_BACKEND=vllm
export SNORLAX_VLLM_BASE_URL=http://127.0.0.1:8000/v1
export SNORLAX_MODEL=meta-llama/Llama-3.3-70B-Instruct-FP8
snorlax-runtime

Default Spark model is 70B-class FP8, config-swappable. v0.25 attachmentIds images are included in that turn; v0.26 agent-sent write_file / screenshot files bind onto the assistant kind=message. Legacy MessageCreate.images persist off-model. Video wait.

v0 vs later

See ROADMAP.md. Short version: v0.26 is named agents plus runtime-owned file/shell/web tools in a ~/.snorlax-bot sandbox, a thin desktop Computer pane over that sandbox, a runtime MCP client (stdio + LAN from mcp.json), connect chrome (GET /v1/plugins + kind=connect), Settings Add custom (POST /v1/plugins + uninstall), curated plugin catalog (GET /v1/plugins/catalog; Slack/GitHub Add; not a store), question widgets in the transcript, cron XOR webhook/Slack/GitHub routines (Add / Remove on the identity pane; fire a SKILL.md into that agent’s 1:1), assistant markdown (clients render; content stays a plain string), desktop Box takeover, teach-a-task (Record inside takeover → SKILL.md), a skill markdown editor (identity-pane Edit sheet), blank New skill (POST { name, body }), iOS Open/Record, and 1:1 composer / skill autocomplete (Send loads SKILL.md; channel / stays plain text), and v0.25 chat attachments (composer paperclip / drop; user-right image + file chips; POST /v1/agents/{id}/attachments + attachmentIds), and v0.26 agent-sent attachments (LEFT kind=message reuses that chrome; runtime binds write_file / screenshot), and v0.30 composer clipboard paste (same pending chips; text-only paste stays in the field; OpenAPI stays 0.18.0), and v0.31 Copy / Regenerate on assistant LEFT kind=message ({ regenerate: true }; OpenAPI stays 0.18.0), and v0.44 local TTS Speak on completed LEFT kind=message (POST /v1/speak; piper; no autoplay; OpenAPI stays 0.18.0), and v0.45 mermaid diagrams on completed LEFT kind=message (official mermaid / iOS WKWebView; invalid falls back to fence chrome; OpenAPI stays 0.18.0), and v0.46 TeX math on completed LEFT kind=message (inline \( \) / block $$; KaTeX; invalid falls back to monospace source; OpenAPI stays 0.18.0), and v0.48 short multi-bubbles on completed LEFT kind=message (split on blank lines after the stream completes; Copy / Speak / Regenerates on the last bubble; OpenAPI stays 0.18.0), and v0.49 optimistic user-RIGHT bubble on Send (text + chips immediately; upload first; block a second Send; reconcile on success; 4xx/5xx restore text + chips + muted Couldn’t send.; Regenerates unchanged; OpenAPI stays 0.18.0), and v0.50 Stop generating mid-stream (12px/12pt muted Stop at the chat-column bottom, above Jump to latest; client abort of the in-flight stream; stop appending tokens; partial LEFT stays as completed; hide Stop when idle; composer stays focused; no POST /cancel; OpenAPI stays 0.18.0), and v0.51 waiting ··· until first token (LEFT 12px/12pt muted pulsing ··· after Send until the first assistant token; first token swaps to the growing LEFT bubble; Stop / error / empty reply dismisses the dots; OpenAPI stays 0.18.0), and v0.52 streaming caret on the growing LEFT bubble (12px/12pt muted blinking caret at the end of mid-stream LEFT text after the first token until complete / Stop; never with waiting ···; Reduce Motion static; OpenAPI stays 0.18.0), and v0.53 Esc = Stop while generating (window Esc / iOS UIKeyCommand Escape aborts the in-flight stream the same as tapping Stop; do not steal Esc during IME or a pending widget / approve / connect card; on-screen Stop unchanged; no POST /cancel; OpenAPI stays 0.18.0), and v0.54 same-turn multi-bubble gap (consecutive LEFT bubbles from the same completed turn 6px/6pt; different turns / after tool-widget-approve-connect stay 12px/12pt; mid-stream and user-right unchanged; OpenAPI stays 0.18.0), and v0.55 Send muted while generating (Send control muted and disabled from Send until complete / Stop / error / empty; Enter does not send; composer stays editable; re-enables immediately; Stop + Esc unchanged; OpenAPI stays 0.18.0), and v0.56 compact tool traces (2+ consecutive kind=tool lines collapse to one 12px/12pt muted N tools + chevron; default collapsed; single tool unchanged; widget/approve/ connect never fold; OpenAPI stays 0.18.0), and v0.57 mid-stream plaintext (live LEFT kind=message is plain text — no live markdown, mermaid, or math; on complete / Stop markdown renders once then the blank-line split; Copy / Speak / Regenerates still last-bubble-only; OpenAPI stays 0.18.0), and v0.58 Reduce Motion static ··· (OS Reduce Motion on → static muted ···, no pulse; off → existing pulse; caret Reduce Motion unchanged; OpenAPI stays 0.18.0), and v0.59 focus composer after Stop (Stop or Esc abort returns focus to the composer immediately; desktop window; iOS only when a hardware keyboard is attached — do not force the software keyboard; complete / error / empty leave focus alone; OpenAPI stays 0.18.0), and v0.60 Esc = Jump when frozen (when the Jump to latest chip is visible and nothing is generating, Esc activates Jump — same as clicking the chip; while generating, Esc still Stop; same IME / pending-card skips as v0.53; desktop window Esc; iOS hardware Escape only; OpenAPI stays 0.18.0), and v0.61 Copied feedback (on Copy of a completed LEFT kind=message, muted 12px/12pt Copied beside the Copy control for 1.2s; Copy stays in place and clickable; no toast; Speak / Regenerate unchanged; OpenAPI stays 0.18.0), and v0.62 focus composer after Jump (chip click or Esc=Jump returns focus after scroll/re-arm/dismiss; desktop always; iOS hardware keyboard only — do not force the software keyboard; complete / error / empty leave focus alone; OpenAPI stays 0.18.0), and v0.63 Jump chip fade (appear/dismiss 120ms opacity fade; Reduce Motion on → instant show/hide; click / Esc=Jump behavior unchanged; OpenAPI stays 0.18.0), and v0.68 recall last message (empty composer Up-arrow, or iOS long-press Edit as new message on your latest bubble, puts that text in the composer; Escape or clearing restores empty; no recall while typing or during IME; Esc still Stop while generating; OpenAPI stays 0.18.0), and v0.69 per-chat drafts persist (unsent text stays with its chat across switches and relaunch; desktop localStorage; iOS UserDefaults; Send clears that chat; Up-arrow recall only when the composer is empty; OpenAPI stays 0.18.0), and v0.70 unread chat dot (a 6px accent dot on a sidebar row when an assistant reply finishes in a chat that is not open, or while the app/window is not focused; opening that chat clears it; the dots stay after a restart; OpenAPI stays 0.18.0), and v0.71 in-chat find (Cmd/Ctrl-F in an open chat shows a find bar over the loaded transcript; matches highlight; Enter/Shift-Enter and up/down jump between them; n of m; Esc closes and clears highlights; iOS search filters and highlights those loaded messages; no new HTTP; OpenAPI stays 0.18.0), and v0.72 quick chat switcher (Cmd/Ctrl-K opens a small overlay that fuzzy-filters existing agents and chats by name; arrow keys and Enter switch to that chat; Esc closes; iOS search field on the chat list narrows chats by name; no new HTTP; OpenAPI stays 0.18.0), and v0.73 keyboard shortcuts (Cmd/Ctrl-/ or ? outside a text field opens a small overlay of the chat shortcuts that already exist; Esc or a click outside closes it; iOS Gestures & shortcuts sheet on the chat screen lists long-press actions and hardware-keyboard shortcuts; no new HTTP; OpenAPI stays 0.18.0), and v0.74 per-chat reading place (switching away remembers if you were at the latest messages or parked on an earlier one; coming back restores that place; within ~64px of the bottom still opens at the latest; a new reply while you were away shows Jump to latest; Send, Regenerate, and Jump still snap to the bottom; session memory only; no new HTTP; OpenAPI stays 0.18.0), and v0.75 drag files onto the chat (dragging files over the open chat shows Drop files to attach; drop adds pending chips like the paperclip; Esc or dragging away clears it; text drags do not; iOS drop on the composer adds the same chips; nothing auto-sends; no new HTTP; OpenAPI stays 0.18.0), and v0.76 New messages divider (a thin muted New messages line above the first assistant reply that arrived since you last viewed that chat; the same not-open or unfocused condition as the unread dot; it stays put while the chat is open; Send or coming back with nothing new clears it; your own bubbles and collapsed tool lines do not anchor it; a day separator stays above it; desktop local storage; iOS UserDefaults; no animation; no new HTTP; OpenAPI stays 0.18.0), and v0.77 unread count outside the chat list (when one or more chats carry the unread dot, the desktop window title reads (2) Snorlax-Bot and the dock or taskbar badge shows that count where Tauri supports it; both clear when nothing is unread; the iOS chat list title shows the same count; the app icon badge shows it only when badge permission is already granted; no new HTTP; OpenAPI stays 0.18.0), and v0.78 retry a failed send (when a Send fails because the network or runtime is down, or an inference_unavailable error arrives before any reply, the user bubble stays in the transcript, muted, with Couldn’t send · Retry; Retry sends the same text and attachments again; Remove dismisses it; no auto-retry; iOS also offers Retry on long-press; no new HTTP; OpenAPI stays 0.18.0). Later: full sandbox computer GUI, MCP marketplace catalog, Slack/GitHub inbound listeners.

Contributing

CONTRIBUTING.md. Next concrete tickets: docs/tickets.md.

License

Apache License 2.0. Copyright 2026 Chinghau Chu.

View this README on GitHub

Recommended Tools

Try a different keyword or remove a filter.

Install

npx skillfish add chinghauchu/snorlax-bot