Files
kandev/docs/specs/integrations/system-design/github-authentication-02.md
T

25 KiB

status, system, requirements, created, owners
status system requirements created owners
draft integrations
REQ-INTEGRATIONS-GITHUB-AUTHENTICATION-001
REQ-INTEGRATIONS-GITHUB-AUTHENTICATION-002
2026-07-19
Kandev

Workspace GitHub Authentication System Design Part 2

Purpose and boundaries

This design preserves the technical source detail for REQ-INTEGRATIONS-GITHUB-AUTHENTICATION-001 during migration.

Requirement mapping

Requirement Design section
REQ-INTEGRATIONS-GITHUB-AUTHENTICATION-001 Migrated source detail
REQ-INTEGRATIONS-GITHUB-AUTHENTICATION-002 Executor host CLI bridge

Migrated source detail

State Machines

Registration

  • absent -> registering: start a manifest flow or prepare an import with a preallocated ID.
  • registering -> active: verify GitHub identity and durable credential bundle, then publish catalog metadata and hot-load the registration generation.
  • registering -> absent: cancellation, expiry, replay, conversion, validation, or persistence failure leaves no selectable registration. An orphan App created on GitHub is reported with recovery instructions.
  • active -> invalid: credential load, signing, or identity validation fails; workspaces selecting it fail closed while PAT/CLI workspaces continue.
  • active -> absent: delete an unreferenced managed/imported registration.

Webhook health is independent: unverified -> verified after the first correctly signed delivery; post-signature processing failures produce failing; a later valid successful delivery restores verified.

Workspace automation

  • The current connection remains active while PAT/CLI validation, App creation/import, and App installation are pending.
  • A successful replacement increments the workspace credential generation, revokes old broker leases, clears incompatible personal auth, and then exposes the new connection atomically.
  • Installation suspension, deletion, permission loss, or registration invalidity updates only connections matching both registration ID and installation ID.
  • Disconnect removes workspace-owned secrets and leaves the reusable registration untouched.

Task Git credential resolution

  • Missing settings start at managed.
  • New workspace creation attempts to persist executor before the workspace is exposed for task launch; a persistence failure leaves the existing managed compatibility fallback in place.
  • Operator-authorized creation snapshots a validated active host gh host/login as a named CLI connection when available; member creation and unavailable/invalid host CLI leave automation disconnected.
  • managed + explicit profile GITHUB_TOKEN/GH_TOKEN -> executor_profile.
  • managed + attached GitHub repository + active workspace connection -> workspace broker.
  • executor -> executor-visible credentials, regardless of PAT/CLI/App automation method.
  • Initial launch and resume run the same resolution. A successful operation replaces the session snapshot; a failed operation leaves the previous snapshot unchanged.
  • Managed admission resolves the task policy and the selected executor profile before it validates repository identity. An explicit profile GITHUB_TOKEN or GH_TOKEN bypasses managed broker admission because the profile token is the effective source.
  • Managed admission completes before Kandev creates, rebinds, resumes, or changes a session, and before a workflow transition changes the current step. A policy error or an invalid repository identity leaves the task, workflow, and session state unchanged.
  • Each attached repository is resolved once for an individual launch or resume. The resolved repository set is shared by task configuration and credential-snapshot construction rather than triggering repeated materialization or origin mutation.
  • Executor-inherited Local and Worktree preparation resolves the clone protocol while it constructs the desired github.com origin. It queries host-specific gh configuration before global configuration and defaults to SSH when neither query returns a supported protocol. The resolver is called again for every launch and resume. Backend startup stores no protocol result.
  • The prepared-workspace path resolves the same repository set before it returns or starts an agent. An existing running-executor record does not bypass managed-checkout origin reconciliation.
  • Broker issuance and redemption use the same persisted repository identity fields. A local Git checkout and its origin are materialization data, not authorization data.
  • A public github.com remote can supply an in-memory origin when persisted provider-host metadata is absent. Kandev does not rewrite repository provider identity from partial legacy metadata.
  • Changing the workspace policy affects new launches and the next resume, not an already-running process.

