A nanobot-class personal agent with none of the Python. joshbot is a single ~19MB static binary — no venv, no runtime dependencies. Self-hosted, curl to running in under a minute, and scriptable enough to test in CI.
curl -fsSL https://raw.githubusercontent.com/bigknoxy/joshbot/main/install.sh | bash
joshbot uses a ReAct (Reasoning + Acting) loop — it thinks, uses tools, learns from results, and iterates — all within a single conversation.
Send a message via CLI, Telegram, or script. Every channel feeds into a common message bus.
The LLM core analyzes your intent with full context — past conversations, stored memories, available skills and tools.
Runs tools (search files, fetch web pages, execute code, delegate subagents) and observes results.
Key facts are extracted and saved to long-term memory. Repeated tool patterns auto-create reusable skills.
From daily productivity to deep research — joshbot handles the spectrum.
Summarizes calendar, tasks, and priorities into a morning plan. Flag urgent items and draft replies.
Gathers information across multiple sources, cross-references, and returns a structured brief with citations.
Retains facts, preferences, and decisions across days. Retrieves relevant context without being reminded. Optional two-stage Dream consolidation clusters past turns into durable insights using local embeddings — no embedding API.
Generates, explains, debugs, and refactors code. Runs lint/tests on results. Investigates bugs systematically.
Schedule reminders in plain durations (30m, 2h, 1d), one-off or repeating — jobs and their delivery survive restarts, so a reminder set before a reboot still reaches your chat. A heartbeat scans for unchecked tasks and follows up on its own.
Observes repeated tool-use patterns and generates reusable skills. Common tasks become one-command operations.
joshbot sessions search "that thing" greps every transcript — case-insensitive, newest first, redacted output — and the agent has the same recall via the session_search tool, so "what did we decide last week" is answered from the record, on any channel. joshbot agent --continue resumes the latest session headlessly with a recap.
Fans out independent tasks to parallel subagents or chains steps sequentially. Each gets focused context.
The interactive CLI and Telegram print the answer as it is generated instead of after the turn ends. On by default since v1.48.0 — set streaming: false to restore whole-reply delivery. Scripted output is unaffected either way.
In a real terminal, joshbot agent gives you Tab slash-command completion, Up/Down history, multiline editing (Alt+Enter) and a prompt that shows the current model.
Stop a running turn from a [⏹ Stop] button on the streaming reply, switch models or personalities per-session (/model, /personality — inline button pickers on Telegram), summarize context (/compact) and inspect status (/status) — in the CLI and from the Telegram menu.
joshbot serve speaks the OpenAI chat API at /v1/chat/completions (streaming included) and /v1/models, so any client with a base-URL setting can use joshbot as a backend. The model is the agent — a request runs the full loop with tools, memory and skills, not a proxy to an upstream provider. Authentication is mandatory (it refuses to start with no key configured) and it binds loopback by default, because whoever reaches this endpoint reaches the shell tool. When stt is configured it also serves POST /v1/audio/transcriptions — the one route that is not the agent — transcribing a multipart upload with the same speech-to-text provider Telegram voice notes use; without stt.provider it answers 501 naming the key. When embeddings is configured it also serves POST /v1/embeddings, embedding text with that provider's key and endpoint — no key needed for a local ollama — and answering 501 naming embeddings.provider when it is not. Setting api.webui also serves a self-contained browser chat page at /, embedded in the binary with no CDN; it is off by default because the page is a login form accepting an API key that reaches the shell tool, and with it off those routes 404.
Chat from anywhere via Telegram or Discord. Full skill and memory access from your phone. On Telegram, replies are authored as Markdown and delivered as HTML so ordinary prose is not mangled, and thread to the message that asked, a live status line shows what tool is running mid-turn, reminders survive restarts, text files you send are actually read, and voice notes are transcribed and answered when STT is configured (honestly refused when not). Telegram uses long-polling, Discord an outbound gateway websocket — no webhooks or inbound ports either way. Both fail closed — an empty allowlist rejects everyone. Both can run at once — the bus fans out replies per channel.
joshbot can also send files out: ask it for a report, a log or a chart and it arrives as a real Telegram attachment — a photo inline, anything else as a document — with the type decided by sniffing the bytes. The file must be inside the workspace, resolved through the same containment walk the filesystem tool uses, so an escape via a symlink sends nothing — and the bytes are read once through that same contained handle, so nothing re-opens the path afterwards. It goes to the conversation you asked in; there is no recipient argument for the model to choose. Channels without attachment support say so and print the workspace-relative path rather than dropping it.
Attach a picture with joshbot agent -m "what is this?" --image shot.png (repeatable), or just send a photo on Telegram. The type is decided by sniffing the bytes, never by the extension or by what the sender declared — PNG, JPEG, GIF and WebP, 5 MB per image and 20 MB per request. If no configured model is known to accept images, the run fails before any provider call with an error naming the models it tried; unknown models count as not vision-capable, so a typo reads as a typo rather than a provider 400. Sessions record what was sent — type, size, SHA-256 — not the bytes.
Send a PDF on Telegram and the file itself rides the turn, read by the model rather than guessed at from its name. The bytes must start with %PDF-; limits are 8 MiB per document and 16 MiB per request, refused from the declared size before any transfer. Document reading is a narrower capability than vision and has its own model list, checked before the first network call on both the streaming and non-streaming paths — an incapable model produces an error naming it, not a provider 400. Office formats (docx, xlsx, pptx) are still refused, now saying what does work.
A transient 429 or 5xx is retried on the same provider with backoff — honouring the upstream Retry-After — before the fallback chain moves on, so one blip never silently switches the model mid-conversation. A provider that keeps failing is deprioritized for a cooldown window instead of being re-dialled on every turn, and when a fallback does answer, the reply says so in one line. Set the chain from the CLI: joshbot configure --fallback "nvidia,poolside". Common failures end with an actionable next step, not just an upstream error dump.
Define profiles for a hosted model and your local Ollama, then pick one per run with --profile — on agent, gateway and preflight. A profile stores the name of the environment variable holding its key, never the key, and a typo, a disabled profile or an unset variable stops the run at startup instead of surfacing as a 401 mid-conversation. joshbot profiles list shows what each would dial and is safe to paste into a bug report.
Every command runs non-interactively. Pipe one message with agent -m, get machine-readable output with --output-format json or NDJSON with stream-json, and thread turns with --resume. Meaningful exit codes make it safe to gate CI on.
API keys, auth headers and your home directory are stripped from logs and joshbot status before you ever see them — so a bug report is safe to paste. Conversations on disk stay verbatim, owner-only.
Downloads a single binary for your platform (Linux/macOS, amd64/arm64), verifies it, and points you at joshbot onboard. Open source, MIT licensed. Your data stays on your machine.