The binary
agentic-job is the interface. The reusable workflow is
a wrapper that moves files between jobs. Another CI system would run
the same commands. The workflow fetches a checksummed static release
binary when its source digest matches the pinned commit; otherwise it
builds that source. build-binary: true forces a build.
Commands
| Command | Runs as | Does |
|---|---|---|
policy --allow FILE --repo OWNER/NAME --clone-url URL --base BRANCH --kind branch|analysis --outputs LIST --max-outputs N | anyone | Checks one run's request against the caller's bounds and prints policy.json. --outputs takes all and --max-outputs takes max, for whatever the bounds allow. |
config [--host] [--from FILE] [--string TABLE.KEY=VALUE]... [--integer ...]... [--boolean ...]... [--list ...]... | anyone | Writes TOML from a file and individual settings. --host checks only the host configuration. |
sandbox setup --config FILE | root | Prepares the host the agent runs on, and copies the configuration to /etc/agentic-job/. |
sandbox check [--record|--unless-passed] | the runner's user | Probes the sandbox as the sandbox user, each probe with a positive control. --record (secure-host's) leaves a record when all probes pass, and --unless-passed (run's) probes nothing when it finds that record, against the same configuration, with nothing having entered the sandbox user since (sandbox-check.md). |
run --policy FILE --task FILE --meta FILE --out DIR | the runner's user | Runs the agent in the sandbox and takes what it hands back. |
collect --policy FILE --outputs DIR --collected FILE | anyone | Validates and sanitizes the handed-back requests as gh-aw's collector does, in check.yml and for a caller off GitHub (safe-outputs.md). Exit 1 if it refused a line. |
check --policy FILE --outputs DIR --collected FILE [--report FILE] | anyone | Checks handed-back outputs and patch against the policy. |
apply --forge forgejo --server URL --repo OWNER/NAME --policy FILE --checked DIR --run-url URL [--comment-target N] [--push-repo OWNER/NAME] | the apply job | Publishes checked comments, and a pull request from the bounds' fork, to Forgejo with FORGEJO_TOKEN (safe outputs). |
event --allow FILE --event-name NAME --event FILE --actor LOGIN --repository OWNER/NAME --out DIR | trusted policy job | Decides event admission. Optional files: --actor-permission, --pull-request, --task. See events.md. |
workflows check [--root DIR] [--exemptions FILE] | anyone | Refuses workflow files whose jobs neither run secure-host nor call the reusable workflow, and the other shapes in workflows-check.md. Exit 1 names each file, job and step. |
acp-drive start|prompt|events|cancel|status|stop | a run's agent | Starts the run's child agent and drives it over ACP (harness.md). |
session connect ENDPOINT [--json] | an operator or their agent | Joins a session run's conversation: each line of standard input is a prompt, and it prints what the agent says, or with --json each message from the bridge as one line (session runs). |
audit DIR [--json] | anyone | Inspects downloaded artifacts offline. Findings do not cause a refusal exit code. See workflow.md. |
Internal commands are hidden: launch-agent
(what run has the session start, as the sandbox user), sandbox probe-local (what sandbox check runs as the sandbox user), and
helper, the root-owned entry point for privileged operations after setup.
sandbox check and run are the supervisor. It runs as the runner's
user, whose general sudo access setup removes by default. A narrow sudo
rule permits the root-owned helper to enter the sandbox with run0, reap
its processes and read the proxy's fixed log. Without --runner-home,
everything it does in the agent's files (the clone, git, reading the
outcome and the requests) it does as the sandbox user, so that a link
the agent planted cannot make it read the runner's files. The reusable
workflow, by default (agent-user: runner), and review.yml pass
--runner-home: run uses no sandbox
user, the agent is the runner's user and can read and change the
transcript and the limits, read run's environment and stop run,
cleanup is the agent's process group only, and the token probes do not
run. What separates the agent there is the job's read-only tokens, no
id-token and no secret but the run token, secure-host's deprivileging
and the check on another machine
(requirements.md).
sandbox-check.md describes the helper, and the action's
lock its limits.
check is half the check on purpose. Which request types exist, how
many of each, and the sanitizing of their text are gh-aw's rules, which
collect ports and is held to by fixtures. --collected is what
collect made of the outputs, and is given by path so that it is never
taken from what the agent's machine uploaded. safe-outputs.md has both halves.
config exists because a workflow gets a run's settings as strings from
whoever called it. Each setting is the value of its one key and is never
read as TOML, so an input that holds a quote or a table header cannot
add a key. Empty settings leave the file unchanged. Use --host when
composing a configuration for secure-host without an agent.
Files read
--allow, the caller's bounds: the hosts a clone URL may name, globs
for repositories and base branches, a ceiling per output type and in
all, the patch's size and file limits.
This repository's is an example.
--config, TOML with these tables. A key that is not known is
refused. run reads only the copy sandbox setup left in
/etc/agentic-job/, which the sandbox user cannot change and which
holds no secret.
| Table | Holds |
|---|---|
[sandbox] | the sandbox user and its groups, directories to close, services to stop; [sandbox.check] has what the probes need and what they accept, before-agent, fetch-repo and containers (sandbox-check.md) |
[egress] | proxy (whether the egress proxy runs), direct (addresses reached without it), policy, rust-proxy (the binary's own proxy in place of mitmproxy; on by default, false needs python3-venv in [setup] packages) |
[setup] | packages, npm, agents (pinned in the binary), and a script for toolchains; optional tools for a caller-pinned installer (toolchains); repo-packages only in a file given to agentic-job config, which adds the --repo entry to packages |
[inference] | url; register, one of github-oidc, plain and token-file, with no default; audience; token-file; identity-proxy; anthropic-url and openai-url |
[agent] | name, model, and config-repo, config-ref, config-path for a public repository holding its configuration; skills to install the built-in skills |
[child-agent] | name (only vibe) and model of a second agent the agent may drive; needs limits.max-requests and a sandbox user (run --runner-home refuses it) |
[limits] | timeout-minutes, max-requests, max-tasks, budget, uncapped; max-repeats (repeated tool calls, off by default) |
[mcp.NAME] | url, secret (only MCP_TOKEN) and allowed of one remote MCP server run serves the agent with a credential it never holds |
[session] | listen, operators, origins, capability (a Tailscale app capability that lets tagged nodes in) and idle-minutes for a conversation; watch-item and watch-seconds to send the agent updates of the run's item (off by default) |
[commit] | author and trailers of the hand-back's commit; a bounds file's commit_author (safe outputs) takes the place of author |
[limits] without a timeout is refused, and so is one that caps neither
model requests nor spending unless it says uncapped = true. A cap on
model requests that nothing counts is refused too
(harness.md). hosted.toml
is a configuration for GitHub's ubuntu-26.04.
--meta, JSON about the run from the CI system, copied into
summary.json. Its run_id names the patch, so a run has to be given
one.
Files written
policy prints policy.json: the request as allowed, the clone URL for
run, and the configuration collect and gh-aw's handlers run with.
run writes under --out, in the names and schemas of the tree this
replaces so that its readers keep working: run/ (summary.json,
summary.md, condensed.log, outcome.json), transcript.tar.zst,
and safe-outputs/ if something was handed back. They exist only for a
run that passed the gate on uploads.
workflow.md describes the artifacts.
check --report writes the verdict as JSON, with the reasons for a
refusal and the files the patch names.
Exit states
Callers branch on these, so they are part of the interface.
| State | run | The other commands |
|---|---|---|
| 0 | the agent ended its turn | accepted; every probe passed |
| 1 | the agent failed or cancelled, or a signal stopped run | refused; a probe failed |
| 2 | bad arguments, an internal error, results that did not pass the gate, or a run that could not be cleaned up after | bad arguments or an internal error |
| 3 | a request, task or spending limit stopped the agent, or it handed back after a turn that ended on an error | |
| 4 | the run never started: something before the agent failed (the check of where the binary is, the agent's configuration, the clone, the registration at the proxy, the probe of the run token); trying again is safe | |
| 124 | the timeout stopped the agent |
For 3 and 124, summary.json says whether the agent handed back.
A session that ends in an internal error still exits 2, but its results
are taken as a failure that hands nothing back, so a run that passes the
gate leaves a summary saying why.
What the binary knows of GitHub
Little, and on purpose: the clone URL comes in through the policy, and
the check that the target is public needs the forge's API, so the
workflow does it. The binary reads the ACTIONS_ID_TOKEN_REQUEST_*
variables to register with github-oidc, and RUNNER_TEMP,
RUNNER_WORKSPACE and GITHUB_WORKSPACE in sandbox check, to probe
the job's work directories. It starts nothing with the job's ACTIONS_*
variables in its environment, and keeps whatever the agent chose from
being read as a command by GitHub's runner on its way to the job log.
No wrapper for another CI system exists. Event admission reads GitHub payloads but leaves API calls to the trusted workflow.