Your first agent
Goal: in 15 minutes you'll have an agent that prints "hello" every few minutes under the dotagent daemon, with logs you can tail and a desktop notification when it fails.
Prerequisites: dotagent --version works. If not, go back to installation.md.
The walkthrough has six steps. Each step ends with a "Verify" line — if that command doesn't behave as described, jump to Troubleshooting at the bottom.
Step 1 — Pick the language
Every example below uses Fish for brevity, but dotagent runs any executable. If you'd rather use Python, Go, or Rust, copy examples/hello-python/, examples/hello-go/, or examples/hello-rust/ instead. The flow is identical — only [run].command and the script body change.
# Verify your shell of choice exists.
which fish # or: which python3 / which go / which cargoStep 2 — Create the agent directory
dotagent discovers agents by scanning ~/.config/dotagent/agents/ (and a few other roots — see reference/paths.md). Each agent is a single directory.
mkdir -p ~/.config/dotagent/agents/hello
cd ~/.config/dotagent/agents/helloVerify:
ls -ld ~/.config/dotagent/agents/hello
# → drwxr-xr-x ... helloStep 3 — Write the script
This script just echoes the env vars dotagent injects. It exits 0 most of the time and exits 1 every 5th minute so we can see the failure notification later.
Make it executable:
Verify (running the script directly should work — env vars will be empty, that's fine):
Step 4 — Write the manifest
agent.toml is the contract. It declares identity, how to run the script, when to run it, what to do on failure.
That's the whole manifest. No SDK. No imports. No registration.
Verify — dotagent should now discover the agent and validate the manifest end-to-end:
The [security] warning is harmless for now — v0 is schema-only. Read security/threat-model.md when you're ready.
Step 5 — Smoke-test (foreground)
Before handing the agent to the daemon, run it once in the foreground. This is what dotagent run does — useful for development too.
The agent ran in the foreground, dotagent injected env vars, captured stdout, wrote the heartbeat, and exited.
Verify:
If exit_code = 1 (because you hit a minute divisible by 5), that's the demo failure firing — try again a minute later.
Step 6 — Hand it to the daemon
Now the real thing: the daemon will fire the agent every 2 minutes on its own, retry on failure, and ping you on the desktop when it gives up.
6a. Install the daemon unit
This writes one unit file. dotagent does NOT install one unit per agent — the daemon manages every schedule internally.
6b. Start the daemon
macOS (launchd):
Linux (systemd):
The daemon process is now alive. macOS launchd / Linux systemd will restart it if it crashes.
Verify (the daemon should be running and have written a PID file):
6c. Watch it work
Tail the agent's log — every 2 minutes a new "=== hello ===" header appears:
In another terminal, see the dashboard:
One row per (agent, schedule), and REASON says which piece of state decided the verdict — see reference/cli.md.
Wait for a "demo failure" minute (:00, :05, :10...) and you'll see:
A desktop banner ("hello — free space low" style) pop up
The dashboard shifts the agent into
failing, with the attempts burned so far inREASON(2 attempts, will retry)After the retry budget runs out it transitions to
given_up, andREASONswitches togave up after 3 attemptsThe audit log records every step
Where files live now
After Step 6, ~/.config/dotagent/ looks like:
Full reference: reference/paths.md.
What just happened (mental model)
The OS keeps the daemon alive. The daemon keeps everything else alive.
Clean up
If this was just a tutorial:
Troubleshooting
dotagent doctor says "no agents discovered"
Discovery scans ~/.config/dotagent/agents/*/agent.toml. Make sure the directory name is hello and the file is named exactly agent.toml (not hello.toml or manifest.toml).
dotagent run says "agent not found"
The agent name in agent.toml ([agent].name = "hello") must match the argument you pass to dotagent run. The directory name is advisory — what dotagent indexes is agent.name.
dotagent run works but the daemon never fires it
Check the daemon is alive:
If empty, the daemon isn't running. Re-do Step 6b.
If alive, tail the daemon log:
You should see a tick line every 30 minutes at most, plus a "dispatching run" line every 2 minutes. If you see tick but no "dispatching", the heartbeat says the previous window already succeeded — which is correct behavior. Wait 2 minutes.
Desktop notification never fires
If notify-send doesn't show anything, the desktop driver won't either — it's the same D-Bus pathway.
See guides/troubleshooting.md for the full sintoma → diagnostics map.
Next
You have:
A working agent
The daemon running
Logs you can tail
A notification that fires on failure
Where to next? next-steps.md maps the rest of the docs to your next questions.
Last updated
Was this helpful?