Repository navigation
Give everything chopin connect starts only Chopin-issued GitHub and MCP credentials, and stop a launch Chopin refuses - #876
Open
lavaman131 wants to merge 4 commits into
Open
lavaman131 wants to merge 4 commits into
lavaman131 wants to merge 4 commits into
Conversation
…use nothing of the machine's own Every Planner command, build, rebuild, investigation and spike now asks Chopin for its GitHub token with what it is for (purpose, run, document), and starts only once it has one. A refusal (repository-forbidden, app-cannot-push, starter-cannot-push, run-unknown, no-grant) stops it before its agent or command starts, with a message naming the repository. A run reports that message as its failure, and a Planner stops and tells Chopin why. The machine never falls back to its own GitHub login, so the fallback, githubCannotPush and its status text are gone. A launch reaches GitHub only through what the connector gives it: - its own gh configuration (GH_CONFIG_DIR), rewritten with each renewed token; - the token file (CHOPIN_GITHUB_TOKEN_FILE), for runs as well as Planners; - one git credential helper reading that file, with the repository's SSH addresses rewritten to https and GIT_SSH_COMMAND=false; - the App bot's identity in GIT_AUTHOR_* and GIT_COMMITTER_*, with user.useConfigOnly set and signing off; - who the work is for in CHOPIN_STARTER_*. GH_*, GITHUB_TOKEN, askpass, the SSH agent, GIT_SSH* and GIT_CONFIG* are removed from what it inherits and from --env values. Chopin's own tools take a separate Chopin token (CHOPIN_MCP_TOKEN_FILE), minted from the launch's credential and renewed with it. A token that expires without a renewal stops what used it. The connector's own git never uses SSH. --sandbox-hide hides a file or folder from every sandboxed agent, even inside a --sandbox-write folder. `chopin status` asks Chopin, without a token being made, whether each registered checkout's repository can have one. The connector speaks protocol 7. The e2e servers get a GitHub App key, and the fake GitHub plays the App, so a connector run there gets a token the way it would from GitHub. Assistant-model: Claude Opus 5.5 Assistant-workflow: inline (subagent) Assistant-verification: bun run types Assistant-verification: bun run ci Assistant-verification: bun test apps/connector/src (375 tests; the 4 failures of the first full run fixed and their files re-run: main, binary, sandbox-linux) Assistant-verification: e2e/connector.e2e.ts, e2e/build-restart.e2e.ts, e2e/implementation-launcher.e2e.ts (28 passed, 1 skipped) Assistant-verification: e2e/machine-choice.e2e.ts User-preference: Chopin and its backends get GitHub credentials from Chopin, never from the machine's gh CLI or its active account Co-authored-by: Alex Lavaee <lavaman131@github.com>
…it no longer inherits docs/chopin-cli.md's "Which GitHub account pushes" now says the App's bot does, for the person who started the work, lists what a launch gets for GitHub and what is removed, shows a refusal in `chopin status`, and adds --sandbox-hide. docs/agent-connection.md adds CHOPIN_MCP_TOKEN_FILE and says the GitHub token is for Git and gh only. Setup is linked, not repeated. Assistant-model: Claude Opus 5.5 Assistant-workflow: inline (subagent) Assistant-verification: bun run ci User-preference: Chopin and its backends get GitHub credentials from Chopin, never from the machine's gh CLI or its active account Co-authored-by: Alex Lavaee <lavaman131@github.com>
…s on any command line MXC starts a sandboxed launch with each variable on the sandbox tool's command line (`bwrap --setenv NAME VALUE`), which any process on the machine can read in /proc/<pid>/cmdline or `ps`. That included a Planner's credential, its GitHub token and every --env value. On Linux and macOS the launch's environment now goes to the sandbox in a 0600 file in its private temporary directory. MXC is given only PATH and that file's path. A one-line shell exports the file, deletes it and becomes the launch's command, so the launch has every variable as before, CHOPIN_PLANNER_TOKEN and CHOPIN_GITHUB_TOKEN included, and only the person's own processes can read them. The proxy MXC sets still wins, and the person's proxy variables, which can carry a password, are not passed on. Windows is unchanged. New sandbox-linux tests, in real bubblewrap, start a bare launch, a Planner, a build, an investigation and a spike. They find none of their credentials on any process's command line, check that each still has its variables, and that the token files are 0600. All five fail without the change. Assistant-model: Claude Opus 5.5 Assistant-workflow: inline (subagent) Assistant-verification: bun test apps/connector/src (384 pass, 1 skip, 0 fail) Assistant-verification: bun test apps/connector/src/sandbox-linux.test.ts in real bubblewrap (21 pass; the 5 new ones fail with the change turned off) Assistant-verification: bun run types Assistant-verification: bun run ci User-preference: Chopin and its backends get GitHub credentials from Chopin, never from the machine's gh CLI or its active account Co-authored-by: Alex Lavaee <lavaman131@github.com>
… a command line docs/chopin-cli.md's sandbox table now says how the environment reaches a sandboxed agent on Linux and macOS. Assistant-model: Claude Opus 5.5 Assistant-workflow: inline (subagent) Assistant-verification: bun run ci Co-authored-by: Alex Lavaee <lavaman131@github.com>
lavaman131
force-pushed
the
connector-no-local-github
branch
from
November 12, 2026 23:38
6dc90cc to
97e3fe3
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Start nothing on a paired machine without Chopin's GitHub token, and use nothing of the machine's own GitHub setup
Requires:
purpose,identity,starter,check, protocol 7): on main as 3df74b1.mcp-chopin-bearerchange (POST /api/connector/mcp-token, and/mcpaccepting MCP tokens): on main as b8bf8eb. This branch is rebased onto origin/main 8634eef.GITHUB_APP_IDand App key, without which every protocol-7 request is refusedno-grantand nothing runs on a paired machine.CHOPIN_MCP_TOKEN_FILEas its/mcpbearer before it requires connector protocol 7. A launch's GitHub token is now a GitHub App installation token, and/mcprefuses those.Don't ship the connector ahead of 3 and 4.
What was wrong
A paired machine's work fell back to the machine's own GitHub setup whenever Chopin gave no token, and in other places even when it did:
chopin statusdescribed this as "this machine uses its own GitHub login".GH_TOKENandGITHUB_TOKENset to the first token. They were never renewed.chopin connect --foregroundpassed a shell'sGITHUB_TOKEN, SSH agent andGIT_CONFIG_*on.core.sshCommandand keys, and fell back to an anonymous fetch.On a machine with two GitHub accounts, this is how work reached GitHub as an account that wasn't the one Chopin checked.
What changes
Every launch asks Chopin first, and nothing starts on a refusal. A Planner, build, rebuild, investigation or spike asks
POST /api/connector/github-tokenwithpurpose(plannerorrun),runanddocument.repository-forbidden,app-cannot-push,starter-cannot-push,run-unknownandno-grant. A run reports the message as its failure, and a Planner stops and tells Chopin why.githubCannotPushand its status text are gone.What a launch gets (
githubEnvironment):GH_CONFIG_DIRhosts.ymlin gh 2.40+'s layout,config.ymlwithversion: "1"). It is rewritten on every renewal, and gh never reads~/.config/gh.CHOPIN_GITHUB_TOKEN_FILECHOPIN_GITHUB_TOKENand its expiry.GIT_SSH_COMMAND=false.GIT_AUTHOR_*,GIT_COMMITTER_*user.useConfigOnly=true,commit.gpgsign=falseandtag.gpgsign=falseare set throughGIT_CONFIG_*.CHOPIN_STARTER_LOGIN,_NAME,_EMAILCo-authored-by.CHOPIN_MCP_TOKEN_FILE/mcp. A Planner's is minted from its credential and renewed with it. A build's or investigation's is minted from the run credential and renewed 10 minutes before it expires. A refusal deletes the file and the launch goes on. A spike's agent gets none.What a launch doesn't get. Before the kit is added,
GH_*,GITHUB_TOKEN,GITHUB_ENTERPRISE_TOKEN,GIT_ASKPASS,SSH_ASKPASS,SSH_AUTH_SOCK,GIT_SSH*,GIT_CONFIG*and inheritedGIT_AUTHOR_*andGIT_COMMITTER_*are removed from the connector's environment and from--envvalues. That covers Planners, runs and the connected agent check. A model login such asCOPILOT_GITHUB_TOKENstays.Renewal and expiry. The token is renewed 25 minutes before it expires. When a renewal fails and the token expires, or Chopin refuses the renewal, the files are removed and the launch stops: a run fails saying so, and a Planner starts again with a new token.
The connector's own git never uses SSH.
GIT_SSH_COMMAND=falsewins over everything, andSSH_AUTH_SOCK,GIT_SSHand the askpass variables are dropped. A spike's host fetch uses Chopin's token, then the token goes before the spike's agent starts. The spike's agent gets no gh login, helper or SSH, and still can't push.No credential on a command line. This was found on a live machine. MXC starts a sandboxed launch with each environment variable on the sandbox tool's command line (
bwrap … --setenv CHOPIN_PLANNER_TOKEN <value> --setenv CHOPIN_GITHUB_TOKEN <value> …), so any process on the machine could read them in/proc/<pid>/cmdlineorps. That covered a Planner's credential and GitHub token and every--envvalue, for every launch kind.Now, on Linux and macOS,
sealedEnvironmentwrites the launch's whole environment to a0600file in its private temporary directory. MXC is given onlyPATHand that file's path, plus any variable whose name no shell can export. A one-line/bin/shlauncher exports the file, deletes it, unsets its path andexecs the launch's own command. So the launch, and what it starts, has every variable as before,CHOPIN_PLANNER_TOKENandCHOPIN_GITHUB_TOKENincluded for backends that read them. They are readable only through/proc/<pid>/environ, which only the same user can read.The proxy MXC sets still wins: the person's
*_proxyvariables are not passed on. They are dropped either way, because MXC overrides them, and a proxy URL can carry a password.On Windows the policy is unchanged; ProcessContainer passes the environment as an environment, not verified here. Unsandboxed launches already get their environment through
spawn, not their command line.--sandbox-hide <path>. This new option (repeatable, saved like--sandbox-write) hides a file or folder from every sandboxed agent:--sandbox-writefolder.A backend can name its own stored sign-in here.
chopin status.--jsongainsgithub: { "owner/repo": "ok" | <refusal> | "unknown" }for registered checkouts. It is asked with{ repository, purpose: "check" }, which mints no token. A refused repository's Checkouts line says why.Protocol 7.
CONNECTOR_PROTOCOLis 7, so a backend can require this behaviour.Docs. In
docs/chopin-cli.md, "Which GitHub account pushes" is rewritten, and--sandbox-hideis added to the options and commands.docs/agent-connection.mdgains theCHOPIN_MCP_TOKEN_FILErow and a note on the bot token.docs/chopin-cli.mdlinks todocs/github-app-identity.mdfor setup. That file lands with the docs branch (docs-github-app-identity), so merge them together.e2e harness. The servers get
GITHUB_APP_IDand a key generated for each run (e2e/github-app.ts).e2e/github.tsplays GitHub for the App: the installation, access tokens, collaborator permission, users and token revoke. Without that, every connector run in e2e would be refusedno-grant.Tests
New tests in
github-token.test.ts:gh auth tokenunderGH_CONFIG_DIR) andgit credential fillboth see the renewed token.expired.withoutGitHubdrops the list above and keeps everything else.commit.gpgsignand a failinggpg.program, a credential helper, insteadOf to SSH), a commit is made as the bot and unsigned, the push URL is https, and the helper never runs.In
run.test.ts, on a decoy machine:GH_TOKEN,GITHUB_TOKEN,SSH_AUTH_SOCKandGIT_ASKPASS. A tripwireghandsshsit first on PATH.gh pr create), in git's credentials and in the token file.Also:
run.test.ts).purposeanddocument, gh's configuration, the bot identity, and that a refusal stops the Planner while an unavailable Chopin is retried (planner.test.ts).status.test.tscovers thecheckanswers.git-host.test.tschecks that the connector's git runs no ssh: not the person's, not the environment's, notPATH's, and not the repository's.sandbox.test.tschecks that a hidden path is in no grant on Linux and macOS, even inside a writable folder, and is denied.main.test.tschecks that--sandbox-hideis saved.sandbox-linux.test.tstests run in real bubblewrap, one for a bare launch and one for each launch kind: a Planner, a build, an investigation and a spike. While each launch runs, they read every process's/proc/<pid>/cmdlineand assert that no value appears: the Planner credential, the GitHub and MCP tokens, the run token, and an--envsecret. They assert that the launch still has its variables, the Planner'sCHOPIN_PLANNER_TOKENandCHOPIN_GITHUB_TOKENamong them, and that the token files and gh'shosts.ymlare0600in a0700folder. A value with quotes, a newline and$arrives intact, and the environment file is gone once the command starts. All five fail with the fix turned off.sandbox.test.tscoverssealedEnvironmentitself on any system.Updated for the new behaviour:
checkouts.test.ts,investigation-run.test.ts, and theimplementation-launcherandbuild-restarte2e specs.Checks run
bun run types: pass.bun run ci: pass.bun test apps/connector/srcon the final head: 384 pass, 1 skip, 0 fail.sandbox-linux.test.tsin real bubblewrap: 21 pass.--workers=2):e2e/connector.e2e.ts: 3 passed.e2e/build-restart.e2e.tsande2e/implementation-launcher.e2e.ts: 28 passed and 1 skipped.e2e/machine-choice.e2e.ts: passed.Judgement calls
sandbox-linux.test.ts(bubblewrap) run with the new launch environment and pass.chopin service install's andconnect --install-service's ACPinitializecheck still gets the person's environment. It runs before pairing and sends the agent nothing from Chopin, as in design §3.githubCannotPushis removed fromchopin status --json. That breaks the rule that its keys are only added to. The value described a fallback that no longer exists.