feat(github): add task git credential policy (#1985)

This commit is contained in:
Carlos Florêncio
2026-07-27 18:10:12 +01:00
committed by GitHub
parent 85820e4a43
commit b39a668e99
62 changed files with 2687 additions and 208 deletions
+141 -16
View File
@@ -1,7 +1,7 @@
---
status: draft
created: 2026-07-19
amended: 2026-07-21
amended: 2026-07-27
owner: Kandev
---
@@ -43,14 +43,27 @@ automation under different GitHub Apps without operating separate Kandev deploym
- User-triggered mutations prefer the workspace's verified personal connection, then a human
PAT/CLI automation connection, then the App installation. The UI always identifies the effective
actor and never labels an App mutation as human-attributed.
- Background watches, cleanup, repository discovery, workflow sync, managed agent git transport,
and agent-initiated GitHub operations always use the workspace automation connection. Personal
credentials are never exposed to agents or executors.
- Managed executors receive task/repository/generation-bound broker leases. App installation tokens
are minted for the requested repository and cached only in memory. PAT/CLI tokens retain their
provider-granted scope once delivered to a trusted agent subprocess.
- Background watches, cleanup, repository discovery, and workflow sync always use the workspace
automation connection. Personal credentials are never exposed to agents or executors.
- Task Git credential routing is a separate workspace policy. **Managed workspace credentials**
gives attached GitHub repositories task/repository/generation-bound broker leases from the
workspace automation connection. **Inherit executor Git credentials** injects no GitHub broker
helper or `gh` shim: Local and Worktree tasks use host-visible Git/SSH credentials, while remote
tasks use credentials configured in that executor.
- Under managed routing, App installation tokens are minted for the requested repository and cached
only in memory. PAT/CLI tokens retain their provider-granted scope once delivered to a trusted
agent subprocess. GitHub HTTPS and the broker-aware `gh` shim fail closed rather than consulting
another ambient helper after a managed-helper failure.
- Kandev composes the indexed `GIT_CONFIG_COUNT` / `GIT_CONFIG_KEY_<n>` /
`GIT_CONFIG_VALUE_<n>` protocol across host, executor, profile, task, and agentctl boundaries.
Unrelated entries such as hooks, notes, safe-directory, and URL-rewrite settings survive in their
original order; later Kandev credential entries affect only their intended GitHub credential
keys. An already-forwarded suffix is not duplicated.
- Explicit executor-profile `GITHUB_TOKEN` or `GH_TOKEN` values remain unmanaged operator overrides
and take precedence over the workspace broker.
and take precedence over the workspace broker when managed routing is selected.
- Every successful task launch or resume records a non-secret session snapshot of the selected
policy, effective credential source, known method and actor, transport, executor, and capture
time. Inherited and profile-token actors are labeled runtime-selected instead of being probed.
- The unpublished `KANDEV_GITHUB_APP_*` configuration introduced on this branch is removed. Setting
those variables does not create or configure a registration; operators use the guided import
flow for an App they already own.
@@ -75,12 +88,18 @@ The workspace UI explains that an App is recommended for background jobs and man
it does not claim agents can only use the selected method. Explicit executor credentials and other
unmanaged tools remain outside Kandev's workspace credential contract.
The automation method and task Git credential policy are independent. Selecting a named `gh`
account does not inherit the host's Git configuration: in managed mode Kandev resolves that exact
host/login and brokers its token to the task. Users who want traditional host or executor behavior
select **Inherit executor Git credentials** explicitly.
## Identity And Routing
| Purpose | First choice | Fallback | Attribution |
| --- | --- | --- | --- |
| Background reads and writes | Workspace automation | None | Automation principal |
| Managed agent git and GitHub access | Workspace automation | None | Automation principal |
| Managed task git and `gh` access | Workspace automation | Explicit profile token | Automation principal, or runtime-selected override |
| Inherited task Git access | Executor-visible Git/SSH credentials | None | Runtime-selected |
| `My GitHub` reads | Personal connection | Human PAT/CLI automation | Human principal |
| User-triggered mutation | Personal connection | Human PAT/CLI, then App installation | Effective principal shown in UI |
@@ -154,6 +173,34 @@ One row per configured workspace automation identity. Existing fields remain, wi
For an App connection, `installation_id`, verified account login/type, and
`app_registration_id` must all be present. PAT/CLI/legacy rows must have no registration ID.
### `github_workspace_settings`
The non-secret operational settings row adds `task_git_credentials_mode`, with allowed values:
| Value | Behavior |
| --- | --- |
| `managed` | Default. Inject the workspace broker contract for attached GitHub repositories unless an explicit executor-profile token overrides it. |
| `executor` | Inject no Kandev GitHub helper or `gh` shim; use credentials available where the selected executor runs. |
Missing or invalid persisted values normalize to `managed`. Workspace-settings copy includes this
policy because it is operational configuration, not authentication material.
### Task-session credential snapshot
After a successful launch or resume, task-session metadata contains a versioned
`git_credential_snapshot` with:
- selected policy (`managed` or `executor`);
- effective source (`workspace`, `executor_profile`, or `executor`);
- workspace method when known (`pat`, `gh_cli`, `github_app_installation`, or
`legacy_shared`);
- a known human/App actor label, or `runtime_selected` when Kandev does not inspect the credential;
- transport (`managed_https`, `profile_token`, or `executor_selected`), executor type, and capture
timestamp.
The snapshot contains no token, lease, helper path, credential-file path, or SSH key detail. A
failed launch/resume does not replace the last successful snapshot.
### `github_user_connections`
One optional personal identity per `(workspace_id, user_id)`. Add required
@@ -212,6 +259,10 @@ before exposing registration management.
- `GET /api/v1/github/status?workspace_id=<id>` returns automation, personal identity, effective
actors, App registration metadata, capabilities, missing permissions, and migration state.
- `GET /api/v1/github/workspace-settings?workspace_id=<id>` returns repository scope, saved
preferences, and `task_git_credentials_mode`.
- `PUT /api/v1/github/workspace-settings` accepts a partial
`task_git_credentials_mode` update and rejects unknown values without changing the prior policy.
- `GET /api/v1/github/auth/gh-cli/accounts?workspace_id=<id>` lists exact local host/login choices.
- `PUT /api/v1/github/workspace-connection?workspace_id=<id>` configures validated PAT or named CLI
auth. App connections can only be committed by the verified installation callback.
@@ -266,6 +317,17 @@ post-signature processing failures produce `failing`; a later valid successful d
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`.
- `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.
- 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
@@ -279,12 +341,22 @@ post-signature processing failures produce `failing`; a later valid successful d
- 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.
- 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 the trusted child operation.
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.
- 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
@@ -299,6 +371,13 @@ post-signature processing failures produce `failing`; a later valid successful d
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 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.
- 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
@@ -306,10 +385,11 @@ post-signature processing failures produce `failing`; a later valid successful d
## Persistence Guarantees
Registrations, workspace/personal bindings, credential generations, health, auth flows, and webhook
dedupe 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
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
@@ -317,6 +397,11 @@ registration and never creates a global default.
- Workspace GitHub settings lead with the active automation identity and a **Change connection**
command. The method chooser uses a menu/list with PAT, GitHub CLI, and GitHub App descriptions,
not a segmented tab control.
- Method descriptions state where the credential is stored/resolved and how managed tasks receive
it. A separate **Task Git credentials** setting visibly explains **Managed workspace
credentials** and **Inherit executor Git credentials**, including local/Worktree versus remote
behavior and explicit profile-token precedence. An information icon provides the complete
per-method delivery explanation as supplementary help.
- 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;
@@ -331,6 +416,10 @@ registration and never creates a global default.
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 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.
## Scenarios
@@ -356,6 +445,26 @@ registration and never creates a global default.
- **GIVEN** a PAT or named CLI workspace, **WHEN** an agent uses the managed credential helper,
**THEN** it receives that workspace's automation token and the UI does not promise provider-side
repository narrowing.
- **GIVEN** a named CLI workspace in managed mode, **WHEN** a task launches, **THEN** Kandev resolves
the selected host/login, makes both managed `git` and `gh` available in standalone and remote
runtimes, and does not depend on the host's currently active CLI account.
- **GIVEN** a workspace selects executor inheritance, **WHEN** a Local/Worktree or remote task
launches, **THEN** Kandev injects no broker helper/shim and the task uses host-visible or
executor-configured credentials respectively.
- **GIVEN** managed mode and an explicit executor-profile GitHub token, **WHEN** a task launches,
**THEN** the profile token wins and the session disclosure labels its actor runtime-selected.
- **GIVEN** a managed helper cannot execute or redeem its lease, **WHEN** Git requests GitHub HTTPS
credentials, **THEN** the command fails without falling through to a personal helper or prompt.
- **GIVEN** the host or executor exports indexed Git config for `core.hooksPath` and
`notes.augment.mergeStrategy`, **WHEN** Kandev appends its managed GitHub helper configuration,
**THEN** the agent receives one contiguous block containing the original entries first and the
Kandev entries afterward, and a real Git commit still runs the configured hook.
- **GIVEN** Docker or a remote control process already contains the same task Git config suffix,
**WHEN** that suffix is forwarded again while creating or configuring an agent instance,
**THEN** Kandev emits it once while retaining executor-added `safe.directory` and URL rewrites.
- **GIVEN** a workspace policy or automation connection changes after launch, **WHEN** the user
views the running session, **THEN** the Changes disclosure still shows its launch snapshot; a
successful resume records and shows the newly resolved contract.
- **GIVEN** an authenticated host GitHub CLI without structured status or named-token flags,
**WHEN** an operator selects its sole account, **THEN** Kandev discovers and validates that
account without requiring a CLI upgrade.
@@ -365,6 +474,9 @@ registration and never creates a global default.
exposing the token.
- **GIVEN** desktop and mobile viewports, **WHEN** users complete every App flow, **THEN** actions and
disclosures remain usable without clipping, overlap, or desktop-only capability.
- **GIVEN** desktop fine-pointer and mobile coarse-pointer task views, **WHEN** the branch
disclosure is opened, **THEN** both show the same credential policy, method, actor truth, and
transport without horizontal overflow.
## Success Criteria
@@ -374,6 +486,11 @@ registration and never creates a global default.
- Secret scans find no PAT, private key, client secret, webhook secret, personal token, refresh
token, or live installation token in logs, API snapshots, redirects, process arguments, or
executor environments.
- Standalone, container, and remote task tests prove the managed helper is discoverable only for
broker-enabled instances, while executor inheritance receives no Kandev GitHub helper/shim.
- A real Git subprocess test proves that host/executor indexed hooks and notes config survive
managed credential injection, and focused tests prove ordered composition and overlap handling
across standalone, container, and remote launch shapes.
## Out Of Scope
@@ -384,11 +501,19 @@ registration and never creates a global default.
- GitHub Enterprise Server, enterprise-owned Apps, or hosts other than `github.com`.
- Kandev multi-user login, workspace membership, or RBAC implementation.
- Publishing Apps to GitHub Marketplace.
- Discovering or verifying the actor behind inherited credential managers, SSH agents, or explicit
profile tokens.
- Preventing a host-authority agent from manually selecting another Git transport outside
Kandev-injected managed HTTPS and `gh` commands.
## Implementation Plan
See [the implementation plan](../../plans/github-authentication/plan.md).
See [the original authentication implementation plan](../../plans/github-authentication/plan.md)
and the
[task Git credential policy follow-up plan](../../plans/task-git-credential-policy/plan.md).
## Decision
See [ADR-2026-07-21-workspace-selectable-github-app-registrations](../../decisions/2026-07-21-workspace-selectable-github-app-registrations.md).
See [ADR-2026-07-21-workspace-selectable-github-app-registrations](../../decisions/2026-07-21-workspace-selectable-github-app-registrations.md)
and
[ADR-2026-07-27-task-git-credential-policy](../../decisions/2026-07-27-task-git-credential-policy.md).