Permissions And Security

  • Registration list/create/import/delete requires registration-manager authority plus access to the initiating workspace. Under the current single-user trust model default-user has both; future multi-user deployments must replace this provisional rule.
  • Workspace connection and personal identity actions require access to that workspace.
  • Registration IDs are not secrets. They select a candidate key; HMAC verification is still mandatory before any webhook payload is trusted.
  • Secret request fields have bounded bodies, are excluded from structured logs and errors, are stored only in the encrypted secret store, and are never returned by status/catalog APIs.
  • Runtime and token-cache keys include registration ID, registration generation, installation ID, workspace ID, and repository scope as applicable. No lookup may fall back to another registration or workspace.
  • Named GitHub CLI bearer tokens remain memory-only and are re-resolved from the exact selected host/login after at most five minutes. Connection-generation invalidation remains immediate.
  • Public base URL validation requires a canonical HTTPS origin with no credentials, query, or fragment and rejects loopback, private, link-local, or non-globally-routable DNS results. Kandev does not fetch the supplied URL as validation.
  • Repository identity accepts credential-free HTTPS and SSH remote forms. Authorities must remain ASCII-only and percent-free before URL parsing or case folding. It rejects an SSH password, query, or fragment before canonicalization, and an error never contains the rejected secret-bearing remote.
  • App private keys, client secrets, webhook secrets, personal tokens, and live installation tokens never enter executor environments. Only brokered PAT/CLI tokens or repository-restricted installation tokens reach a managed trusted child operation; explicit executor-profile GITHUB_TOKEN/GH_TOKEN values are an unmanaged exception and can reach that child instead.
  • Managed helper configuration resets the inherited GitHub HTTPS helper chain, disables terminal prompts, and activates Kandev's agentctl/gh tool directory only for broker-enabled task instances. It does not claim to prevent a host-authority agent from manually switching a remote to SSH or invoking another credential-bearing tool.
  • The helper command uses a Kandev-owned absolute executable variable rather than searching ambient PATH. The variable is bound before executor preparation and refreshed from os.Executable by a running agentctl. Unix shell-startup restoration composes with an inherited BASH_ENV, resolves simple environment-variable references in that hook path from the effective child environment, remains conditional on the broker contract, and never places broker leases, tokens, or credential scopes in shell arguments or startup files.
  • The effective task Git environment is runtime-only. It is copied only after the existing task/session or task-environment ownership check, is never persisted in task metadata or terminal records, and is never written to logs, errors, browser payloads, or process arguments.
  • The broker accepts a fork owner/repository only when it exactly matches a valid contribution_destination or remote_contribution on the same authorized task-repository attachment. Unknown binding versions, malformed URLs, cross-workspace rows, unrelated forks, and target mismatches fail closed.
  • Indexed Git configuration is validated and composed as a single ordered block at environment merge boundaries. Kandev never replaces a complete inherited block merely by assigning its own GIT_CONFIG_COUNT; managed helper reset semantics are expressed as later Git config entries.

