cd /news/artificial-intelligence/show-hn-dora-a-tiny-llm-agent-with-o… · home topics artificial-intelligence article
[ARTICLE · art-91664] src=github.com ↗ pub= topic=artificial-intelligence verified=true sentiment=· neutral

Show HN: Dora – a tiny LLM agent with only bash tool

Dora, a tiny modular LLM agent kernel for Go, has been released as an open-source project on GitHub, featuring a core loop and two interfaces (Model and Tool) with a stateless design and sequential tool execution. The project provides installers for macOS, Linux, and Windows, supports self-update with checksum verification, and requires Go 1.25 or newer to build.

read11 min views1 publishedAug 11, 2026
Show HN: Dora – a tiny LLM agent with only bash tool
Image: source

dora

is a tiny, modular LLM agent kernel for Go. Its core is one loop and two interfaces: Model

and Tool

.

See docs/architecture.md for module boundaries, dependencies, interfaces, and runtime flows.

model := newMyModel()
weather := newWeatherTool()

agent, err := dora.New(model, weather)
if err != nil {
	log.Fatal(err)
}

result, err := agent.Run(ctx, []dora.Message{
	{Role: dora.RoleUser, Content: "What's the weather?"},
})
if err != nil {
	log.Fatal(err)
}

fmt.Println(result.Content)

The agent is stateless. Keep result.Messages

and pass them to a later call to continue a conversation.

The kernel supports optional model streaming events while keeping its baseline Model

interface synchronous. Tool calls are deliberately executed only after the current model response completes, and are still sequential. There is no built-in memory, policy engine, middleware, or provider SDK.

On macOS or Linux, install the latest release with curl:

curl -LsSf https://github.com/lgxz/dora/releases/latest/download/dora-installer.sh | sh

Use wget when curl is unavailable:

wget -qO- https://github.com/lgxz/dora/releases/latest/download/dora-installer.sh | sh

On Windows, use PowerShell:

powershell -ExecutionPolicy Bypass -c "irm https://github.com/lgxz/dora/releases/latest/download/dora-installer.ps1 | iex"

The installers download the archive for the current OS and architecture, verify it against the release SHA-256 checksums, and install dora

into $HOME/.local/bin

by default. Set DORA_INSTALL_DIR

to choose another directory:

curl -LsSf https://github.com/lgxz/dora/releases/latest/download/dora-installer.sh \
  | env DORA_INSTALL_DIR=/usr/local/bin sh

Install a specific release by using its tagged installer URL. Each installer is pinned to the release that contains it:

curl -LsSf https://github.com/lgxz/dora/releases/download/v0.1.0/dora-installer.sh | sh

Run dora --version

to inspect the installed version, source commit, and build date. Standalone releases that include self-update support can update themselves:

dora -update

The updater checks the latest stable GitHub Release, verifies its archive against checksums.txt

, validates the downloaded executable, and replaces the current binary with rollback on failure. Go builds, manual archive installs, and package-manager installs remain unmanaged; upgrade those through their original installation method. Re-run the latest installer once to enable self-update on an older installation. Release archives and checksums remain available on the GitHub Releases page for manual installation and verification.

To replace an unmanaged or development build (for example, one installed with make install

) with the latest release, bypassing the standalone-install marker and version checks:

dora -update --force

Building Dora requires Go 1.25 or newer. CI checks both the minimum supported Go 1.25 line and the current Go 1.26 line; release binaries use the latest Go 1.26 patch release.

Build the command with debug information:

make build

The binary is written to build/dora

(build/dora.exe

on Windows). For a smaller distribution binary without symbol and DWARF debug data, use:

make release

The release target uses -trimpath

, strips debug data, and embeds the version, commit, and build date. Override VERSION

, COMMIT

, or BUILD_DATE

when needed. On Windows the equivalent Go command can be run directly with build/dora.exe

as the output path when make

is unavailable.

To build a release binary and install it into $(PREFIX)/bin/dora

(default $HOME/.local/bin/dora

), use:

make install

install

depends on release

and creates $(PREFIX)/bin

if needed. Override PREFIX

to choose another location, for example make install PREFIX=/usr/local

.

Dora reads each provider's API key from a dedicated environment variable. The following table lists the supported providers, their environment variables, and their default models:

Provider Environment variable Default model
openai OPENAI_API_KEY
gpt-5
deepseek DEEPSEEK_API_KEY
deepseek-v4-flash
trust TRUST_API_KEY
auto

Set the environment variable for the provider you want to use. The commands differ by operating system.

macOS / Linux, temporary (current terminal only):

export OPENAI_API_KEY="sk-..."

macOS / Linux, permanent: append the export

line above to ~/.zshrc

(zsh) or ~/.bashrc

(bash), then reload it:

source ~/.zshrc

Windows PowerShell, temporary (current session only):

