freebucks-proxy is a Go wire gateway in front of the upstream service: pooled multi-account OpenAI-compatible and Anthropic-compatible endpoints, an embedded Svelte dashboard, optional browser-like TLS stealth, and automatic session lifecycle.
- Speaks OpenAI chat (
POST /v1/chat/completions,GET /v1/models) and an Anthropic-compatible layer, then translates to the upstream wire protocol. - Runs in pooled, bridge, or hybrid mode (
EffectiveMode):- Pooled —
AUTH_TOKENSset +BRIDGE_ENABLED=0; pool only. - Bridge —
AUTH_TOKENSempty; each request carries its own token. - Hybrid (default with
AUTH_TOKENS) —API_KEYScredential uses the pool, any other credential relays upstream as a bridge token.
- Pooled —
- Dashboard at
/admin(Svelte SPA embedded in the binary). - Credit metering follows the wire
pricesmap (upstream credits, wire fieldsfreebucks*): charged once per session-hour at session start, refunded on earlyDELETE, and refilled at the daily reset the server advertises (resetTimeZone/resetAtin the session payload — the account's own local midnight; Pacific midnight only on servers that omit the zone). The proxy's own counters (per-day requests, spend buckets, IP re-admit caps) keep bucketing on the Pacific day (pool/spend.go:bucketStart).
cp .env.example .env # then edit: AUTH_TOKENS, ADMIN_TOKEN, ...
task build # frontend bundle + gateway binary (output in bin/)
task dev # run the gateway from sourceBoth tasks are defined in Taskfile.yml. With plain Go instead of a Task
runner, go build ./backend/... compiles everything and the gateway's main
package lives under backend/cmd/.
Run from GHCR (release image, no local build):
cp .env.example .env # then edit: AUTH_TOKENS, ADMIN_TOKEN, ...
export VERSION="$(gh release view --json tagName -q .tagName)"
docker compose pull
docker compose up -dThat resolves the newest release tag (prereleases excluded); pin VERSION to it
for a reproducible deploy, or leave VERSION unset to follow the latest image.
gh resolves the repository from the checkout — pass --repo <owner>/<name> if
you run it elsewhere. Verify GET /healthz → 200, and note /admin sits behind
the login gate (redirects to /admin/login).
Then:
GET http://localhost:3457/healthz→ 200GET http://localhost:3457/v1/models→ live model listhttp://localhost:3457/admin→ dashboard
Defaults that matter (.env.example): SAFE_MODE=true (anti-ban preset),
COST_MODE=free. The per-day/per-minute request caps are gone — upstream quota
and 429s are the enforcement — and per-IP rate limiting is off by default
(RATE_LIMIT_PER_IP=0; set it and RATE_LIMIT_BURST to enable).
Configuration persistence: the first boot imports the effective config
(process env wins over .env over defaults) into the dashboard DB
(DB_PATH, a SQLite file under data/, mode 0600) as config: overlay rows
plus a config:migrated_env_v1 marker — later boots are no-ops via the marker.
The DB is then the persisted home the dashboard saves write to, secrets
included (AUTH_TOKENS, ADMIN_TOKEN, API_KEYS, WEBHOOK_URL rows);
keep its 0600 mode on copies/backups. Explicit process env still wins at
runtime, so a migrated row never overrides the environment.
Two-path layout: the live store is the DB_PATH file on the db_data named
volume (compose pins it under /app/data — an overlay row can never repoint
the open file), while the host checkout bind (.:/app/state, the working
directory) holds .env, logs, and the pre-volume bind DB under ./data/.
A fresh volume auto-imports that bind DB on first boot — display history plus
the full operator state (settings overlay with secrets, pages, sessions,
tokens, pool blobs), per-table, idempotent, secrets as opaque DB values — then
later boots are strict no-ops. Legacy files are never deleted. Never copy a
live DB with plain cp of the .db/-wal/-shm trio; stop first or use the
backup script.
Every update runs three commands (any trip = roll back, never cut traffic):
docker compose stop # whole stack, incl. the optional https front
scripts/backup-state.sh # snapshot + count manifest
docker compose up -d --build # recreate on the same volume
ADMIN_TOKEN="$ADMIN_TOKEN" scripts/verify-state.sh # healthz + 401 probe + migrate.noop + manifest countsThe gate requires /healthz 200, a wrong-token login 401, a strict no-op
boot (migrate.fresh=false, migrate.noop=true, applied=[]), and live
row counts matching the backup manifest (operator tables exact,
pool_state anti-stranding, history grow-only). First-ever volume adoption
boots fresh=true while it carries the bind DB — confirm the
carried legacy state log line against the manifest, restart once, then
the gate goes green.
The project was renamed from freebuff-proxy (repository, binary, compose
service, container, image). Two things need care exactly once:
git remote set-url origin https://github.com/trefeon/freebucks-proxy.git
docker compose down --remove-orphans # clears the pre-rename container--remove-orphansmatters: the service was renamed, so the old container is no longer part of the stack — leaving it running means two gateways sharing one account pool, which burns quota twice and supersedes sessions.- The image path follows the repository name (
ghcr.io/trefeon/freebucks-proxy), so it exists only once a release is published after the rename. Until then, pinVERSIONto a tag from the previous image path or wait for that release. - The DB filename and session-state filename are deliberately unchanged
(
DB_PATH,SESSION_STATE_FILE): the live volume keeps its store, and pointing either at a new name on an existing volume would open an empty DB. - Installed as a background service? Its unit/task name and install paths
changed too. Uninstall the old one before installing the new: run the old
binary with
-uninstall-service(pre-rename systemd unitfreebuff-proxy.service, launchdcom.freebuff-proxy, or thefreebuff-proxyscheduled task), then move your.envfrom the old config directory to the new one — the renamed installer writesfreebucks-proxypaths, so the old service would otherwise keep serving from the same account pool while the new one starts empty.
backend/— gateway source.frontend/— dashboard SPA source.scripts/— upstream sync / drift tooling, plus the client tool-name corpus generator (extract-tool-calls.sh).docs/— client-compatibility recipes, upstream CLI notes, and architecture decisions (docs/decisions/).
Protected main: branch → PR → green CI → squash merge, Conventional Commits.
See AGENTS.md for the full operating guide. Never commit secrets.