Observed since 21 Oct, 09:15 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
Run AgentMatrix work on your own machine, under your own coding-agent subscription.
Run AgentMatrix work on your own machine, under your own coding-agent subscription.
AgentMatrix is a coordination service for coding agents: it plans work, holds
the review gates, and decides what lands. agmx is the other half — a small
client that picks up the work the server hands out and executes it locally,
inside a throwaway git worktree of a repository you already have checked out.
The server dispatches a project_id; your machine decides which directory
that is. When the runner enrolls it reports, per project, the checkout's path and
its remote URL — with any username, password or token stripped — so the server
can confirm the checkout belongs to that project. A project the server rejects
is printed and dropped for the session; if every project is rejected,
agmx runner start exits instead of polling for work it can never take.
The control plane needs Postgres, Redis and a pile of orchestration code. A
runner does not. agmx depends on httpx and nothing else, so installing it
never asks you for a database URL or a shared secret.
$ pip list
agmx anyio certifi h11 httpcore httpx idna typing_extensions
pipx install agmx
Requires Python 3.10+ and git on PATH.
# 1. Authenticate. Opens your browser at your team's sign-in page.
agmx login
# 2. Tell the runner where your checkouts live.
agmx runner add agenticmatix=~/code/AgentMatrix
# 3. Let it work -- in this terminal, or as a background service.
agmx runner start
agmx runner install
agmx runner add appends to the saved list, so registering a repo months later
does not mean retyping the ones already there. agmx runner rm <id> takes one
out, agmx runner list shows what this machine serves, and both reload a
running service so the change takes effect immediately. The list lives in
~/.agmx/settings.json and survives logging in again.
agmx login asks the server where your team signs in (its OAuth metadata) and
logs you in there with your browser: a loopback redirect to 127.0.0.1, PKCE
and a state check. On a machine you reach over SSH, agmx login --device
shows a code to enter on any device with a browser. agmx login --token takes a
personal access token instead (create one with manage_user create_token from a
logged-in coordinator CLI); it is read from the terminal, never from the command
line. The session refreshes itself; refresh tokens rotate, so each refresh is
saved at once and taken under a file lock, which lets two agmx processes share
one login. A server without single sign-on still gets --email and a password.
If the server refuses to enroll you (not_a_member, email_not_verified,
user_disabled, subject_mismatch, invite_expired), the runner prints the
server's answer and exits: those are for your team's admin, and retrying does
not change them.
A project_id you have not registered is reported back as failed rather than
guessed at, so a run can never be executed against the wrong tree. runner start --project ID=PATH still works and replaces the saved list with what you
passed, so the terminal and the service never disagree about which projects this
machine serves.
Point at a different control plane with --server, or AGMX_SERVER.
The first runner start enrolls the machine with your login and receives the
machine's own runner token, stored next to your login token. From then on
every call the runner makes — including enrolling again when it restarts — uses
that token, not your login session. If the server stops accepting it (the
runner was revoked, or you logged in elsewhere and re-enrolled), the runner
exits with one line telling you to agmx login and start again.
By default the server gives this machine only your own runs. --serve-team
(remembered; --no-serve-team to stop) lets it take your teammates' runs too —
they then run on the CLI subscriptions logged in on this machine.
On start, the runner removes worktrees that a runner which died mid-run left
behind in your registered checkouts — only its own (agmx-*/worktree in its
data directory, or in the temp directory where older versions put them, whose
owning process is gone), never yours or those of a runner that is still running.
Every command the server sends carries that CLI's bypass flag: the agent can do anything your OS user can. There is no sandbox in between.
This is the same deal as a self-hosted GitHub Actions runner, and the same warning applies: the boundary is which server you plug this machine into, not a confinement layer. Enroll only against your own team's control plane, and only on a machine whose repositories and credentials you are willing to let a coding agent touch.
A sandboxed mode was built on 2026-09-16 and removed the same day. Under each CLI's own sandbox the agent could edit files but could not run tests, install packages or reach the network — runs failed halfway instead of being safer.
The run's own secret (its MCP token) still lives in ~/.agmx/run-secrets with
0600, and the runner never edits your CLI settings files.
The runner leases one run: {run_id, task_id, project_id, command, timeout_seconds, kind, base_ref, needs}. No path, no repository URL.
It resolves project_id against the checkouts you registered.
The commits the run needs must be here. Every SHA in needs (and
base_ref) is checked; any that is missing is fetched first from the
agmx-run/* branches on your remote — that is where another machine's run
left it — and, if still missing, directly by its SHA from the remote, so a
commit pushed on any branch name (work attached by hand, for instance) is
found too. Still missing means the run is reported as failed, naming the
SHA and saying it must be pushed. It is never run against some other commit
instead.
It creates a git worktree at base_ref when the server gave one (a fix
round starts from the previous attempt; a review sits at the head it
reviews), otherwise at the freshly fetched tip of the shared branch on the
remote — never your local checkout, even when it is on a detached HEAD, so
every machine starts from the same base. The shared branch is your current
branch's upstream, or the remote's default branch when your checkout is
detached — or the branch the server names for the project, when it names
one. A repository with no remote uses local HEAD. .agmx/ is created
inside it for the files an agent hands back to the server.
The command runs there through sh -c, with AGMX_WORKTREE set to the
worktree's absolute path — some agent CLIs start their shell tool somewhere
else and would otherwise go looking for the repository across your disk.
The run's MCP server goes into ~/.agmx/run-secrets/<run_id>/ (0700,
files 0600) — inside ~/.agmx (0700), so one run cannot read the token of
another run on the same machine — in the form each CLI reads:
AGMX_MCP_CONFIG (the lease's config verbatim, for --mcp-config of claude
and qwen) and AGMX_AGY_DIR (agy, --add-dir: it holds an agmx plugin).
The run's token is exported as AGMX_MCP_TOKEN, and the command gets its own private
TMPDIR. All of it is deleted when the run ends — on timeout, cancellation
or interruption too — and the token is masked in any output streamed back.
An older server that sends no MCP still gets these, with no servers in them.
The command does not inherit the runner's environment. It gets an
allowlist: PATH, HOME, USER, LOGNAME, SHELL, LANG, LC_*, TERM,
TMPDIR, TZ, XDG_*, the proxy variables (http(s)_proxy, no_proxy, in
both cases), CA bundles (NODE_EXTRA_CA_CERTS, SSL_CERT_FILE,
SSL_CERT_DIR), and the agent CLIs' own configuration: ANTHROPIC_*,
CLAUDE_* and CLOUD_ML_REGION (claude); CODEX_* and OPENAI_API_KEY
(codex); AGY_*, ANTIGRAVITY_*, GEMINI_API_KEY, GOOGLE_API_KEY,
GOOGLE_GEMINI_BASE_URL, GOOGLE_GENAI_USE_ENTERPRISE (agy); QWEN_*,
OPENAI_BASE_URL, GOOGLE_CLOUD_PROJECT, GOOGLE_GENAI_USE_VERTEXAI (qwen).
Anything else — cloud credentials for Bedrock or Vertex, say — is passed only
when you ask: --pass-env NAME (repeatable), or "pass_env": [...] in
~/.agmx/settings.json. The runner's own git commands keep the full
environment, since pushing needs your SSH agent. If the repository has no git
identity configured, the command also gets GIT_AUTHOR_*/GIT_COMMITTER_*
as agmx runner <agmx@localhost> in its environment, so an agent never
writes git config into your clone. Output is streamed back in batches while
it runs, and every batch is delivered before the result is reported. The
runner keeps sending heartbeats throughout, so a long run keeps its lease.
What happens on exit depends on kind:
execute is the only kind that commits. The agent may commit by
itself; whatever it leaves uncommitted is committed after it on
agmx-run/<run_id> — never anything under .agmx/, which your repository
does not ignore. The run's head is the worktree's HEAD; one that does
not descend from the base (the agent checked out something unrelated)
fails the run instead of being reported as the work. The branch is
pushed to your remote, because the
reviewer is usually a different machine. A push that fails fails the run,
saying the commit exists only on this machine; the local branch is kept.
A pre-commit hook that rejects the change fails the run too, but the work
is still committed to the branch so someone can see what was rejected.
If the commit itself fails (a signing key that cannot be used, say), the
worktree is kept on disk and its path is in the error, so the agent's
uncommitted work can be recovered.review, and any kind added later) is read-only: no
branch, no commit, no push. A review run's
.agmx/review-<task_id>.json is read before the worktree goes and sent with
the result — as an object when it is a JSON object, verbatim otherwise.The worktree is removed and the runner reports facts: exit code, base and
head SHA, whether it timed out. It does not decide whether the run
succeeded; that judgement stays on the server, with the review history.
The exit code is the agent's own: 0 when the agent finished cleanly and a
step of the runner's failed afterwards (commit, push), and absent when the
agent never started (a missing commit, a worktree that could not be
created) — so the server does not blame the agent for this machine's git.
A report the server did not record (a 5xx, a dropped connection) is resent a
bounded number of times; a refusal (4xx) is final.
A run that exceeds timeout_seconds has its whole process group terminated,
not just the process the runner spawned — coding agents spawn children. The same
happens at once when the server cancels a run (the heartbeat lists it, or the
server answers run_cancelled/not_your_run): nothing is committed or pushed,
the worktree is cleaned up, and the runner reports failed once.
The control plane keeps no checkout of your code, so it cannot merge and it does not pretend to. Once a result passes review, the server offers the merge to a runner that has registered that project, and the merge happens here:
agmx-run/* branch the executing machine pushed.--no-ff. Your own checkout is never checked out, merged into, or
required to be clean: a local branch that is behind, a detached HEAD, or
uncommitted edits make no difference.HEAD to the shared branch, then remove the temporary worktree. A
merge that is not pushed leaves the work on exactly one laptop, and the merge
commit reported back to the server would name something nobody else can see.agmx-run/* branches, locally and on the remote, that the landed
commit now contains — only those, and a failure to delete never fails the
landing.done.A repository with no remote is a single-machine project: the merge goes into the checked-out branch, which then has to be on a branch and clean.
Landing refuses rather than improvising, and says which kind of failure it
was, because that decides who waits: local — this machine's own state (not
a repository, the commit not found even after fetching, and without a remote a
detached HEAD or uncommitted tracked changes); another machine of the team may
still land it.
transient — the push timed out, or the remote moved ahead
(non-fast-forward, fetch first); retried shortly. conflict — a merge
conflict, or a push refused for any other reason (a protected branch, no
permission); only a person can fix those. Each failure is reported with the
reason, and your repository is left as it was — the temporary worktree goes
with the failed merge or the unpushed one, and the next attempt starts again
from the remote's new tip. Your uncommitted work is never touched.
The practical consequence: if nobody is running agmx runner start, no task
reaches done. That is the design, not an outage.
The runner updates itself automatically when the server declares a newer target
version. At the start of each poll iteration, before leasing work, it checks
runner_update.target from the heartbeat response and:
agmx was installed — pipx, uv tool, or venv. This happens
first, whether or not the machine is idle: a machine that cannot be detected
has to know that about itself before it decides to stop taking work.agmx==<target>.agmx --version prints the expected version.The server decides the target, not PyPI. Rolling back a broken release is an
admin lowering runner_target_version, and every machine follows. The runner
never runs pipx upgrade without a pinned version.
Draining: a machine holding runs stops leasing new work until those runs
finish, then upgrades. The heartbeat still carries every held run id — no lease
is lost while draining. A machine whose install method cannot be detected (say,
pip install --user with no pipx or uv) does not drain: draining to reach an
upgrade it can never perform would cost it all its capacity and gain nothing, so
it keeps working and prints the manual upgrade command once.
Opt-outs (they delay, they do not override runner_min_version):
AGMX_DISABLE_AUTO_UPDATE=1 in the environment."auto_update": false in ~/.agmx/settings.json.runner_auto_update="never".A machine below runner_min_version is refused new work at /runner/lease, but
may still land merges. An install that fails (non-zero exit, or --version not
matching) leaves the current version running and retries after 15 minutes.
| Path | Mode | Contents |
|---|---|---|
~/.agmx/.credentials.json | 0600 | login session (access and refresh token) or personal access token, runner token |
~/.agmx/settings.json | 0644 | server, email, issuer, runner_id, pass_env, serve_team, data_dir |
~/.agmx/run-secrets/ | 0700 | one directory per running run: its MCP config and agy plugin, removed when it ends |
~/.local/share/agmx/runs/ | 0700 | one directory per run: its worktree, secrets, TMPDIR |
Secrets are kept in a separate file from settings, and the credentials file is
created with its mode already set rather than being widened and then narrowed.
Set AGMX_CONFIG_DIR to move the directory. Run directories live apart from
it ($XDG_DATA_HOME/agmx, or AGMX_DATA_DIR): a worktree does not belong next
to your tokens, and one in /tmp would be writable by every other run.
agmx runner data-dir /data/agmx-data # save, create <path>/runs, copy cli-models.json, reload the service
agmx runner data-dir /data/agmx-data --when-idle # wait until no run is in flight, then reload
agmx runner data-dir # show the current dir, where it came from, its mount point
agmx runner install --data-dir /data/agmx-data # same, as part of installing the service
The path must be absolute. It is saved as data_dir in ~/.agmx/settings.json
and read BEFORE AGMX_DATA_DIR (which stays as a fallback), so no systemd
drop-in is needed and a later runner install keeps it. Without --when-idle
the reload kills runs in flight (the server reclaims and re-issues them); the
command prints how many. Landing is unaffected. The old directory is remembered
in previous_data_dirs: its abandoned worktrees are swept by the normal
cleanup, and kept work (agmx-keep, .kept-*) is never deleted. agmx runner list shows the data dir, its source, mount point and free space.
This needs a runner release that reads data_dir from settings.json: on an
older runner the setting is ignored.
--password flag: it would survive in shell history and in ps output.GIT_DIR and GIT_WORK_TREE are stripped from the environment handed to the
command, and from the runner's own git calls, so neither an agent nor a merge
can reach outside the repository it was pointed at. Those variables beat
git -C, always.agmx login sends a password in the request
body; plain HTTP would hand it to anyone on the path.git clone https://github.com/nothan-agentic/AgentMatrix
cd AgentMatrix/runner
pip install -e .
python -m pytest tests -q
MIT — see LICENSE.
Download counts and popularity will appear here as the collector receives them.
| Version | Published | Direct dependencies | Open version |
|---|---|---|---|
| 0.5.11 | 5 Oct 2026 | 1 |
This describes our collected data; it is not a security assessment.
Observed since 21 Oct, 09:15 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package:56a8992fd57ff390e61ad90920f7a1ef7f8b71c660466b845520e04c01100aaf