Skip to content

Add multi-instance Docker manager for isolated auth profiles#312

Open
ShutovKS wants to merge 2 commits intoCJackHwang:mainfrom
ShutovKS:feat/issue-300-multi-launch
Open

Add multi-instance Docker manager for isolated auth profiles#312
ShutovKS wants to merge 2 commits intoCJackHwang:mainfrom
ShutovKS:feat/issue-300-multi-launch

Conversation

@ShutovKS
Copy link

@ShutovKS ShutovKS commented Mar 9, 2026

Summary

  • add a reusable multi-instance Docker manager for launching one container per saved auth profile
  • isolate each instance with dedicated auth profile mounting and runtime directories
  • document setup and operations in the main README and deployment guides

Testing

  • not run (workflow/docs changes only)

Related issue

Closes #300

Add a reusable multi-instance Docker manager that launches one container per saved auth profile with isolated runtime directories and documented operational guidance.\n\nCloses CJackHwang#300
Copilot AI review requested due to automatic review settings March 9, 2026 17:16
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an external multi-instance Docker launcher to run one container per saved auth profile, plus documentation references for operating it.

Changes:

  • Introduces scripts/multi-instance-manager/run-multi-instance.sh to discover auth_profiles/saved/*.json and launch one Docker container per profile with port allocation and safety checks.
  • Adds usage/operational documentation for the multi-instance manager and links it from existing docs/README.
  • Ignores the runtime state directory (.multi-instance-runtime/) created by the script.

Reviewed changes

Copilot reviewed 4 out of 5 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
scripts/multi-instance-manager/run-multi-instance.sh New multi-container launcher with per-profile container creation, port checks, and runtime profile mounting logic
scripts/multi-instance-manager/README.md New guide describing behavior, options, and operational workflows for the launcher
docs/deployment-and-operations.md Adds a deployment/ops section pointing to the multi-instance manager
README.md Adds a top-level link to the multi-instance manager docs
.gitignore Adds .multi-instance-runtime/ to prevent committing runtime artifacts

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +301 to +302
cp "$profile_path" "$runtime_active_host_path"

Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--dry-run still copies the profile into .multi-instance-runtime/active/ because cp runs unconditionally before the dry-run check. This makes dry-run mode have side effects and can overwrite files. Gate the copy (and any other mutations) behind DRY_RUN != true, or simulate the copy when dry-running without touching the filesystem.

Copilot uses AI. Check for mistakes.
Comment on lines +309 to +310
-v "$PROJECT_ROOT/auth_profiles:/app/auth_profiles"
-v "$runtime_active_host_path:${container_active_profile_path}:ro"
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This mounts the entire host auth_profiles/ directory into every container (and writable), so each instance can read/modify other profiles on disk. That conflicts with the PR goal of “dedicated auth profile mounting / isolated auth profiles”. If strict isolation is intended, mount only the selected saved profile file (and the per-container active file) and keep the rest read-only or container-local.

Copilot uses AI. Check for mistakes.
Comment on lines +51 to +52
This dual-path startup is intentional. Although [`launcher/runner.py::_resolve_auth_file_path()`](../../launcher/runner.py:270) and [`browser_utils/initialization/core.py::initialize_page_logic()`](../../browser_utils/initialization/core.py:48) can consume [`ACTIVE_AUTH_JSON_PATH`](../../launcher/runner.py:413), real Docker headless startup may still traverse logic that expects at least one file under [`auth_profiles/active/`](../../auth_profiles/active/). The runtime copy avoids polluting the shared host [`auth_profiles/active/`](../../auth_profiles/active/) pool while still satisfying that container-local requirement.

Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These Markdown links use runner.py:270 / core.py:48 style suffixes, which GitHub won’t interpret as line anchors (it expects #L270, etc.). Update the links to proper GitHub line anchors or remove the line-specific suffix so the references don’t 404.

Copilot uses AI. Check for mistakes.

1. It mounts the shared [`auth_profiles/`](../../auth_profiles/) tree into the container.
2. It sets `ACTIVE_AUTH_JSON_PATH=/app/auth_profiles/saved/<profile>.json` for that specific container.
3. It copies the selected host profile into [` .multi-instance-runtime/active/`](../../.multi-instance-runtime/) and bind-mounts that copy as `/app/auth_profiles/active/<profile>.json` inside the container.
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There’s an extra leading space inside the inline-code link text ([ .multi-instance-runtime/active/]), which renders as a different path and looks like a typo. Remove the leading space so the path displays consistently as .multi-instance-runtime/active/.

Copilot uses AI. Check for mistakes.

如需为多个已保存认证同时启动隔离容器,可使用外部管理脚本 [`scripts/multi-instance-manager/README.md`](../scripts/multi-instance-manager/README.md)。

当前 multi-instance 管理器会为每个容器显式设置独立的 `ACTIVE_AUTH_JSON_PATH`,并把选中的 profile 复制到 [` .multi-instance-runtime/active/`](../.multi-instance-runtime/) 后再挂载为容器内的 [`auth_profiles/active/`](../auth_profiles/active/) 单文件。默认同时关闭启动时自动轮转与运行时自动轮转,这样既能维持一容器一认证隔离,也能满足 Docker headless 启动阶段对 active-dir 的运行时要求。
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There’s an extra leading space inside the inline-code link text ([ .multi-instance-runtime/active/]), which renders as a different path and looks like a typo. Remove the leading space so the path displays consistently as .multi-instance-runtime/active/.

Copilot uses AI. Check for mistakes.
print_summary() {
log ""
log "✅ Done. Processed profiles: ${#PROFILE_FILES[@]}"
log "🔑 Shared API key: ${API_KEY}"
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

print_summary logs the full value of API_KEY to stdout via log "🔑 Shared API key: ${API_KEY}", which can expose a real production API key in terminal history or aggregated CI logs. Anyone with access to those logs could reuse the key to impersonate this service or access upstream resources. Avoid printing sensitive secrets; if you need to indicate that an API key is configured, log a redacted or boolean status instead of the raw value.

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 5 changed files in this pull request and generated 6 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +15 to +17
CONTAINER_PREFIX="${CONTAINER_PREFIX:-ai-proxy}"
API_KEY="${API_KEY:-sk-dummy}"
SERVER_LOG_LEVEL="${SERVER_LOG_LEVEL:-INFO}"
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The script treats --api-key/API_KEY as the “shared API key”, but the server’s API-key enforcement is based on auth_profiles/key.txt (see api_utils/auth_utils.py), and I can’t find any runtime code that reads an API_KEY env var. As-is, this option is likely a no-op and may give a false sense of auth being enabled; consider either wiring this flag to write/mount a per-container key.txt, or remove the flag/docs and instruct users to manage auth_profiles/key.txt instead.

Copilot uses AI. Check for mistakes.
Comment on lines +299 to +303
local container_active_profile_path="/app/auth_profiles/active/${profile_name}.json"

cp "$profile_path" "$runtime_active_host_path"

local -a docker_args=(
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--dry-run still executes cp "$profile_path" "$runtime_active_host_path", which writes a copy of potentially sensitive auth JSONs into .multi-instance-runtime/ even though the user asked for a preview. Consider skipping the copy when DRY_RUN=true (or at least making it explicit) and ensuring the runtime copy is created with restrictive permissions (e.g., chmod 600 / umask 077) since it contains credentials.

Copilot uses AI. Check for mistakes.
Comment on lines +307 to +311
-p "127.0.0.1:${api_port}:2048"
-p "127.0.0.1:${stream_port}:3120"
-v "$PROJECT_ROOT/auth_profiles:/app/auth_profiles"
-v "$runtime_active_host_path:${container_active_profile_path}:ro"
-e "ACTIVE_AUTH_JSON_PATH=${container_profile_path}"
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-v "$PROJECT_ROOT/auth_profiles:/app/auth_profiles" mounts the entire host auth profile tree into every container (including other saved/active profiles and key.txt), which weakens the “one container per auth profile” isolation goal. If isolation is a requirement, consider mounting only the selected saved profile + a container-specific active/runtime location (or mount the directory read-only and ensure only the intended files are accessible).

Copilot uses AI. Check for mistakes.
Comment on lines +239 to +243
local container_name="$1"
local port="$2"

docker ps --filter "name=^${container_name}$" --format '{{.Ports}}' | grep -E "(^|[ ,])127\.0\.0\.1:${port}->|(^|[ ,])0\.0\.0\.0:${port}->|(^|[ ,])\[::\]:${port}->" >/dev/null 2>&1
}
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

docker ps --filter "name=^${container_name}$" uses a regex name filter; since sanitize_name allows . and other regex metacharacters, this can mis-match containers and make the “port already in use by same container” check unreliable. Consider avoiding regex filters here (e.g., inspect by exact name via docker inspect, or list names/ports and match with grep -Fx on the name column).

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +4
#!/usr/bin/env bash

set -euo pipefail

Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR description says “workflow/docs changes only”, but this PR adds an operational launcher script (run-multi-instance.sh) that affects how users run the service. Please update the PR description/testing note to reflect that executable logic was added (and ideally include at least a basic smoke test plan, e.g., --dry-run + one-profile run).

Copilot uses AI. Check for mistakes.
Comment on lines +127 to +133
| `--docker-env-file PATH` | Path to mounted Docker env file | `docker/.env` |
| `--image NAME` | Docker image name | `ai-studio-proxy:latest` with automatic fallback to `docker-ai-studio-proxy:latest` after a standard `docker compose build` |
| `--container-prefix PREFIX` | Prefix for launched containers | `ai-proxy` |
| `--api-key KEY` | Shared API key for all containers | `sk-dummy` |
| `--log-level LEVEL` | `SERVER_LOG_LEVEL` value | `INFO` |
| `--base-api-port PORT` | Starting host API port | `2048` |
| `--base-stream-port PORT` | Starting host stream port | `3120` |
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doc describes --api-key / API_KEY as the shared API key for launched containers, but the current codebase enforces API keys via auth_profiles/key.txt (and doesn’t appear to read an API_KEY env var). Please either adjust the manager to actually provision key.txt (and document that), or update the README to direct users to configure auth_profiles/key.txt for API-key auth.

Copilot uses AI. Check for mistakes.
@CJackHwang CJackHwang added the enhancement New feature or request label Mar 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Multi launch

3 participants