donkeyspace is a self-hosted harness for agentic repository work.
It coordinates issue triage, clarification, agent implementation, automated checks, reviewer-agent feedback, and human approval around the tools teams already use. The first target is a GitHub-first workflow where labels and comments remain the visible collaboration surface, while donkeyspace manages policy, orchestration, containerized agent execution, and audit history.
- GitHub-first: Issues, labels, comments, pull requests, and CI.
- Self-hosted: Docker Compose deployment for small engineering teams.
- Agent-runtime agnostic: orchestrates external agent CLIs instead of building a new coding agent.
- Human-gated by default: agents can prepare, implement, and review, but humans merge pull requests.
- Policy driven: repository config defines allowed automation, required checks, risk gates, and escalation behavior.
- Architecture, scope, and known limitations
- Default-lifecycle agent contract
- Default GitHub workflow
- Policy guide
- Plugin interface reference
- Installation and authentication
- Default-lifecycle example policy
- Lifecycle-plugin example policy
- Run result JSON Schema
- Policy config lives at
.donkeyspace/policy.yml. - GitHub labels drive visible workflow state.
- GitHub comments carry human clarification and agent summaries.
- Repository policy selects either the built-in lifecycle or an opt-in plugin that replaces it with its own roles and task graph.
- Agent work runs in ephemeral workspaces inside the worker container.
- Tool-using agent work runs through external CLIs in a prepared workspace; bounded prompt context is only used for OpenAI-compatible triage.
- PostgreSQL stores run history, decisions, locks, and audit records.
- Rust powers the backend and worker; TypeScript React powers the dashboard.
Run the Rust checks:
cargo check --workspace
cargo test --workspaceRun the dashboard build:
cd web
npm install
npm run buildInitialize and inspect a local deployment:
cargo run -p donkeyspace-cli -- init --source-tree .
cargo run -p donkeyspace-cli -- connect github --repositories OWNER/REPOSITORY
cargo run -p donkeyspace-cli -- connect codex
cargo run -p donkeyspace-cli -- doctor
cargo run -p donkeyspace-cli -- upFor an interactive setup and operations console, run the binary without a subcommand:
cargo run -p donkeyspace-cliThe TUI guides initialization, GitHub repository selection, Codex login, diagnostics, and stack startup. Explicit subcommands remain available for scripts and non-interactive environments.
The GitHub connection command creates a private deployment-owned GitHub App
through GitHub's manifest flow by default. App and installation identifiers are
stored in the versioned instance configuration; the private key and webhook
secret are separate mode 0600 files mounted read-only as Compose secrets.
Setup discovers the installation for the selected repository owner. Installation
tokens are short-lived and refreshed immediately before authenticated Git
operations when necessary.
The CLI also supports importing an existing App:
donkeyspace connect github \
--app-id 123 --installation-id 456 \
--private-key-file /secure/app.pem \
--repositories OWNER/REPOSITORYFine-grained PAT authentication remains available only as a deprecated migration/emergency path:
donkeyspace connect github --pat --repositories OWNER/REPOSITORYPATs remain user-linked. Do not configure App fields and
DONKEYSPACE_GITHUB_TOKEN together.
When the token is configured, the worker ensures every configured workflow, allow, and block label exists in GitHub repositories already seen by donkeyspace webhooks.
Deployments that cannot expose a public webhook URL can poll GitHub's repository
event feed instead. Configure one or more repositories in .env:
DONKEYSPACE_GITHUB_AUTH_MODE=app
DONKEYSPACE_GITHUB_APP_ID=...
DONKEYSPACE_GITHUB_INSTALLATION_ID=...
DONKEYSPACE_GITHUB_PRIVATE_KEY_FILE=/run/secrets/github_private_key
DONKEYSPACE_GITHUB_POLL_REPOSITORIES=example/rtl-project,example/another-project
DONKEYSPACE_GITHUB_POLL_INTERVAL_SECONDS=60
DONKEYSPACE_GITHUB_POLL_MAX_PAGES=2The API converts IssuesEvent, IssueCommentEvent, PullRequestEvent, and
PushEvent records into the same internal ingestion format used by webhooks.
GitHub event IDs become delivery IDs, so overlapping polling windows are
idempotent. Webhooks and polling may be enabled together.
On first startup, the poller ingests the recent events returned by the configured page window; normal policy labels still determine whether those events queue work.
Polling is opt-in and requires both DONKEYSPACE_DATABASE_URL and configured
GitHub authentication. The event feed is finite, so the interval and page
count must be sized for the configured repositories' event volume; webhooks
remain preferable for high-volume or low-latency deployments.
Triage defaults to DONKEYSPACE_TRIAGE_PROVIDER=agent in Docker Compose. Set DONKEYSPACE_TRIAGE_PROVIDER=auto to use an OpenAI-compatible chat endpoint when DONKEYSPACE_LLM_API_KEY or OPENROUTER_API_KEY is present. The default test configuration for that path is:
DONKEYSPACE_LLM_BASE_URL=https://openrouter.ai/api/v1
DONKEYSPACE_LLM_MODEL=openrouter/free
OPENROUTER_API_KEY=...If the OpenAI-compatible triage path has no usable key, hits provider quota, or fails before producing a valid result, donkeyspace does not use deterministic fallback triage. It marks the triage job blocked and comments that LLM triage token usage was exceeded.
Set DONKEYSPACE_TRIAGE_PROVIDER=agent to run the configured agents.triage.command from .donkeyspace/policy.yml inside the prepared workspace. In this mode the worker writes .donkeyspace/run-input.json, expects .donkeyspace/run-result.json, and records the command exit code plus captured stdout/stderr in command results.
The default agent triage command is donkeyspace-codex-triage, a small wrapper around Codex CLI. The wrapper uses schemas/run-result.codex.schema.json for Codex structured output, then donkeyspace validates the result against the stricter Rust orchestration rules. It disables Codex's inner bubblewrap sandbox because the worker already runs inside Docker and common Docker hosts do not allow the user namespaces bubblewrap needs. The setup command delegates authentication to Codex CLI and supports ChatGPT browser login or an API key piped through stdin:
donkeyspace connect codex --method chatgpt
donkeyspace connect codex --method api-keyThe installer never stores the API key in instance configuration. See the official Codex authentication documentation.
When triage returns ready, the worker queues a developer job if agents.developer.enabled is true. The default developer command is donkeyspace-codex-developer, which runs Codex CLI against the cloned checkout. If it returns implemented, donkeyspace commits the changed files, pushes a branch named donkeyspace/issue-{number}-{job-id}, opens a GitHub PR with a Conventional Commit title, and moves the issue to ai:pr-open.
Before pushing a developer branch, the worker runs every command in checks.required_commands from the policy file inside the repository checkout. Each command result is recorded in command_results and exposed through /api/runs/{id}. If any required command fails or cannot start, donkeyspace marks the developer job failed, moves the issue to ai:blocked, and writes the failed command summary to the issue through the GitHub action outbox.
The default local policy runs git diff --check, cargo test --workspace, and the dashboard build before pushing a developer branch. Repo-specific commands must be available inside the worker image or wrapped by a custom worker image.
Policy lives in .donkeyspace/policy.yml. It gates automation with allow/block labels, defines agent commands, runs required local checks, and routes high-risk, unknown-risk, or sensitive-path work to humans. See the policy guide for the supported fields and current limitations.
When GitHub sends a pull_request webhook for a donkeyspace-managed PR, the API links it back to the source issue and queues a reviewer job. The default reviewer command is donkeyspace-codex-reviewer. Reviewer jobs fetch the PR head into the ephemeral checkout, receive PR metadata plus changed-file and diff context, and post their result as a PR conversation comment. In the default lifecycle, reviewer findings do not automatically start another developer job; lifecycle plugins can define bounded feedback edges between their own tasks.
When GitHub sends a push webhook for a repository's default branch, donkeyspace queues repair checks for open donkeyspace-managed PRs targeting that branch. The repair worker locally attempts to merge the updated base branch into the PR branch. If the merge is clean, it records that no repair was needed. If Git reports conflicts, the default repair command donkeyspace-codex-repair resolves the merge conflict in the checkout, then donkeyspace commits and pushes the repair to the existing PR branch.
The worker also reconciles open donkeyspace-managed PRs on every poll. If a PR has no queued, leased, running, or completed repair check for its current recorded head/base pair, the worker queues one. DONKEYSPACE_REPAIR_RECONCILE_LIMIT controls the per-poll batch size and defaults to 1.
The worker also reconciles ready issues on every poll. If a workflow item is already ready and has no queued, leased, or running developer job, the worker queues one from the most recent completed triage input. This covers worker restarts and old ready issues without requiring a new GitHub comment. DONKEYSPACE_READY_RECONCILE_LIMIT controls the per-poll batch size and defaults to 1.
Closed GitHub issues are not eligible for agent work. The API records GitHub's issue state from webhooks and will not queue triage for closed issues. The worker also skips any already-queued closed-issue job before running an agent, and developer jobs verify the current GitHub issue state when DONKEYSPACE_GITHUB_TOKEN is available.
The worker clones the target repository into an ephemeral workspace before each agent run. The OpenAI-compatible triage path receives bounded excerpts from that checkout. The external-agent path runs the configured CLI in the prepared workspace so the agent can use its own file search, file read, and edit tools, then report through .donkeyspace/run-result.json. Tune prompt context with:
DONKEYSPACE_WORKSPACE_ROOT=/tmp/donkeyspace/workspaces
DONKEYSPACE_REPO_CONTEXT_MAX_BYTES=20000
DONKEYSPACE_REPO_CONTEXT_MAX_FILE_BYTES=4000
DONKEYSPACE_REPO_CONTEXT_MAX_FILES=12Useful local endpoints:
GET /healthz- Dashboard:
http://localhost:5173by default;donkeyspace statusprints the configured URL GET /api/runsGET /api/outbound-actionsGET /api/runs/{id}GET /api/runs/{id}/transitionsPOST /api/runs/{id}/leasePOST /api/runs/{id}/retryPOST /webhooks/github
The current workflow supports OpenAI-compatible and external-command triage plus Codex-backed developer, reviewer, and repair paths. A signed issues.opened webhook creates a triage run, the worker leases it, records the result, updates workflow state, and applies GitHub label/comment actions. Ready issues can now proceed to an agent-authored PR and Donkeyspace-managed PRs can be reviewed or repaired when needed.