feat: add proxy API key authentication with usage tracking#1
feat: add proxy API key authentication with usage tracking#1lmichaelwar wants to merge 9 commits intoTylerx404:mainfrom
Conversation
Co-authored-by: lmichaelwar <20604470+lmichaelwar@users.noreply.github.com>
Co-authored-by: lmichaelwar <20604470+lmichaelwar@users.noreply.github.com>
Co-authored-by: lmichaelwar <20604470+lmichaelwar@users.noreply.github.com>
Co-authored-by: lmichaelwar <20604470+lmichaelwar@users.noreply.github.com>
Co-authored-by: lmichaelwar <20604470+lmichaelwar@users.noreply.github.com>
Co-authored-by: lmichaelwar <20604470+lmichaelwar@users.noreply.github.com>
…e-key Add Tailscale sidecar with ephemeral auth key support
Add a separate authentication layer for proxy access, keeping the Z.ai token server-side. Clients now use independent proxy API keys that can be tracked and managed separately. Features: - Proxy API key authentication middleware - Usage tracking per key (request count, last used) - Admin endpoint at /admin/usage for stats - Configurable via PROXY_API_KEYS env var (key:name format) - Graceful fallback to open access if no keys configured Security benefits: - Z.ai token never exposed to clients - Revocable per-service credentials - Audit trail for usage per team/service Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR adds a proxy authentication layer with API key management and usage tracking, alongside comprehensive Tailscale integration for secure private networking. The authentication system separates client-facing API keys from the upstream Z.ai token, enabling multi-tenant access control and usage monitoring.
Changes:
- Introduces proxy API key authentication middleware with concurrent-safe usage tracking
- Adds admin endpoint for viewing per-key usage statistics
- Integrates Tailscale VPN with Docker sidecar pattern and ephemeral auth keys
- Provides multiple deployment configurations (local, Tailscale, override patterns)
Reviewed changes
Copilot reviewed 13 out of 14 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| middleware/auth.go | New authentication middleware with singleton pattern, API key validation, and usage tracking |
| handlers/usage.go | New admin endpoint for retrieving usage statistics per API key |
| main.go | Integrates auth middleware into request pipeline and adds startup logging |
| config/config.go | Adds proxy key parsing from environment variable in key:name format |
| docker/tailscale-entrypoint.sh | Tailscale initialization script with validation and monitoring |
| docker-compose.yml | Main compose file with Tailscale sidecar and network sharing |
| docker-compose.local.yml | Local development compose without Tailscale |
| docker-compose.override.yml.example | Example override for exposing ports or disabling Tailscale |
| Dockerfile.tailscale | Tailscale sidecar container image |
| .env.example | Updated with proxy keys and Tailscale configuration examples |
| README.md | Added Tailscale setup and configuration documentation |
| TAILSCALE.md | Comprehensive Tailscale integration guide |
| QUICKSTART.md | Quick start guide for Tailscale deployment |
| .gitignore | Excludes docker-compose.override.yml |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| network_mode: "service:tailscale" | ||
| environment: | ||
| - TOKEN= | ||
| - TOKEN=125a583265464446be50412ef8701d72.tBGGNJmAQMvOhvXO |
There was a problem hiding this comment.
A production Z.ai API token is hardcoded in the docker-compose.yml file. This is a security risk as it exposes the actual API credential in version control. The token should be removed and referenced from environment variables instead (e.g., - TOKEN=${TOKEN} or - TOKEN=).
| - TOKEN=125a583265464446be50412ef8701d72.tBGGNJmAQMvOhvXO | |
| - TOKEN=${TOKEN} |
| ports: | ||
| - "8080:8080" | ||
| environment: | ||
| - TOKEN=125a583265464446be50412ef8701d72.tBGGNJmAQMvOhvXO |
There was a problem hiding this comment.
A production Z.ai API token is hardcoded in the docker-compose.local.yml file. This should be removed and replaced with an environment variable reference (e.g., - TOKEN=${TOKEN} or - TOKEN=) to prevent credential exposure.
| - TOKEN=125a583265464446be50412ef8701d72.tBGGNJmAQMvOhvXO | |
| - TOKEN=${TOKEN} |
| environment: | ||
| - TOKEN= | ||
| - TOKEN=125a583265464446be50412ef8701d72.tBGGNJmAQMvOhvXO | ||
| - PROXY_API_KEYS=sk-internal-dev:Internal Dev Team,sk-prod-service:Production Service |
There was a problem hiding this comment.
Example proxy API keys are hardcoded in the docker-compose.yml file. While these appear to be examples, they should be removed or replaced with environment variable references (e.g., - PROXY_API_KEYS=${PROXY_API_KEYS}) to avoid accidentally committing real keys and to encourage proper configuration practices.
| - PROXY_API_KEYS=sk-internal-dev:Internal Dev Team,sk-prod-service:Production Service | |
| - PROXY_API_KEYS=${PROXY_API_KEYS} |
| - "8080:8080" | ||
| environment: | ||
| - TOKEN=125a583265464446be50412ef8701d72.tBGGNJmAQMvOhvXO | ||
| - PROXY_API_KEYS=sk-internal-dev:Internal Dev Team,sk-prod-service:Production Service |
There was a problem hiding this comment.
Example proxy API keys are hardcoded in docker-compose.local.yml. Replace with environment variable reference (e.g., - PROXY_API_KEYS=${PROXY_API_KEYS}) to avoid accidentally using example keys in production or committing real keys.
| - PROXY_API_KEYS=sk-internal-dev:Internal Dev Team,sk-prod-service:Production Service | |
| - PROXY_API_KEYS=${PROXY_API_KEYS} |
| ) | ||
|
|
||
| // UsageHandler returns usage statistics for proxy API keys | ||
| func UsageHandler(w http.ResponseWriter, r *http.Request) { |
There was a problem hiding this comment.
The UsageHandler does not validate the HTTP method. It should only accept GET requests for retrieving usage statistics. Add method validation at the beginning: if r.Method != http.MethodGet { w.WriteHeader(http.StatusMethodNotAllowed); return }
| func UsageHandler(w http.ResponseWriter, r *http.Request) { | |
| func UsageHandler(w http.ResponseWriter, r *http.Request) { | |
| if r.Method != http.MethodGet { | |
| w.WriteHeader(http.StatusMethodNotAllowed) | |
| return | |
| } |
| # - Optional =value with safe characters (alphanumeric, hyphen, underscore, comma, dot, colon, slash) | ||
| # - Flags separated by spaces | ||
| # This strict pattern prevents injection while allowing legitimate Tailscale flags | ||
| if ! echo "${TS_EXTRA_ARGS}" | grep -qE '^[[:space:]]*(--[a-zA-Z0-9_-]+(=[a-zA-Z0-9._,:/\\-]+)?[[:space:]]*)+$'; then |
There was a problem hiding this comment.
The regex pattern allows backslash in the character class [a-zA-Z0-9._,:/\\-], but the backslash escaping may not work as intended in this context. The pattern should either use [a-zA-Z0-9._,:/-] (without backslash support) or properly escape it. Additionally, the comment on line 51 mentions allowing slash but the actual use case for backslash in Tailscale flags is unclear.
| if ! echo "${TS_EXTRA_ARGS}" | grep -qE '^[[:space:]]*(--[a-zA-Z0-9_-]+(=[a-zA-Z0-9._,:/\\-]+)?[[:space:]]*)+$'; then | |
| if ! echo "${TS_EXTRA_ARGS}" | grep -qE '^[[:space:]]*(--[a-zA-Z0-9_-]+(=[a-zA-Z0-9._,:/-]+)?[[:space:]]*)+$'; then |
| func (am *AuthMiddleware) AuthHandler(next http.Handler) http.Handler { | ||
| return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
| // Skip auth for health endpoint | ||
| if r.URL.Path == "/health" { |
There was a problem hiding this comment.
The health endpoint path is hardcoded as a string literal. Consider defining it as a constant (e.g., const HealthEndpoint = \"/health\") for better maintainability and consistency across the codebase.
|
@copilot Can you review my fork of the zai proxy and check if it's suitable for this upstream PR? |
Summary
Adds a proxy authentication layer that separates client access from the upstream Z.ai API token. This allows the proxy to be shared across teams/services without exposing the actual Z.ai credential.
Motivation (The Problem)
Previously, the proxy either:
This created two issues:
Solution (What This Does)
This PR adds an independent authentication system with its own API keys that are completely separate from the Z.ai token.
What's Added
Configuration
.env or docker-compose.yml
PROXY_API_KEYS=sk-dev-team:Development Team,sk-prod-service:Production Service
Client Usage
Before: anyone could access
After: requires a proxy key
Usage Tracking
See request counts per key
Response:
Use Cases