Skip to content

Multi-org GitHub credentials: pushed token store, git credential helper, per-session GH_TOKEN#1307

Open
cyrusagent wants to merge 6 commits into
mainfrom
cyhost-913
Open

Multi-org GitHub credentials: pushed token store, git credential helper, per-session GH_TOKEN#1307
cyrusagent wants to merge 6 commits into
mainfrom
cyhost-913

Conversation

@cyrusagent

Copy link
Copy Markdown
Contributor

Assignee: @Connoropolous (connor)

Runtime half of CYHOST-913: teams can attach multiple GitHub App installations (multiple orgs), and cyrus-hosted now mints per-installation tokens and pushes them to the runtime instead of the droplet refreshing its own auth with an on-box PEM.

Summary

  • POST /api/update/github-tokens (new ConfigUpdater route, standard Bearer auth): persists pushed tokens {installationId, organization, accountType, token, expiresAt} to ~/.cyrus/github-tokens.json (atomic write, mode 0600) via a new GitHubTokenStore in cyrus-core.
  • Git credential helper (git-credential-cyrus.cjs, dependency-free, ships with cyrus-config-updater): installed to <cyrusHome>/scripts/ and wired into git with credential."https://github.com".useHttpPath=true + a helper-list reset. Resolves the org from the request path and prints the matching token (single-valid-token fallback; silent pass-through otherwise). Reads the store file per git invocation, so many concurrent sessions across different orgs each authenticate correctly.
  • resolveGitHubToken gains a first tier: org-matched pushed token for the session's repository, ahead of the forwarded installation token / self-minted App token / GITHUB_TOKEN fallbacks.
  • Per-session gh auth: sessions whose repository org matches a stored token get GH_TOKEN + CYRUS_GH_TOKEN in their env. No token file → zero behavior change for self-host users.

Testing

pnpm typecheck clean; pnpm test:packages:run 1537 passed; pnpm lint clean. New suites: token store (atomicity, perms, expiry, URL parsing), handler (payload validation, exact git config invocations, idempotency), the real credential helper script spawned against stdin fixtures, token-resolution precedence, and session-env injection.

Deploy ordering

This must be released and rolled out to droplets before the cyrus-hosted PR (cyrusagents/cyrus-hosted#960) deploys, since that deploy starts pushing to the new endpoint.

Related PRs: cyrusagents/cyrus-hosted#960, ceedaragents/cyrus-images (PEM removal).


Tip: I will respond to comments that @ mention @cyrusagent on this PR. You can also submit a review with all your feedback at once, and I will automatically wake up to address each comment.

…lper, per-session GH_TOKEN (CYHOST-913)

- New POST /api/update/github-tokens ConfigUpdater route persists
  per-installation GitHub App tokens pushed by cyrus-hosted to an atomic
  0600 ~/.cyrus/github-tokens.json
- Ships a dependency-free git credential helper that resolves the right
  token per GitHub org (useHttpPath), safe under concurrent sessions
- resolveGitHubToken prefers the org-matched pushed token; sessions get
  GH_TOKEN/CYRUS_GH_TOKEN for their repository's org
Restores droplet-wide gh auth parity with the removed on-droplet refresh
service: the github-tokens push now runs 'gh auth login --with-token'
with the first pushed token (non-fatal if gh is absent). Per-session
GH_TOKEN still provides per-org correctness in sessions.
Customers set their own GH_TOKEN for private npm registries on GitHub
Packages; Cyrus must not clobber it. The droplet gh wrapper maps
CYRUS_GH_TOKEN to GH_TOKEN inside the gh process only, and bare gh is
covered by the hosts.yml auth refreshed on every token push.

Learning test: gh does not consult git credential helpers — with a
helper configured for github.com but a clean gh config dir and no env,
'gh auth status' reports not logged in. So git auth flows through the
credential helper while gh needs hosts.yml or its env vars.
- handleRepository now uses plain 'git clone' whenever cyrus-hosted has
  pushed installation tokens, so auth flows through the credential helper
  and resolves the token for the repo's own org. 'gh repo clone' (which
  authenticates as gh's stored login — the first org) remains only as the
  self-host fallback when no tokens were pushed. Fixes wrong-token clones
  when a repo from a second org is added after onboarding.
- Token pushes rewrite an old-image ~/.local/bin/gh wrapper to honor
  CYRUS_GH_TOKEN, so per-org gh in sessions works on existing droplets
  without an image rebuild.
Multi-repo sessions can span repositories from different GitHub orgs, so
a session-wide gh token is not enough. New gh-cyrus.cjs resolver (execed
by the droplet gh wrapper) picks the installation token per command:
explicit -R/--repo argument, else the cwd's origin remote, then
CYRUS_GH_TOKEN, then the single valid token, else gh's own stored auth.
Customer GITHUB_TOKEN/GH_TOKEN stay stripped from gh's env only.

Installed on token pushes and EdgeWorker startup; wrapper self-heal now
upgrades both the original strip-everything wrapper and the interim
CYRUS_GH_TOKEN one.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant