Merge pull request #120 from digitopvn/codex/issue-117-agent-scoped-git-credentials-plan

feat(cli-credentials): add agent-scoped git credentials
This commit is contained in:
Duy /zuey/ authored and GitHub committed 2026-05-31 17:40:50 +07:00
commit 10e663f21d
51 files changed
+3060 -85

No files matched your search

+11 -1
View File
@@ -480,7 +480,14 @@ Pre-computed usage snapshots (hourly aggregations) for analytics dashboards. Tra
### SecureCLIStore
CLI binary credential configuration with encrypted environment variable injection. Credentials are auto-injected into child processes without exposing them to command output.
CLI binary credential configuration with encrypted environment variable
injection. Credentials are auto-injected into child processes without exposing
them to command output.
Credential rows can live at binary, agent, channel/context, or user scope.
Runtime resolution prefers user overrides, then context credentials, then agent
credentials, then binary defaults. The `secure_cli_agent_credentials` table
stores one encrypted PAT/SSH/env payload per `(binary_id, agent_id, tenant_id)`.
| Method | Purpose |
|--------|---------|
@@ -492,6 +499,9 @@ CLI binary credential configuration with encrypted environment variable injectio
| `ListByAgent(agentID)` | Return configs for a specific agent |
| `LookupByBinary(binaryName, agentID)` | Find best-matching config (agent-specific > global) |
| `ListEnabled()` | Return enabled configs for TOOLS.md generation |
| `ListAgentCredentials(binaryID)` | Return masked agent credential metadata |
| `SetAgentCredentialsTyped(binaryID, agentID, env, type, hostScope)` | Store agent-scoped PAT/SSH/env payload |
| `DeleteAgentCredentials(binaryID, agentID)` | Remove an agent-scoped credential |
### APIKeyStore
+7 -1
View File
@@ -231,7 +231,7 @@ AES-256-GCM encryption for secrets stored in PostgreSQL. Key provided via `GOCLA
| LLM provider API keys | `llm_providers` | `api_key` |
| MCP server API keys | `mcp_servers` | `api_key` |
| Custom tool env vars | `custom_tools` | `env` |
| Credentialed CLI env vars | `secure_cli_binaries`, `secure_cli_agent_grants`, `secure_cli_user_credentials` | `encrypted_env` |
| Credentialed CLI env vars | `secure_cli_binaries`, `secure_cli_agent_grants`, `secure_cli_user_credentials`, `secure_cli_agent_credentials` | `encrypted_env` |
**Format**: `"aes-gcm:" + base64(12-byte nonce + ciphertext + GCM tag)`
@@ -534,6 +534,11 @@ Implementer guide: [credential-adapter-playbook.md](./credential-adapter-playboo
Both paths coexist. A typo in `adapter_name` falls back to passthrough, which
restores the legacy denylist-only behavior — no silent bypass.
Runtime credential precedence is explicit: user override, then channel/context
credential, then agent credential, then binary-level env defaults. Agent
credentials are the default git trust boundary; granting access to an agent
also grants the ability to make that agent use its stored git credential.
### Audit log: `security.system_env_injection`
Every adapter injection emits **exactly one** structured slog line. Field
@@ -547,6 +552,7 @@ the test and operator-facing log-search recipes.
| `adapter` | string | e.g. `git`, `psql`, `passthrough` |
| `binary` | string | binary name (`git`, `kubectl`, …) |
| `user_id` | string | tenant user UUID (empty for global-only contexts) |
| `credential_source` | string | `user`, `context`, `agent`, or empty when no scoped credential row was selected |
| `env_keys` | []string | sorted env-var NAMES (never values) |
| `argv_prefix_len` | int | number of argv elements prepended (NOT their content) |
| `host_scope_hash` | string | SHA-256 first 8 hex chars of normalized host_scope, or `"none"` |
+37
View File
@@ -1173,8 +1173,45 @@ CLI authentication credentials for secure command execution. Requires **admin ro
| `DELETE` | `/v1/cli-credentials/{id}` | Delete credential |
| `POST` | `/v1/cli-credentials/{id}/test` | Test credential connection (dry-run) |
### Agent Credentials
Agent credentials store PAT/SSH/env material for one CLI credential and one
agent. They are the default git setup path; agent access controls who can cause
the credential to be used. Responses return metadata only and never include raw
typed token/key blobs.
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/v1/cli-credentials/{id}/agent-credentials` | List agent credentials for CLI cred |
| `GET` | `/v1/cli-credentials/{id}/agent-credentials/{agentId}` | Get agent credential metadata |
| `PUT` | `/v1/cli-credentials/{id}/agent-credentials/{agentId}` | Set agent credential |
| `DELETE` | `/v1/cli-credentials/{id}/agent-credentials/{agentId}` | Delete agent credential |
Typed git request body:
```json
{
"credential_type": "pat",
"host_scope": "github.com",
"blob": { "token": "ghp_..." }
}
```
Env request body:
```json
{
"env": {
"GH_TOKEN": { "kind": "sensitive", "value": "..." }
}
}
```
### Per-User Credentials
Advanced personal overrides. These remain for backward compatibility and have
higher runtime precedence than channel/context and agent credentials.
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/v1/cli-credentials/{id}/user-credentials` | List user credentials for CLI cred |
+4
View File
@@ -335,6 +335,10 @@ CREATE TABLE secure_cli_binaries (
| `DELETE` | `/v1/cli-credentials/{id}` | Delete a SecureCLI config |
| `POST` | `/v1/cli-credentials/{id}/test` | Dry-run test (requires admin) |
| `GET` | `/v1/cli-credentials/presets` | List preset templates for common CLIs |
| `GET` | `/v1/cli-credentials/{id}/agent-credentials` | List agent-scoped credentials |
| `GET` | `/v1/cli-credentials/{id}/agent-credentials/{agentId}` | Get agent-scoped credential metadata |
| `PUT` | `/v1/cli-credentials/{id}/agent-credentials/{agentId}` | Create or replace agent-scoped credential |
| `DELETE` | `/v1/cli-credentials/{id}/agent-credentials/{agentId}` | Delete agent-scoped credential |
### Google Workspace CLI preset
+39 -9
View File
@@ -24,9 +24,31 @@ The typed `git` adapter accepts either a **Personal Access Token (PAT)** or an
| **SSH** | Self-hosted git over SSH. You manage `~/.ssh/known_hosts` or accept TOFU risk. | Passphrase-protected keys are NOT supported (see below). |
| **Env** | Legacy path — you have a custom env-var-driven workflow. | Loses host-scoped routing; same trust profile as other CLIs. |
## Adding a credential (UI)
## Adding an agent credential (UI)
1. Open **Settings → CLI Credentials → User Credentials → Add**.
Agent credentials are the default path for git auth. They avoid channel-user
ID ambiguity: the selected agent owns the credential, and anyone allowed to use
that agent can cause it to run git with the stored credential.
1. Open **Packages → CLI Credentials**.
2. Pick the `git` row and open **Agent Credentials**.
3. Select the agent.
4. Choose **Credential Type**: `Personal Access Token` or `SSH Private Key`.
5. Enter **Host Scope** (required for PAT/SSH): the hostname the credential
authenticates to.
- Examples: `github.com`, `gitlab.example.com`, `gitea.internal:8443`.
- Case-insensitive. Punycode normalized via `idna.ToASCII`.
- Port included only when non-default for the scheme.
6. Paste the token (PAT) or the unencrypted PEM body (SSH).
7. Save.
## Advanced user overrides
Per-user credentials remain available for personal overrides and backward
compatibility. Use them only when a stable tenant user ID is the intended
credential boundary.
1. Open **Packages → CLI Credentials → Advanced User Overrides → Add**.
2. Select user.
3. Choose **Credential Type**: `Personal Access Token` or `SSH Private Key`.
4. Enter **Host Scope** (required for PAT/SSH): the hostname the credential
@@ -41,6 +63,13 @@ The stored secret is encrypted (AES-256-GCM) and can never be read back through
the API or UI. Editing the row shows a `••••••••` placeholder; leaving the
secret field blank preserves the stored value, typing a new value replaces it.
Effective credential precedence is:
1. User override.
2. Channel/context credential.
3. Agent credential.
4. Binary-level env defaults.
## What gets auto-injected
The adapter runs ONLY for these subcommands:
@@ -140,7 +169,7 @@ Every successful credential injection emits exactly one structured log line:
```
level=WARN msg=security.system_env_injection
adapter=git binary=git user_id=<uuid>
adapter=git binary=git user_id=<uuid> credential_source=agent
env_keys=[GIT_CONFIG_COUNT,GIT_CONFIG_KEY_0,GIT_CONFIG_VALUE_0]
argv_prefix_len=0
host_scope_hash=3aeb0024
@@ -159,12 +188,12 @@ See `docs/09-security.md` → "CLI credential adapters" for the full schema.
## Migration from legacy env-paste
Existing rows in `secure_cli_user_credentials` with `credential_type IS NULL`
or `= 'env'` continue to work via the passthrough adapter — they keep
emitting their env vars exactly as before. There is no forced migration.
or `= 'env'` continue to work via the passthrough adapter. Existing user
overrides remain higher precedence than agent credentials. There is no forced
migration.
To upgrade an existing git credential, open the user-credentials dialog, pick
PAT or SSH, paste the secret, and save. The legacy env-paste row is replaced
atomically.
To move to the agent-scoped model, create a matching Agent Credential for the
agent and remove the user override when the override is no longer needed.
## Operator notes
@@ -186,7 +215,8 @@ atomically.
## Known limitations (v1)
- One credential per (user, binary, host_scope) row.
- One credential per (agent, binary) row, plus legacy one credential per
(user, binary) override.
- No multi-host wildcard (`*.github.com`).
- No passphrase-protected SSH keys.
- No persistent `known_hosts` per credential (TOFU only).
+23
View File
@@ -4,6 +4,29 @@ Significant changes, features, and fixes in reverse chronological order.
---
## 2026-05-31
### Agent-scoped git credentials (issue #117)
**New**
- Added agent-scoped Secure CLI credentials with PostgreSQL migration `000077`
and SQLite schema version `46`.
- Added HTTP APIs under
`/v1/cli-credentials/{id}/agent-credentials/{agentId}` for listing,
reading metadata, saving, and deleting agent credentials.
- Web CLI Credentials now exposes Agent Credentials as the primary git PAT/SSH
setup path, with User Credentials renamed to advanced personal overrides.
**Security**
- Runtime credential precedence is now user override, context credential, agent
credential, then binary env defaults.
- Git adapter audit logs include `credential_source` without logging raw
secrets or plaintext host scopes.
---
## 2026-05-29
### Passive channel memory extraction (issue #64)