Autonomous AI agent runtime daemon. agentd runs agent sessions in ephemeral Podman containers on infrastructure you control. Each session gets an isolated execution environment — its own identity, credentials, a fresh repository clone, and read-only methodology context — supervised from setup through teardown. agentd prepares and supervises these environments; model inference and MCP transport belong to the agent runtime inside the container.
Running autonomous agents requires infrastructure: isolated environments, credential injection, workspace setup, identity management. Operators building this ad-hoc re-solve the same problems for each agent and each deployment.
agentd is the self-hosted runtime layer. The operator declares what through profile configuration — which image, which credentials, which methodology. agentd owns how — container lifecycle, privilege management, resource cleanup. The agent gets an isolated, ephemeral workspace with exactly what it needs and nothing more.
v0.1.0 — early development.
The session lifecycle works end-to-end: profile configuration, foreground daemon
startup, operator-triggered sessions, ephemeral Podman containers, credential
injection, execution, and teardown. Startup reconciliation cleans stale
resources from prior runs. Structured JSON tracing provides operational
visibility.
Profiles may now declare a default repository and an optional cron schedule.
Manual runs still flow through agentd run, and scheduled runs dispatch
through the same daemon socket intake without introducing a separate job type.
Profiles may also declare additional bind mounts for host-managed state such as
subscription auth directories or persistent audit storage.
A profile is a named environment specification: base image, methodology
directory, optional additional bind mounts, optional default repo, optional
cron schedule, credentials, and runtime command. Define profiles in a TOML
config file — start from
examples/agentd.toml:
# Static profile registry for agentd.
# A profile can carry its own default repo and optional schedule.
[[profiles]]
# Stable operator-facing profile name used for lookup and container identity.
name = "site-builder"
# Prebuilt image containing the agent runtime and runa.
base_image = "ghcr.io/example/site-builder:latest"
# Methodology directory to mount read-only into the session environment.
methodology_dir = "../groundwork"
# Default repository URL cloned for manual runs when `agentd run` omits a repo,
# and for every scheduled run of this profile.
repo = "https://github.com/pentaxis93/agentd.git"
# Optional five-field cron expression in daemon-local time.
schedule = "*/15 * * * *"
# Static session command executed from the cloned repository. This profile is
# tightly bound to one app, so the session repo is the project being built.
command = [
"/bin/sh",
"-lc",
'''
runa init --methodology /agentd/methodology/manifest.toml
cat > .runa/config.toml <<'EOF'
[agent]
command = ["site-builder", "exec"]
EOF
if [ -n "${AGENTD_WORK_UNIT:-}" ]; then
exec runa run --work-unit "${AGENTD_WORK_UNIT}"
fi
exec runa run
''',
]
# Optional environment variable name resolved by the daemon for clone-only
# repository authentication. This value does not flow into the agent runtime.
repo_token_source = "SITE_BUILDER_REPO_TOKEN"
#[[profiles.mounts]]
# Additional host bind mounts are declared explicitly per profile.
# `source` must be an absolute host path and must already exist.
# `target` must be an absolute path inside the container and must not
# duplicate or overlap another mount target in the same profile.
# `read_only = true` is appropriate for host-managed auth directories.
#source = "/home/core/.claude"
#target = "/home/site-builder/.claude"
#read_only = true
[[profiles.credentials]]
# Secret name exposed inside the session environment.
name = "GITHUB_TOKEN"
# Environment variable name read from the daemon's own process environment.
source = "AGENTD_GITHUB_TOKEN"
[[profiles]]
# A home-repo review agent that carries its own review configuration and scans
# repositories beyond the repo used to launch the session.
name = "code-reviewer"
base_image = "ghcr.io/example/code-reviewer:latest"
methodology_dir = "../groundwork"
repo = "https://github.com/pentaxis93/agentd.git"
command = [
"/bin/sh",
"-lc",
'''
runa init --methodology /agentd/methodology/manifest.toml
cat > .runa/config.toml <<'EOF'
[agent]
command = ["code-reviewer", "exec"]
EOF
if [ -n "${AGENTD_WORK_UNIT:-}" ]; then
exec runa run --work-unit "${AGENTD_WORK_UNIT}"
fi
exec runa run
''',
]
repo_token_source = "CODE_REVIEWER_REPO_TOKEN"
[[profiles.credentials]]
name = "GITHUB_TOKEN"
source = "AGENTD_GITHUB_TOKEN"Credential source fields name environment variables in the daemon's process
environment — export them before starting the daemon. Additional mounts
entries are bind mounts: source must be an absolute existing host path,
target must be an absolute container path, targets must be unique within the
profile, and runner-managed targets are reserved: /agentd/methodology,
/home/{profile}, and /home/{profile}/repo plus its descendants. Other
targets under /home/{profile} remain supported, including read-only auth
mounts such as /home/site-builder/.claude. Additional mounts are not
relabelled; on SELinux-enabled hosts, operators must ensure each host path
already has a container-compatible label. The base image must provide
/bin/sh, find, git, useradd, gosu, and whatever binaries the
configured session command uses. When a profile declares schedule, it must
also declare repo. Schedules are evaluated in daemon-local time and missed
fires are not backfilled after downtime.
Build from source with cargo build --release. Requires rootless Podman for
container execution.
Start the daemon:
agentd daemon --config /etc/agentd/agentd.tomlagentd with no subcommand is equivalent to agentd daemon.
The daemon runs in the foreground, reconciles stale resources from prior runs,
and binds a Unix socket for operator control. Default paths:
/run/agentd/agentd.sock and /run/agentd/agentd.pid. On SIGINT or SIGTERM,
the daemon stops accepting connections and drains in-flight sessions; a second
signal exits immediately.
The Unix socket protocol is internal to agentd in v0.1.x: daemon and CLI must be the same build, and operators must restart the daemon after replacing the binary before using agentd run again.
Trigger a session through the running daemon:
agentd run site-builder --work-unit issue-42agentd run reads the same config file and connects to the socket path defined
there. When the profile declares repo, the CLI can omit the positional repo
argument; an explicit repo still overrides the configured default:
agentd run site-builder https://github.com/pentaxis93/agentd.git --work-unit issue-42Both manual and scheduled dispatches use the same daemon socket intake. Inside the container, the agent sees:
- An unprivileged user with
$HOMEat/home/site-builder - A fresh clone of the repository at
/home/site-builder/repo - Read-only methodology mount at
/agentd/methodology - Any operator-declared additional bind mounts, read-only or read-write per profile
- Credentials injected as environment variables
AGENTD_WORK_UNITwhen the invocation includes one- The configured session command executing from the repo directory
The container is force-removed on completion. No session state persists on the host.
Profiles with schedule run autonomously while the daemon is up. The scheduler
evaluates cron expressions in daemon-local time and opens the same Unix-socket
client path that agentd run uses. Multiple scheduled profiles may overlap,
and their sessions dispatch independently. Session outcomes do not affect later
schedule evaluation: the next occurrence runs at its next scheduled time.
- ARCHITECTURE.md — session lifecycle phases, container isolation model, credential flow, and workspace crate boundaries. How the system is built and why.
- AGENTS.md — development discipline, BDD workflow, commit and branch conventions. Read this before contributing.
- examples/agentd.toml — annotated profile configuration. Starting point for writing your own.