# WorkBuddy Manager: a web console for a CodeBuddy account pool, with an OpenAI compatible gateway in front

> WorkBuddy Manager is a Next.js and FastAPI console that sits on top of workbuddy2api, a Go reverse proxy which turns a pool of Tencent CodeBuddy accounts into an OpenAI compatible API. The console adds account enrolment by QR code, scheduled check-ins, per key quotas and IP whitelists, and full call auditing, and it deliberately does not modify the upstream gateway's code.

**ithtelab/workbuddy-manager** — 腾讯 CodeBuddy 账号池管理控制台，内置 OpenAI 兼容反代网关：扫码批量纳管账号、定时签到与保活、密钥分组分发、IP 与模型白名单、调用日志与用量统计；支持带签名校验的一键更新。

- Repository: https://github.com/ithtelab/workbuddy-manager
- Stars: 1,877 · Forks: 425
- Language: Python
- License: NOASSERTION
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/ithtelab-workbuddy-manager

## A control plane for an upstream gateway, not a replacement for it

The relationship between the two projects is the first thing to understand, because the project is unusually explicit about it.

workbuddy2api is a reverse proxy service, written in Go, that wraps a pool of Tencent CodeBuddy accounts into an OpenAI compatible interface. Its capabilities are said to be complete, but it is command line only: adding an account means running a script, checking status means curl /status, and distributing keys has no interface at all.

WorkBuddy Manager fills that gap. It is the web management end for workbuddy2api, offering QR code enrolment, scheduled check-in and keep-alive, key distribution, IP and model whitelists, and call logs with usage statistics. The upstream source code is distributed with the release package under the MIT licence.

The non-invasion claim is specific and worth taking at face value. The panel does not intrude into the upstream: it has not modified a single line of it, and account rotation, concurrency and circuit breaking remain the upstream's responsibility. The stated division is that the upstream handles capability while the panel handles presentation, so account scheduling, token refresh, rate limiting and circuit breaking stay in workbuddy2api while the panel visualises those capabilities and adds the operational layer of key distribution, IP control and usage accounting.

The rationale for the split is stated too: the upstream stays lean, and the panel does not require the upstream to change code for it. The upstream lists this project as one of its community front end panels, and the panel side says that operational needs found in the panel feed back into the upstream.

The consequence for an operator is that you are adopting a pair, and the console's own behaviour depends on the upstream's configuration file. The environment file points at the upstream's config.json and its auths directory, and the panel reads the api_key from that file when its own is left empty.

## Three deployment routes, and the one-line Docker run

Server deployment needs Docker, from one core and one gigabyte of memory upwards, and Windows desktop users can use a graphical installer. Three routes are documented, and the Docker one is a single command:

```bash
docker run -d --name workbuddy-manager -p 7864:7864 -v /opt/workbuddy-manager/data:/app/data ghcr.io/ithtelab/workbuddy-manager:latest
```

The port mapping is 7864, the same port the manager listens on by default, and the single volume is the data directory, which holds the database, the logs and the update state. Everything else, including the account credentials, is expected to live in the upstream's own volumes.

The server script route installs the dependencies, pulls the package and sets up systemd:

```bash
bash <(curl -fsSL https://raw.githubusercontent.com/ithtelab/workbuddy-manager/main/deploy/install.sh)
```

After installation you open http://<server>:7864 in a browser, and the initial administrator password is printed in the logs from the first startup. The Docker Compose file describes the same thing, and notes that if WB_ADMIN_PASSWORD is not set a random password is generated and printed to the container logs.

The Windows installer route is the interesting one because of what it does about dependencies. The default successful single file installer build has the whole runtime baked in, including the Python runtime, the upstream gateway and the front end pages, so the target machine needs no dependencies installed at all and Windows 10 (1903 and later) or Windows 11 can run it on a double click. The installer is built by CI on a Windows runner at release time and comes with a .sha256 file next to it for verification.

Two caveats about the installer are stated plainly. It does not have an Authenticode signature, so SmartScreen may show a warning, and the documentation advises confirming the file comes from a trusted release or building it yourself. And the installer does not update itself: to upgrade you fetch a new exe and overwrite the installation, and the .env, the accounts and the data directory are preserved. There is also a history note, that the v1.0.88 installer was withdrawn because uninstalling could delete the wrong files, and that from v1.0.89 uninstall only removes the program's own files, with the install directory changed to a selected directory plus a WorkBuddyManager subdirectory.

## The API surface, and the separation between gateway keys and admin sessions

The interface table is long, and it separates into two families that have different authentication models. The distinction is the security design, not an accident of routing.

The gateway family is under /v1 and authenticated with a gateway key. POST /v1/chat/completions is the OpenAI compatible chat endpoint with streaming and non-streaming support, and /v2/chat/completions is the same on the v2 path. /v1/responses and /responses are the OpenAI Responses API compatible endpoints. /v1/messages is the Anthropic Messages API compatible endpoint, for tools such as Claude Code, and /v1/messages/count_tokens estimates input tokens by character count. GET /v1/models lists models, and GET /v1/usage is a key self-check returning used and remaining quota, the validity period and the model scope, reading only the panel's own records and not making a call.

