Skip to main content

Module gh_guard

Module gh_guard 

Source
Expand description

Plain gh inside an agent process must not act as a human’s gh login.

Agents reach GitHub through gh-agent, which hands gh a per-call token of the agent’s own. The gh CLI, run directly, instead reads whatever login a human made on the host — so an agent that types gh where it meant gh-agent silently acts as that human, with nothing failing. gh finds its accounts through $GH_CONFIG_DIR/hosts.yml; pointing that variable at a directory with no hosts.yml leaves gh with no accounts, so it reports “not logged in” instead. gh-agent is unaffected: it passes its own token.

apply_gh_guard is called on every path that spawns an agent process or runs a command on an agent’s behalf. It is a RESERVED variable: it always overwrites whatever the env already carries (a persisted cmd:env, a caller’s override), because a single user-supplied value would re-open the hole. Containers keep it on CONTAINER_ENV_DENYLIST deliberately — the host path means nothing inside the image.

Design: a5af/shared-infrastructure SPEC_AGENT_PLAIN_GH_GUARD_2026_09_25.

Constants§

GH_CONFIG_DIR
GH_HOSTS_FILE 🔒
The file gh records its accounts in. Its absence is what “logged out” means; nothing else in the directory is touched.
META_KEY_PTYSHELL_GH_CONFIG_DIR
Block meta on an agent’s PtyShell: the agent’s guard directory, which the PTY spawn sets as GH_CONFIG_DIR (blockcontroller::shell::lifecycle). A PTY shell’s env is built from its own block, not from the agent’s, so the guard has to travel on that block.
UNATTRIBUTED 🔒
Directory name for an env with no agent identity on it. Still guarded — an unattributed spawn is not a reason to fall back to the human’s login.

Functions§

agent_gh_config_dir
<config_home>/gh-<slug>, the same location agent.open has always used.
apply_gh_guard
apply_gh_guard_in under this instance’s config dir, plus the publish guard’s core.hooksPath (crate::backend::publish_guard): every path that spawns an agent process or runs a command for one comes through here.
apply_gh_guard_in
Point GH_CONFIG_DIR at this agent’s login-free directory, under config_home. The agent is read from the env itself (AGENTMUX_AGENT_SLUG, then AGENTMUX_AGENT_ID).
clear_gh_login
Create dir if needed and remove a hosts.yml from it.
guard_config_home
Where guard dirs live. Under test, a per-process temp dir: every test that builds a spawn env reaches this, and none of them should create or clear anything in the developer’s real config home.
guard_dir_for
The guard directory for an agent whose stored cmd:env is agent_env (the slug is read from it, as for the agent’s own spawns), prepared the same way: created, with any login in it cleared.