An E-Ink AI pet for the Raspberry Pi Zero 2W β chat-first, remote-LLM brain, a face that lives on your desk.
π Read the Wiki β architecture, how it works, and how to run it on real hardware.
The real thing: the reacting face, the battery widget, and an on-screen caption β live on the panel.
shelldon is a tiny AI pet you talk to β a little face on a screen that talks back. Think Tamagotchi, but the brain is a real AI.
What it does:
- π¬ Chats with you β type to it, it replies with a genuine LLM brain
- π Has a face and moods β an expressive E-Ink face that shifts with how it feels and what's happening
- π§ Remembers you β it builds up memory of who you are and what you've talked about
- π± Learns over time β jots down what matters as you talk, then in a dream cycle consolidates the durable bits into lasting memory and lets the rest go
- π οΈ Writes its own tools β missing a capability? it writes a new tool (with a test), runs it past you for one-tap approval, and has that ability for good β more β
- π Acts on its own β reaches out with a thought when you've been quiet a while, bounded by a daily budget and battery state so it never spams or overspends
- π«§ Feels alive β blinks, idles, and drifts in mood between chats, even when you're not around
- πͺΆ Runs anywhere β fully in your terminal (zero hardware) or on a palm-sized Raspberry Pi with a screen
No subscription needed. Point it at a free AI provider and it costs $0/month to run β or a few dollars for a fancier brain. Jump to costs β
That's the gist. Want to run it? Getting started β. Everything else below is the how and why.
shelldon can extend itself. When it hits something it can't do, it writes a brand-new tool β the code and a test for it β then grows that capability permanently, gated by your approval.
How a new tool is born:
- It proposes. Mid-conversation the pet writes a small tool module plus a pytest test for it.
- It's gated β automatically. Before you ever see it, core runs that test in a bounded subprocess and statically rejects any tool that tries to import an LLM SDK or reach into the pet's own brain. A tool that fails its own test is thrown away β you're never asked about broken code.
- You approve. If it passes, you get a one-tap Approve / Deny in Telegram and review the actual code before anything goes live.
- It's live next turn. On approve, the tool is promoted and the next forked worker discovers it automatically β no restart. From then on the pet just has that ability.
Safe by construction:
- You're the gate. Nothing untrusted runs unseen β the pet pauses for your approval, and risky built-in actions (file writes, shell, network, git) gate the same way.
- Bounded on the hardware. Self-coded tools run under CPU + memory limits (
RLIMIT) so a runaway can't OOM the 512MB Pi, and the agentic loop is credit-capped so it can't burn your budget. - Self-healing. A tool that starts misbehaving is automatically quarantined after repeated failures β it can never wedge the pet.
- The brain stays out of core. A self-coded tool is mechanically barred β by a static import check and the CI import-linter β from importing the LLM or the pet's core, the same invariant that protects the whole system.
This is the headline of Epic 9, and it isn't just built β it's been validated live on the Pi: a real turn where the pet wrote, tested, promoted, and registered a working tool against its live brain, end to end.
A few decisions that shape everything:
Autonomy over convenience. The project exists because building it is the point β not finding the quickest path to a working bot. Every major component is designed to be understood and owned, not imported-and-forgotten.
Mechanical invariants beat vigilance. The LLM-free core isn't a policy β it's enforced by an import-linter in CI. The β€1-worker-in-flight guarantee isn't a comment β it's tested. The principle: if a constraint matters, make it impossible to break accidentally.
512MB as a design constraint, not an excuse. The Pi Zero 2W's memory limit is the load-bearing reason for half the architectural decisions (fork-server workers, RAM-resident personality state, WAL sqlite, atomic markdown writes). Designing around it produces a cleaner system than ignoring it.
Chat-first, embodiment optional. The pet's "soul" lives in the conversation β the face and hardware are enrichment, not the point. This means the system works fully in a terminal (CLI transport, no E-Ink) while still scaling up to full hardware.
You need two things: a brain (an LLM API key) and a chat (a Telegram bot). Both have free or cheap options.
1. Get a brain. shelldon defaults to GLM via Z.ai β Anthropic-compatible, well under $20/month for daily use. Sign up here and copy an API key. (Prefer free? Point it at a free-tier provider or a local Ollama instead β see Cost of running it.)
2. Get a chat. In Telegram:
- Message @BotFather, send
/newbot, give it a name and a username ending inbot. It replies with a bot token. - Message your new bot once (
/start) so it's allowed to see you. - Message @userinfobot to get your numeric user id (your allowlist entry).
3. Get the code and configure it.
git clone https://github.com/elliotboney/shelldon.git
cd shelldon
cp .env.example .env # then edit .env and fill in:
# GLM_API_KEY=... your Z.ai key
# SHELLDON_TELEGRAM_BOT_TOKEN=... from @BotFather
# ALLOWED_USERS=123456789 your Telegram user id (comma-separated for more)4. Run it β pick one path:
On a Raspberry Pi (the full pet: E-Ink face + a service that starts on boot):
./deploy/setup-pi.sh # installs uv, deps, the E-Ink stack, and a systemd service
sudo systemctl start shelldon # start it (autostarts on every boot from here)
journalctl -u shelldon -f # watch it thinkThe script detects the Waveshare panel; on a board without one it runs headless. Tune the faces in shelldon/display/waveshare.py.
On any Linux box (a server, a spare machine, or WSL β no hardware, chat only):
uv sync # install deps (get uv: https://docs.astral.sh/uv/)
set -a; . ./.env; set +a # load your config into the environment
SHELLDON_TRANSPORT=telegram uv run python -m shelldonThe forked-per-turn worker needs real
os.fork(), so the running app is Linux-only (a Pi, a server, or WSL β not macOS; the test suite runs everywhere, the live app does not).
Now message your bot. It'll reply with its own voice, remember what you tell it, reach out on its own when you've been quiet, and β on a Pi β show its mood on the screen.
A multi-process actor model over a typed message bus, around a hexagonal LLM-free core.
flowchart LR
chat["chat-transport<br/>(pluggable)"] <--> core
core["core (LLM-free)<br/>state Β· memory Β· arbiter Β· reflexes Β· scheduler"]
core <--> broker["broker<br/>creds + provider chain"]
core --> display["display<br/>(E-Ink face)"]
core <--> plugins["plugin-host<br/>(optional: XP, sensors)"]
core -->|fork per turn| worker["worker<br/>(ephemeral brain)"]
worker --> broker
broker --> llm["remote LLM<br/>GLM / Ollama / Claude / β¦"]
Everything talks over an Envelope bus (Unix domain sockets); core/ is mechanically barred from importing LLM code. The broker holds an ordered provider chain β reorder or extend it with a single env-var change, no code. Memory is hybrid β sqlite for conversation history (WAL, FTS5) and a human-readable markdown tree for curated knowledge.
The broker sits at the only egress to any LLM. It holds an ordered chain of adapters, two wire formats:
- Anthropic-format β the
anthropicSDK, serving both GLM-4.7 via Z.ai's Anthropic-compatible endpoint and native Claude. One adapter, two endpoints β the only difference is config. - OpenAI-compatible β the
openaiSDK, serving Ollama-over-LAN, OpenAI, OpenRouter, and any OpenAI-compatible endpoint. One adapter reaches the whole free-tier crowd β Groq, Cerebras, Gemini, NVIDIA NIM, Mistral β by config alone (see Cost of running it).
PROVIDER_CHAIN="glm,ollama" builds a two-element chain. glm,groq,openrouter builds three. An unknown preset fails at startup β no silent degradation.
- Raspberry Pi Zero 2W (~512MB RAM)
- Waveshare V4 E-Ink display
- PiSugar2 battery HAT (power + button)
- SANDISK 32GB High Endurance microSDHC
Sensors and other peripherals are optional, added as plugins. The system runs fully in a terminal (CLI transport, no display) β hardware is enrichment.
Disclaimer: Amazon Affiliate Links to help me out with development
shelldon doesn't run a model on the Pi Zero β it's a thin client that sends prompts to a remote LLM over the network. That means you control the cost entirely.
Free β local Ollama. Run a model on any machine with a decent GPU on your LAN and point shelldon at it. PROVIDER_CHAIN="ollama" and OLLAMA_API_BASE=http://<your-machine>:11434 is all the config needed. I run Qwen on a 3090 β it handles tool calls and vision well, and latency over LAN is negligible. Zero API cost, zero cloud dependency.
Free β hosted, no credit card. Several providers offer genuine free tiers (not trials) that renew daily and need no card. All of them speak the OpenAI-compatible wire format, so they work today through shelldon's existing openai preset β just point OPENAI_BASE_URL at them, no code change:
PROVIDER_CHAIN="openai"
OPENAI_API_KEY=<your-free-key>
OPENAI_BASE_URL=https://api.groq.com/openai/v1
OPENAI_MODEL=llama-3.3-70b-versatile
| Provider | OPENAI_BASE_URL |
Free tier (June 2026) | Good for |
|---|---|---|---|
| Gemini (Google AI Studio) | https://generativelanguage.googleapis.com/v1beta/openai/ |
1,500 req/day, 1M context | Best free frontier-class model |
| Groq | https://api.groq.com/openai/v1 |
~1,000 req/day, 100K tok/day | Fastest replies (~320 tok/s) |
| Cerebras | https://api.cerebras.ai/v1 |
1M tokens/day | Highest daily volume |
OpenRouter (:free models) |
https://openrouter.ai/api/v1 |
~50β1,000 req/day | Variety β DeepSeek R1, Llama 3.3, Qwen3 through one key |
| NVIDIA NIM | https://integrate.api.nvidia.com/v1 |
email signup | 100+ open-weight models |
| Mistral | https://api.mistral.ai/v1 |
developer free tier | Mistral's own models |
Free-tier quotas are independent per provider, so the smart move is to stack them in the chain and let it rotate when one hits a rate limit β e.g. PROVIDER_CHAIN="glm,groq,cerebras,openrouter". (Dedicated one-word presets β gemini, groq, cerebras β are a small planned convenience on top of the generic openai preset.) Two caveats: free tiers usually train on your prompts, so keep anything sensitive off them; and providers cut quotas without notice β check live limits.
Under $20/month β GLM via Z.ai. GLM-4.7 is a capable hosted model with an Anthropic-compatible API, which is why it's shelldon's default provider. Pricing is token-based and in practice lands well under $20/month for a pet that talks with you daily. Use this link for a discount at signup.
π’ Deployed and running on real hardware. shelldon lives on a Raspberry Pi Zero 2W as a systemd service β text it from your phone (Telegram), it thinks with a live LLM brain, replies, shows its face on the E-Ink panel, remembers you, drifts in mood between chats, and writes its own tools on request. Epics 1β9 done (45 stories, 745 tests) β including live, tiered self-coding; what's left is polish.
β Full progress, the epic-by-epic breakdown, and the roadmap: STATUS.md.
Built on the ideas of openclawgotchi by Dmitry Turmyshev (MIT). shelldon is a clean-room reimplementation β v1 is studied as reference, never copied.
Form-factor inspiration from pwnagotchi by @evilsocket β the original E-Ink virtual pet on Pi Zero.
shelldon is a ground-up v2 rebuild of openclawgotchi (MIT, by Dmitry Turmyshev). At its core it's a chat-bot pet: you converse with a remote-LLM brain by text, over a pluggable chat transport (not hardcoded to any one service), while the pet's face and mood live on a Waveshare E-Ink screen. It's built to be genuinely owned β a clean, tested spine that engineers out v1's documented pains.
It sits at the end of a short but meaningful lineage.
pwnagotchi (by @evilsocket) pioneered the form factor: an E-Ink "virtual pet" on a Pi Zero that feels alive. It showed that a small, cheap piece of hardware with a face on it could become a companion object β something you put on your desk and check in on. Two things come directly from pwnagotchi's design: the expressive E-Ink face (expressions that shift with mood and activity, idle animations between events) and the XP leveling system (the pet grows and levels up through interaction, giving the relationship a sense of progression over time). Both of those are being brought forward into shelldon.
openclawgotchi (by Dmitry Turmyshev) took that same form factor and made it a chat pet β connecting the E-Ink face to an LLM brain via Telegram. The Tamagotchi-meets-AI idea is genuinely compelling. But v1 accumulated real operational pain: OOM crashes on the Pi Zero's 512MB of RAM, a 1513-line Telegram connector with safety logic scattered through it, zero test coverage, and a transport hardcoded to one service.
shelldon is the v2 rebuild: same spirit, different spine. Clean-room β v1 code is studied as reference, never copied.
| v1 pain | shelldon solution |
|---|---|
| OOM crashes on Pi Zero's 512MB | Ephemeral fork-server workers β each turn forks a worker that runs once and dies; RAM never accumulates across turns |
| Hardcoded Telegram β one transport, all safety woven into a single massive connector | Transport-agnostic adapter contract β CLI, Telegram, SMS, or anything else slots in; none wired into core |
| Zero tests β bugs discovered in production | M0 test harness from day one β contract round-trips, worker-bound invariant, and atomic-write crash-safety all verified before first feature |
| Safety scattered across 1513-line connector | One security boundary β a single capability broker is the sole holder of LLM creds; nothing else can call a model |
| No provider flexibility | Pluggable, ordered provider chain β GLM default, Ollama/OpenAI/OpenRouter fallback, all config β never a code change |
| No offline life | Resident reflexes (blink, idle, mood drift) run between turns so the pet never freezes when the LLM is busy |
