agentyard is a local developer tool. It reads your git worktrees, the GitHub CLI's view of your pull requests, and your Claude Code and Codex transcripts, and serves them over HTTP.
- The page has no login. Anyone who can reach the listening address sees branch names, PR titles
and repository names. By default it listens on
127.0.0.1:4777, reachable only from this machine. - LAN mode is opt-in (
agentyard install --lan, which listens on:80and announces<name>.local). Only requests from loopback and private (RFC 1918, link-local, unique-local) addresses are accepted; public addresses get a refusal. That is a filter, not authentication: do not expose the port to the internet or to untrusted networks, and do not use LAN mode on shared or public Wi-Fi. - Host names are checked. Only requests addressed to
localhost, an IP address, the-mdnsname or this Mac's host name are answered, so a web page cannot use DNS rebinding to read the dashboard or the Sessions tab from the browser. - The Sessions tab is loopback-only by default. Transcripts contain tool output, which can
include tokens and customer data. Other devices on the LAN see a notice instead.
-sessions-lanlifts that; only use it if every device on your network is trusted.installsaves it only together with--lan. Session ids are validated and shell-quoted in the copied resume command. - The dashboard can remove worktrees, from this Mac only. The HTTP server is no longer
read-only (through 0.6.0 it had no write endpoint):
POST /api/removeremoves worktrees. It is refused unless the client is this Mac itself (loopback or one of its own interface addresses; never another LAN device, even with-sessions-lan), theOriginheader names the dashboard's own host (a missingOriginis refused), theX-Agentyard-Tokenheader carries the random token generated when the process starts and embedded only in pages served to this Mac, the body isapplication/json, and the body is at most 64 KB. Requests are handled one at a time. Every response carriesX-Frame-Options: DENYandContent-Security-Policy: frame-ancestors 'none', so no other page can frame the dashboard and disguise its Remove button.agentyard demoanswers with simulated outcomes and never touches the disk. Apart from removals and therefs/agentyard/removed/refs they keep, the server writes nothing to your repositories; it does rungit fetch --prunein repos that have worktrees. - One guarded removal path. The dashboard endpoint and
agentyard mcp'sremove_worktreestool run the same code.agentyard mcpis a separate process that your AI client starts on this machine and talks to over stdin/stdout, running as you; it never listens on the network. Removal re-scans the repository first and removes only worktrees whose fresh verdict is Safe to delete or Prunable, one at a time withgit worktree remove. A dirty worktree is reclaimable only when every change is in the default branch by content; git refuses it, so it is removed with--force, and only after the content proof is re-run on a snapshot that is first kept underrefs/agentyard/removed/. The same copy is kept before removing a detached HEAD whose commits are on no branch, tag or remote. The files and HEAD are read again right before git deletes them, which narrows, but does not close, the window in which a write is lost: a file saved after that last look and before git's delete is gone, and so are ignored files, which no copy holds. The proof fails closed (Keep) on submodules, nested repositories, clean/smudge filters, custom merge drivers, staged versions the disk lacks, assume-unchanged or skip-worktree files, and when the fresh scan's fetch fails. Such worktrees get no copy-ready command, since a copied command would skip all of this. A dry run returns a plan per path; a removal passing it refuses a worktree that changed since. Anything else, and any path git does not list as a worktree at that moment, is refused. Ignored files inside a removed worktree (.envand the like) are deleted with it and listed in every result. Branches are never deleted.agentyard mcpreads its listings from the agent at the address it is given, the installed agent's by default; the decision to remove always comes from its own local scan, never from that data. - Credentials. It uses the GitHub CLI's existing login and stores no tokens; the LaunchAgent never
carries
GH_TOKENorGITHUB_TOKEN. Caches in~/Library/Caches/agentyard/hold repository, PR and session metadata, not credentials. - Installer.
scripts/install.shverifies the archive's SHA-256 against the release'schecksums.txt. Binaries are unsigned; the Homebrew cask clears the quarantine flag for that reason.agentyard installis the only thing that creates the LaunchAgent, and it asks first; it refuses to run as root.
Please report privately through GitHub: Security > Report a vulnerability on MilosMosovsky/agentyard. Do not open a public issue. Expect an acknowledgement within a few days. Only the latest release is supported.