The management family is under /api and authenticated with a session, with administrator rights for some operations. /api/me returns the current logged in user and /api/login and /api/logout handle the session. /api/accounts lists accounts, and /api/accounts/credits-snapshot is a read-only snapshot of registered credits for dashboards and reconciliation scripts that does not query the upstream. /api/auth/start and /api/auth/poll drive the QR code authorisation flow, and /api/accounts/{file}/checkin, /test and /refresh do check-in, liveness testing and refreshing, with DELETE /api/accounts/{file} removing an account.

Then the configuration and audit routes. /api/keys, /api/upstreams, /api/tokens, /api/logs and /api/stats, /api/security, /api/settings, and /api/settings/pg-sync for the PostgreSQL off site backup with configure, test connection, migrate and restore operations. The upstream routes have a probe operation, POST /api/upstreams/{id}/probe, for connectivity testing.

GET /healthz needs no authentication and is a liveness probe that includes upstream connectivity. The management API details are browsable at /docs once the service is running, which is the Swagger UI.

The security note in the documentation is worth reading alongside the table. Gateway keys are shown only once at creation and only the hash is stored in the database, and account credentials are not returned to the front end. The environment file shows the session model, with WB_SESSION_DAYS as the total session lifetime and WB_SESSION_IDLE_HOURS as the idle window with sliding renewal, where zero disables the idle check.

## What the console actually adds over the command line

The documentation states the workflow as three steps: enrol accounts, distribute keys, point downstream at /v1. What is new relative to the upstream is everything around that.

On the account side, the panel does QR code enrolment through WeChat or QQ scan authorisation, and after a successful scan it performs the daily check-in automatically, writes the authorisation file and reloads the upstream. There is validity monitoring with a progress bar and an approaching expiry warning, one click renewal, connectivity probing and manual check-in. Accounts can be grouped into several pools, with lists, check-ins and keys independent per group. There is a real time balance display with colouring by balance band and a countdown for credits about to expire. And there is a task record showing the results of check-in, travel, activity and keep-alive runs, with the credit flows, paginated.

On the key side, the panel moves from one global key for all downstream users to multiple keys, each with its own version, validity period, IP count limit, IP whitelist, model whitelist and quota. That is the single most valuable operational change, because it turns a shared secret into a set of revocable, attributable credentials.

There is also model alias mapping, so the names downstream uses can be mapped to the actual models, which makes a migration painless, and a model centre page that lists the models available on the accounts along with context size, maximum output and reasoning level.

And there is full auditing. Every call records the key, the IP, the model, the status code, the time to first token, the charge and the cache hit. The usage statistics screen breaks this down by day or by hour with failures and latency, and the log screen shows each call's model, status, first token latency and charge.

The safety side is in the settings: inbound IP allow and deny lists in CIDR form, per key IP limits and whitelists, audit logs, and session and token separation. Scheduled tasks cover the four types of check-in, travel, activity and keep-alive with configurable execution times. Multiple upstreams can be configured under settings, each with an address and an api_key, and a key can be bound to an upstream, which is how an upstream maps to an account pool group. The interface is available in five languages: Simplified Chinese, Traditional Chinese, English, Japanese and Korean.

## The container design decisions, including the ones that are degraded

The Dockerfile documents its own trade-offs at length, and the honesty is unusual enough to be the most useful part of the project to read.

The image does not contain the upstream workbuddy2api. The upstream is a separate Go service with its own compose file and its own auths and data volumes, and bundling it would make the two upgrade cycles interfere with each other. The recommendation is to run both with compose and have the manager reach the upstream through WB2API_BASE.

The consequence is that the container form of the one click update cannot restart itself. There is no systemd inside a container and the process cannot restart the container it is running in, so the update in a container means replacing the code, ending the container, and letting the compose restart policy bring it back up with the new code. That requires restart: unless-stopped in compose, which the repository's compose file already sets. The comment also explains the user site packages detail, since the image's dependencies are installed as root while the process runs as uid 10001, so an update installs into the user site under /app/.local rather than writing into root's directories.

Mounting docker.sock is optional and provided by default. With it, the manager in the container can operate the upstream container the way a host deployment would, reloading configuration, reading logs and updating the upstream. The comment goes out of its way to correct an earlier claim about risk, noting that the host deployment already runs this service as root because the systemd unit has no User= and the install script requires root, and a root host process can already reach the host filesystem with a bind mount, so the two are equivalent in that respect rather than the socket introducing a new risk level. If your threat model requires least privilege, comment out the volume and the docker dependent features degrade to a prompt to operate on the host, which the interface states honestly rather than failing silently.

Data and credentials all go through volumes and are not baked into the image: the data directory with the database, logs and update state, plus the upstream's auths and config.json.

