feat(cli-credentials): plan agent-scoped git credentials

This commit is contained in:
Goon
2026-05-31 15:52:04 +07:00
parent 4a907135a6
commit e6242dbbbc
9 changed files with 926 additions and 0 deletions
@@ -0,0 +1,99 @@
---
phase: 1
title: "Research and contract tests"
status: pending
effort: ""
---
# Phase 1: Research and contract tests
## Context Links
- Issue: https://github.com/digitopvn/goclaw/issues/117
- Current user ID resolver: `internal/agent/user_identity_resolver.go:40`
- Tool execution context injection: `internal/agent/loop_pipeline_tool_callbacks.go:47`
- Secure CLI store contract: `internal/store/secure_cli_store.go:120`
- Current per-user HTTP API: `internal/http/secure_cli_user_credentials.go:13`
- Current git UI entry point: `ui/web/src/pages/cli-credentials/cli-credentials-table.tsx:80`
## Overview
Write characterization and contract tests before schema or UI changes. The goal is to pin the current failure mode: git typed credentials depend on a user credential row, but cross-channel usage often cannot map to the same credential user ID.
Priority: P1.
Status: pending.
## Key Insights
- Agent identity is stable for the runtime path; external user identity is not stable across channels.
- User credentials should stay supported, but they should not be the primary git credential setup path.
- Existing context credentials already show that credential resolution is not purely per-user; agent credentials should join that explicit precedence chain.
## Requirements
- Preserve existing per-user credential behavior.
- Add tests that fail under the current implementation when no matching `userID` exists but an agent credential exists.
- Make API and UI requirements explicit before implementation.
## Architecture
Effective credential source should become a small explicit enum in tests and later code:
1. `user` for explicit per-user override.
2. `context` for group/member/channel scoped credential.
3. `agent` for the new agent-scoped credential.
4. `binary` for legacy/global binary env.
5. `none` when no typed credential is available.
## Related Code Files
- Modify tests under `internal/store/pg/`, `internal/store/sqlitestore/`, `internal/tools/`, and `internal/http/`.
- Add or extend UI tests under `ui/web/src/pages/cli-credentials/__tests__/`.
- No production code changes in this phase except test fixtures if needed.
## Implementation Steps
1. Add store contract tests proving the intended precedence: user > context > agent > binary.
2. Add a runtime test where the same agent executes `git clone` from two different credential user IDs and resolves the same agent credential.
3. Add an HTTP route contract test for planned agent credential endpoints:
- `GET /v1/cli-credentials/{id}/agent-credentials`
- `GET /v1/cli-credentials/{id}/agent-credentials/{agentId}`
- `PUT /v1/cli-credentials/{id}/agent-credentials/{agentId}`
- `DELETE /v1/cli-credentials/{id}/agent-credentials/{agentId}`
4. Add negative API tests:
- invalid `binaryID`
- invalid `agentID`
- missing `host_scope` for `pat` and `ssh_key`
- unsupported `credential_type`
- response never includes raw token/key/blob
5. Add Web UI tests that the git credential action defaults to Agent Credentials, with User Credentials shown as advanced/personal override.
6. Document which tests fail before implementation.
## Todo List
- [ ] Store precedence tests written.
- [ ] Runtime cross-channel agent credential test written.
- [ ] HTTP endpoint contract tests written.
- [ ] UI default-flow test written.
- [ ] Initial failing test set documented in the phase notes.
## Success Criteria
- [ ] Tests prove the current user-id keyed design cannot satisfy the target behavior.
- [ ] Tests define the exact endpoint contract and response masking.
- [ ] No implementation-only code is added before the contract tests exist.
## Risk Assessment
- Risk: tests could encode a wrong precedence order. Mitigation: keep user override highest for compatibility, but make agent credential the default UI path.
- Risk: agent credential could accidentally grant binary execution access. Mitigation: test that credential rows do not bypass non-global agent grants.
## Security Considerations
- Contract tests must assert no plaintext credential values appear in list/detail responses, audit labels, logs, or errors.
- Tests must assert tenant isolation for every new endpoint.
## Next Steps
- Phase 2 adds schema and store implementation until Phase 1 tests pass.
@@ -0,0 +1,135 @@
---
phase: 2
title: "Schema and store resolver"
status: pending
effort: ""
---
# Phase 2: Schema and store resolver
## Context Links
- User credential struct: `internal/store/secure_cli_store.go:79`
- Agent grant struct: `internal/store/secure_cli_store.go:97`
- PostgreSQL lookup path: `internal/store/pg/secure_cli.go:337`
- Context credential overlay: `internal/store/pg/secure_cli.go:505`
- SQLite schema version map: `internal/store/sqlitestore/schema.go`
- PostgreSQL latest migration at planning time: `migrations/000076_channel_memory_extraction.up.sql`
## Overview
Create durable agent-scoped typed credential storage and make `LookupByBinary` return an effective credential source without depending on channel-specific user IDs.
Priority: P1.
Status: pending.
## Key Insights
- `secure_cli_agent_grants.encrypted_env` is a policy override payload, not a typed credential identity. It lacks `credential_type` and `host_scope`.
- A dedicated table keeps grant authorization separate from credential material.
- SQLite must be updated in both fresh schema and incremental migrations.
## Requirements
- Add `secure_cli_agent_credentials` for both PostgreSQL and SQLite.
- Encrypt secret blob with the same AES-256-GCM pattern as existing SecureCLI credentials.
- Preserve per-user and context credentials.
- Return source metadata for audit and UI.
- Do not let an agent credential row grant access to a non-global CLI binary by itself.
## Architecture
New table shape:
```sql
secure_cli_agent_credentials (
id uuid/text primary key,
tenant_id uuid/text not null,
binary_id uuid/text not null,
agent_id uuid/text not null,
encrypted_env bytea/blob not null,
metadata jsonb/text not null default '{}',
credential_type text null,
host_scope text null,
created_by text not null default '',
created_at timestamptz/text not null,
updated_at timestamptz/text not null,
unique (tenant_id, binary_id, agent_id)
)
```
Effective precedence:
1. Per-user credential when `userID` maps to a row.
2. Context scoped credential from the channel scope chain.
3. Agent credential for `(tenant_id, binary_id, agent_id)`.
4. Binary/global env.
Authorization rule:
- If `secure_cli_binaries.is_global = false`, `secure_cli_agent_grants` must still allow the agent before runtime uses the binary or its credential.
- If `is_global = true`, an agent credential can specialize the otherwise global binary for that agent.
## Related Code Files
- Add migration: `migrations/000077_secure_cli_agent_credentials.up.sql` and `.down.sql`, if `000077` is still the next number.
- Update `internal/upgrade/version.go`.
- Update `internal/store/secure_cli_store.go`.
- Add `internal/store/pg/secure_cli_agent_credentials.go`.
- Add `internal/store/sqlitestore/secure-cli-agent-credentials.go`.
- Update `internal/store/pg/secure_cli.go`.
- Update `internal/store/sqlitestore/secure-cli.go`.
- Update `internal/store/sqlitestore/schema.sql` and `internal/store/sqlitestore/schema.go`.
## Implementation Steps
1. Re-run `find migrations -name '*.up.sql' | sort | tail` and choose the next migration number.
2. Add PG migration with foreign keys to `secure_cli_binaries`, `agents`, and tenant scope. Add indexes on `(tenant_id, binary_id)`, `(tenant_id, agent_id)`, and unique `(tenant_id, binary_id, agent_id)`.
3. Add SQLite fresh schema and incremental migration. Bump `SchemaVersion`.
4. Add `SecureCLIAgentCredential` struct and store methods:
- `GetAgentCredentials(ctx, binaryID, agentID)`
- `SetAgentCredentialsTyped(ctx, binaryID, agentID, encryptedEnv, credentialType, hostScope)`
- `SetAgentCredentials(ctx, binaryID, agentID, encryptedEnv)` for legacy env
- `DeleteAgentCredentials(ctx, binaryID, agentID)`
- `ListAgentCredentials(ctx, binaryID)`
5. Extend lookup result with effective credential source fields. Prefer a neutral name such as `CredentialEnv`, `CredentialType`, `CredentialHostScope`, `CredentialSource`, and `CredentialSubjectID` instead of reusing `User*` fields for non-user sources.
6. Update PG and SQLite lookup:
- join user credential only when `userID` exists
- join agent credential when `agentID` exists
- apply context credentials before agent credential if context credential exists
- preserve grant authorization check before returning a non-global binary
7. Update fake stores in tests to implement the new interface.
8. Run targeted store tests for PG and SQLite.
## Todo List
- [ ] PG migration added and down migration removes table/indexes.
- [ ] SQLite schema and version migration added.
- [ ] Store interface and concrete PG/SQLite methods added.
- [ ] Effective source metadata added without breaking JSON responses.
- [ ] Phase 1 store tests pass.
## Success Criteria
- [ ] `LookupByBinary` can resolve typed git credentials for an agent even when `userID == ""`.
- [ ] Per-user credential still wins when present.
- [ ] Context credential still wins over agent credential.
- [ ] Non-global binaries still require enabled grants.
- [ ] PG and SQLite tests cover fresh and migrated schemas.
## Risk Assessment
- Risk: reusing `UserEnv` for agent credentials hides source semantics. Mitigation: introduce source-neutral fields and keep `User*` only for backward compatibility during refactor.
- Risk: migration number collision. Mitigation: verify immediately before implementation.
- Risk: SQLite desktop startup breaks. Mitigation: update both schema.sql and incremental migration map.
## Security Considerations
- Secret values remain encrypted at rest.
- Store methods must scope by tenant in every query.
- Delete binary or agent should cascade or fail predictably; use foreign keys consistent with existing store behavior.
## Next Steps
- Phase 3 exposes API CRUD over the new store methods.
@@ -0,0 +1,138 @@
---
phase: 3
title: "HTTP API credential management"
status: pending
effort: ""
---
# Phase 3: HTTP API credential management
## Context Links
- Route registration: `internal/http/secure_cli.go:38`
- Current user credential handlers: `internal/http/secure_cli_user_credentials.go:13`
- Typed credential validator: `internal/http/secure_cli_typed_credentials.go:54`
- API docs table: `docs/18-http-api.md:1176`
## Overview
Add HTTP API endpoints for agent-scoped CLI credential management. This is required by the user request and must not be left as a UI-only feature.
Priority: P1.
Status: pending.
## Key Insights
- The validator for `{credential_type, host_scope, blob}` already exists for user credentials and should be reused for agent credentials.
- API must make clear that credentials are not grants. A credential row stores secret material; `agent-grants` still controls non-global binary access.
- Responses should mirror the user credential API, but use `agent_id` and optional agent display metadata.
## Requirements
- Add routes:
- `GET /v1/cli-credentials/{id}/agent-credentials`
- `GET /v1/cli-credentials/{id}/agent-credentials/{agentId}`
- `PUT /v1/cli-credentials/{id}/agent-credentials/{agentId}`
- `DELETE /v1/cli-credentials/{id}/agent-credentials/{agentId}`
- Reuse typed payload body:
- `credential_type: "pat" | "ssh_key" | "env"`
- `host_scope`
- `blob: {"token": "..."} | {"key": "..."}`
- legacy `env` for env-only CLIs
- Return masked metadata only.
- Emit audit events with credential type and IDs, never secret values.
- Require admin auth and tenant scope.
## Architecture
Response list shape:
```json
{
"agent_credentials": [
{
"id": "...",
"binary_id": "...",
"agent_id": "...",
"agent_key": "builder",
"has_secret": true,
"credential_type": "pat",
"host_scope": "github.com",
"created_at": "...",
"updated_at": "..."
}
]
}
```
Detail response:
```json
{
"agent_id": "...",
"credential_type": "pat",
"host_scope": "github.com",
"has_secret": true
}
```
Legacy env credentials may include sanitized `env` entries. Typed credentials must not return `blob`, `token`, or `key`.
## Related Code Files
- Add `internal/http/secure_cli_agent_credentials.go`.
- Extend `internal/http/secure_cli_typed_credentials.go` if shared helpers need neutral names.
- Update `internal/http/secure_cli.go` route registration.
- Add tests near `internal/http/secure_cli_typed_credentials_test.go`.
- Update API docs in `docs/18-http-api.md` and `docs/20-api-keys-auth.md`.
## Implementation Steps
1. Refactor `typedCredentialBody`, `prepareTypedCredentialEnv`, and `writeTypedCredentialError` only if needed to support both user and agent handlers.
2. Add handler methods for list/get/put/delete agent credentials.
3. Validate `binaryID` and `agentID` path params with `uuid.Parse`.
4. Verify binary and agent exist through store methods before writing.
5. For PUT:
- typed branch: validate blob and call `SetAgentCredentialsTyped`
- env branch: merge env object and call `SetAgentCredentials`
6. For GET/list:
- return metadata and `has_secret`
- suppress `env` for typed credentials
7. Emit audit events:
- `secure_cli.agent_credentials.updated`
- `secure_cli.agent_credentials.deleted`
8. Invalidate SecureCLI cache after update/delete.
9. Run HTTP tests.
## Todo List
- [ ] Agent credential routes registered.
- [ ] Handler tests cover list/get/put/delete.
- [ ] Typed validation shared with user credential API.
- [ ] Responses mask typed secrets.
- [ ] API docs updated with endpoint table and body examples.
## Success Criteria
- [ ] API can fully manage agent-scoped PAT and SSH credentials.
- [ ] API can edit legacy env credentials for non-git binaries.
- [ ] Invalid agent/binary returns a clear 404 or 400.
- [ ] Non-admin cannot manage credentials.
- [ ] No API response leaks raw credential material.
## Risk Assessment
- Risk: new credential endpoint may be mistaken for grant endpoint. Mitigation: docs and UI state credential does not grant access.
- Risk: duplicate validation forks. Mitigation: reuse the existing typed credential validator.
## Security Considerations
- Use `http.MaxBytesReader` or existing JSON binding limits.
- Do not log raw request body.
- Audit should include `credential_type` and resource IDs only.
- All writes must be tenant-scoped.
## Next Steps
- Phase 4 consumes these endpoints from the Web UI.
@@ -0,0 +1,118 @@
---
phase: 4
title: "Web UI credential management"
status: pending
effort: ""
---
# Phase 4: Web UI credential management
## Context Links
- CLI credentials table action: `ui/web/src/pages/cli-credentials/cli-credentials-table.tsx:70`
- Current user credential dialog: `ui/web/src/pages/cli-credentials/cli-user-credentials-dialog.tsx:70`
- Current git typed fields: `ui/web/src/pages/cli-credentials/cli-credential-git-fields.tsx`
- Current hooks: `ui/web/src/pages/cli-credentials/hooks/use-cli-credentials.ts`
- Current i18n namespace: `ui/web/src/i18n/locales/en/cli-credentials.json`
## Overview
Make agent credentials the primary UI path for git PAT and SSH setup. Keep user credentials available but visually demote them to advanced personal overrides.
Priority: P1.
Status: pending.
## Key Insights
- Current UI exposes git typed fields inside `User Credentials`, which requires operators to know a stable `user_id`.
- Issue #117 asks for obvious `GH_PAT` or SSH fields. The primary action should therefore be per-agent credential setup from the git template row.
- Agent access becomes the practical permission boundary. UI must say this plainly without leaking secrets.
## Requirements
- Add Agent Credentials action/button in the CLI credentials table.
- For git adapter rows, default the credential form to PAT with `host_scope = github.com` placeholder.
- Support SSH key as second option.
- Show effective credential source where useful: user override, context, agent, binary.
- Move User Credentials into advanced/personal override wording.
- Keep mobile-safe dialog behavior.
- Add en/vi/zh i18n keys.
## Architecture
Preferred UI structure:
- Main table actions:
- Grants
- Agent Credentials
- Advanced: User Credentials
- Edit
- Delete
- New dialog:
- agent picker
- credential type selector
- host scope input
- PAT token field or SSH private key textarea
- masked secret state on edit
- help text explaining agent access implies credential use
- Use existing `CliCredentialGitFields` by extracting labels/state shape into reusable props if needed.
## Related Code Files
- Add `ui/web/src/pages/cli-credentials/cli-agent-credentials-dialog.tsx`.
- Reuse or refactor `cli-credential-git-fields.tsx`.
- Extend `ui/web/src/pages/cli-credentials/hooks/use-cli-credentials.ts`.
- Update `cli-credentials-table.tsx` and panel state.
- Update all locale files under `ui/web/src/i18n/locales/{en,vi,zh}/cli-credentials.json`.
- Add/update tests in `ui/web/src/pages/cli-credentials/__tests__/`.
## Implementation Steps
1. Add API hook methods:
- `listAgentCredentials(binaryId)`
- `getAgentCredential(binaryId, agentId)`
- `setAgentCredential(binaryId, agentId, payload)`
- `deleteAgentCredential(binaryId, agentId)`
2. Add `CliAgentCredentialsDialog`.
3. Reuse typed git fields and env vars section. Avoid copy-pasting validation logic unless component boundaries require it.
4. Update table/panel to open Agent Credentials as the main credential action.
5. Rename current User Credentials copy to "Advanced user overrides" or equivalent in all locale files.
6. Add a warning/info line: users with access to this agent can cause it to use this credential.
7. Add UI tests:
- git row shows Agent Credentials action
- PAT form posts to `/agent-credentials/{agentId}`
- edit state shows masked secret and does not submit empty replacement
- User Credentials remains reachable as advanced override
8. Run `pnpm` tests/build for `ui/web`.
## Todo List
- [ ] Agent credential dialog implemented.
- [ ] Hooks added for all new endpoints.
- [ ] Table/panel actions updated.
- [ ] i18n updated in en/vi/zh.
- [ ] UI tests pass.
## Success Criteria
- [ ] Operator can configure `GH_PAT` for `github.com` without typing a channel user ID.
- [ ] Operator can configure SSH private key for a host-scoped git remote.
- [ ] User Credentials path remains available but no longer looks like the default git setup.
- [ ] UI makes the agent-access security boundary visible.
## Risk Assessment
- Risk: table action area becomes crowded. Mitigation: use icon buttons/tooltips or a compact menu if needed.
- Risk: duplicated form state between user and agent dialogs. Mitigation: extract only the shared typed git fields, not the whole dialog.
- Risk: mobile overflow in credential dialog. Mitigation: keep `max-h` scroll region and mobile-safe input sizes.
## Security Considerations
- Never render raw stored token/key after save.
- Clear plaintext form state on close/unmount.
- Do not store plaintext in Zustand or route state.
## Next Steps
- Phase 5 validates runtime behavior against the new source model.
@@ -0,0 +1,116 @@
---
phase: 5
title: "Runtime git adapter validation"
status: pending
effort: ""
---
# Phase 5: Runtime git adapter validation
## Context Links
- Adapter prepare call: `internal/tools/credentialed_exec.go:466`
- Synthetic user credential helper: `internal/tools/credentialed_exec.go:511`
- Git adapter: `internal/tools/credential_adapter_git.go`
- Git adapter tests: `internal/tools/credential_adapter_git_test.go`
- SSH adapter tests: `internal/tools/credential_adapter_git_ssh_test.go`
- Current docs mention User Credentials: `docs/git-credential-adapter.md:29`
## Overview
Wire the effective credential source into runtime execution and validate the git adapter still injects PAT/SSH credentials only for remote git operations.
Priority: P1.
Status: pending.
## Key Insights
- `credentialed_exec.go` currently synthesizes a `SecureCLIUserCredential` from `UserEnv`, `UserCredentialType`, and `UserHostScope`.
- After Phase 2, the adapter should receive a source-neutral credential object, or the helper should be renamed so non-user sources are not misrepresented.
- Git operations must be tested with same-agent, different-channel contexts.
## Requirements
- Keep `git status`, `git log`, and other local-only commands uncredentialed.
- Continue denying sandbox mode for non-passthrough adapters unless sandbox support is explicitly added later.
- Add audit source metadata so operators can tell whether `user`, `context`, or `agent` credential was used.
- Validate PAT header behavior against a GitHub-like HTTP endpoint or fixture.
- Validate SSH key injection still scrubs temp paths and key bytes.
## Architecture
Runtime should deal with a neutral credential payload:
```go
type SecureCLIEffectiveCredential struct {
BinaryID uuid.UUID
SubjectID string
Source string // user, context, agent
EncryptedEnv []byte
CredentialType *string
HostScope *string
}
```
If implementation keeps `SecureCLIUserCredential` as the adapter input for minimal change, add comments/tests that prove `UserID` is metadata-only and do not expose it as the credential source.
## Related Code Files
- Update `internal/tools/credential_adapter.go` if a neutral type is introduced.
- Update `internal/tools/credentialed_exec.go`.
- Update `internal/tools/credential_audit_log_test.go`.
- Update `internal/tools/shell_credentialed_gate_test.go` fake store.
- Update git adapter tests.
## Implementation Steps
1. Decide minimal runtime shape:
- preferred: introduce neutral `SecureCLIEffectiveCredential`
- fallback: keep `SecureCLIUserCredential` but add source metadata elsewhere
2. Update `userCredFromBinary` or replace it with `effectiveCredentialFromBinary`.
3. Ensure adapters receive credential data from user/context/agent source.
4. Add audit source to `emitSystemEnvInjectionAudit`.
5. Add tests:
- no `userID`, agent credential present, git clone injects PAT
- two different `CredentialUserID` values use same agent credential
- user credential overrides agent credential
- context credential overrides agent credential
- agent credential does not bypass grant for non-global binary
- PAT and SSH paths scrub secrets and temp paths
6. Validate PAT transport:
- create a local HTTP test server that captures git extra header behavior, or unit-test the generated Git config/env args
- reconcile docs and code on Basic vs Bearer if mismatch is found
7. Run targeted Go tests for tools/store/http.
## Todo List
- [ ] Runtime uses effective credential from agent source.
- [ ] Audit includes credential source without raw host or secret value.
- [ ] Cross-channel runtime tests pass.
- [ ] Git PAT and SSH adapter tests pass.
- [ ] Grant boundary tests pass.
## Success Criteria
- [ ] Git clone/fetch/pull/push can use an agent credential without a matching user credential.
- [ ] Same agent uses the same credential from Discord, Telegram, HTTP, and cron contexts.
- [ ] Per-user overrides remain backward compatible.
- [ ] Local git operations remain uncredentialed.
- [ ] Secrets remain scrubbed from output, logs, and errors.
## Risk Assessment
- Risk: adapter API churn touches many tests. Mitigation: start with a small neutral adapter type and update fake stores in one pass.
- Risk: PAT auth behavior is wrong for GitHub. Mitigation: add characterization test and update docs/code together.
- Risk: audit source reveals too much host info. Mitigation: keep host hashed or omit host value, matching current audit style.
## Security Considerations
- Host scope validation remains exact host or host:port, no wildcards.
- Deny patterns from binary/grant/context still apply after credential resolution.
- Temporary SSH files must be removed and scrubbed from errors.
## Next Steps
- Phase 6 updates docs and performs final plan/implementation validation.
@@ -0,0 +1,106 @@
---
phase: 6
title: "Docs validation and handoff"
status: pending
effort: ""
---
# Phase 6: Docs validation and handoff
## Context Links
- Git guide: `docs/git-credential-adapter.md`
- HTTP API docs: `docs/18-http-api.md`
- API auth docs: `docs/20-api-keys-auth.md`
- Security docs: `docs/09-security.md`
- Store model docs: `docs/06-store-data-model.md`
- Project changelog: `docs/project-changelog.md`
## Overview
Update user-facing and developer docs, run validation, and leave a clean handoff for implementation review.
Priority: P1.
Status: pending.
## Key Insights
- The current git guide says to open User Credentials. That will become the advanced path.
- API docs must include the new endpoints because user specifically asked for endpoint support.
- Docs must state the trust model: agent access implies ability to cause that agent to use its configured git credential.
## Requirements
- Update docs for agent credential default.
- Keep user credential override documented.
- Add HTTP API endpoint table and examples.
- Update security/data-model docs.
- Update changelog.
- Run code, test, and build validation appropriate to touched files.
## Architecture
Documentation model:
- Quick start: create git CLI credential, grant/use agent, add agent credential.
- PAT path: fine-grained PAT preferred where possible, `host_scope = github.com`.
- SSH path: unencrypted private key only, public key added to git host by operator.
- Advanced path: per-user credential overrides for personal credentials.
- Security model: any principal with access to run the agent can trigger the credential.
## Related Code Files
- `docs/git-credential-adapter.md`
- `docs/18-http-api.md`
- `docs/20-api-keys-auth.md`
- `docs/09-security.md`
- `docs/06-store-data-model.md`
- `docs/project-changelog.md`
## Implementation Steps
1. Rewrite git guide "Adding a credential" around Agent Credentials first.
2. Add "Advanced user overrides" section.
3. Add endpoint documentation for all agent credential routes.
4. Add request/response examples for PAT and SSH.
5. Update security docs with trust boundary and secret masking.
6. Update data model docs with `secure_cli_agent_credentials`.
7. Update changelog with issue #117 entry.
8. Run validation:
- `go test ./internal/store/... ./internal/http/... ./internal/tools/...`
- `go build ./...`
- `go build -tags sqliteonly ./...`
- `cd ui/web && pnpm test -- --run`
- `cd ui/web && pnpm build`
9. If full integration tests are skipped due local database requirements, state that explicitly in final handoff.
## Todo List
- [ ] Git guide updated.
- [ ] HTTP/API auth docs updated.
- [ ] Security and data-model docs updated.
- [ ] Changelog updated.
- [ ] Validation commands recorded with pass/fail status.
## Success Criteria
- [ ] A new operator can find where to enter `GH_PAT` or SSH key without knowing channel user IDs.
- [ ] API consumers can manage agent credentials without reading code.
- [ ] Security docs describe agent access as the permission boundary.
- [ ] Build/test output supports merge readiness.
## Risk Assessment
- Risk: docs overpromise support for GitHub App or passphrase SSH. Mitigation: keep out-of-scope section explicit.
- Risk: endpoint examples drift from implementation. Mitigation: generate examples from handler tests where practical.
## Security Considerations
- Do not include real tokens, keys, or screenshots with secrets.
- Use placeholder values only.
- State least-privilege recommendation: fine-grained PAT or deploy key per host/repo where possible.
## Next Steps
- After implementation and validation, open PR referencing issue #117 and this plan.
@@ -0,0 +1,64 @@
---
title: "Agent-scoped Git credentials"
description: "TDD plan for moving git typed credentials from channel/user-id keyed defaults to agent-scoped credentials, with HTTP API and Web UI management."
status: pending
priority: P1
issue: 117
branch: "codex/issue-117-agent-scoped-git-credentials-plan"
tags: []
blockedBy: []
blocks: []
created: "2026-05-31T08:45:33.071Z"
createdBy: "ck:plan"
source: skill
---
# Agent-scoped Git credentials
## Overview
Issue #117 started as a UI gap: the git template does not make it obvious where to enter `GH_PAT` or SSH key material. The deeper design problem is that current `User Credentials` are keyed by credential user ID, while the same human can appear as different external IDs across Discord, Telegram, HTTP, or group contexts.
Decision: make agent-scoped git credentials the primary model. Granting access to an agent becomes the security boundary for whether a user can cause that agent to use a git PAT or SSH key. Keep per-user credentials as an advanced override for backward compatibility and truly personal credentials.
TDD target:
- Add contract tests first for effective credential precedence and API behavior.
- Add a dedicated agent credential storage surface instead of mixing typed secrets into agent grant policy rows.
- Add HTTP endpoints for create, edit, list, detail, and delete of agent-scoped CLI credentials.
- Update Web UI so git PAT and SSH setup is managed from Agent Credentials by default.
- Validate runtime injection across Discord/Telegram/userless contexts uses the same agent credential.
## Phases
| Phase | Name | Status |
|-------|------|--------|
| 1 | [Research and contract tests](./phase-01-research-and-contract-tests.md) | Pending |
| 2 | [Schema and store resolver](./phase-02-schema-and-store-resolver.md) | Pending |
| 3 | [HTTP API credential management](./phase-03-http-api-credential-management.md) | Pending |
| 4 | [Web UI credential management](./phase-04-web-ui-credential-management.md) | Pending |
| 5 | [Runtime git adapter validation](./phase-05-runtime-git-adapter-validation.md) | Pending |
| 6 | [Docs validation and handoff](./phase-06-docs-validation-and-handoff.md) | Pending |
## Dependencies
- Current typed git adapter and validation: `internal/tools/credential_adapter_git.go`, `internal/http/secure_cli_typed_credentials.go`.
- Current lookup joins per-user credentials in `internal/store/pg/secure_cli.go` and SQLite equivalent.
- Current UI git form lives under `ui/web/src/pages/cli-credentials/cli-user-credentials-dialog.tsx`.
- Migration number must be re-verified at implementation time. As of plan creation, latest PostgreSQL migration is `000076_channel_memory_extraction`.
## Verified Facts
- `LookupByBinary` takes `(binaryName, agentID, userID)` and only joins `secure_cli_user_credentials` when `userID` is non-empty.
- Context credentials can currently override/fill credential fields via `applyContextSecureCLI`, but there is no agent credential typed secret row.
- `SecureCLIAgentGrant` already has `encrypted_env`, but lacks `credential_type` and `host_scope`; using it for typed git secrets would mix policy and secret identity.
- HTTP routes currently expose `/v1/cli-credentials/{id}/user-credentials...` but not agent credential endpoints.
- The Web UI currently opens User Credentials from the CLI credentials table action.
## Out of Scope
- GitHub App installation tokens.
- OAuth/device-code token minting.
- Wildcard host scopes.
- Passphrase-protected SSH keys.
- Sandbox-mode git credential injection.
@@ -0,0 +1,75 @@
# Redteam Report: Agent-scoped Git Credentials
Date: 2026-05-31
Scope: plan review for issue #117 before implementation.
## Findings
### R1 - Credential rows could accidentally bypass grants
Risk: If runtime joins `secure_cli_agent_credentials` without preserving the existing non-global grant gate, creating a credential row becomes an implicit grant.
Fix in plan: Phase 2 and Phase 5 require tests that non-global binaries still need `secure_cli_agent_grants`. Credential rows store secrets only.
Status: fixed in plan.
### R2 - Agent grants are the wrong place for typed secrets
Risk: `secure_cli_agent_grants.encrypted_env` already exists and is tempting to reuse, but it is policy override state and lacks `credential_type` and `host_scope`.
Fix in plan: Phase 2 uses dedicated `secure_cli_agent_credentials`.
Status: fixed in plan.
### R3 - User credential precedence could preserve the confusing default
Risk: Keeping user credentials as the visible default would not solve cross-channel identity confusion.
Fix in plan: Phase 4 makes Agent Credentials the primary git path and moves User Credentials to advanced overrides.
Status: fixed in plan.
### R4 - API support could lag behind UI
Risk: A UI-only feature would block automation and contradict the user requirement.
Fix in plan: Phase 3 defines full CRUD endpoints, request bodies, response masking, audit events, docs, and tests.
Status: fixed in plan.
### R5 - Typed validation might fork and drift
Risk: Copying PAT/SSH validation into a new handler can create inconsistent behavior between user and agent credentials.
Fix in plan: Phase 3 requires reusing `prepareTypedCredentialEnv` or a shared equivalent.
Status: fixed in plan.
### R6 - SQLite migration can be missed
Risk: Desktop edition uses SQLite and can break if only PostgreSQL migrations are added.
Fix in plan: Phase 2 requires PG migration, SQLite fresh schema, SQLite incremental migration, and version bump.
Status: fixed in plan.
### R7 - Runtime audit source can become misleading
Risk: Existing audit uses credential user ID. Agent credentials would make that label inaccurate.
Fix in plan: Phase 5 requires source-neutral credential metadata and audit source coverage.
Status: fixed in plan.
### R8 - PAT transport behavior needs proof
Risk: Docs and adapter assumptions around GitHub HTTPS auth can silently diverge.
Fix in plan: Phase 5 requires a GitHub-like PAT transport characterization test and code/docs reconciliation.
Status: fixed in plan.
## Unresolved Questions
None.
@@ -0,0 +1,75 @@
# Validation Report: Agent-scoped Git Credentials Plan
Date: 2026-05-31
Scope: validate plan completeness and consistency against current code.
## Checks
### V1 - Current failure mode is represented
Evidence: current resolver can return channel-specific user IDs, and `LookupByBinary` only joins user credentials when `userID` is non-empty.
Plan coverage: Phase 1 and Phase 5 include cross-channel/no-user tests.
Status: pass.
### V2 - API endpoints are included
Evidence: current routes include user credential endpoints only.
Plan coverage: Phase 3 defines list/get/put/delete agent credential endpoints and docs updates.
Status: pass.
### V3 - Store model separates policy from secret identity
Evidence: `SecureCLIAgentGrant` has policy fields plus encrypted env override but no typed credential metadata.
Plan coverage: Phase 2 adds dedicated `secure_cli_agent_credentials`.
Status: pass.
### V4 - Dual database migration is covered
Evidence: repo has separate PostgreSQL migrations and SQLite schema/version migrations.
Plan coverage: Phase 2 explicitly updates both systems.
Status: pass.
### V5 - UI default path matches product decision
Evidence: current table opens User Credentials, and current git guide documents User Credentials.
Plan coverage: Phase 4 and Phase 6 make Agent Credentials the default and keep User Credentials as advanced override.
Status: pass.
### V6 - TDD gates are explicit
Evidence: implementation phases depend on failing tests from Phase 1.
Plan coverage: every phase lists tests or validation commands.
Status: pass.
### V7 - Security boundary is explicit
Evidence: agent access is the proposed operational permission boundary.
Plan coverage: Phase 3, Phase 4, and Phase 6 all require warnings/docs/tests that credential does not grant binary access but agent users can trigger credential use.
Status: pass.
## Fixes Applied During Validation
- Added explicit non-global grant boundary tests.
- Added exact HTTP endpoint list and response shapes.
- Added SQLite migration requirement.
- Added runtime audit source requirement.
- Added PAT transport characterization requirement.
## Unresolved Questions
None.