Failure Modes

  • A registration create/import failure never replaces workspace auth or exposes submitted secrets.
  • A duplicate import directs the user to select the known registration instead of storing another copy of its root credentials.
  • Callback route/state/registration/workspace mismatches fail closed and consume no unrelated flow.
  • An invalid webhook signature performs no delivery claim, health update, or connection mutation.
  • Missing App permissions produce capability-specific diagnostics; unrelated capabilities continue to work.
  • GitHub CLI account discovery and token resolution tolerate host CLI releases both before and after structured status and multi-account flags. Genuine discovery failures are shown as errors, not as an empty account list. A CLI without named-token support may resolve only the active account; selecting another stored login fails with guidance to activate it or upgrade the CLI.
  • If the managed agentctl helper, broker, or gh shim is unavailable, the command fails with a managed-credential error and does not fall through to another HTTPS helper or interactive prompt.
  • If a contribution task cannot prove direct target write access or prepare an exact fork writable by the workspace automation connection, task creation fails before launch. A same-name repository that is not a fork of the canonical target is reported as a conflict and is never overwritten.
  • If an authorized task terminal, passthrough PTY, or task-scoped command cannot receive its effective managed Git environment, Kandev fails that process start before it runs the requested command. It does not silently fall back to an ambient credential helper or host gh login.
  • If any environment source supplies a malformed or unreasonably large indexed Git configuration, task environment preparation fails with a sanitized configuration error rather than silently truncating, partially merging, or executing a different block.
  • If executor inheritance is selected but no usable credential exists in that executor, Git/SSH reports its normal authentication failure. Kandev does not probe or guess the actor.
  • If host-specific gh clone-protocol configuration is unavailable or unsupported, resolution tries the global value. If neither scope returns ssh or https, executor inheritance uses SSH. A configuration lookup failure does not reuse an earlier process-cached result.
  • If host gh is absent, unauthenticated, or fails active-account validation while a workspace is created, the workspace remains disconnected for automation, retains executor task access, and creation succeeds.
  • If executor-default persistence fails while a workspace is created, the workspace remains available with the existing managed task-access fallback and a warning for retry/configuration; Kandev does not claim executor inheritance was applied.
  • If Kandev cannot reconcile a managed checkout's origin with the selected task policy, Local and Worktree preparation fails before the agent starts instead of silently using the other policy's transport.
  • If Git rejects a managed checkout because its filesystem owner differs from the Kandev service account, preparation fails with an ownership-specific, credential-safe diagnostic. Kandev does not retry with a global trust override, suppress the Git check, or mutate filesystem ownership.
  • Generic origin-inspection and origin-update failures include only bounded, credential-redacted Git output. Credentials embedded in URL userinfo or known authentication material never appear in logs, session errors, or browser payloads.
  • Deleting a registration with any workspace or personal reference returns github_app_registration_in_use with a non-secret binding count.
  • Changing workspace auth while a flow is open makes the stale callback fail without reverting the newer connection.
  • A PAT replacement is validated against GitHub before it replaces the current workspace connection. An invalid PAT leaves the previous connection unchanged, keeps the submitted draft available for correction, and shows the validation error in the connection dialog.
  • If a previously valid GitHub credential expires or is revoked, My GitHub stays on the current route and renders a reconnect/loading error instead of treating GitHub's 401 as an expired Kandev login session.

Persistence Guarantees

Registrations, workspace/personal bindings, task Git credential policy, credential generations, health, auth flows, webhook dedupe, and successful task-session credential snapshots survive restart. Installation tokens, App JWTs, CLI-derived tokens, and broker lease plaintext remain memory-only. Active encrypted-bundle pointers are crash consistent; orphan inactive bundles are reconciled after restart. Restart rebuilds runtime clients independently for every valid stored registration and never creates a global default.

UX And Mobile Contract

  • Workspace GitHub settings lead with the active automation identity and a Change connection command. The method chooser presents GitHub CLI first, followed by PAT and GitHub App, with descriptions rather than a segmented tab control.
  • Method descriptions state where the credential is stored/resolved and how managed tasks receive it. The same access group shows a compact Task access summary and the Change GitHub connection dialog visibly explains and edits Managed workspace credentials and Inherit executor Git credentials, including local/Worktree versus remote behavior and explicit profile-token precedence. The page does not repeat those controls in a standalone settings section.
  • The Task Git access heading exposes supplementary help that explains the managed Git credential helper, repository-scoped broker lease redemption, broker-aware gh shim, executor inheritance, and when a changed policy takes effect. The visible option descriptions remain plain-language decision guidance and use the same explanatory typography as the rest of the dialog.
  • One Save changes submission persists every changed local draft in the dialog: a PAT or named CLI workspace connection and the task Git access policy. It reports success only after all changed drafts save, keeps failed drafts available for retry, and never implies that one setting silently determines the other. The action stays visible in a fixed bottom row while the content above owns scrolling; a bottom fade cues additional content. App create/import/install remain separate workflow actions.
  • Task Git access options use compact spacing while retaining their full descriptions and minimum touch targets.
  • GitHub App selection first explains when to use it and the sharing/isolation trade-off, then lists known registrations and actions to Add existing App or Create new App.
  • The import guide provides copyable callback, setup, and webhook URLs; required permissions/events; exact GitHub settings navigation; bounded secret inputs; validation; and an install handoff.
  • The manifest guide asks owner, visibility, display name, and public URL. Visibility help explicitly distinguishes installability from Marketplace publication and repository access.
  • Permission details use a button and dialog. Current actor, installation account, App label, source, visibility, webhook health, and sharing warning are scannable without exposing secrets.
  • My GitHub identity appears as a connectable section only for App automation. For PAT/CLI, Workspace GitHub access states that My GitHub and user-triggered actions use the same verified human identity; the page does not render a redundant identity section or a fake selector.
  • The workspace identity and task-access summary lines expose concise help through a tooltip on hover or keyboard focus and the same explanation in a 44px-target drawer on touch devices.
  • Refreshing an already loaded workspace GitHub status keeps the current identity, task-access summary, and actions visible while the request is in flight. The refresh control alone shows progress and prevents duplicate activation. A workspace whose status has not loaded yet still shows the initial connection-status placeholder and never inherits another workspace's data.
  • When rate-limit snapshots are available, the connection status row exposes a Show GitHub API limits icon. Its desktop tooltip and touch drawer show remaining and total API requests, GraphQL query points, Search requests, and reset timing. Exhausted buckets explain that background PR and issue checks are paused.
  • Desktop and mobile support the same create/import/select/install/switch/disconnect flows. Mobile uses a single-column sheet/page, one scroll owner, safe-area padding, 44px targets, no fixed footer, and no horizontal overflow. External GitHub navigation is deliberate and returns to the same workspace settings route.
  • The desktop connection dialog is wide enough for the three method cards and explanatory content without cramped columns. Mobile keeps the existing full-height single-column drawer; its one Save changes action remains inside the scroll owner and is at least 44px tall.
  • The Changes panel branch disclosure includes the active session's launch-time Git credential snapshot. Desktop supports hover and keyboard focus; coarse-pointer/mobile users open the same information in a 44px-target drawer. Unknown inherited/profile actors are explicitly labeled runtime-selected.

Executor host CLI bridge

This section implements REQ-INTEGRATIONS-GITHUB-AUTHENTICATION-002 and its criteria 002.1 through 002.7. The integration system owns the credential policy, even though executor and agentctl code carry the environment. The implementation package records delivery status.

Eligibility and credential precedence

Executor.configureGitCredentialBrokerForRepositories owns the policy branch shared by full launch and resume. The executor branch removes managed credentials, then considers an optional host helper. Only explicit Local and Worktree executor types qualify. An unknown type does not imply Local. The branch never runs for Docker, SSH, Sprites, Kubernetes, or other remote execution.

For each attached GitHub repository, resolve its host through gitcredentials.ResolveRepositoryIdentity. Use provider identity and persisted remote metadata. Do not discover new hosts from arbitrary checkout remotes. The existing public GitHub compatibility fallback remains available. An explicit enterprise provider host must pass the existing identity validation. Invalid or ambiguous optional bridge identities are skipped without weakening managed admission. A local-source repository qualifies only when its recorded metadata establishes a GitHub identity. Deduplicate hosts within each preparation operation.

For github.com, a non-empty explicit GH_TOKEN or GITHUB_TOKEN in the request bypasses probing and bridge injection for that host. For a non-public host, only a non-empty GH_ENTERPRISE_TOKEN or GITHUB_ENTERPRISE_TOKEN bypasses probing and bridge injection for that host. Mixed-host launches evaluate these token variables independently for each attached host. Later profile merges must preserve the same effective token precedence. The helper invokes ordinary gh, which selects explicit environment tokens before stored login credentials. An invalid explicit token must not trigger a retry with a stored host login. Tests must exercise late profile tokens at the child-process boundary, not only inspect the early request map.

Managed mode retains its existing helper reset and fail-closed behavior. No host helper can follow the managed reset/helper pair in the effective Git configuration. A policy transition rebuilds the environment from its source layers instead of retaining a prior generated bridge. The existing credential snapshot remains executor / executor_selected, with no inferred actor.

Optional host probe

Add a small, injectable command runner beside executor credential routing. Resolve the real host gh executable before managed shim activation. Invoke gh auth token --hostname <validated-host> without a shell through subproc.RunGH. Discard stdout and stderr. The command result exposes only availability, never token bytes. Use a five-second operation deadline and honor earlier caller cancellation. Deduplicate probes per host for that operation. Do not cache a result across launches or resumes. Command failure or its own timeout skips the optional bridge. Caller cancellation aborts preparation normally. A successful token command establishes token availability, not repository access or token validity on GitHub.

The probe resolves the selected profile's HOME, GH_CONFIG_DIR, and XDG_CONFIG_HOME values before it runs. The helper receives the same values in the final effective child environment, so a profile override cannot silently probe one account and execute Git against another account. A secret-backed directory value is resolved through the global secret store for the probe and is never logged or placed in the command. Use a safely quoted absolute helper executable when a child shell can replace PATH. Do not embed a token or unvalidated host text in a shell command.

Indexed configuration and runtime propagation

For each eligible host, append one URL-scoped helper with the semantics of credential.https://<host>.helper = !gh auth git-credential. The generated shell function contains the unambiguous kandev-host-gh-bridge ownership marker. Existing user helpers remain earlier in the chain. Do not insert an empty reset for this optional fallback. Compose the helper block through gitconfigenv.Merge, never a map overlay or a hardcoded count. Keep the existing malformed-block error behavior and preserve meaningful duplicate entries. Validate the combined count against the existing 256-entry limit before dispatch. Complete replacement and the agentctl composition boundary remove obsolete host bridge helpers carrying the Kandev marker, including when no replacement is available. A user-selected quoted gh command and an unrelated host helper remain according to ownership. Do not premerge the entire host environment into a request that agentctl will merge with that same host environment again.

The strict source-aware resolver treats GIT_CONFIG_COUNT, GIT_CONFIG_KEY_*, and GIT_CONFIG_VALUE_* as one indexed block per environment source. It composes agent-profile, executor-profile, managed-runtime, and repository blocks in source order while ordinary variables retain the existing conflict, secret, and tier rules. The final lifecycle boundary resolves this composition after every managed definition has been added.

configureResumeGitHubCredentials reuses the shared policy branch. The prepared-workspace path through configureExistingWorkspace also needs the resolved executor identity and repository set. It must apply the refreshed environment before SetExecutionEnv and agent start. Reuse the repository set already resolved for preparation instead of cloning or reconciling it a second time. An environment delivery failure must prevent a claim that the refreshed environment is active.

The agentctl configure boundary has two explicit modes. The existing API mode composes a request-only overlay with the instance block, removing only marker-owned bridge entries first. Lifecycle launch and Kubernetes restart pass a composed runtime snapshot through this overlay mode; the indexed merge recognizes an already-forwarded snapshot and does not append it to itself. Complete-environment mode remains available to callers that supply the complete indexed block, including an intentional empty block. Both modes update the agent environment, one-shot adapter, workspace tracker, task shells, and task-scoped processes from the same canonical slice. A reused executor must replace obsolete generated entries when a policy, token, or host eligibility changes, including when a later preparation supplies no bridge at all. A login-shell test covers executable paths with spaces and a replaced PATH. No global Git file, repository configuration file, token cache, or workspace connection is modified.

Compatibility and evidence

This extends the existing executor-inheritance contract in the task Git policy decision. It does not create a new credential source or change the managed security boundary. The existing workspace-creation identity rules remain unchanged.

A real Git subprocess with a fake host CLI supplies deterministic end-to-end credential evidence. It covers success, a foreign host, an explicit token, and a failed managed helper. No live credential or network request is needed for these regressions. An authenticated login can still lack repository permissions, and a later logout can still make an operation fail.