The last design note explains a fix for issue 38. The front end output directory is a build artefact excluded by gitignore, so a git clone followed by docker build failed at the copy step with a not found error for that path. The fix is that the front end build stage builds the front end inside the container when there is no prebuilt output, so both the release package and a git clone work with one command and the user does not need Node installed. The compose file also exposes build arguments for a Debian mirror, a PyPI index and an npm registry, with a comment explaining that the official sources are on international CDNs and are measured to fail frequently from mainland China, and that the Dockerfile already has an automatic fallback to a mirror when the official source fails.

## Licence, release pace and the questions to settle before deploying

The licence is recorded as a custom one that GitHub could not classify, and there is a LICENSE file at the repository root. The badge in the header reads MIT, and the documentation says the upstream source is distributed with the release package under MIT. So the upstream code carries an MIT grant while the console's own licence is a separate document, and for anyone redistributing this, the two need to be read separately rather than assumed identical.

The release pace is fast. The three most recent tags are v1.0.92, v1.0.91 and v1.0.90, all on 2026-10-15 within a few hours of each other, and the last push was on 2026-10-15. The project is not archived and is clearly being worked on, with a CHANGELOG.md in the repository and a changelog badge pointing at it.

The version numbering is worth a note, because v1.0.92 is not a mature 1.0 in the usual sense. It is a high patch number on a 1.0 line that is being incremented very frequently, which is what a project shipping a console and a gateway together does as it iterates on both.

There are three things to settle before you deploy this, and the documentation gives you the places to look for each.

The first is the docker.sock decision, which the compose file and Dockerfile both discuss at length. If you mount it, the container can manage the upstream container. If you do not, several features degrade to a message telling you to operate on the host, which is the safer default if your threat model says the console should not be able to control the Docker daemon.

The second is the security posture of the account credentials. The console holds the authorisation files and never returns credentials to the front end, and the keys are hashed at rest, but the upstream's auths directory is mounted into the service and readable by it.

The third is the environment file, and it is where the operational decisions live. The manager host and port default to 0.0.0.0 and 7864, and the secure cookie setting has a value of auto, which decides the Secure flag from the X-Forwarded-Proto header, with the documented advice that if the reverse proxy does not pass that header, auto silently omits Secure and logs a warning, so a deployment known to be HTTPS only should set it to true rather than depend on the proxy. The allowed cross origin sources are empty by default for same origin deployment. The upstream base URL, the upstream key, the upstream mode, the container name, the auth directory, the upstream config path and the data directory are all configured in the same file, and the documentation notes that the upstream directory is the single place that the task scripts, the upstream update and the native start and stop scripts all read from.

## Conclusion

WorkBuddy Manager fits an operator who already runs workbuddy2api and needs a control plane for it, who wants per key quotas, IP and model restrictions and call level auditing rather than a single shared secret, and who is comfortable administering a service that holds account credentials. It is the wrong tool if you want a standalone API gateway, since the console is explicitly a companion to the upstream and the upstream source ships inside the release package, and it is the wrong tool if you need a signed, off the shelf installer, since the Windows installer carries no Authenticode signature. Before deploying, read the deploy/README.md guide and the docs/development.md architecture notes, decide how you will mount docker.sock, and note that the container form cannot restart itself, so compose needs a restart policy.

## FAQ

### What does WorkBuddy Manager do and how does it relate to workbuddy2api?

workbuddy2api is a Go reverse proxy that turns a pool of Tencent CodeBuddy accounts into an OpenAI compatible interface, but it is command line only. WorkBuddy Manager is the web console for it, adding QR code account enrolment, scheduled check-ins, per key quotas, IP and model whitelists and call auditing, and it does not modify the upstream's code.

### How do I run WorkBuddy Manager with Docker?

Run docker run -d --name workbuddy-manager -p 7864:7864 -v /opt/workbuddy-manager/data:/app/data ghcr.io/ithtelab/workbuddy-manager:latest, then open http://<server>:7864. The initial administrator password is printed in the logs on first startup, or is generated randomly if WB_ADMIN_PASSWORD is not set.

### Which API paths does the WorkBuddy Manager gateway expose?

Under /v1 with a gateway key: chat completions at /v1/chat/completions and /v2/chat/completions, the Responses API at /v1/responses, the Anthropic Messages API at /v1/messages, a token estimate at /v1/messages/count_tokens, a model list at /v1/models and a quota self-check at /v1/usage. Management routes live under /api and use a session, and /healthz is an unauthenticated liveness probe.

### Does the WorkBuddy Manager container support one click updates?

Partly. There is no systemd inside a container and the process cannot restart its own container, so an update replaces the code, ends the container, and relies on the compose restart policy to bring it up again, which requires restart: unless-stopped. The repository's compose file already sets that.

## Sources

- [Issues](https://github.com/ithtelab/workbuddy-manager/issues)
- [ithtelab/workbuddy-manager on GitHub](https://github.com/ithtelab/workbuddy-manager)
- [README](https://github.com/ithtelab/workbuddy-manager/blob/main/README.md)
- [Releases](https://github.com/ithtelab/workbuddy-manager/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/ithtelab-workbuddy-manager
