A local-first AI agent runtime in one compact Rust binary.
Bring your own model, your own workspace, your own identity, and a sandbox you can actually read.
DuckAgent is an agent runtime you configure and run locally. It talks to 30+ LLM providers, including Anthropic, OpenAI, Gemini, Bedrock, Copilot, OpenRouter, DeepSeek, Kimi, Qwen, xAI, Ollama Cloud, Azure Foundry, and custom OpenAI-compatible endpoints. It reaches people and systems through 30+ Gateway channels: Telegram, Slack, Discord, Matrix, Signal, email, SMS, WhatsApp, Home Assistant, voice bridges, webhooks, and an API server. It acts through capabilities such as filesystem, shell/process, web search/extract, memory, cron, MCP servers, skills, and custom runtime tools.
Everything runs on your machine, with your keys, in your workspace. Use it as a
fast terminal UI (duck) or keep it alive as a service
(duck gateway service start) so chat apps, webhooks, and API clients can all
talk to the same Agent Loop.
Linux, macOS, and WSL2:
curl -fsSL https://raw.githubusercontent.com/selfonomy/duckagent/main/scripts/install.sh | bashWindows PowerShell:
irm https://raw.githubusercontent.com/selfonomy/duckagent/main/scripts/install.ps1 | iexAfter installation, choose how you want to run it:
# Local terminal UI: fastest way to chat, approve tools, switch models, and work.
duck
# Long-running service: Gateway channels, API clients, automations, and webhooks.
duck gateway service startFull docs: GitHub Pages
- 🦆 One compact Rust runtime: a native binary for macOS, Linux, Windows, and WSL2. No giant app shell required.
- 🧠 30+ model providers: Anthropic, OpenAI, Gemini, Bedrock, Copilot, OpenRouter, DeepSeek, Kimi, Qwen, xAI, Ollama Cloud, Azure Foundry, and custom OpenAI-compatible endpoints.
- 💬 30+ channels: use the same Agent Loop from the TUI, Telegram, Slack, Discord, Matrix, Signal, email, SMS, WhatsApp, Home Assistant, voice bridges, webhooks, and the OpenAI-compatible API Server.
- 🛡️ Real sandbox policy: JSON-configured filesystem mounts, path rules, network allow/ask/deny, environment handling, tool approvals, and shell command permissions.
- 🧩 MCP + skills: load MCP servers, built-in capabilities, and profile
skills backed by
SKILL.mdfiles. - 🪪 Portable identity: every profile owns its own model config,
credentials, Gateway config, memory, skills,
SOUL.md,USER.md, optionalAGENTS.md, and avatar files. - 🎭 SillyTavern card support:
avatar.pngcan be a SillyTavern PNG card. DuckAgent extracts embedded character metadata and injects it as profile context. - 🔁 Rewind instead of regret:
/rewindlists user turns and can restore tracked file edits fromwrite_file,edit, andapply_patchsnapshots when checksums prove it is safe. - 🧬 Self-improving memory: profile-scoped memories, active memory
catalogs,
SOUL.md, andUSER.mdlet the agent get better at working with you without smearing one profile into another. - 🔎 Search works out of the box:
web_searchdefaults to Exa MCP, so a new install can search immediately. Configure an Exa key later if you want your own quota. Extraction defaults to local parsing with optional browser fallback. - 📅 Automations without OS cron: ask for reminders or recurring tasks in normal language; the Gateway service runs them from append-only job logs.
- 💸 Cache-friendly prompt design: one stable system prompt stays first, dynamic context is appended after it, and long tool history is projected into recoverable summaries instead of being resent forever.
DuckAgent ships an offline benchmark suite under benchmark/ for long-running
Agent Loop context policy. The current runtime uses
ContextProjectionPolicy::guarded_mid, reported as
duckagent_recoverable_decay_guarded_mid.
The important idea: DuckAgent keeps the active loop rich while there is room, then compresses completed tool history into recoverable summaries with exact handles such as path, offset, limit, process id, cursor, mode, and query. The model can recover the exact detail it needs without paying to resend every raw tool result forever.
In benchmark/results/guarded-mid-vs-balanced-combined/report.md, guarded-mid
completed 1188/1188 simulated turns and saved roughly 108M raw tool
tokens through structured projection.
| Model profile | Guarded-mid cache hit | Versus balanced |
|---|---|---|
openai-gpt-5.4 |
96.6% |
22.6% lower simulated cost, 23.7% fewer expected tokens |
openai-gpt-5.5 |
96.6% |
22.6% lower simulated cost, 23.7% fewer expected tokens |
openai-gpt-5.4-mini |
87.5% |
8.1% lower simulated cost, 30.8% fewer expected tokens |
kimi-k2.6 |
87.3% |
14.5% lower simulated cost, 30.6% fewer expected tokens |
deepseek-chat |
91.3% |
Same as balanced on the 128K prompt profile |
deepseek-v4-flash |
97.0% |
16.9% lower simulated cost, 21.3% fewer expected tokens |
deepseek-v4-pro-promo |
97.0% |
15.6% lower simulated cost, 21.3% fewer expected tokens |
These numbers come from an offline simulator, not a billing guarantee. They are still useful because they make context policy measurable instead of vibes-only.
DuckAgent defaults to the workspace sandbox. It is built for daily agent work:
read broadly, write only to the current workspace and temp directory, hide
common secrets, keep .git read-only, route ordinary network through a managed
proxy, and ask before risky shell command classes.
The active preset is stored in ~/.duckagent/config.json:
{
"sandbox": {
"preset": "workspace"
}
}The default workspace preset expands to:
{
"filesystem": {
"mounts": [
{ "path": "*", "access": "ro" },
{ "path": ".", "access": "rw" },
{ "path": "$TMPDIR", "access": "rw" }
],
"rules": [
{ "path": ".env", "access": "none" },
{ "path": ".env.*", "access": "none" },
{ "path": ".git", "access": "ro" },
{ "path": "*.pem", "access": "none" },
{ "path": "*.key", "access": "none" },
{ "path": "id_rsa", "access": "none" },
{ "path": "id_ed25519", "access": "none" },
{ "path": "*.p12", "access": "none" },
{ "path": "*.pfx", "access": "none" }
]
},
"network": {
"mode": "proxy",
"hosts": {
"*": "ask",
"127.0.0.1": "allow",
"::1": "allow",
"localhost": "allow"
},
"addresses": {
"127.0.0.0/8": "ask",
"::1/128": "ask",
"10.0.0.0/8": "ask",
"172.16.0.0/12": "ask",
"192.168.0.0/16": "ask",
"100.64.0.0/10": "ask",
"169.254.0.0/16": "deny",
"0.0.0.0/8": "deny",
"::/128": "deny"
}
},
"env": {
"*": "allow"
},
"permissions": {
"tools": {},
"shell": {
"bash -c": "ask",
"bash -lc": "ask",
"chmod": "ask",
"chown": "ask",
"dd": "ask",
"find -delete": "ask",
"git push": "ask",
"git reset --hard": "ask",
"mkfs": "ask",
"node -e": "ask",
"python -c": "ask",
"python3 -c": "ask",
"rm -fr": "deny",
"rm -r": "ask",
"rm -rf": "deny",
"sh -c": "ask",
"sudo": "ask",
"zsh -c": "ask",
"zsh -lc": "ask"
}
}
}Inspect or switch sandbox behavior:
duck sandbox list
duck sandbox get workspace
duck sandbox check workspace
duck --sandbox readonly
duck --sandbox dangerduck
duck --profile work
duck --sandbox readonly
duck model
duck profiles
duck gateway channels
duck gateway service start
duck gateway service log
duck gateway service stop
duck sandbox list
duck sandbox get workspace
duck sandbox check workspace
duck mcp list
duck mcp add docs https://example.com/mcpScheduled tasks are created from normal chat, for example:
Remind me in five minutes to buy groceries.
Every day at 8 AM, summarize what we talked about before.
Pause that reminder.
Change the summary task to 9 AM.
Tasks fire while the profile's long-running service is active:
duck gateway service startSession control slash commands work in the TUI and Gateway channels:
/new
/resume
/resume 2
/rewind
/rewind 3
/rewind appends a rewind event instead of rewriting session JSONL. When file
snapshots were recorded after the target turn, DuckAgent restores old file
contents or deletes newly-created files only if the current file still matches
the recorded post-change checksum.
~/.duckagent/
config.json
profiles/
<name>/
config.json
auth.json
mcp-auth.json
sessions/
memories/
skills/
gateway/
cron/
jobs.jsonl
runs.jsonl
SOUL.md
USER.md
AGENTS.md
avatar.png
The root config.json stores the active profile and machine-level sandbox
configuration. Profile config.json stores non-sensitive runtime settings for
models, Web providers, Gateway, MCP, and related features. Secrets live in
auth.json or mcp-auth.json.
New profiles receive editable copies of the bundled default avatar.png,
SOUL.md, and USER.md. Empty SOUL.md files are initialized from the default
bundled persona. The default USER.md is intentionally blank so the agent does
not assume user background or preferences.
The documentation site lives in docs/ and uses Astro + Starlight:
cd docs
pnpm install
pnpm run dev
pnpm run build| Path | Purpose |
|---|---|
.github/workflows/ |
CI and release automation. |
benchmark/ |
Offline context-policy benchmark harness and pricing data. |
docs/ |
Astro + Starlight documentation site. |
scripts/ |
Linux/macOS and Windows installers. |
src/ |
Rust runtime, TUI, gateway, capabilities, sandbox, MCP, and providers. |
LICENSE.txt |
Apache-2.0 license text. |
CI validates Rust formatting, repository metadata, docs builds, cross-target
checks, native tests, and sandbox smoke coverage. Release tags matching v*
build archive checksums for supported macOS, Linux, and Windows targets.
DuckAgent is licensed under Apache-2.0. See LICENSE.txt.