D.U.H. — Duh is a Universal Harness. Provider-agnostic AI coding agent.
Project description
$ duh -p "fix the bug in auth.py"
⠋ Thinking...
I'll read the file first to understand the issue.
⠸ Running Read(file="auth.py")
Found the bug: token validation checks `expires_at < now` but should be `<=` —
tokens expiring at exactly the current second are incorrectly rejected.
⠹ Running Edit(file="auth.py", old_string="expires_at < now", new_string="expires_at <= now")
✓ Applied edit to auth.py (line 42).
⠼ Running Bash(command="python -m pytest tests/test_auth.py -q")
...........
11 passed in 0.43s
Done in 3 turns.
A universal, open-source AI coding agent. One harness, any provider — Anthropic Claude, OpenAI API, ChatGPT Plus/Pro subscription (Codex-family models), local Ollama, or a deterministic stub for tests. Drop-in compatible with the Claude Agent SDK NDJSON protocol, so it can replace claude wherever that binary is expected.
Install
# From source (recommended during alpha)
git clone https://github.com/nikhilvallishayee/duh
cd duh
python3.12 -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
# From PyPI
pip install duh-cli # core: Anthropic + Ollama + httpx
pip install 'duh-cli[openai]' # + OpenAI API key provider
pip install 'duh-cli[rich]' # + Rich TUI rendering
pip install 'duh-cli[bridge]' # + WebSocket remote bridge
pip install 'duh-cli[attachments]' # + PDF attachment support (pdfplumber)
pip install 'duh-cli[security]' # + vulnerability monitoring (ruff, pip-audit, detect-secrets, cyclonedx)
pip install 'duh-cli[all]' # everything above
Quick start
duh -p "fix the bug in auth.py" # print mode, auto-detect provider
duh --provider anthropic --model claude-sonnet-4-6 -p "hello" # force Claude
duh --provider openai --model gpt-5.2-codex -p "refactor db" # ChatGPT Plus/Pro Codex (OAuth)
duh # interactive REPL
duh doctor # diagnostics + health checks
duh security scan # vulnerability scan (SARIF output)
duh security init --non-interactive # set up security policy
duh security doctor # scanner health check
Providers
| Provider | Auth | Example models |
|---|---|---|
| Anthropic | ANTHROPIC_API_KEY env var or /connect anthropic |
claude-sonnet-4-6, claude-opus-4-6, claude-haiku-4-5 |
| OpenAI API | OPENAI_API_KEY env var or /connect openai (API key) |
gpt-4o, o1, o3 |
| OpenAI ChatGPT (Codex) | /connect openai → PKCE OAuth against auth.openai.com; tokens stored in ~/.config/duh/auth.json (0600). See ADR-051, ADR-052. |
gpt-5.2-codex, gpt-5.1-codex, gpt-5.1-codex-max, gpt-5.1-codex-mini |
| Ollama | Local daemon on localhost:11434, no key |
Any locally-pulled model |
| Stub | DUH_STUB_PROVIDER=1 |
Deterministic canned response for tests / offline runs |
Provider and model auto-detect: give --model gpt-5.2-codex and D.U.H. will route to the ChatGPT/Codex Responses endpoint if OAuth exists; otherwise it falls back to the standard OpenAI Chat Completions adapter with your API key.
Slash commands (REPL)
/help /model /connect /models /cost /status /context /changes /git /tasks /brief /search /template /plan /pr /undo /jobs /health /clear /compact /snapshot /exit
Run /help in the REPL for the full description of each command. Highlights: /connect openai runs the ChatGPT OAuth flow; /snapshot takes a ghost filesystem snapshot you can apply or discard; /plan switches to design-first two-phase execution; /pr list|view|diff|checks integrates with gh.
Built-in tools
Read, Write, Edit, MultiEdit, Bash, Glob, Grep, Skill, ToolSearch, WebFetch, WebSearch, Task, EnterWorktree, ExitWorktree, NotebookEdit, TestImpact, MemoryStore, MemoryRecall, HTTP, Docker, Database, GitHub, TodoWrite, AskUserQuestion, plus LSP (deferred, loaded via ToolSearch).
Security
D.U.H. ships a three-layer pluggable security module (ADR-053 + ADR-054) that addresses every published agent RCE in the 2024-2026 CVE corpus:
Layer 1 — Vulnerability monitoring (duh security CLI):
- 13 scanners across 3 tiers: Minimal (ruff-sec, pip-audit, detect-secrets, cyclonedx-sbom + 5 D.U.H.-specific scanners), Extended (semgrep, osv-scanner, gitleaks, bandit), Paranoid (GitHub Actions CodeQL, Scorecard, Dependabot)
- D.U.H.-specific scanners detect project-file RCE (CVE-2025-59536), MCP tool poisoning (CVE-2025-54136), sandbox bypass (CVE-2025-59532), command injection (CVE-2026-35022), and OAuth hardening violations
- SARIF output for GitHub Code Scanning, delta mode (
--baseline), exception management with expiry duh security initwizard,duh security doctor, pre-push git hook installer
Layer 2 — Runtime hardening (ADR-054):
- Taint propagation:
UntrustedStrsubclass tags every string with its origin (user_input,model_output,tool_output,file_content,mcp_output,network) and propagates through all string operations - Confirmation tokens: HMAC-bound tokens prevent model-originated tainted strings from reaching dangerous tools (Bash, Write, Edit) without explicit user confirmation
- Lethal trifecta check: Sessions with simultaneous read-private + read-untrusted + network-egress capabilities require explicit acknowledgement (
--i-understand-the-lethal-trifecta) - MCP Unicode normalization: NFKC normalization + rejection of zero-width, bidi, tag, and variation selector characters in MCP tool descriptions (GlassWorm defense)
- Per-hook filesystem namespacing: Each hook gets a private temp directory; cross-hook file access is blocked
- PEP 578 audit hook bridge:
sys.addaudithooktelemetry onopen,subprocess.Popen,socket.connect,exec,import pickle— sub-500ns fast path - Signed plugin manifests: TOFU trust store with sigstore-ready verification and revocation
- Provider differential fuzzer: Hypothesis property tests ensure all 5 adapters parse tool_use identically
Layer 3 — Sandboxing:
Shell commands and MCP stdio servers are wrapped by the host OS sandbox: macOS Seatbelt (sandbox-exec profiles), Linux Landlock (syscall-level filesystem access control), and a network policy layer that blocks outbound traffic unless explicitly allowed (see duh/adapters/sandbox/). Approval behaviour is controlled with --approval-mode suggest|auto-edit|full-auto (reads only / reads+writes / everything), with --dangerously-skip-permissions as the hard bypass.
MCP (Model Context Protocol)
MCP servers connect via four transports: stdio (via the mcp SDK), SSE, streamable HTTP, and WebSocket. Configure with --mcp-config <file-or-json>. See ADR-010 and ADR-040.
Hooks
29 lifecycle events (6 original + 22 extended + AUDIT) including PreToolUse, PostToolUse, PostToolUseFailure, SessionStart, SessionEnd, UserPromptSubmit, PermissionRequest, PreCompact, PostCompact, FileChanged, SubagentStart, Elicitation, and more. Hooks support glob matchers, blocking semantics (a hook can refuse a tool call or rewrite its input), and both shell-command and Python-callable handlers. See ADR-013, ADR-036, ADR-044, ADR-045.
Tests and coverage
4160 tests, 100% line coverage, ~28s on a laptop. Includes 330+ security-specific tests (unit, integration, property-based), CVE replay fixtures, and performance regression gates. CI runs on GitHub Actions with a --cov-fail-under=85 floor (current actual: 100%).
.venv/bin/python -m pytest tests/ # full suite
.venv/bin/python -m pytest tests/ --cov=duh # with coverage
DUH_STUB_PROVIDER=1 .venv/bin/python -m pytest tests/ # force stub provider
Selected ADRs
| # | Topic |
|---|---|
| 001 | Project vision: provider-agnostic, <5K LOC kernel |
| 005 | Safety architecture (schema + approval + patterns) |
| 009 | Provider adapter contract (.stream() async generator) |
| 021 | Claude Agent SDK NDJSON protocol compatibility |
| 037 | Seatbelt / Landlock platform sandboxing |
| 039 | Ghost snapshots for /snapshot |
| 040 | MCP stdio / SSE / HTTP / WebSocket transports |
| 045 | Hook blocking + input rewriting |
| 051 | OAuth provider authentication (ChatGPT Plus/Pro) |
| 052 | ChatGPT Codex adapter (Responses API) |
| 053 | Continuous vulnerability monitoring (pluggable scanner module) |
| 054 | LLM-specific security hardening (taint propagation, confirmation tokens, lethal trifecta) |
Full list: docs/adrs/ (54 ADRs).
Development
git clone https://github.com/nikhilvallishayee/duh
cd duh
python3.12 -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
# Run tests
.venv/bin/python -m pytest tests/
# Run with coverage
.venv/bin/python -m pytest tests/ --cov=duh --cov-report=term
# Offline / deterministic mode (no real provider calls)
export DUH_STUB_PROVIDER=1
duh -p "hello" # → "stub-ok"
Source layout:
duh/
kernel/ # agentic loop, sessions, tasks, plan mode, undo, skills, memory
ports/ # abstract provider / tool / approver interfaces
adapters/ # anthropic, openai, openai_chatgpt, ollama, stub, mcp_executor, mcp_transports
sandbox/ # seatbelt, landlock, network policy
auth/ # credential store + OpenAI ChatGPT PKCE OAuth
providers/ # provider registry + model resolution
tools/ # 25+ built-in tools (see list above)
security/ # vulnerability monitoring, policy resolver, scanner plugins, CI templates
scanners/ # 13 scanners (4 minimal, 5 D.U.H.-custom, 4 extended)
cli/ # parser, main, runner, repl, sdk_runner, ndjson
bridge/ # optional WebSocket remote bridge
ui/ # Rich TUI rendering
plugins/ # plugin loader, signed manifests, TOFU trust store
hooks.py # 29-event hook system with per-hook FS namespacing
License
Apache 2.0 — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file duh_cli-0.4.2.tar.gz.
File metadata
- Download URL: duh_cli-0.4.2.tar.gz
- Upload date:
- Size: 1.2 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2ff061b5b364638437a84f4ed0cf9babfe6b1c57c9d823a7a1e95d8ae40ed536
|
|
| MD5 |
ef821334c72d50c094a44767b5d53ee7
|
|
| BLAKE2b-256 |
b972663c7016b38b234e6f0d7fca7b8d6cfe2ceefbc867003197edb7d761f596
|
Provenance
The following attestation bundles were made for duh_cli-0.4.2.tar.gz:
Publisher:
publish.yml on nikhilvallishayee/duh
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
duh_cli-0.4.2.tar.gz -
Subject digest:
2ff061b5b364638437a84f4ed0cf9babfe6b1c57c9d823a7a1e95d8ae40ed536 - Sigstore transparency entry: 1306378010
- Sigstore integration time:
-
Permalink:
nikhilvallishayee/duh@63bafb3735b6eb674132dab420a5539debc3f252 -
Branch / Tag:
refs/tags/v0.4.2 - Owner: https://github.com/nikhilvallishayee
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@63bafb3735b6eb674132dab420a5539debc3f252 -
Trigger Event:
push
-
Statement type:
File details
Details for the file duh_cli-0.4.2-py3-none-any.whl.
File metadata
- Download URL: duh_cli-0.4.2-py3-none-any.whl
- Upload date:
- Size: 311.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0a6b7af5a190521e5183170acf1edad35030a0d8710b89b0ec7ae0c6a7ecfdef
|
|
| MD5 |
ba0c75837e32cb63576a373e4070c7dc
|
|
| BLAKE2b-256 |
cc46f963ff436f143c37ffd167d774ac41cb467aae02f7618c3640d521007397
|
Provenance
The following attestation bundles were made for duh_cli-0.4.2-py3-none-any.whl:
Publisher:
publish.yml on nikhilvallishayee/duh
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
duh_cli-0.4.2-py3-none-any.whl -
Subject digest:
0a6b7af5a190521e5183170acf1edad35030a0d8710b89b0ec7ae0c6a7ecfdef - Sigstore transparency entry: 1306378110
- Sigstore integration time:
-
Permalink:
nikhilvallishayee/duh@63bafb3735b6eb674132dab420a5539debc3f252 -
Branch / Tag:
refs/tags/v0.4.2 - Owner: https://github.com/nikhilvallishayee
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@63bafb3735b6eb674132dab420a5539debc3f252 -
Trigger Event:
push
-
Statement type: