359 lines
25 KiB
Markdown
359 lines
25 KiB
Markdown
---
|
|
status: draft
|
|
system: integrations
|
|
requirements:
|
|
- REQ-INTEGRATIONS-GITHUB-AUTHENTICATION-001
|
|
- REQ-INTEGRATIONS-GITHUB-AUTHENTICATION-002
|
|
created: 2026-07-19
|
|
owners:
|
|
- 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](#migrated-source-detail) |
|
|
| `REQ-INTEGRATIONS-GITHUB-AUTHENTICATION-002` | [Executor host CLI bridge](#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](../../../plans/executor-host-gh-bridge/plan.md) 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](../../../decisions/2026-07-27-task-git-credential-policy.md).
|
|
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.
|