freebuff-proxy is a small Go service that exposes a local OpenAI- and Anthropic-compatible HTTP surface in front of the Freebuff/Codebuff session infrastructure. It lets you sign in to a Freebuff account via CLI, securely use the credential stored in credentials.json, keep the session alive, and serve compatible endpoints at /v1/models, /v1/chat/completions, /v1/messages, and /v1/messages/count_tokens.
Chat endpoints accept OpenAI or Anthropic-shaped requests, prepare the Freebuff session, start an agent run via /api/v1/agent-runs (like the Codebuff CLI flow), send the request to the verified upstream chat route with codebuff_metadata, and convert the response into chat.completion, OpenAI SSE chunk, or Anthropic Messages/SSE format according to the client contract.
🇹🇷 Türkçe doküman için bkz. README.TR.md
- Go 1.25+
- A Freebuff/Codebuff account
- Docker CLI and a running daemon (only if you want to build a container image)
The HTTP layer runs on Fiber v3. On startup the project root .env file is loaded automatically via github.com/joho/godotenv; process env values take precedence over .env entries.
go test ./...
go vet ./...
go build -o bin/freebuff-proxy ./cmd/freebuff-proxyMakefile targets:
make test
make vet
make build
make run
make run-bin
make doctor
make smoke-openai
make smoke-anthropic
make dockermake build produces the binary at bin/freebuff-proxy. make run-bin builds then runs ./bin/freebuff-proxy serve. make doctor inspects the process on port 1455 and stale root binary artifacts. make smoke-openai and make smoke-anthropic send sample requests to the running local proxy. To install directly:
go install ./cmd/freebuff-proxy| Variable | Default | Description |
|---|---|---|
FREEBUFF_PROXY_ADDR |
127.0.0.1:1455 |
HTTP listen address. Use 0.0.0.0:1455 to expose the port inside a container. |
FREEBUFF_API_BASE_URL |
https://www.codebuff.com |
Freebuff/Codebuff API base URL for login, logout, and session endpoints. |
FREEBUFF_MODEL |
deepseek/deepseek-v4-pro |
Model name listed in /v1/models and used by sample requests. |
FREEBUFF_PROXY_API_KEY |
(empty) | When set, the proxy requires Authorization: Bearer <value> or x-api-key: <value> on every endpoint. Use as api_key for OpenAI-compatible clients or ANTHROPIC_AUTH_TOKEN / ANTHROPIC_API_KEY on the Anthropic/Claude Code side. |
FREEBUFF_CREDENTIALS_PATH |
$HOME/.config/manicode/credentials.json |
Path to the credential file written after login and read during serve. |
Example .env:
FREEBUFF_PROXY_ADDR=127.0.0.1:1455
FREEBUFF_API_BASE_URL=https://www.codebuff.com
FREEBUFF_MODEL=deepseek/deepseek-v4-pro
FREEBUFF_PROXY_API_KEY=local-proxy-key
FREEBUFF_CREDENTIALS_PATH=$HOME/.config/manicode/credentials.jsonDo not commit real production secrets to .env; prefer a secret manager or runtime env in CI/container environments.
-
Build the binary:
make build
-
Export local settings (or write them to
.env):export FREEBUFF_PROXY_ADDR=127.0.0.1:1455 export FREEBUFF_API_BASE_URL=https://www.codebuff.com export FREEBUFF_MODEL=deepseek/deepseek-v4-pro export FREEBUFF_PROXY_API_KEY=local-proxy-key export FREEBUFF_CREDENTIALS_PATH="$HOME/.config/manicode/credentials.json"
-
Sign in:
./bin/freebuff-proxy login
The command prints a login link. Open it in your browser, authenticate, and the CLI writes a credential file to
FREEBUFF_CREDENTIALS_PATHonce verification completes. -
Start the proxy:
./bin/freebuff-proxy serve
In another terminal, run quick checks:
make smoke-openai make smoke-anthropic
-
Sign out when done:
./bin/freebuff-proxy logoutLogout tears down the upstream session and removes the local credential file. To stop the proxy process only, press
Ctrl-C.
- Start login:
freebuff-proxy login
- The command prints a Freebuff login URL.
- Open the URL in a browser and authenticate.
- The CLI polls the status endpoint until verification completes.
- On success a credential is written to
$HOME/.config/manicode/credentials.jsonby default. - The file is created atomically with
0600permissions.
Custom credential path:
FREEBUFF_CREDENTIALS_PATH=/secure/path/credentials.json freebuff-proxy loginSign out:
freebuff-proxy logoutLogout calls the upstream logout endpoint with stored metadata and removes the local credential file on success.
Start the local service:
make build
./bin/freebuff-proxy serveOr with a single Makefile target:
make run-binRun from source during development:
make runThe default address is 127.0.0.1:1455 so the service is only reachable from localhost. To use a different address or require an API key:
FREEBUFF_PROXY_ADDR=0.0.0.0:1455 \
FREEBUFF_PROXY_API_KEY=local-proxy-key \
./bin/freebuff-proxy serveIf a stale ./freebuff-proxy binary exists in the repo root it may be executed instead of the current bin/freebuff-proxy, causing errors like upstream_chat_route_not_verified. Diagnose with:
make doctor
strings ./freebuff-proxy | grep -F upstream_chat_route_not_verified
strings ./bin/freebuff-proxy | grep -F upstream_chat_route_not_verifiedHealth and model endpoints:
curl http://127.0.0.1:1455/healthz
curl -H 'Authorization: Bearer ***' http://127.0.0.1:1455/v1/modelsThe chat endpoint accepts OpenAI-shaped requests, prepares the Freebuff session, starts an agent run (like the Codebuff CLI flow), and forwards the request to the upstream route with run_id, client_id, cost_mode, and freebuff_instance_id metadata fields. Short model aliases like deepseek-v4-pro and deepseek-v4-flash are resolved to their canonical deepseek/... form in the upstream request:
curl -X POST http://127.0.0.1:1455/v1/chat/completions \
-H 'Authorization: Bearer ***' \
-H 'Content-Type: application/json' \
-d '{"model":"deepseek/deepseek-v4-pro","messages":[{"role":"user","content":"Hello"}]}'A successful call returns object: "chat.completion" with the assistant reply in choices[0].message.content:
{
"object": "chat.completion",
"model": "deepseek/deepseek-v4-pro",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "Hello!"
},
"finish_reason": "stop"
}
]
}Streaming:
curl -N -X POST http://127.0.0.1:1455/v1/chat/completions \
-H 'Authorization: Bearer ***' \
-H 'Content-Type: application/json' \
-d '{"model":"deepseek/deepseek-v4-pro","stream":true,"messages":[{"role":"user","content":"Hello"}]}'Stream responses are delivered as Content-Type: text/event-stream with data: {...} chunks terminated by data: [DONE].
Smoke targets for quick verification:
make smoke-openai
make smoke-anthropic
PROXY_URL=http://127.0.0.1:1455 \
PROXY_API_KEY=local-proxy-key \
SMOKE_MODEL=deepseek/deepseek-v4-pro \
make smoke-anthropicfrom openai import OpenAI
client = OpenAI(
base_url="http://127.0.0.1:1455/v1",
api_key="local-proxy-key",
)
response = client.chat.completions.create(
model="deepseek/deepseek-v4-pro",
messages=[{"role": "user", "content": "Hello"}],
)
print(response.choices[0].message.content)For OpenAI Chat Completions compatible clients set base_url to http://127.0.0.1:1455/v1 and api_key to your FREEBUFF_PROXY_API_KEY value.
export OPENAI_BASE_URL=http://127.0.0.1:1455/v1
export OPENAI_API_KEY=local-proxy-keyChat Completions clients can then use model=deepseek/deepseek-v4-pro through the proxy.
Note: The current OpenAI Codex CLI may use the Responses API (
/v1/responses). This proxy does not yet serve that endpoint. If your Codex version supports Chat Completions or a custom provider, theOPENAI_BASE_URL/OPENAI_API_KEYsettings above should work; Responses API support needs to be added separately.
Anthropic Messages-compatible surfaces:
POST /v1/messagesPOST /v1/messages/count_tokensGET /v1/models
/v1/messages converts Anthropic messages (string or type: "text" content blocks) to OpenAI-compatible chat requests. The response is returned as an Anthropic message object (non-stream) or message_start, content_block_delta, message_delta, and message_stop SSE events (stream). Tool definitions are best-effort converted to OpenAI function tool format; the current upstream chat service returns text responses so Anthropic tool_use response blocks are not produced in this version.
Simple Anthropic curl:
curl -X POST http://127.0.0.1:1455/v1/messages \
-H 'x-api-key: local-proxy-key' \
-H 'anthropic-version: 2023-06-01' \
-H 'Content-Type: application/json' \
-d '{"model":"deepseek/deepseek-v4-pro","max_tokens":256,"messages":[{"role":"user","content":"Hello"}]}'Stream example:
curl -N -X POST http://127.0.0.1:1455/v1/messages \
-H 'x-api-key: local-proxy-key' \
-H 'anthropic-version: 2023-06-01' \
-H 'Content-Type: application/json' \
-d '{"model":"deepseek/deepseek-v4-pro","max_tokens":256,"stream":true,"messages":[{"role":"user","content":"Hello"}]}'Claude Code local gateway setup:
export ANTHROPIC_BASE_URL=http://127.0.0.1:1455
export ANTHROPIC_AUTH_TOKEN=local-proxy-key
export ANTHROPIC_MODEL=deepseek/deepseek-v4-pro
export ANTHROPIC_SMALL_FAST_MODEL=deepseek/deepseek-v4-flash
export CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY=1Then start Claude Code in the same terminal session:
claudeIf the proxy API key is empty ANTHROPIC_AUTH_TOKEN is not required; however providing a non-empty local value on the Claude Code side makes client behavior more predictable.
Pre-built binaries for Linux, macOS, and Windows are published on the GitHub Releases page.
Example download:
gh release download v0.1.0 \
--repo ferdiunal/freebuff-proxy \
--pattern "*darwin-arm64*" \
--dir dist
tar -xzf dist/freebuff-proxy-darwin-arm64.tar.gz -C dist
./dist/freebuff-proxy-darwin-arm64 serveRelease assets contain only the compiled freebuff-proxy binary; credential files are never included.
Build the image:
docker build -t freebuff-proxy:local .Or via Makefile:
make dockerRun:
docker run --rm \
-p 1455:1455 \
-e FREEBUFF_PROXY_ADDR=0.0.0.0:1455 \
-e FREEBUFF_PROXY_API_KEY=local-proxy-key \
-v "$HOME/.config/manicode:/home/freebuff/.config/manicode:ro" \
freebuff-proxy:local serveIf you need to generate the credential file inside the container, mount the volume as read-write. In production, prefer mounting the credential file read-only.
Pushing a v* tag triggers the GitHub Actions release workflow (.github/workflows/release-proxy.yml). The workflow runs go test and go vet, then builds binaries for:
linux-amd64linux-arm64darwin-amd64darwin-arm64windows-amd64
Each binary is packaged as .tar.gz and published as a GitHub Release asset using the default GITHUB_TOKEN. No custom secrets are required.
Manual trigger:
gh workflow run release-proxy.yml -f tag=v0.1.0- Keep
credentials.jsonsecret; it contains your Freebuff session token. - Do not store
credentials.jsonin the repo root or commit it to git. The safe default path is$HOME/.config/manicode/credentials.json. - If the credential file is accidentally pushed to a public repository, rotate/revoke the token and scrub it from git history.
- The credential file should be stored with
0600permissions. The application applies this permission on files it creates; verify permissions on externally mounted files. - Session tokens must not be printed to terminals, logs, error reports, or CI output.
- In externally exposed deployments, set
FREEBUFF_PROXY_API_KEYand require clients to sendAuthorization: Bearer <value>. - The proxy API key does not replace the upstream Freebuff token; it only protects the local proxy surface.
- When running in containers, mount the credential volume read-only if possible and restrict access to the necessary user only.