api: list GitHub repositories across installations
What
Adds the smallest cmux-api seams needed by cmux-platform C1: a development-only GET /v1/integrations/github/repos listing across active GitHub App installations, plus the machine-only POST /v1/integrations/github/machine-token path with a bounded TTL. User/daemon callers continue using the existing /token contract ({team, repo, permissions, label?}); no ttlSec is accepted there. The caller bearer is always required and the App key remains inside cmux-api.
Repository pagination is bounded to one upstream page per request with an opaque installation/page cursor. Results re-check the current connection and policy before returning. Machine tokens are limited to a live vm install, and install writes require the trusted mutate-own grant. The ledger cache includes TTL, retains provider expiry for finite cleanup, and never logs token contents.
Why
Leo, 2026-10-28:
"Like it frequently just picks your GitHub repositories, which is, like, I guess common, right? Whatever's under your organization, like you can just have that, and it just works. And then for whatever other sort of things, like if you have GitLab, I think that's probably, like, relatively easy to support and whatever. But anyway, apart from that, I was thinking, okay, there's the GitHub connector integration MCP thing in the ChatGPT chat mode, which I think is quite convenient. So that kind of setup flow, I think, should be doable, should be possible. Like just having our own MCP endpoint, I don't think is too big of a deal. ... having that option, I don't think is, you know, worse than only having the GH auth login and whatever, reading from your machine or whatever remote machine you have."
Security
- Repository listing returns only repository names, visibility and a cursor. The App key never leaves cmux-api; only the dedicated token routes return a scoped installation token to an authorized caller.
- User/daemon
/tokenrequests retain the existing excess-property-checked contract and forward the caller bearer. - Machine token minting is VM-only, one repo, short TTL, and grant checked; writes require
mutate-own. - Upstream bodies and tokens are never returned on errors.
Size
The repository listing, VM-only token contract and ledger expiry migration ship together because they implement the same development-only connector contract. The expiry migration is required to revoke a machine token at its requested TTL while retaining failed revocations until GitHub's native expiry. Keeping those changes together avoids deploying a connector whose requested token lifetime is not enforced. The current diff is substantially smaller than the original 1,700-line estimate because the shared token ledger has already landed on the base.
Tested
- Pending: backend typecheck, backend tests and focused token-lifetime regressions through CI.
- Static contract review: platform forwards
team,queryandcursor; cmux-api returnsreposwithfull_name/privateand a nullable cursor.
Observed / Not run
The previous head fails backend Typecheck because Number.isSafeInteger does not narrow unknown cursor fields. The repair adds explicit numeric guards, refuses expired minted tokens through durable revocation, and reports expired connections as needing relink. No local builds or test suites were run.
Agent session: agent session