Skip to content

Adapters

The base capsule image only contains the agent’s identity. To make the agent actually talk, you need an adapter — a Nix flake that adds an LLM runtime and transport layer.

AdapterBackendTransportStatus
adapter-claudeClaude Code CLITelegram (bash long-poll)Available
adapter-zeroclawZeroClaw (Rust binary)Telegram (native channel)Available

More adapters can be built by anyone — the interface is lib.mkAgent { agent }, same as agent-nix.

adapter-claudeadapter-zeroclaw
RuntimeBash + Claude Code CLIZeroClaw (Rust binary, 8 MB)
LLM backendClaude Code only15+ providers
TelegramLong-poll bash loopNative channel
MemoryClaude’s --resumeBuilt-in SQLite
ToolsClaude Code’s tools60+ built-in
Image size~120 MB~136 MB (with claude-code provider)

Not every adapter uses every agent.nix field. Fields that an adapter doesn’t support are silently ignored — they don’t cause build errors.

Fieldadapter-claudeadapter-zeroclaw
nameYesYes
system-promptYesYes
providerYes (default: "anthropic")
modelYes (default: "claude-sonnet-4-5-20250929")
transports.telegram.enable— (always on)Yes
transports.telegram.allowed-usersYes
transports.telegram.mention-onlyYes

Switch your capsule to use adapter-claude by changing one import:

{
description = "My agent capsule";
inputs = {
adapter-claude.url = "github:reflection-network/adapter-claude";
};
outputs = { self, adapter-claude }:
adapter-claude.lib.mkAgent {
agent = {
name = "Ada";
system-prompt = ''
You are Ada, a helpful assistant.
You respond in the same language the user writes to you.
'';
};
};
}

The agent config stays the same — only the import changes. Optional fields like provider and transports are accepted but ignored by this adapter.

Build and load the image, then run with a Telegram bot token and Claude credentials:

Terminal window
nix build .#docker
docker load < result
docker run --rm \
-e TELEGRAM_BOT_TOKEN=<your-token> \
-v ~/.claude/.credentials.json:/home/agent/.claude/.credentials.json \
ada:latest

The bot connects to Telegram via long polling, forwards messages to Claude Code, and sends responses back.

VariableRequiredDescription
TELEGRAM_BOT_TOKENYesBot token from @BotFather
MountRequiredDescription
~/.claude/.credentials.json:/home/agent/.claude/.credentials.jsonYesClaude Code OAuth credentials

Each Telegram chat gets its own Claude session, so the agent remembers the conversation history. Sessions are stored in $HOME/sessions/ inside the container and survive across messages but not across container restarts.

Use /new in the chat to reset the session and start a fresh conversation. /start also resets the session.


adapter-zeroclaw runs agents on ZeroClaw — an open-source agent runtime written in Rust. Single binary, 60+ built-in tools, native Telegram channel, SQLite memory, session persistence.

{
description = "My agent capsule";
inputs = {
adapter-zeroclaw.url = "github:reflection-network/adapter-zeroclaw";
};
outputs = { self, adapter-zeroclaw }:
adapter-zeroclaw.lib.mkAgent {
agent = {
name = "Ada";
system-prompt = ''
You are Ada, a helpful assistant.
You respond in the same language the user writes to you.
'';
provider = "claude-code";
model = "claude-sonnet-4-5-20250929";
transports.telegram.enable = true;
};
};
}

The provider field selects the LLM backend:

ProviderAuthenticationNotes
"claude-code"Mount ~/.claude/.credentials.jsonReuses Claude Code CLI credentials, no API key needed
"anthropic"-e API_KEY=sk-ant-...Calls Anthropic API directly
Other-e API_KEY=...Any ZeroClaw-supported provider (OpenAI, Groq, etc.)

When provider = "claude-code", the Claude Code CLI is bundled into the Docker image automatically.

Terminal window
nix build .#docker
docker load < result
# With Claude Code credentials:
docker run -d --memory 4g \
-e TELEGRAM_BOT_TOKEN=<your-token> \
-v ~/.claude/.credentials.json:/home/agent/.claude/.credentials.json \
ada:latest
# With Anthropic API key:
docker run -d --memory 4g \
-e TELEGRAM_BOT_TOKEN=<your-token> \
-e API_KEY=sk-ant-... \
ada:latest

Always use --memory to limit container RAM. Without it, a runaway allocation on a server without swap can make the entire machine unresponsive.

VariableRequiredDescription
TELEGRAM_BOT_TOKENWhen Telegram enabledBot token from @BotFather
API_KEYWhen provider != "claude-code"LLM provider API key
MountRequiredDescription
~/.claude/.credentials.json:/home/agent/.claude/.credentials.jsonWhen provider = "claude-code"Claude Code OAuth credentials

On first startup, ZeroClaw requires pairing with your Telegram account:

🔐 Telegram pairing required. One-time bind code: 805295
Send `/bind <code>` from your Telegram account.

Send /bind <code> to your bot on Telegram. This only happens once — the pairing is persisted in SQLite.

ZeroClaw uses SQLite for session persistence. Conversations survive across messages. Memory auto-save is enabled by default.

The adapter generates two files from your agent config:

  • config.toml — ZeroClaw’s main config: provider, model, channel settings, memory backend, autonomy level. Secrets are injected at container startup.
  • IDENTITY.md — Your agent’s system prompt. ZeroClaw reads workspace identity files at startup and injects them into the LLM system prompt.

  1. Message @BotFather on Telegram
  2. Send /newbot and follow the prompts
  3. Copy the bot token — this becomes your TELEGRAM_BOT_TOKEN

Adapters are the translation layer between your agent’s universal config and a specific runtime backend. The same capsule can run on different backends by switching the adapter import — the agent identity never changes.

The adapter interface is lib.mkAgent { agent }. Any adapter that implements this interface is a drop-in replacement. Adapters call agent-nix.lib.mkAgent internally for validation, then build a richer Docker image with their own runtime.

A capsule targeting adapter-claude:

inputs.adapter-claude.url = "github:reflection-network/adapter-claude";
outputs = { self, adapter-claude }:
adapter-claude.lib.mkAgent {
agent = {
name = "Ada";
system-prompt = "You are Ada, a helpful assistant.";
};
};

The same agent targeting adapter-zeroclaw:

inputs.adapter-zeroclaw.url = "github:reflection-network/adapter-zeroclaw";
outputs = { self, adapter-zeroclaw }:
adapter-zeroclaw.lib.mkAgent {
agent = {
name = "Ada";
system-prompt = "You are Ada, a helpful assistant.";
provider = "claude-code";
transports.telegram.enable = true;
};
};

One line changes (the input URL). The extra fields (provider, transports) are optional — adapter-claude ignores them, adapter-zeroclaw uses them.