Go CLI tool for managing multiple Claude API provider configurations with proxy failover and TUI/Web interfaces.
- Language: Go
- TUI: Bubble Tea (charmbracelet/bubbletea) + Lip Gloss
- Web: Embedded static files (HTML/CSS/JS, no framework), Go HTTP server
- CLI: Cobra
- Config: JSON store at
~/.zen/zen.json
cmd/ # Cobra commands (root, config, pick, web, upgrade, bind)
internal/
config/ # Config store, types, legacy migration, compat helpers
daemon/ # Web server daemon management (start/stop, platform-specific)
proxy/ # Reverse proxy with failover, token calculation, session cache
update/ # Non-blocking version update checker with 24h cache
web/ # HTTP API server + embedded static frontend
static/ # app.js, index.html, style.css (vanilla JS, no build step)
tui/ # All TUI models (editor, pick, config_main, fallback, routing, etc.)
go build ./...
go test ./...Dev daemon runs on separate ports to avoid interfering with production:
- Dev Web UI:
http://localhost:29840 - Dev Proxy:
http://localhost:29841 - Dev Config:
~/.zen-dev/zen.json - Production ports (19840/19841): NEVER touch during development
./scripts/dev.sh # Start dev daemon (builds + starts)
./scripts/dev.sh stop # Stop dev daemon
./scripts/dev.sh restart # Rebuild and restart dev daemon
./scripts/dev.sh status # Check dev daemon status
./scripts/dev.sh web # Start frontend dev server (Vite)After modifying Go code, always run ./scripts/dev.sh restart to rebuild and restart the dev daemon.
Push a git tag to trigger GitHub Actions release workflow:
git tag v2.x.0
git push origin v2.x.0Do NOT use gh release create — the CI pipeline handles release creation automatically.
- Commit often: Each completed task/item should be committed individually, not batched into one large commit. After finishing a feature or fix, commit immediately before moving to the next task.
- Branch protection: The
mainbranch is protected. All changes MUST go through a pull request — never push directly to main. Create a feature branch, commit there, then usegh pr createto open a PR. - Pre-release check: Before tagging a release, check for unpushed commits (
git log origin/main..HEAD) and push them first. - Update version constant: Before releasing, update
Versionincmd/root.goto match the release tag. - Update README: Before releasing, check that
README.mdreflects all new features and changes. All four language versions must be updated together:README.md(English),docs/README.zh-CN.md(简体中文),docs/README.zh-TW.md(繁體中文),docs/README.es.md(Español). - No summary/explanation docs: Do NOT create markdown files to summarize or explain completed work. No "implementation notes", no "changes summary", no "feature documentation". The commit message and code comments are sufficient.
- Keep planning docs: Architecture planning and design docs should be kept for context across sessions. Store them in
.dev/folder (gitignored, for internal dev use only).docs/is for user-facing content only. - No example files: Do NOT create example config files (*.json, *.yaml, etc.) in the repository root. Examples belong in README.md or
docs/. - Minimal test files: Only add tests for new public APIs or complex logic. Do not create excessive test files for simple functions. Prefer table-driven tests in existing *_test.go files.
- No unnecessary files: Before committing, review
git statusand remove any generated, temporary, or example files that should not be in the repository. - TDD for new features: Use Test-Driven Development (TDD) for new feature development to ensure code quality. Write tests first, then implement the feature to make tests pass.
Before tagging a release, complete the following checklist:
- Bug check: Review all code for unresolved bugs. Run
go test ./...and ensure all tests pass. - Version number: Verify
Versionincmd/root.gomatches the release tag. - Website documentation:
- Ensure website contains documentation for the release version
- Verify documentation is accurate and complete
- Confirm all new features are documented
- Remove or update documentation for changed/removed features
- README files: Update all README files to reflect the latest version:
README.md(English)docs/README.zh-CN.md(简体中文)docs/README.zh-TW.md(繁體中文)docs/README.es.md(Español)
When modifying OpenCCConfig or its nested types in a way that changes the JSON schema (e.g. changing a field's type, renaming a field, restructuring data):
- Bump
CurrentConfigVersionininternal/config/config.go. - Add migration logic so older config files are parsed correctly. The current pattern uses a custom
UnmarshalJSONonOpenCCConfigwith a fallback path that parses changed fields asjson.RawMessageand converts them to the new format. - The version check in
loadLocked()(store.go) automatically upgradescfg.Version < CurrentConfigVersionto the current version after unmarshal. - Add tests covering: old format parsing, mixed old/new format, field preservation on the fallback path, and marshal round-trip.
Primary palette for website, Web UI, and marketing materials:
| Name | Dark Mode | Light Mode | Usage |
|---|---|---|---|
| Teal (Primary) | #5eead4 |
#0d9488 |
Primary accent, buttons, links |
| Lavender (Secondary) | #c4b5fd |
#7c3aed |
Secondary accent, highlights |
| Sage | #86efac |
- | Success states |
| Red | #fb7185 |
- | Error states |
| Amber | #fbbf24 |
- | Warning states |
| Blue | #93c5fd |
- | Info states |
Background (Dark): #0f1117 → #181a24 → #1e2030 → #252837
Background (Light): #f8fafc → #ffffff → #f1f5f9 → #e2e8f0
- Config convenience functions in
internal/config/compat.gowrapDefaultStore()methods - TUI models follow Bubble Tea pattern:
newXxxModel(),Init(),Update(),View() - Standalone TUI entry points:
RunXxx()functions in tui package - Inline config sub-editors use wrapper types implementing
tea.Model(e.g.editorWrapper,fallbackWrapper) - Web API routes:
/api/v1/providers,/api/v1/profiles,/api/v1/health,/api/v1/reload - Web frontend uses vanilla JS (no build tools), CSS custom properties for theming
- Model IDs in autocomplete must be verified against official Anthropic docs
- Environment variable prefix:
GOZEN_(e.g.,GOZEN_WEB_DAEMON)
- v1.0.0: Initial release
- v1.1.0: Go rewrite with proxy failover and TUI
- v1.1.1: Upgrade command, sorted configs
- v1.2.0: Fallback profiles, pick command, installer
- v1.3.0: Web UI, profile assignment on provider add, model autocomplete, CLI name args
- v1.3.1: Download progress bar, README refresh
- v1.3.2: Fix progress bar display (show downloaded/total size)
- v1.4.0: Scenario routing, token calculation, session cache, project bindings
- v1.5.0: Dashboard TUI, project binding CLI support, centralized CLI list
- v1.5.1: Fix symlink dedup in bindings, Web UI dropdown style, --port restriction, config v3→v5 migration, SQLite log storage
- v1.5.2: Allow reinstalling same version in upgrade command
- v1.5.3: Per-binary PID files to avoid multi-binary conflicts
- v2.0.0: Rename to GoZen (opencc → zen), config migration from ~/.opencc/ to ~/.zen/, non-blocking version update check
- v3.0.0: Usage tracking & budget control, provider health monitoring, smart load balancing, webhooks, context compression, middleware pipeline, agent infrastructure
- v3.0.1: Scenario routing redesign (protocol-agnostic normalization, per-scenario RoutePolicy), proxy transform correctness (streaming id/model/tool_calls, multi-format SSE usage tracking)
- Go 1.21+ +
net/http,net/url,golang.org/x/net/proxy(for SOCKS5) (001-provider-proxy) - JSON config at
~/.zen/zen.json(001-provider-proxy) - Go 1.21+ +
net,net/http,internal/config,internal/proxy(002-fix-proxy-port) - Go 1.21+ + Bubble Tea (TUI), Cobra (CLI), net/http (proxy/web) (003-skill-intent-recognition)
- JSON config at
~/.zen/zen.json, match logs in memory (ring buffer) (003-skill-intent-recognition) - Go 1.21+ +
net/http,net/url,golang.org/x/net/proxy(SOCKS5), Cobra (CLI) (004-fix-proxy-stability) - JSON config at
~/.zen/zen.json(no schema changes needed) (004-fix-proxy-stability) - Go 1.21+ +
net/http,encoding/json,bufio(SSE parsing), React + TypeScript (Web UI) (005-provider-model-tag) - JSON config at
~/.zen/zen.json(newshow_provider_tagboolean field, version 10 → 11) (005-provider-model-tag) - Go 1.21+ +
net/http,encoding/json,sync(for in-memory buffer), React + TypeScript (Web UI) (006-revert-tag-add-monitoring) - In-memory ring buffer (default 1000 requests), no database persistence for MVP (006-revert-tag-add-monitoring)
- Go 1.21+ + Cobra (CLI), net/http (proxy/web), syscall (file lock, process management), React+TypeScript (Web UI) (007-fix-daemon-stability)
- JSON config at
~/.zen/zen.json, SQLite for proxy logs (007-fix-daemon-stability) - Go 1.21+ (backend), TypeScript (frontend, React 18) +
net/http,net/http/httptest,os/exec,encoding/json(Go tests); vitest 4, @testing-library/react, MSW (frontend tests) (008-automated-testing) - JSON config at
~/.zen/zen.json(test isolation viaGOZEN_CONFIG_DIRand ephemeral ports) (008-automated-testing) - Go 1.21+ + Cobra (CLI), Bubble Tea + Lip Gloss (TUI), React + TypeScript + Vite (Web UI) (009-code-scenario-routing)
- JSON at
~/.zen/zen.json— no schema migration needed (string-keyed routing map) (009-code-scenario-routing) - Go 1.21+ + Cobra (CLI framework), existing
cmd/root.gopermission handling (001-use-command-enhancements) - JSON config at
~/.zen/zen.json(schema version bump required) (001-use-command-enhancements) - JSON config at
~/.zen/zen.json(version 12 → 13) (011-feature-gates-daemon-persistence) - File system (reads website docs directory structure at
website/) (012-website-i18n-docs) - Go 1.21+ +
net/http,encoding/json,net/url(proxy); existinginternal/proxy/transformpackage (013-fix-proxy-model-transform) - Go 1.21+ +
net/http,encoding/json,bufio,io(stdlib only) (014-fix-responses-api-transform) - N/A (no config schema changes, no version bump) (014-fix-responses-api-transform)
- Go 1.21+ + Cobra (CLI), net/http (proxy/web), React + TypeScript + Vite (Web UI) (015-mark-provider-unavailable)
- JSON config at
~/.zen/zen.json(version 13 → 14) (015-mark-provider-unavailable) - Go 1.21+ + net/http (stdlib), runtime (metrics), debug (stack traces), sync (concurrency), encoding/json (structured logging), existing internal packages (config, proxy, daemon, web, httpx) (017-proxy-stability)
- JSON config at ~/.zen/zen.json (existing), in-memory metrics (no persistence), structured JSON logs to stderr (017-proxy-stability)
- New packages: internal/daemon/logger.go (structured JSON logging), internal/proxy/limiter.go (semaphore-based concurrency control), internal/daemon/metrics.go (request metrics with percentiles) (017-proxy-stability)
- JSON config at ~/.zen/zen.json (existing), in-memory metrics (no persistence) (017-proxy-stability)
- Go 1.21+ +
bufio,encoding/json,io,net/http(stdlib only) (018-proxy-transform-correctness) - N/A (no config schema changes) (018-proxy-transform-correctness)
- Go 1.21+ +
net/http,sync,time,encoding/json(stdlib only); existinginternal/config,internal/proxypackages (019-profile-strategy-routing) - SQLite (existing LogDB at
~/.zen/logs.db) for latency metrics persistence; in-memory ring buffer for round-robin state (019-profile-strategy-routing)
- 020-scenario-routing-redesign: Protocol-agnostic scenario routing with middleware extensibility
- Protocol-agnostic normalization: Anthropic Messages, OpenAI Chat, OpenAI Responses → unified NormalizedRequest
- Middleware-driven routing: RoutingDecision/RoutingHints in RequestContext, middleware precedence over builtin classifier
- Open scenario namespace: Custom scenario keys (camelCase, kebab-case, snake_case), no source code changes needed
- Per-scenario routing policies: Strategy, weights, model overrides, threshold per scenario
- Strong config validation: Provider existence, empty list, weights, strategy, scenario key format validated at load time
- Routing observability: Structured logs (scenario, source, reason, confidence), request features, fallback logging
- Config migration: Automatic v14→v15 migration (ScenarioRoute → RoutePolicy), backward compatible
- UI support: TUI and Web UI support custom scenarios (add/remove/configure)
- New types: NormalizedRequest, RequestFeatures, RoutingDecision, RoutingHints, RoutePolicy
- New files: routing_normalize.go, routing_classifier.go, routing_resolver.go, config_migration_test.go
- 019-profile-strategy-routing: Profile strategy-aware provider routing with 5 strategies
- Least-latency: Routes to provider with lowest average response time (100-request rolling window, min 10 samples from SQLite LogDB)
- Least-cost: Routes to provider with lowest model pricing (uses built-in Anthropic pricing table)
- Round-robin: Even distribution across healthy providers using atomic counter
- Weighted: Configurable weight-based random distribution (recalculates on health change, falls back to equal weights)
- Failover: Default strategy, first healthy provider in configured order
- Strategy decision logging for all strategies (provider, reason, candidates)
- LoadBalancer integration in ProxyServer.ServeHTTP() with per-request provider reordering
- New fields: Provider.Weight, ProviderConfig.Weight, ProfileConfig.Strategy/ProviderWeights
- 017-proxy-stability: Daemon proxy stability improvements for 24-hour uptime and 100 concurrent request handling
- Auto-restart with exponential backoff (max 5 restarts, 1s→30s backoff)
- Goroutine leak detection with baseline comparison and stack dumps (1-minute ticker, 20% growth tolerance)
- Panic recovery middleware in httpx package (captures stack traces, prevents daemon crashes)
- Concurrency limiter with semaphore pattern (100 concurrent requests, buffered channel)
- Request timeout enforcement (10-minute HTTP client timeout)
- Connection pool cleanup on cache invalidation (ProfileProxy.InvalidateCache/Close)
- Streaming write error handling (early return on client disconnect)
- Structured JSON logging for daemon lifecycle events (daemon_started, daemon_shutdown, goroutine_leak_detected, daemon_crashed_restarting)
- Request metrics collection with percentile calculation (P50/P95/P99, ring buffer, error grouping by provider/type)
- Health monitoring API (/api/v1/daemon/health) with three-tier status (healthy/degraded/unhealthy)
- Metrics API (/api/v1/daemon/metrics) with latency percentiles, error breakdowns, resource peaks
- Test coverage: comprehensive unit tests for limiter, logger, metrics; integration tests for load (100 concurrent), timeout, connection pool cleanup
- 006-revert-tag-add-monitoring: Removed provider tag injection from responses, added comprehensive request monitoring with detail view and filtering
- Removed
show_provider_tagsetting and all tag injection logic from proxy responses - Added RequestMonitor with thread-safe ring buffer (1000 request capacity, LRU eviction)
- Added GET /api/v1/monitoring/requests API with filtering (provider, model, status, time range)
- Added GET /api/v1/monitoring/requests/:id API for detailed request metadata
- Added React-based monitoring page with auto-refresh, filters, and detail modal
- Monitoring tracks: timestamp, provider, model, status, duration, tokens, cost, failover chain, errors
- Test coverage: 81.6% (proxy), 85.6% (config), 80.3% (web)
- Removed