Access Control
Team-mode gate for who can invoke the agent. Configure via .codebuddy/access.json at the workspace root, or set a default via editor settings.
| Mode | Behavior |
|---|---|
open | No restrictions. Everyone in. Default. |
allow | Only users in the users array may invoke the agent. |
deny | Users in the users array are blocked; everyone else allowed. |
Config
Section titled “Config”{ "mode": "allow", "users": ["alice@company.com", "bob-dev"], "admins": ["alice@company.com"], "logDenied": true}| Field | Type | Default | Purpose |
|---|---|---|---|
mode | string | open | open / allow / deny |
users | string[] | [] | Emails or GitHub usernames (max 200) |
admins | string[] | [] | Bypass restrictions AND skip escalation prompts (max 50) |
logDenied | boolean | true | Log denied attempts to the audit ring |
File capped at 64 KB.
Identity resolution
Section titled “Identity resolution”
sequenceDiagram
participant ACL
participant GH as GitHub auth
participant Git as git config
participant Cache
ACL->>Cache: 5-min TTL check
alt Hit
Cache-->>ACL: identity
else Miss
ACL->>GH: getSession(silent)
alt GitHub session
GH-->>ACL: username
else No session
ACL->>Git: git config user.email
Git-->>ACL: email
end
ACL->>Cache: store
end
ACL->>ACL: lowercase + validate
ACL->>ACL: check allow/deny
Order: silent GitHub session → git config --get user.email (3 s timeout) → unknown fallback.
Validation: emails match user@domain.tld (max 254 chars per RFC 5321). GitHub usernames = 1–39 alphanumeric/hyphen, no leading/trailing hyphens.
Cache: 5-min TTL to avoid repeated auth + git lookups.
Admins
Section titled “Admins”Users in admins get:
- Bypass the allow/deny gate regardless of mode.
- Skip escalation prompts for sensitive operations (destructive file ops etc.) — auto-approved.
- Still audited. Full trail retained.
Audit log
Section titled “Audit log”Every access decision recorded when logDenied is on:
{ "timestamp": 1711612800000, "user": "bob-dev", "action": "agent_invoke", "allowed": false}In-memory ring buffer, 500-entry cap (oldest evicted). Denied-access log writes throttled to one per 100 ms to prevent flooding.
Live reload
Section titled “Live reload”.codebuddy/access.json is watched:
- Changes debounced.
- Concurrent reload requests serialized (single
loadConfigat a time). onAccessChangedfires so UI can react.
Diagnostics (Doctor)
Section titled “Diagnostics (Doctor)”| Code | Severity | Meaning |
|---|---|---|
no-config | info | No access.json — running in open mode |
config-loaded | info | Config loaded |
no-user-identity | warn | Could not determine current user |
empty-user-list | warn | Mode is allow/deny but list is empty |
user-denied | critical | Current user denied |
user-allowed | info | Current user allowed |
Editor default (no config file)
Section titled “Editor default (no config file)”{ "codebuddy.accessControl.defaultMode": "open" }Workspace .codebuddy/access.json takes priority when present.
Security guarantees
Section titled “Security guarantees”- Path traversal — config path resolved + verified within workspace boundary.
- Size limit — 64 KB before parsing.
- Input validation — every identity, mode, and list entry sanitized.
- No token exposure — identity resolution uses silent auth, reads username/email only, never tokens.
Related
Section titled “Related”- Security — the broader security layers this fits into
- Credential proxy — the other team-mode control (centralized keys)