feat: implement multi-tenant credential storage#178
Conversation
Add credential-helper.sh for managing multiple credential sets (tenants)
per service, enabling agency/freelance workflows with per-client API keys.
Architecture:
- ~/.config/aidevops/tenants/{tenant}/mcp-env.sh per tenant
- Priority: project (.aidevops-tenant) > global > default
- Backward compatible: existing mcp-env.sh migrates to 'default' tenant
Features:
- create/switch/delete tenants
- set/get/remove keys per tenant
- copy keys between tenants
- per-project tenant override (.aidevops-tenant)
- export command for script integration
- MCP tool updated with tenant-* actions
Closes #t005
Summary of ChangesHello @marcusquinn, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request introduces a significant enhancement to credential management by implementing a multi-tenant storage system. This allows users to organize and switch between distinct sets of API keys and environment variables, catering to scenarios like managing credentials for multiple clients, separating development and production environments, or handling personal and work accounts. The change provides a flexible and secure way to handle credentials, improving workflow efficiency and reducing the risk of using incorrect keys. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughAdds a new multi-tenant credential manager via Changes
Sequence DiagramssequenceDiagram
participant User
participant Shell
participant CredHelper as credential-helper.sh
participant Storage as ~/.config/aidevops/
participant Legacy as Legacy mcp-env.sh
User->>Shell: credential-helper.sh init
Shell->>CredHelper: run init
CredHelper->>Legacy: check legacy mcp-env.sh
alt legacy exists
CredHelper->>CredHelper: parse legacy
CredHelper->>Storage: create default tenant dir & write mcp-env.sh
CredHelper->>Legacy: rewrite to source active tenant
else no legacy
CredHelper->>Storage: create config structure and default tenant
end
CredHelper->>Shell: print completion
sequenceDiagram
participant User
participant Shell
participant CredHelper as credential-helper.sh
participant ProjFile as .aidevops-tenant
participant GlobalFile as ~/.config/aidevops/active-tenant
participant TenantStorage as tenants/{tenant}/mcp-env.sh
User->>Shell: credential-helper.sh set KEY VALUE
Shell->>CredHelper: run set
CredHelper->>ProjFile: check project override
alt project override exists
CredHelper->>TenantStorage: write KEY=VALUE to project tenant file
else
CredHelper->>GlobalFile: read active tenant
CredHelper->>TenantStorage: write KEY=VALUE to global tenant file (or default)
end
CredHelper->>Shell: confirm stored
User->>Shell: credential-helper.sh get KEY
Shell->>CredHelper: run get
CredHelper->>ProjFile: check override
CredHelper->>GlobalFile: fallback if no override
CredHelper->>TenantStorage: read mcp-env.sh and output KEY value
Estimated Code Review Effort🎯 4 (Complex) | ⏱️ ~50 minutes
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
🔍 Code Quality Report�[0;35m[MONITOR]�[0m Code Review Monitoring Report �[0;34m[INFO]�[0m Latest Quality Status: �[0;34m[INFO]�[0m Recent monitoring activity: 📈 Current Quality Metrics
Generated on: Sat Jan 24 16:35:15 UTC 2026 Generated by AI DevOps Framework Code Review Monitoring |
There was a problem hiding this comment.
Code Review
This pull request introduces a robust multi-tenant credential management system, centered around the new credential-helper.sh script. The implementation is comprehensive, covering tenant lifecycle management, backward compatibility with legacy credentials, and integration with existing tools. The code is well-structured and includes thorough documentation.
My review focuses on improving the robustness and maintainability of the new bash script. I've identified a high-severity issue in how credential values are updated, which could fail with special characters. I've also included a couple of medium-severity suggestions to improve code clarity and efficiency. Overall, this is a great feature addition.
.agent/scripts/credential-helper.sh
Outdated
| # Update or append | ||
| if grep -q "^export ${env_var}=" "$env_file" 2>/dev/null; then | ||
| local tmp_file="${env_file}.tmp" | ||
| sed "s|^export ${env_var}=.*|export ${env_var}=\"${value}\"|" "$env_file" > "$tmp_file" |
There was a problem hiding this comment.
The sed command used here is not safe for arbitrary values. If the <value> contains the sed delimiter (|) or other special characters like &, the command will fail or have unexpected behavior. A more robust approach is to use grep -v to remove the existing line and then echo to append the new one. This avoids issues with special characters in the value, though it may change the key's position in the file, which should be an acceptable trade-off for correctness.
| sed "s|^export ${env_var}=.*|export ${env_var}=\"${value}\"|" "$env_file" > "$tmp_file" | |
| { grep -v "^export ${env_var}=" "$env_file"; echo "export ${env_var}=\"${value}\""; } > "$tmp_file" |
| print_success() { | ||
| local msg="$1" | ||
| echo -e "${GREEN}[OK] $msg${NC}" | ||
| return 0 |
There was a problem hiding this comment.
The explicit return 0 is redundant in this and the other print_* functions (print_info, print_warning, print_error). Shell functions implicitly return the exit status of the last command executed (in this case, echo). The calling code is already responsible for the script's overall exit status. Removing these return 0 statements would make the functions slightly cleaner.
.agent/scripts/credential-helper.sh
Outdated
| fi | ||
|
|
||
| local result | ||
| result=$(grep "^export ${env_var}=" "$env_file" 2>/dev/null | sed 's/^export [^=]*="//' | sed 's/"$//' || true) |
There was a problem hiding this comment.
This line uses grep piped to two separate sed commands to extract the value. This can be made more efficient and readable by using a single sed command with a capturing group to extract the value in one step.
| result=$(grep "^export ${env_var}=" "$env_file" 2>/dev/null | sed 's/^export [^=]*="//' | sed 's/"$//' || true) | |
| result=$(grep "^export ${env_var}=" "$env_file" 2>/dev/null | sed 's/^export [^=]*="\(.*\)"$/\1/' || true) |
Replace sed substitution with grep -v + append pattern to safely handle values containing pipe (|) and ampersand (&) characters. Also simplify the get command's value extraction to a single sed expression.
🔍 Code Quality Report�[0;35m[MONITOR]�[0m Code Review Monitoring Report �[0;34m[INFO]�[0m Latest Quality Status: �[0;34m[INFO]�[0m Recent monitoring activity: 📈 Current Quality Metrics
Generated on: Sat Jan 24 16:40:35 UTC 2026 Generated by AI DevOps Framework Code Review Monitoring |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Fix all issues with AI agents
In @.agent/scripts/credential-helper.sh:
- Around line 58-91: The tenant read paths (get_active_tenant reading
PROJECT_TENANT_FILE and ACTIVE_TENANT_FILE) currently return unchecked values;
ensure every tenant value is validated with the existing validate_tenant_name
function (or a single centralized validate_tenant_name wrapper) before being
used to construct or read paths and before honoring any --tenant CLI input or
project/global file content; if validation fails, log/print an error and fall
back to "default" rather than returning the untrusted value (apply same
validation to cmd_* entrypoints and any callers that consume tenant names).
- Around line 292-412: The current cmd_set writes raw secret values into the
tenant env file and uses sed/echo unescaped, which corrupts or executes values
containing $, \, ", &, etc.; update cmd_set to safely escape or encode values
before writing/updating (e.g. escape backslashes, double-quotes, dollar signs
and ampersands or encode with base64) and perform the file update by writing a
new line with the already-escaped/encoded payload to a temp file then atomically
mv it over env_file (use ensure_tenant_dir/get_tenant_env_file and refer to
env_var for the var name); likewise change the update path that does sed
"s|^export ${env_var}=.*|...|" to use the safe temp-file write with the escaped
value. In cmd_get, do not parse with brittle sed pipelines—read the real value
by sourcing the tenant env file in a subshell or by decoding the stored encoding
(if you opted for base64) and then output the variable (use env_var to locate
the variable). Ensure chmod 600 is preserved and both cmd_set and cmd_get handle
empty/missing files the same as before.
In @.agent/scripts/setup-local-api-keys.sh:
- Around line 317-322: The tenant branch currently calls credential-helper.sh
but ignores its exit status, causing main to return 0; capture the helper's exit
code (e.g., after bash "$script_dir/credential-helper.sh" "$@") and propagate it
by exiting or returning that code from the script (e.g., store "$?" in a
variable and call exit with that variable) so the "tenant"|"tenants") case and
the overall main return the helper’s status.
In @.agent/tools/credentials/api-key-setup.md:
- Around line 218-235: Update the Multi-Tenant section and the Quick
Reference/Storage sections to clarify that when tenants are enabled credentials
live in per-tenant files under ~/.config/aidevops/tenants/{tenant}/mcp-env.sh
and that ~/.config/aidevops/mcp-env.sh acts only as a loader for the active
tenant; add a short note (exact wording like the suggested clarification) and
link to multi-tenant.md so readers aren’t misled about where secrets are stored
after running credential-helper.sh init/create/switch/use.
In @.agent/tools/credentials/multi-tenant.md:
- Around line 68-105: The examples invoke credential-helper.sh directly which
assumes it is on PATH; update the documentation examples and the Initialize
section to invoke the script explicitly (e.g. bash
.agent/scripts/credential-helper.sh init and bash
.agent/scripts/credential-helper.sh create <tenant>, set, switch, use) or add a
short note before the first usage explaining how to run it if not on PATH
(alternatively reference setup-local-api-keys.sh tenant as an option); adjust
every occurrence of credential-helper.sh in the Create Tenants, Add Credentials,
and Switch Tenants blocks to the explicit invocation or add the single NOTE line
so new clones can run the commands without PATH assumptions.
| # Validate tenant name (alphanumeric, hyphens, underscores) | ||
| validate_tenant_name() { | ||
| local name="$1" | ||
| if [[ ! "$name" =~ ^[a-zA-Z0-9][a-zA-Z0-9_-]*$ ]]; then | ||
| print_error "Invalid tenant name: '$name'. Use alphanumeric, hyphens, underscores." | ||
| return 1 | ||
| fi | ||
| return 0 | ||
| } | ||
|
|
||
| # Get the active tenant name | ||
| get_active_tenant() { | ||
| # Priority: 1) Project override, 2) Global active, 3) "default" | ||
| if [[ -f "$PROJECT_TENANT_FILE" ]]; then | ||
| local project_tenant | ||
| project_tenant=$(cat "$PROJECT_TENANT_FILE" 2>/dev/null | tr -d '[:space:]') | ||
| if [[ -n "$project_tenant" ]]; then | ||
| echo "$project_tenant" | ||
| return 0 | ||
| fi | ||
| fi | ||
|
|
||
| if [[ -f "$ACTIVE_TENANT_FILE" ]]; then | ||
| local active | ||
| active=$(cat "$ACTIVE_TENANT_FILE" 2>/dev/null | tr -d '[:space:]') | ||
| if [[ -n "$active" ]]; then | ||
| echo "$active" | ||
| return 0 | ||
| fi | ||
| fi | ||
|
|
||
| echo "default" | ||
| return 0 | ||
| } |
There was a problem hiding this comment.
Validate tenant names everywhere before constructing paths.
Project/global tenant values (and --tenant inputs) are used without validation, so a crafted name like ../... could write or source outside ~/.config/aidevops/tenants. Please validate on read and on all tenant inputs (or centralize validation) and fall back to default when invalid.
🔒 Proposed hardening (apply similarly in cmd_* entrypoints)
validate_tenant_name() {
local name="$1"
if [[ ! "$name" =~ ^[a-zA-Z0-9][a-zA-Z0-9_-]*$ ]]; then
print_error "Invalid tenant name: '$name'. Use alphanumeric, hyphens, underscores."
return 1
fi
return 0
}
+
+require_valid_tenant() {
+ local name="$1"
+ validate_tenant_name "$name" || return 1
+ return 0
+}
# Get the active tenant name
get_active_tenant() {
# Priority: 1) Project override, 2) Global active, 3) "default"
if [[ -f "$PROJECT_TENANT_FILE" ]]; then
local project_tenant
project_tenant=$(cat "$PROJECT_TENANT_FILE" 2>/dev/null | tr -d '[:space:]')
if [[ -n "$project_tenant" ]]; then
- echo "$project_tenant"
- return 0
+ if require_valid_tenant "$project_tenant"; then
+ echo "$project_tenant"
+ return 0
+ fi
+ print_warning "Ignoring invalid project tenant '$project_tenant'"
fi
fi
if [[ -f "$ACTIVE_TENANT_FILE" ]]; then
local active
active=$(cat "$ACTIVE_TENANT_FILE" 2>/dev/null | tr -d '[:space:]')
if [[ -n "$active" ]]; then
- echo "$active"
- return 0
+ if require_valid_tenant "$active"; then
+ echo "$active"
+ return 0
+ fi
+ print_warning "Ignoring invalid active tenant '$active'"
fi
fi🧰 Tools
🪛 GitHub Check: Codacy Static Code Analysis
[notice] 73-73: .agent/scripts/credential-helper.sh#L73
Useless cat. Consider 'cmd < file | ..' or 'cmd file | ..' instead.
[notice] 82-82: .agent/scripts/credential-helper.sh#L82
Useless cat. Consider 'cmd < file | ..' or 'cmd file | ..' instead.
🤖 Prompt for AI Agents
In @.agent/scripts/credential-helper.sh around lines 58 - 91, The tenant read
paths (get_active_tenant reading PROJECT_TENANT_FILE and ACTIVE_TENANT_FILE)
currently return unchecked values; ensure every tenant value is validated with
the existing validate_tenant_name function (or a single centralized
validate_tenant_name wrapper) before being used to construct or read paths and
before honoring any --tenant CLI input or project/global file content; if
validation fails, log/print an error and fall back to "default" rather than
returning the untrusted value (apply same validation to cmd_* entrypoints and
any callers that consume tenant names).
| # Set a key for a tenant | ||
| cmd_set() { | ||
| local key="" | ||
| local value="" | ||
| local tenant="" | ||
|
|
||
| # Parse arguments | ||
| while [[ $# -gt 0 ]]; do | ||
| case "$1" in | ||
| --tenant|-t) | ||
| tenant="$2" | ||
| shift 2 | ||
| ;; | ||
| *) | ||
| if [[ -z "$key" ]]; then | ||
| key="$1" | ||
| elif [[ -z "$value" ]]; then | ||
| value="$1" | ||
| fi | ||
| shift | ||
| ;; | ||
| esac | ||
| done | ||
|
|
||
| if [[ -z "$key" || -z "$value" ]]; then | ||
| print_error "Usage: credential-helper.sh set <KEY_NAME> <value> [--tenant <name>]" | ||
| return 1 | ||
| fi | ||
|
|
||
| # Default to active tenant | ||
| if [[ -z "$tenant" ]]; then | ||
| tenant=$(get_active_tenant) | ||
| fi | ||
|
|
||
| migrate_legacy | ||
| ensure_tenant_dir "$tenant" | ||
|
|
||
| local env_file | ||
| env_file=$(get_tenant_env_file "$tenant") | ||
|
|
||
| # Convert service name to env var if needed | ||
| local env_var | ||
| if [[ "$key" =~ ^[A-Z_][A-Z0-9_]*$ ]]; then | ||
| env_var="$key" | ||
| else | ||
| env_var=$(echo "$key" | tr '[:lower:]-' '[:upper:]_') | ||
| fi | ||
|
|
||
| # Update or append | ||
| if grep -q "^export ${env_var}=" "$env_file" 2>/dev/null; then | ||
| local tmp_file="${env_file}.tmp" | ||
| sed "s|^export ${env_var}=.*|export ${env_var}=\"${value}\"|" "$env_file" > "$tmp_file" | ||
| mv "$tmp_file" "$env_file" | ||
| chmod 600 "$env_file" | ||
| print_success "Updated $env_var in tenant '$tenant'" | ||
| else | ||
| echo "export ${env_var}=\"${value}\"" >> "$env_file" | ||
| chmod 600 "$env_file" | ||
| print_success "Added $env_var to tenant '$tenant'" | ||
| fi | ||
|
|
||
| return 0 | ||
| } | ||
|
|
||
| # Get a key from a tenant | ||
| cmd_get() { | ||
| local key="" | ||
| local tenant="" | ||
|
|
||
| # Parse arguments | ||
| while [[ $# -gt 0 ]]; do | ||
| case "$1" in | ||
| --tenant|-t) | ||
| tenant="$2" | ||
| shift 2 | ||
| ;; | ||
| *) | ||
| if [[ -z "$key" ]]; then | ||
| key="$1" | ||
| fi | ||
| shift | ||
| ;; | ||
| esac | ||
| done | ||
|
|
||
| if [[ -z "$key" ]]; then | ||
| print_error "Usage: credential-helper.sh get <KEY_NAME> [--tenant <name>]" | ||
| return 1 | ||
| fi | ||
|
|
||
| if [[ -z "$tenant" ]]; then | ||
| tenant=$(get_active_tenant) | ||
| fi | ||
|
|
||
| local env_file | ||
| env_file=$(get_tenant_env_file "$tenant") | ||
|
|
||
| if [[ ! -f "$env_file" ]]; then | ||
| print_error "Tenant '$tenant' not found" | ||
| return 1 | ||
| fi | ||
|
|
||
| # Convert service name to env var if needed | ||
| local env_var | ||
| if [[ "$key" =~ ^[A-Z_][A-Z0-9_]*$ ]]; then | ||
| env_var="$key" | ||
| else | ||
| env_var=$(echo "$key" | tr '[:lower:]-' '[:upper:]_') | ||
| fi | ||
|
|
||
| local result | ||
| result=$(grep "^export ${env_var}=" "$env_file" 2>/dev/null | sed 's/^export [^=]*="//' | sed 's/"$//' || true) | ||
|
|
||
| if [[ -n "$result" ]]; then | ||
| echo "$result" | ||
| return 0 | ||
| else | ||
| print_error "Key $env_var not found in tenant '$tenant'" | ||
| return 1 | ||
| fi | ||
| } |
There was a problem hiding this comment.
Escape secret values before writing/updating env files.
Values are interpolated into sed/echo without escaping, so tokens containing $, \", &, or backslashes can be corrupted or even executed when sourced. Use shell-safe escaping and a safer replace path; adjust cmd_get to read the real value.
🛡️ Safer write/read path for secrets
# Update or append
+ local escaped_value
+ escaped_value=$(printf '%q' "$value")
if grep -q "^export ${env_var}=" "$env_file" 2>/dev/null; then
local tmp_file="${env_file}.tmp"
- sed "s|^export ${env_var}=.*|export ${env_var}=\"${value}\"|" "$env_file" > "$tmp_file"
+ awk -v k="$env_var" -v v="$escaped_value" '
+ $0 ~ "^export "k"=" { print "export "k"="v; next }
+ { print }
+ ' "$env_file" > "$tmp_file"
mv "$tmp_file" "$env_file"
chmod 600 "$env_file"
print_success "Updated $env_var in tenant '$tenant'"
else
- echo "export ${env_var}=\"${value}\"" >> "$env_file"
+ printf 'export %s=%s\n' "$env_var" "$escaped_value" >> "$env_file"
chmod 600 "$env_file"
print_success "Added $env_var to tenant '$tenant'"
fi
@@
- local result
- result=$(grep "^export ${env_var}=" "$env_file" 2>/dev/null | sed 's/^export [^=]*="//' | sed 's/"$//' || true)
-
- if [[ -n "$result" ]]; then
- echo "$result"
+ # shellcheck disable=SC1090
+ source "$env_file"
+ local result="${!env_var-}"
+ if [[ -n "$result" ]]; then
+ echo "$result"
return 0🤖 Prompt for AI Agents
In @.agent/scripts/credential-helper.sh around lines 292 - 412, The current
cmd_set writes raw secret values into the tenant env file and uses sed/echo
unescaped, which corrupts or executes values containing $, \, ", &, etc.; update
cmd_set to safely escape or encode values before writing/updating (e.g. escape
backslashes, double-quotes, dollar signs and ampersands or encode with base64)
and perform the file update by writing a new line with the
already-escaped/encoded payload to a temp file then atomically mv it over
env_file (use ensure_tenant_dir/get_tenant_env_file and refer to env_var for the
var name); likewise change the update path that does sed "s|^export
${env_var}=.*|...|" to use the safe temp-file write with the escaped value. In
cmd_get, do not parse with brittle sed pipelines—read the real value by sourcing
the tenant env file in a subshell or by decoding the stored encoding (if you
opted for base64) and then output the variable (use env_var to locate the
variable). Ensure chmod 600 is preserved and both cmd_set and cmd_get handle
empty/missing files the same as before.
| "tenant"|"tenants") | ||
| # Delegate to multi-tenant credential helper | ||
| local script_dir | ||
| script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | ||
| bash "$script_dir/credential-helper.sh" "$@" | ||
| ;; |
There was a problem hiding this comment.
Propagate tenant-helper exit codes.
Errors from credential-helper.sh are currently swallowed and main returns 0, which breaks automation that relies on exit status. Please return the helper’s status.
✅ Propagate the helper status
"tenant"|"tenants")
# Delegate to multi-tenant credential helper
local script_dir
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
- bash "$script_dir/credential-helper.sh" "$@"
+ bash "$script_dir/credential-helper.sh" "$@" || return $?
;;As per coding guidelines, preserve proper exit codes for automation.
🤖 Prompt for AI Agents
In @.agent/scripts/setup-local-api-keys.sh around lines 317 - 322, The tenant
branch currently calls credential-helper.sh but ignores its exit status, causing
main to return 0; capture the helper's exit code (e.g., after bash
"$script_dir/credential-helper.sh" "$@") and propagate it by exiting or
returning that code from the script (e.g., store "$?" in a variable and call
exit with that variable) so the "tenant"|"tenants") case and the overall main
return the helper’s status.
| ## Multi-Tenant Support | ||
|
|
||
| For managing multiple accounts (clients, environments, organizations): | ||
|
|
||
| ```bash | ||
| # Initialize multi-tenant storage | ||
| credential-helper.sh init | ||
|
|
||
| # Create per-client tenants | ||
| credential-helper.sh create client-acme | ||
| credential-helper.sh set GITHUB_TOKEN ghp_xxx --tenant client-acme | ||
|
|
||
| # Switch globally or per-project | ||
| credential-helper.sh switch client-acme | ||
| credential-helper.sh use client-acme # per-project override | ||
| ``` | ||
|
|
||
| See `multi-tenant.md` for full documentation. |
There was a problem hiding this comment.
Clarify where secrets live after enabling tenants.
This new section introduces tenant storage, but the doc still states keys live only in ~/.config/aidevops/mcp-env.sh; with multi-tenant, that file is a loader and secrets live in per-tenant files. Please add a short note (and update the Quick Reference/Storage sections) to avoid confusion.
✏️ Suggested clarification
# Switch globally or per-project
credential-helper.sh switch client-acme
credential-helper.sh use client-acme # per-project override+Note: with multi-tenant enabled, credentials live in
+~/.config/aidevops/tenants/{tenant}/mcp-env.sh; ~/.config/aidevops/mcp-env.sh
+is now a loader for the active tenant.
+
See multi-tenant.md for full documentation.
</details>
<details>
<summary>🤖 Prompt for AI Agents</summary>
In @.agent/tools/credentials/api-key-setup.md around lines 218 - 235, Update the
Multi-Tenant section and the Quick Reference/Storage sections to clarify that
when tenants are enabled credentials live in per-tenant files under
~/.config/aidevops/tenants/{tenant}/mcp-env.sh and that
~/.config/aidevops/mcp-env.sh acts only as a loader for the active tenant; add a
short note (exact wording like the suggested clarification) and link to
multi-tenant.md so readers aren’t misled about where secrets are stored after
running credential-helper.sh init/create/switch/use.
</details>
<!-- fingerprinting:phantom:medusa:eagle -->
<!-- This is an auto-generated comment by CodeRabbit -->
| ### Initialize | ||
|
|
||
| ```bash | ||
| # First time: migrates existing mcp-env.sh to 'default' tenant | ||
| credential-helper.sh init | ||
| ``` | ||
|
|
||
| ### Create Tenants | ||
|
|
||
| ```bash | ||
| # Create tenants for different contexts | ||
| credential-helper.sh create personal | ||
| credential-helper.sh create work | ||
| credential-helper.sh create client-acme | ||
| ``` | ||
|
|
||
| ### Add Credentials | ||
|
|
||
| ```bash | ||
| # Add to specific tenant | ||
| credential-helper.sh set GITHUB_TOKEN ghp_personal_xxx --tenant personal | ||
| credential-helper.sh set GITHUB_TOKEN ghp_work_xxx --tenant work | ||
| credential-helper.sh set GITHUB_TOKEN ghp_acme_xxx --tenant client-acme | ||
|
|
||
| # Add to active tenant (no --tenant flag) | ||
| credential-helper.sh set OPENAI_API_KEY sk-xxx | ||
| ``` | ||
|
|
||
| ### Switch Tenants | ||
|
|
||
| ```bash | ||
| # Global switch (affects all terminals after reload) | ||
| credential-helper.sh switch client-acme | ||
|
|
||
| # Per-project (overrides global, stays in this directory) | ||
| cd ~/projects/acme-webapp | ||
| credential-helper.sh use client-acme | ||
| ``` |
There was a problem hiding this comment.
Make command invocation explicit to avoid PATH assumptions.
The examples call credential-helper.sh directly, but new clones won’t have it on PATH. Consider using bash .agent/scripts/credential-helper.sh ... (or setup-local-api-keys.sh tenant ...) in examples, or add a short note near the first usage.
✏️ Example tweak
# First time: migrates existing mcp-env.sh to 'default' tenant
-credential-helper.sh init
+bash .agent/scripts/credential-helper.sh init🤖 Prompt for AI Agents
In @.agent/tools/credentials/multi-tenant.md around lines 68 - 105, The examples
invoke credential-helper.sh directly which assumes it is on PATH; update the
documentation examples and the Initialize section to invoke the script
explicitly (e.g. bash .agent/scripts/credential-helper.sh init and bash
.agent/scripts/credential-helper.sh create <tenant>, set, switch, use) or add a
short note before the first usage explaining how to run it if not on PATH
(alternatively reference setup-local-api-keys.sh tenant as an option); adjust
every occurrence of credential-helper.sh in the Create Tenants, Add Credentials,
and Switch Tenants blocks to the explicit invocation or add the single NOTE line
so new clones can run the commands without PATH assumptions.
During setup, prompts user to enable multi-tenant credential storage. Shows existing key count, explains benefits (agency/freelance, environments, multiple accounts), and migrates on confirmation. Non-breaking - skipping leaves everything unchanged.
|
🔍 Code Quality Report�[0;35m[MONITOR]�[0m Code Review Monitoring Report �[0;34m[INFO]�[0m Latest Quality Status: �[0;34m[INFO]�[0m Recent monitoring activity: 📈 Current Quality Metrics
Generated on: Sat Jan 24 16:56:47 UTC 2026 Generated by AI DevOps Framework Code Review Monitoring |
- Add WaterCrawl to browser automation tools (7 tools now) - Add LibPDF and Unstract to document processing section - Add Cloudron app packaging enhancement note - Add multi-tenant credential storage documentation - Update MCP count to 19 (added Unstract) - Update subagent count to 560+ and scripts to 146+ - Document MCP lazy-loading optimization (12-24s startup savings) - Add WaterCrawl to tool selection guide Based on PRs #178-#192
- Add WaterCrawl to browser automation tools (7 tools now) - Add LibPDF and Unstract to document processing section - Add Cloudron app packaging enhancement note - Add multi-tenant credential storage documentation - Update MCP count to 19 (added Unstract) - Update subagent count to 560+ and scripts to 146+ - Document MCP lazy-loading optimization (12-24s startup savings) - Add WaterCrawl to tool selection guide Based on PRs #178-#192



Summary
credential-helper.shwith full tenant lifecycle: create, switch, set/get/remove keys, copy, deletemcp-env.shauto-migrates todefaulttenant on first initArchitecture
Key Features
.aidevops-tenant(gitignored)eval $(credential-helper.sh export))tenant-*actionsUse Cases
Testing
All commands verified manually:
init(migrates 13 existing keys to default tenant)create,set,get,keys,copy,switch,remove,deleteexportfor eval integrationstatusandlistfor visibilityFiles Changed
.agent/scripts/credential-helper.sh.agent/tools/credentials/multi-tenant.md.agent/scripts/setup-local-api-keys.shtenantcommand delegation.agent/tools/credentials/api-key-setup.md.opencode/tool/api-keys.ts.gitignore.aidevops-tenant, credential exceptionsSummary by CodeRabbit
New Features
Documentation
Chores
✏️ Tip: You can customize this high-level summary in your review settings.