Skip to content

security: give agent sessions their own GitHub App identity before unattended merge #7711

Open

Description

@kyle-sexton

Parent

Refs #7570 (thread: Merge authority and verifier), #7709 item 10 (decided by the owner on 2026-10-17: agent sessions get their own GitHub identity before report-only flips). Research: .work/poteto-review/design/agent-identity-research.md in the owner's checkout; GitHub docs fetched 2026-10-17.

Problem

The verifier's trusted-spec check and the admission marker rule (D33) trust any issue or comment from an account on trusted-actors.json. Agent sessions run gh with the owner's login (an OAuth gho_ token), so whatever they post reads as the owner's. The marker check (ratification.mjs) accepts a comment from a listed human, not posted through an App, and never edited. An agent posting as the owner passes all three tests. A trusted account therefore proves the account, not that a person wrote or reviewed the text.

Proposal

Agent sessions act as a GitHub App of their own, separate from the lanes App, using one-hour installation tokens minted per session. Reuse the dormant melodic-ai App: it has received zero events, its key is already in the estate's GitHub key vault, and dotfiles user-env.yaml:30 lists it with no local consumer. kyle-sexton stays the only kind: human trusted actor. The App is listed only as kind: bot, and only where a trigger has to accept it.

Why an App, not a machine user:

  • Two gates decide "human" by account type: babysit's is_human (babysit_merge_policy.py:735-760) and the pr-merge labeler rule (pr-pipeline/README.md:432-435). A machine user is a User and would pass both as human. An App is a Bot and already fails both.
  • A machine user needs an org seat, because an outside collaborator cannot use a fine-grained PAT. The org's one seat is filled. Apps take no seat, and GitHub's PAT docs point organization automation to Apps.
  • GitHub's Terms allow one free machine account per person, created by that person, who is responsible for what it does.

What it closes beyond the spec check:

  • Sessions can no longer post a valid admission marker. That is intended, and the code needs no change.
  • github-iac ADR 0015 accepted risk 2: the App holds no org-admin role, so a session cannot merge with --admin.

Owner decisions

  1. App: reuse melodic-ai (recommended) or register a new one. Either way: no administration permission, installed on selected repositories with azure-iac excluded (ADR 0015 trigger 5: there it could mint lane tokens), a new key generated and the old one retired.
  2. Token isolation: without it, an agent can unset GH_TOKEN and act as the owner again, so the App changes the credit, not the reach. Recommended: unattended lanes run as a separate OS user that cannot read the owner's token. Interactive sessions get a separate GH_CONFIG_DIR, plus a guardrails rule that denies gh auth switches and unsetting GH_TOKEN. Basis: judgment, since interactive sessions have the owner present.
  3. Key handling: sign the App JWT inside the vault, the azure-iac broker pattern, so the key never reaches a host (recommended). The alternative is vault-exec, which hands the key to the host process. Basis: GitHub's App key guidance, and the existing broker pattern.
  4. Merge-lane eligibility: PRs from agent sessions stay out of unattended merge (not-lane-authored is unchanged); recommended. The lanes App remains the only unattended author.
  5. Required review for agent PRs: github-iac ADR 0007 revisit trigger 3 fires, because an owner approval becomes a real second review. Recommended: record it and defer. The verifier is the gate for lane PRs.

Agent-doable work, as draft PRs after the decisions

  • dotfiles: a token mint helper and session launcher (bash and PowerShell) that set GH_TOKEN, GH_CONFIG_DIR and the git author.
  • claude-code-plugins: the App login in babysit's self_logins default; a guardrails rule against identity switches; ratification tests proving an App-posted marker is refused.
  • architecture: a trust-link entry and an ADR; amendments to github-iac ADR 0015 and ADR 0007.
  • Post-cutover probes: a session's issue reads as the App, and its marker does not admit.
  • Not yet checked: commit signing on requires-signing repos, and the commit author format.

Done when

