Skip to content

feat(cloud): let an agent cloud use the stored GitHub sign-in - #1896

Merged
peters merged 1 commit into
mainfrom
feat/cloud-worker-token
Oct 27, 2026
Merged

peters merged 1 commit into
mainfrom
feat/cloud-worker-token

Conversation

@peters

@peters peters commented Oct 27, 2026

Copy link
Copy Markdown
Owner

Purpose

An agent's cloud now gets its GitHub access from this computer's stored GitHub sign-in, and from
nothing else.

#1895 removed the App installation token, which lasted one hour. What replaces it is the chain
Horizon already keeps for a person: an 8-hour access token and a refresh token that lives about six
months. Installed in the worker, the worker renews it itself, so a cloud keeps pushing after the
person's computer is switched off — which is the whole point of renting one.

The chain carries the person's account, so the worker's commits and pull requests are authored as
the person and not as a bot. The author address is always <id>+<login>@users.noreply.github.com:
GitHub attributes that address to the account whose numeric ID begins it, so it works when the person
keeps their email private, and no private address can leak into a commit.

Behavior

  • An agent's create installs the stored chain for the repositories of the agent_lifecycle
    policy. The person's own create is unchanged, and so is the card's Connect GitHub.
  • Refusals happen before anything is rented: no GitHub App in the settings, no repository in the
    policy, a policy path that is not a Git checkout with a GitHub origin, or a sign-in that did not
    finish. Each names what to do.
  • When this computer holds no chain and allow_github_sign_in_skip is false, the create runs the
    sign-in itself
    : the device code reaches the agent through its operation status, and the create
    continues when the person approves it. Without that the first cloud could never bootstrap. With
    allow_github_sign_in_skip: true the create continues without GitHub access instead of asking.
  • The worker reports kind: "person" and renewal: "worker" in its status, so an agent can tell
    that the access renews itself and needs nobody at this computer.

What stays the same

The person chain, worker payload version 1, cloud_create, cloud_delete, cloud_exec, the
delete lease, the policy gate and the provider choice. There is no second credential field: an
agent's cloud has one way to get GitHub access.

Trust boundary

docs/review/trust-boundaries/cloud-worker-person-chain.md — a pull request body, a repository name
and a worker's status line are untrusted; the chain, its refresh token and the client secret are
this computer's, and the worker holds them.

No visible change

No visible change: no new window, control or card state. An agent's create shows the same sign-in
prompt the person's path shows when it has to ask.

Validation

  • cargo fmt --all -- --check — pass
  • cargo nextest run -p horizon-core -p horizon-ui -p horizon-cloud -E 'test(cloud) or test(github)' — 2795 pass
  • cargo clippy --all-targets --features speech,trace-profiling -- -D warnings — pass
  • ./scripts/check-maintainability.sh — pass
  • python3 -B scripts/check-horizon-mcp-skills.py — pass

Rebased onto #1895, so the worker payload has one version, the shared Serves/serving helpers
stay deleted, and the person chain reads the worker's status directly.

Review size

18 files, 1190 lines, inside both thresholds.

Comment thread crates/horizon-core/src/cloud_runtime/deployment/git_credentials.rs Outdated
@github-actions

Copy link
Copy Markdown

Claude review, round 1 of 2

  • deployment/git_credentials.rs:25: The deployment reads the person chain a second time, after the worker is rented, and fails the whole deployment if that read fails. Two cases: GitHub answers 503 or the network drops during the image build (Refusal::Kept), or GitHub ends the chain during the build with skip off. In the second case the new sign-in's device prompt is handled as a worker prompt, which cancels the deployment. Both happen after the worker is rented, which the trust boundary forbids. Fix: at deploy time, report the failure and leave the cloud without GitHub access instead of returning the error and signing in again, or reuse the chain read in prepare. Posted as an inline comment.

Push the fixes, then add the claude-review label for round 2.

An agent's cloud takes the chain of this computer's own GitHub sign-in
instead of a GitHub App installation token. The worker renews that chain
from its refresh token, so the cloud keeps pushing, opening pull requests
and merging while this computer is off, and the chain's account makes
every commit and pull request the person's own.

An agent's create reads the chain from this computer and installs it as
payload version 1, which the person's own card sign-in already used. When
no chain is stored and the policy forbids continuing without GitHub
access, the create runs the same sign-in once and reports the device code
in its operation status; a policy that allows the skip asks nobody. Every
refusal happens before a card exists or a worker is rented, and names the
setting that resolves it.

The installation token is no longer a credential of an agent's cloud:
github/installation.rs, the version-2 installation payload and the
cloud-worker-github-token boundary page are removed. github.private_key_file
and the app's private key stay, because the app and the card's sign-in
still use them.
@peters
peters force-pushed the feat/cloud-worker-token branch from 5ca603a to 9e3ee94 Compare October 27, 2026 17:04
@peters
peters merged commit 244f076 into main Oct 27, 2026
13 checks passed
@peters
peters deleted the feat/cloud-worker-token branch October 27, 2026 17:37
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