$env:OPENAI_API_KEY = "sk-..."

Windows PowerShell, permanent (persists for the current user):

[Environment]::SetEnvironmentVariable("OPENAI_API_KEY", "sk-...", "User")

Windows CMD, temporary (current session only):

set OPENAI_API_KEY=sk-...

When you set keys for more than one provider, specify model.provider

explicitly in ~/.dora/config.yaml

; otherwise Dora reports an ambiguity error. Setting exactly one key lets Dora select that provider automatically.

With exactly one supported provider API key set, Dora runs without a configuration file and selects that provider automatically. For example, DEEPSEEK_API_KEY

selects deepseek

, OPENAI_API_KEY

selects openai

, and TRUST_API_KEY

selects trust

. If multiple provider keys are set, configure model.provider

explicitly. If none are set, Dora retains deepseek

as the fallback and reports that DEEPSEEK_API_KEY

is missing.

To customize the defaults, create ~/.dora/config.yaml

. Dora uses this ~/.dora/

layout on every operating system, including macOS. Set the DORA_HOME

environment variable to an absolute path to override the home directory. You can also place the file anywhere and pass --config path/to/config.yaml

; an explicitly requested file must exist.

model:
  provider: deepseek

An explicit provider always takes precedence over environment-based selection. The same automatic selection applies when a configuration file exists but omits model.provider

.

The deepseek

preset defaults to the chat_completions

API, deepseek-v4-flash

, https://api.deepseek.com

, and DEEPSEEK_API_KEY

. The openai

preset defaults to chat_completions

, gpt-5

, https://api.openai.com/v1

, and OPENAI_API_KEY

. The trust

preset defaults to chat_completions

, auto

, https://api.trustoken.cn/v1

, and TRUST_API_KEY

. Override any preset field when needed, and set api: responses

to use the Responses API. Both APIs always use SSE streaming. Responses tool loops replay typed output items locally and do not depend on server-side response storage.

To use any third-party provider that speaks the OpenAI Chat Completions protocol (for example Ollama, LM Studio, vLLM, Groq, Together, OpenRouter, or a self-hosted endpoint), keep model.provider: openai

and override base_url

, name

, and api_key_env

(or api_key

). The Chat Completions endpoint is base_url + "/chat/completions"

, so base_url

should be the provider's /v1

(or equivalent) root.

For a self-hosted Ollama endpoint that requires no authentication, set api_key_env: ""

to disable the API key:

model:
  provider: openai
  name: llama3.1
  base_url: http://localhost:11434/v1
  api_key_env: ""

For a hosted OpenAI-compatible service that requires a key, such as OpenRouter or Groq, point api_key_env

at a custom environment variable:

model:
  provider: openai
  name: openrouter/auto
  base_url: https://openrouter.ai/api/v1
  api_key_env: OPENROUTER_API_KEY

For a one-off invocation, override the model and base URL on the command line:

./dora --model llama3.1 --base-url http://localhost:11434/v1 "prompt"

Literal api_key

is also supported, but an environment variable keeps secrets out of the configuration file. A non-empty literal key takes precedence over api_key_env

. Set api_key_env: ""

explicitly for a local endpoint that does not require authentication.

Dora runs up to 64 model-tool rounds per segment by default. Keep the safeguard but adjust it for unusually long tool workflows when needed:

agent:
  max_rounds: 96

Override it for one invocation with --max-rounds

:

./dora --max-rounds 96 "Complete a long task"

When the limit is reached with both stdin and stderr attached to a terminal, Dora asks whether to continue for another segment. Confirming resumes from the completed tool output without replaying work. Declining stops normally and saves the partial state of a named session. With piped or redirected I/O, Dora does not prompt and returns dora: maximum rounds exceeded

instead.

Run a one-shot prompt or combine an instruction with piped input:

export DEEPSEEK_API_KEY="..."
./dora "Explain this repository"
git diff | ./dora "Review this change"

Progress is shown on stderr with a small Dora personality, while the final answer remains on stdout. Use --quiet

or -q

when only the answer is wanted:

./dora --quiet "Explain this repository"

When stdout is a terminal, Dora prints the final answer as plain text. Redirected or piped stdout is identical, preserving stable output for scripts:

./dora "Write release notes"
./dora "Write release notes" > release-notes.md

Colors are enabled automatically for terminal output. Set NO_COLOR=1

to keep the layout without ANSI colors; progress remains visible on stderr.

Use a session name to continue the same conversation across CLI invocations:

./dora -s system-status "Analyze this machine's system status"
./dora -s system-status "Continue with the busiest processes"

Start over under the same session name with --fresh

. Existing history is ignored for this run and replaced only after the new task succeeds; if the run fails, the previous session remains intact:

./dora -s system-status --fresh "Analyze this machine from scratch"

Session names may contain letters, numbers, .

, _

, and -

. Dora stores each session as a versioned JSON snapshot with 0600

permissions. Session v3 binds the configured provider, API, model, and base URL: Chat Completions resumes from messages, while Responses additionally persists its opaque typed-item continuation. Use --fresh

before changing a session's backend. Version 1 and 2 session files are not supported. The default directory is ~/.dora/sessions

on every operating system. Omit --session

/-s

to keep the existing stateless behavior. Session files can contain commands and tool output, so treat them as sensitive. Do not run two Dora processes against the same session name concurrently.

Use --config

, --model

, --base-url

, --max-rounds

, --skills-dir

, or --no-skills

to override the corresponding configuration for one invocation.

Skills are local instruction packages loaded by the model only when relevant. Each skill is a directory containing a SKILL.md

with strict YAML front matter:

skills/
└── system-status/
    └── SKILL.md
---
name: system-status
description: Analyze CPU, memory, disk, and busy processes.
---


Inspect the machine methodically and summarize actionable findings.

By default, Dora discovers the skills

directory at ~/.dora/skills

(or DORA_HOME/skills

), independent of the active config.yaml

path. No configuration is needed.

Use skills.directories

only to add more parent directories:

skills:
  directories:
    - /absolute/path/to/additional-skills

For a one-off run, add one or more parent directories on the command line:

dora --skills-dir ./project-skills --skills-dir ~/shared-skills "Run checks"

Command-line directories are merged with the default and configured directories, converted to absolute paths, and deduplicated. Use --no-skills

to disable every skill source for one invocation; it takes precedence over both --skills-dir

and skills.directories

.

Dora advertises only skill names and descriptions in the skill

tool schema. The absolute skill directory and complete SKILL.md

are returned when the model calls that tool, allowing instructions to reference files such as scripts/check.sh

. The skill tool never executes those files; execution still requires an enabled tool such as Bash. Names must contain lowercase letters, numbers, and hyphens, and must match their directory name. Duplicate names are rejected. A missing or empty default directory simply leaves the tool disabled; malformed discovered skills and missing explicitly configured or command-line directories are errors.

Dora does not automatically read or migrate the previous macOS directory. Move existing files manually before running the new version:

mkdir -p "$HOME/.dora"
mv "$HOME/Library/Application Support/dora/config.yaml" "$HOME/.dora/config.yaml"
mv "$HOME/Library/Application Support/dora/skills" "$HOME/.dora/skills"
mv "$HOME/Library/Application Support/dora/sessions" "$HOME/.dora/sessions"

Skip any mv

command whose source does not exist. If DORA_HOME

is set, use its directory instead of the fallback destination shown above.

Pushing a semantic version tag runs the release workflow:

git tag v0.1.0
git push origin v0.1.0

The workflow runs the full validation, renders installers pinned to the tag, and uses GoReleaser to publish static archives for Linux, macOS, and Windows on amd64 and arm64. It also publishes checksums.txt

; public repositories receive GitHub build provenance attestations. Tags that do not match semantic version syntax fail before publishing.

Command tools use platform-aware automatic defaults: Bash is enabled on Linux and macOS, while PowerShell is enabled on Windows. The other command tool is disabled even if its executable is on PATH

. Omit enabled

to use that policy, or set it explicitly to override the platform default:

tools:
  bash:
    enabled: false
    timeout_seconds: 30
  powershell:
    enabled: true
    timeout_seconds: 30

Automatic tools whose executable is absent are skipped. A tool explicitly enabled with enabled: true

must exist on PATH

, otherwise Dora reports an error. Discovery currently checks executable presence only; it does not launch the shell to probe its runtime environment.

The Bash tool runs bash -lc

in Dora's current directory. The model can use cd

inside a command when it needs another directory. The tool returns exit code, stdout, stderr, timeout, and truncation information to the model as JSON. Output is limited to 1 MiB per stream. This tool grants the model the same filesystem and process permissions as the dora

process, so disable it unless you trust the environment in which Dora runs.

The independent powershell

tool prefers PowerShell Core (pwsh

) and falls back to Windows PowerShell (powershell.exe

). If both tools are explicitly enabled, they are exposed separately so their command syntaxes remain distinct.

PowerShell also starts in Dora's current directory and can use Set-Location

inside a command when needed.

Both command tools accept an optional per-command timeout. It overrides the configured default for that call and cannot exceed 3600 seconds:

{
  "command": "go build ./...",
  "timeout_seconds": 300
}

When omitted, timeout_seconds

comes from the corresponding YAML tool setting, or defaults to 30 seconds when that setting is zero or absent.

── more in #artificial-intelligence 4 stories · sorted by recency
── more on @dora 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/show-hn-dora-a-tiny-…] indexed:0 read:11min 2026-08-11 ·