Activity

  1. added
    needs-triageNot yet classified. Floor until a type and one priority tier are set.
    on Oct 17, 2026
  2. github-actions commented on Oct 17, 2026

    @github-actions
    Contributor

    Proposed work class: work-class: structural
    Basis: automated intake triage; the lanes App applied this class.

    Size: Large
    Complexity: High
    Uncertainty: Knowable
    Flags: intent
    Blocked by: none

    Understood: agent sessions should act as their own GitHub App identity, not the owner's login, so a trusted account proves a person wrote the text. The owner's 2026-10-17 revisions are the current design: a new App, a dedicated WSL distro for unattended lanes, and a vault-signed token broker. The work spans dotfiles, this repo (babysit self_logins, a guardrails rule, ratification tests), architecture and github-iac.

    One question stays open in the comments and needs an owner answer:

    1. Should the new App's bot id join AUTOMATION_LANES_APP_SENDER_ID, so review lanes review the App's pushes? RECOMMENDED: no, because decision 4 keeps agent-session PRs out of unattended merge, and review lanes can be added later. Basis: least privilege and the least change to the lanes' trust set.

    Also unchecked: commit signing on requires-signing repos, and the commit author format. No duplicates found.

    Automated triage, applied by the lanes App.

  3. added
    needs-humanHuman-in-the-loop required; autonomous sessions must not resolve items carrying this.
    and removed
    needs-triageNot yet classified. Floor until a type and one priority tier are set.
    on Oct 17, 2026
  4. kyle-sexton commented on Oct 17, 2026

    @kyle-sexton
    MemberAuthor

    Decision 1 (owner, 2026-10-17): reuse melodic-ai; register no new App. The owner prefers fewer Apps. A machine user would need a second paid seat and would pass the account-type human checks. The lanes App's key must stay in the cloud broker (github-iac ADR 0015). So the existing dormant App is the only identity that separates agents from the owner without adding one. Decisions 2-5 remain open.

  5. kyle-sexton commented on Oct 17, 2026

    @kyle-sexton
    MemberAuthor

    Decisions 2-5 (owner, 2026-10-17, 'go with recommended'): (2) unattended lanes run as a separate OS user that cannot read the owner's gh token; interactive sessions get their own GH_CONFIG_DIR plus a guardrails rule denying gh auth switches and unsetting GH_TOKEN. (3) The App JWT is signed inside the vault, the azure-iac broker pattern; the private key never reaches a host. (4) PRs from agent sessions stay out of unattended merge; the lanes App remains the only unattended author. (5) ADR 0007 revisit trigger 3 is recorded and deferred; the verifier is the gate for lane PRs.

  6. kyle-sexton commented on Oct 17, 2026

    @kyle-sexton
    MemberAuthor

    Decisions revised (owner, 2026-10-17, in the Automation Lanes session, after that session's design research):

    • Decision 1 now: register a new App. melodic-ai is live (a medley workflow mints it from org secrets), so it stays medley's. The new App's key is generated into Key Vault only.
    • Decision 2 now: unattended lanes get a dedicated WSL distro with interop and automount off, not a second OS user in the same distro. With interop on, any Linux user can launch Windows processes as the owner, and /mnt/c is readable. A probe confirms that interop off blocks Windows launches before the distro is used.
    • Caller auth: one certificate service principal per host per role. The function accepts app tokens only (idtyp=app), so every user token is rejected, the owner's included; it keeps an oid allowlist plus an explicit owner deny, and the certs rotate yearly.
      The interface stands: a separate function, POST /token narrowing repositories (up to 500) and permissions, a one-hour token, no cache, fail closed. Automation Lanes owns the azure-iac function and the shared signing package. This session owns the dotfiles helper (git author = the new App's bot), the guardrails rule, babysit self_logins and the ratification tests.
  7. kyle-sexton commented on Oct 20, 2026

    @kyle-sexton
    MemberAuthor

    github-iac's share of the new agent App, carried here because https://github.com/melodic-software/github-iac/issues/691 was closed as superseded by this issue: once the App is registered, github-iac needs (1) a row in the README installed-apps table (synced with live installations in https://github.com/melodic-software/github-iac/pull/717), (2) its install scope recorded (selected repositories, excluding azure-iac, unless this issue decides otherwise), (3) a trust-links entry in melodic-software/architecture, and (4) a decision on whether its bot id joins AUTOMATION_LANES_APP_SENDER_ID (open here: should review lanes review the new App's pushes?). #691's permission list (contents: read; pull requests: write; issues: write; actions: write; variables: read) is a starting point, not contradicted by the 2026-10-17 decisions; only its per-host token delivery is.

  8. removed
    needs-humanHuman-in-the-loop required; autonomous sessions must not resolve items carrying this.
    on Oct 25, 2026
  9. kyle-sexton commented on Oct 25, 2026

    @kyle-sexton
    MemberAuthor

    Released for re-triage under the owner's 2026-10-24 rule: an issue stops for a person only for an intent or vision question, a security call, a host or attended run, or spend outside prepaid credit. The hold label came off and intake triage runs again now; it re-applies a hold, with a numbered question, when one of those applies.

    To hold this issue again, add needs-human (or status: needs-decision) and say why in a comment.

  10. added
    needs-triageNot yet classified. Floor until a type and one priority tier are set.
    area: securitySecurity-relevant: vulnerability, hardening, or disclosure follow-up.
    priority: highSignificant impact, or blocks an imminent release; staff this cycle.
    status: needs-decisionAwaiting a human or maintainer judgment call.
    on Oct 25, 2026
  11. melodic-automation-lanes commented on Oct 25, 2026

    @melodic-automation-lanes
    Contributor

    The triage verdict left this issue at status: needs-decision:

    • flagged: intent
    • held by status: needs-decision
    • no lanes App verdict comment shows whether an earlier verdict was flagged
  12. added
    work-class: structuralRefactors, migrations, contract changes; cross-cutting and hard to reverse.
    and removed
    needs-triageNot yet classified. Floor until a type and one priority tier are set.
    on Oct 25, 2026
  13. melodic-automation-lanes commented on Oct 25, 2026

    @melodic-automation-lanes
    Contributor

    Refinement. A read-only run explored this repository, searched its issues and pull requests, and read the allowed docs to draft what follows. On an issue a listed human filed, it counts as the issue's spec once posted, while unedited; it is still model text, so check it.

    Intended outcome: Agent sessions act as a new, separate GitHub App through a vault-signed token broker with isolated credentials, and this repo gets the babysit self_logins entry, a guardrails rule and ratification tests.

    Dependencies

    • New App registration and Key Vault key (owner or host action)
    • Automation Lanes work: azure-iac token function and shared signing package
    • Dedicated WSL distro for unattended lanes (host work)
    • github-iac README row and install scope, plus architecture trust-links entry and ADR

    Related work

    Open questions

    1. Should the new App's bot id join AUTOMATION_LANES_APP_SENDER_ID so review lanes review the App's pushes?
      RECOMMENDED: No. Decision 4 keeps agent-session PRs out of unattended merge, and review lanes can be added later. Basis: judgment
    2. Which parts should the unattended lane do? Registering the App, the WSL distro, the Key Vault key and the live probes need a person or a host.
      RECOMMENDED: Split the repo-local work into its own issue: the babysit self_logins default, a guardrails rule against gh auth switches and unsetting GH_TOKEN, and ratification tests refusing an App-posted marker. Keep registration, the distro and the live probes as owner or host checks. Basis: judgment
    3. What is the new App's login and bot id, which the self_logins default and the tests need?
      RECOMMENDED: Wait for the App to be registered, then record the login and numeric id in the issue before the lane starts. Basis: judgment
    4. How should commit signing on requires-signing repos and the commit author format be settled? Both are marked not yet checked.
      RECOMMENDED: Probe both after the App exists and record the result before the dotfiles helper sets the git author. Basis: judgment

    Sources read

    • .base/.github/workflows/intake-triage.yml:120: passes vars.AUTOMATION_LANES_APP_SENDER_ID as the lanes App id
    • .base/docs/adr/0039-keep-babysit-identity-keys-in-userconfig-and-move-repository-keys-to-the-cascade.md:30: babysit_self_logins is a userConfig identity key

    Automated refinement by the lanes App. Answer with a comment whose first line is **Decision recorded.**.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: securitySecurity-relevant: vulnerability, hardening, or disclosure follow-up.priority: highSignificant impact, or blocks an imminent release; staff this cycle.status: needs-decisionAwaiting a human or maintainer judgment call.work-class: structuralRefactors, migrations, contract changes; cross-cutting and hard to reverse.

    Type

    Fields

    No fields configured for Task.

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions