c3api

module
v0.0.1-beta.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 26, 2026 License: AGPL-3.0

README

⚡ c3api

A lightweight AI gateway — one entry point for the OpenAI Responses API, the Anthropic Messages API, and the OpenAI Chat Completions API, with a built-in admin console, usage tracking, and billing.

English | 中文

Release Stars License Go

OpenAI Responses API Anthropic Messages API OpenAI Chat Completions API

c3api is a self-hosted AI gateway that fronts multiple upstream providers with one unified entry point. It speaks all six request formats — OpenAI Responses API (including its WebSocket variant), Anthropic Messages API, OpenAI Chat Completions API, OpenAI Images API, Codex web search, and an OpenAI-compatible model list — and maps them onto your configured upstream accounts with model routing, quotas, usage accounting, and an embedded admin console.

Status: Beta

c3api is in beta: feature-complete, but breaking changes are free to happen.

  • Not backward-compatible — database schemas and configuration are not backward-compatible across versions, and no migration path is provided.
  • Upgrade = fresh setup — upgrading from an earlier version means provisioning a brand-new database and re-checking your configuration from scratch.
  • See CHANGELOG.md for release notes.

Features

Six formats, one gateway OpenAI Responses API (REST + WebSocket), Anthropic Messages API, OpenAI Chat Completions API, OpenAI Images API, Codex web search, and the OpenAI-compatible model list — each with its own upstream orchestration and protocol conversion
Template & account management Model templates, upstream accounts, groups, credentials, and per-template format/model allowlists
Admin console React web UI embedded in the binary (/app), plus a full OpenAPI-defined admin API (/api/admin)
Billing & usage Per-user balance with pre-check deductions, FEFO temporary quotas, per-model pricing synced from litellm, daily-partitioned usage logs and statistics — billing is enabled by default (config.example.toml billing.enabled=true)
Rules engine Customizable routing, rate limiting, and 429/error backoff rules with a built-in scheduler
Multi-instance ready PostgreSQL-based state, NOTIFY-based cross-instance invalidation, Redis-heartbeat instance discovery (cluster size auto-detected) — zero-config horizontal scaling
Single binary Go binary with embedded frontend, non-root Docker image, drop-in deployment
Request formats
Format Endpoint Upstream
OpenAI Responses API POST /v1/responses OpenAI Responses (REST)
OpenAI Responses API — WebSocket WS /v1/responses (GET with upgrade header) OpenAI Responses WebSocket (e.g. Codex client)
Anthropic Messages API POST /v1/messages Anthropic Messages (REST + SSE)
OpenAI Chat Completions API POST /v1/chat/completions OpenAI Chat Completions (REST + SSE)
OpenAI Images API POST /v1/images/generations / POST /v1/images/edits OpenAI Images (JSON + multipart, REST + SSE)
Codex web search POST /v1/alpha/search Codex SDK Search (Codex client only)
OpenAI model list GET /v1/models In-memory scheduler snapshot (zero DB)

Quick Start

Run the prebuilt image (pull) — for production, remove the build: block in compose.yml so up runs the pulled image instead of building:

cp .env.example .env        # fill in AUTH_JWT_SECRET (ADMIN_TOKEN optional — see below)
docker compose pull
docker compose up -d

Self-build — compose builds the image locally (image and build coexist, build wins):

cp .env.example .env        # fill in AUTH_JWT_SECRET (ADMIN_TOKEN optional — see below)
docker compose up -d --build

The gateway listens on http://127.0.0.1:18080 — admin console at /app, user console at /user, health check at /healthz.

First admin user (bootstrap) — the first user to register on a fresh database automatically becomes a platform_admin and can sign into the admin console (/app) right after startup; later signups get the regular user role.

Prebuilt images are published to GHCR (ghcr.io/is7qin/c3api): :beta tracks the latest beta release, and version-pinned tags such as :v0.0.1-beta.1 are also available. Pull standalone: docker pull ghcr.io/is7qin/c3api:beta.

Option B: Local development
# 0. Inject local dev secrets once (config.toml keeps empty values; placeholder
#    values like change-me are rejected by config.Load)
export C3API_ADMIN_TOKEN=local-admin-token
export C3API_AUTH_JWT_SECRET=$(openssl rand -hex 16)

# 1. Start the gateway (default :18080)
go run ./cmd/server -config config.toml

# 2. Start the frontend dev server (:5173, proxies /api to 18080)
cd web && pnpm install && pnpm run dev

Point any OpenAI/Anthropic-compatible SDK at the gateway URL — the request format is selected by path, so a single base URL serves all six request formats.

Sponsors

Sponsor About
ForZTN Next-gen VPC-based IDC cloud panel · network/VM exchange

Architecture

                    ┌───────────────────────────────┐
 OpenAI SDK / curl ─▶│   c3api gateway (1 binary)    │
 Anthropic SDK ─────▶│  ┌─────────────────────────┐  │
 Codex client ──────▶│  │ chi router              │  │──▶ OpenAI upstream (REST + SSE)
   Browser (SPA) ───▶│  │ /healthz /api/admin /api/user + SPA / /user /app │  │──▶ Anthropic upstream (REST + SSE)
                    │  │ /v1/*                    │  │──▶ Responses / resp-ws upstream
                    │  │ proxy: auth → gate →     │  │
                    │  │         route → forward  │  │
                    │  └──────────┬──────────────┘  │
                    │   workers: billing / usage /  │
                    │   errlog / scheduler / notify │
                    │   retention / stats-agg /     │
                    │   pricing-sync / rule-engine  │
                    │   auth-sync / invalidate /    │
                    │   discovery                   │
                    └───────┼───────────────┼──────┘
                            ▼               ▼
              PostgreSQL 18 (state + NOTIFY) │
                                             ▼
                        Redis 8 (ephemeral: coordination +
                        short-lived verification codes)
  • Single binary: the frontend is built and embedded via go:embed, so the runtime is one server process plus a mounted config file.
  • Stateless gateway, stateful DB: all shared state lives in PostgreSQL; instances coordinate through NOTIFY on the c3api_invalidate channel. The cluster instance count for multi-instance budget sharing is auto-discovered via Redis heartbeats — scale horizontally by just adding instances (no manual setting).
  • Persistent workers: billing deduction, usage/statistics flushing, error-log auditing, partition retention, offline stats aggregation, price sync, and the rule scheduler run as long-lived workers with graceful shutdown draining.

Performance

A 30 s CPU profile of the gateway (go tool pprof -http=:8081 <profile.pprof>):

CPU flame graph

The hot spots are I/O and garbage collection, not request-path logic:

  • ~23% syscall — network reads/writes (upstream connections, pgx)
  • ~15% GC — allocation-heavy paths (span class sizing + object/span scanning)
  • ~4% selectgo — concurrency wait; ~1% memmove — data copies

The request path itself (JSON parsing, routing, quota/billing) stays a single-digit share of total CPU, leaving headroom for higher throughput. GC tuning hooks (GOGC / GOMEMLIMIT) are wired through the compose stack — see .env.example.

Configuration

The gateway loads config.toml (see config.example.toml), overlaid by C3API_-prefixed environment variables (the prefix must be uppercase):

Variable Description
C3API_ADMIN_TOKEN Admin API token (optional; leave empty to disable static-token auth — /api/admin then accepts platform_admin JWTs only)
C3API_AUTH_JWT_SECRET JWT signing secret for user auth (required; stable across restarts and instances)
C3API_DB_DSN PostgreSQL DSN
C3API_REDIS_ADDR Redis address (required; e.g. 127.0.0.1:6379 — instance discovery, short-lived verification codes and other ephemeral state)
C3API_SERVER_TIME_ZONE Deployment timezone for pricing time/day-of-week conditions (IANA name, e.g. Asia/Shanghai; empty = process local)

See config.example.toml for the full schema (server, log, admin, auth, db, redis, proxy, upstream, limit, scheduler, usage, billing).

  • Fresh setup only (no migration path) — schemas and configuration are not backward-compatible between versions: an upgrade means a brand-new database and a re-checked configuration (see Status: Beta).
  • Env-only deployments (e.g. K8s): pass -config "" to skip the config file entirely — the flag defaults to config.toml, and a missing file is a startup error.
  • Config is read once at startup — changes require a rolling restart (no hot reload).
  • Invalid config fails fast at startup with the offending key: non-positive durations/intervals, unknown keys (typos, removed legacy keys), missing required secrets, and placeholder values (change-me, dev-admin-token, …) are all rejected.

Deployment

  • compose.yml — production stack: one db (postgres:18-alpine, bind-mounted data under deploy/data/pg) + one redis (redis:8-alpine, ephemeral coordination + short-lived verification codes — no persistence) + one app container (non-root, read-only config mount from deploy/config.toml, healthcheck).
  • Dockerfile — three-stage build (node → go → alpine), producing a single static binary with the UI embedded.
  • Dual required dependencies: PostgreSQL 18 (all durable state, source of record) + Redis 8 (ephemeral coordination + short-lived email verification codes — instance discovery heartbeats and verification codes; never a cache layer). Both are startup-mandatory. Do not set an eviction policy (allkeys-lru etc.) for this instance: an evicted code is benign (the user just re-requests one), but keep it out of the configuration.
GC tuning (optional, default off)

Under high concurrency (25k+ concurrent streams) the default Go GC becomes a measurable cost. A/B-tested at 50k concurrency on a 24-core box, GOGC=off + GOMEMLIMIT=17179869184 (16 GiB — plain bytes, the env var rejects unit suffixes like 16G) measured:

  • Throughput +14.5% (25.1k → 28.75k req/s)
  • Per-request CPU -27.7% (331 → 239 µs)
  • First-byte latency -21% (1103 → 873 ms); p99 first-byte -36% (5785 → 3705 ms)

Trade-off: the heap grows to the limit (~13 GiB with a 16 GiB cap) and GC fires roughly every ~10 s instead of every ~2-3 s — fine on a 64 GiB box; raise GOMEMLIMIT to cut the frequency further at the cost of memory. Set both in .env (empty values keep Go defaults):

GOGC=off
GOMEMLIMIT=17179869184

License

c3api is open source under the GNU AGPL v3.0-or-later (LICENSE) — free to use, modify, and deploy, including for commercial and hosted services, with the single obligation that modifications are contributed back under the same terms. No purchase is required.

Need closed-source deployment (exempt from the AGPL obligations on your deployment)? A commercial license (LICENSE.commercial) is available — it waives the AGPL obligations for your deployment.

External code contributions require a CLA (contributor license agreement) so that contributed code can be merged under this dual-license scheme — see CLA.md and CONTRIBUTING.md.

Contact

  • GitHub: is7Qin/c3api
  • Issues: open a GitHub issue for bugs, questions, or feature requests
  • Security: report vulnerabilities via SECURITY.md (private report)
  • Changelog: CHANGELOG.md

Directories

Path Synopsis
cmd
server command
github.com/is7qin/c3api 入口:配置 → DB/ent → 各模块装配 → 优雅退出。
github.com/is7qin/c3api 入口:配置 → DB/ent → 各模块装配 → 优雅退出。
internal
auth
Package auth 承载用户认证体系:bcrypt 密码哈希(与 sub2api 同参数)、 JWT 签发/验证(HS256,TTL 24h)与 RBAC 中间件(/user 组 RequireJWT + 快照用户状态校验;/admin = 静态 token OR platform_admin JWT)。
Package auth 承载用户认证体系:bcrypt 密码哈希(与 sub2api 同参数)、 JWT 签发/验证(HS256,TTL 24h)与 RBAC 中间件(/user 组 RequireJWT + 快照用户状态校验;/admin = 静态 token OR platform_admin JWT)。
billing
Package billing 计费核心:service_tier 归一化 + 价格矩阵纯函数 + 余额快照 + 批量扣费 flusher(T2/T3)。
Package billing 计费核心:service_tier 归一化 + 价格矩阵纯函数 + 余额快照 + 批量扣费 flusher(T2/T3)。
credential
Package credential 是账号凭据抽象层:类型注册表 + Provider 分发,为后续 多种号池类型(codex oauth、codex personal_access_token、claude code 等) 打地基。
Package credential 是账号凭据抽象层:类型注册表 + Provider 分发,为后续 多种号池类型(codex oauth、codex personal_access_token、claude code 等) 打地基。
discovery
Package discovery Redis 实例发现(spec 2026-08-25-redis-instance-discovery-design, 基建面见 2026-08-25-redis-foundation-design):单 ZSET 心跳成员协议替换手工 cluster.instances 设置——实例心跳注册、活体计数即多实例预算分摊基数 N。
Package discovery Redis 实例发现(spec 2026-08-25-redis-instance-discovery-design, 基建面见 2026-08-25-redis-foundation-design):单 ZSET 心跳成员协议替换手工 cluster.instances 设置——实例心跳注册、活体计数即多实例预算分摊基数 N。
domain
SPDX-License-Identifier: AGPL-3.0-or-later Dual-licensed: AGPL-3.0-or-later (open source) or commercial license (closed-source deployment exemption); see LICENSE and LICENSE.commercial.
SPDX-License-Identifier: AGPL-3.0-or-later Dual-licensed: AGPL-3.0-or-later (open source) or commercial license (closed-source deployment exemption); see LICENSE and LICENSE.commercial.
ent
handler
Package handler provides primitives to interact with the openapi HTTP API.
Package handler provides primitives to interact with the openapi HTTP API.
handler/httpface
Package httpface 管理面/用户面 HTTP 边界:响应书写(WriteJSON/WriteErr/ WriteServiceErr,service 错误→HTTP 状态映射表唯一一份)+ 请求参数边界 (ClampLimit——两面包共享的分页上限钳制)。
Package httpface 管理面/用户面 HTTP 边界:响应书写(WriteJSON/WriteErr/ WriteServiceErr,service 错误→HTTP 状态映射表唯一一份)+ 请求参数边界 (ClampLimit——两面包共享的分页上限钳制)。
handler/user
Package user provides primitives to interact with the openapi HTTP API.
Package user provides primitives to interact with the openapi HTTP API.
invalidate
Package invalidate 管理面变更的去抖定向失效(Phase 6 O2):
Package invalidate 管理面变更的去抖定向失效(Phase 6 O2):
notify
Package notify 多实例变更广播(#14 T1 基础层):PG LISTEN/NOTIFY 定向刷新。
Package notify 多实例变更广播(#14 T1 基础层):PG LISTEN/NOTIFY 定向刷新。
protoconv
Package protoconv 提供网关级协议转换(W5,只补差语义):客户端协议请求体 → 模板协议请求体(ConvertRequest)、模板协议响应 → 客户端协议响应 (ConvertResponse 非流式 JSON / StreamMapper 流式 SSE 事件映射)。
Package protoconv 提供网关级协议转换(W5,只补差语义):客户端协议请求体 → 模板协议请求体(ConvertRequest)、模板协议响应 → 客户端协议响应 (ConvertResponse 非流式 JSON / StreamMapper 流式 SSE 事件映射)。
proxy
Package proxy 是 AI 请求热路径:分组 key 鉴权 → 调度器选号 → SDK 转发 → 用量采集。
Package proxy 是 AI 请求热路径:分组 key 鉴权 → 调度器选号 → SDK 转发 → 用量采集。
repository
SPDX-License-Identifier: AGPL-3.0-or-later Dual-licensed: AGPL-3.0-or-later (open source) or commercial license (closed-source deployment exemption); see LICENSE and LICENSE.commercial.
SPDX-License-Identifier: AGPL-3.0-or-later Dual-licensed: AGPL-3.0-or-later (open source) or commercial license (closed-source deployment exemption); see LICENSE and LICENSE.commercial.
rule
Package rule 实现规则引擎(可编排状态管理):事件 → 有界 channel → 规则 worker (priority 首中匹配)→ 状态/冷却/权重更新。
Package rule 实现规则引擎(可编排状态管理):事件 → 有界 channel → 规则 worker (priority 首中匹配)→ 状态/冷却/权重更新。
scheduler
Package scheduler 实现内存优先的账号调度:规则驱动的状态管理(internal/rule 引擎 事件投递 + apply 回调)、选号(格式硬过滤 + 模型硬白名单 + 全模型账号 tier2 兜底 + 预生成加权轮询序列)、并发槽、快照缓存与异步状态回写。
Package scheduler 实现内存优先的账号调度:规则驱动的状态管理(internal/rule 引擎 事件投递 + apply 回调)、选号(格式硬过滤 + 模型硬白名单 + 全模型账号 tier2 兜底 + 预生成加权轮询序列)、并发槽、快照缓存与异步状态回写。
sdkbridge
Package sdkbridge 是 SDK 适配层与网关之间的契约面(T1,零 SDK 依赖——SDK 调用从 T2 起):统一失效回调 + 失效处理链装配(写失效字段 / 调度摘除 / 审计)、网关侧信封错误(P2-1)与凭据传递形态(AccountCredential 派生在 internal/domain)。
Package sdkbridge 是 SDK 适配层与网关之间的契约面(T1,零 SDK 依赖——SDK 调用从 T2 起):统一失效回调 + 失效处理链装配(写失效字段 / 调度摘除 / 审计)、网关侧信封错误(P2-1)与凭据传递形态(AccountCredential 派生在 internal/domain)。
server
Package server 装配 chi 路由:/api/admin/*(静态 token OR platform_admin JWT)+ /api/user/*(JWT 保护,register/login 公开)+ 三个 AI 端点 + /healthz。
Package server 装配 chi 路由:/api/admin/*(静态 token OR platform_admin JWT)+ /api/user/*(JWT 保护,register/login 公开)+ 三个 AI 端点 + /healthz。
service
SPDX-License-Identifier: AGPL-3.0-or-later Dual-licensed: AGPL-3.0-or-later (open source) or commercial license (closed-source deployment exemption); see LICENSE and LICENSE.commercial.
SPDX-License-Identifier: AGPL-3.0-or-later Dual-licensed: AGPL-3.0-or-later (open source) or commercial license (closed-source deployment exemption); see LICENSE and LICENSE.commercial.
service/errors
Package serviceerr 定义 service 层错误哨兵(单一真相)。
Package serviceerr 定义 service 层错误哨兵(单一真相)。
snapshot
Package snapshot 统一网关各内存快照的生命周期:启动就绪(ReloadAll 全量首刷) + NOTIFY 事件分发(按 scope 精确重载)+ 状态可观测(Status)。
Package snapshot 统一网关各内存快照的生命周期:启动就绪(ReloadAll 全量首刷) + NOTIFY 事件分发(按 scope 精确重载)+ 状态可观测(Status)。
usage
归属说明:retention worker 放 usage 包——原 Recorder.janitorLoop(逐行 DELETE)已删除,保留策略整体移交本 worker;保留天数与 Recorder 配置同源 (config usage.log_retention_days),故与 Recorder 同包管理。
归属说明:retention worker 放 usage 包——原 Recorder.janitorLoop(逐行 DELETE)已删除,保留策略整体移交本 worker;保留天数与 Recorder 配置同源 (config usage.log_retention_days),故与 Recorder 同包管理。
verification
Package verification 邮箱验证码的 Redis 存储(spec 2026-08-25-emailcode-redis-migration §2):一个 (purpose,email) 一个 HASH,四命令直映 service.EmailCodeStore,零 Lua。
Package verification 邮箱验证码的 Redis 存储(spec 2026-08-25-emailcode-redis-migration §2):一个 (purpose,email) 一个 HASH,四命令直映 service.EmailCodeStore,零 Lua。
worker
SPDX-License-Identifier: AGPL-3.0-or-later
SPDX-License-Identifier: AGPL-3.0-or-later
pkg
aiclient
Package aiclient 是 openai/anthropic 官方 SDK 的唯一引用点:客户端懒构建 + 鉴权头注入 + 非流式超时策略。
Package aiclient 是 openai/anthropic 官方 SDK 的唯一引用点:客户端懒构建 + 鉴权头注入 + 非流式超时策略。
httpx
Package httpx 构造共享的上游 HTTP 客户端(规格 §10.2 连接层参数)。
Package httpx 构造共享的上游 HTTP 客户端(规格 §10.2 连接层参数)。
logx
Package logx 是 zap 的薄包装:业务代码只允许经本包取日志,禁止直接 import zap。
Package logx 是 zap 的薄包装:业务代码只允许经本包取日志,禁止直接 import zap。
redisx
Package redisx 全仓唯一 Redis 客户端构造点(spec 2026-08-25-redis-foundation-design §2.2):cmd/server/main.go 调 Open,产物 *redis.Client 以构造注入分发。
Package redisx 全仓唯一 Redis 客户端构造点(spec 2026-08-25-redis-foundation-design §2.2):cmd/server/main.go 调 Open,产物 *redis.Client 以构造注入分发。
sserelay
Package sserelay 提供原始字节级 SSE relay:从 io.Reader 增量读取 SSE 帧, 原样转发给 http.ResponseWriter,自适应批量 Flush,并以 Observer 旁路暴露 事件信息(仅用于 usage 提取,不参与转发决策)。
Package sserelay 提供原始字节级 SSE relay:从 io.Reader 增量读取 SSE 帧, 原样转发给 http.ResponseWriter,自适应批量 Flush,并以 Observer 旁路暴露 事件信息(仅用于 usage 提取,不参与转发决策)。
tools
fakeupstream command
fakeupstream 模拟 OpenAI chat/completions 上游:支持流式(chunks 个事件 + usage + [DONE])。
fakeupstream 模拟 OpenAI chat/completions 上游:支持流式(chunks 个事件 + usage + [DONE])。
loadtest command
loadtest 对网关打压测:固定并发 goroutine 持续请求,支持流式首字节和非流式完整响应延迟。
loadtest 对网关打压测:固定并发 goroutine 持续请求,支持流式首字节和非流式完整响应延迟。
loadtest/setup command
setup 构造多租户压测数据(Phase 3a 数据模型 + Phase 5 计费字段): 模板(三格式 × 随机模型池)→ 公开组 → 账号(分散模板/组/上游)→ 用户(可选 余额/并发区间)→ 逐个登录建 key(可选多 key/并发/额度区间),key 明文写文件 (loadtest -keys 用)。
setup 构造多租户压测数据(Phase 3a 数据模型 + Phase 5 计费字段): 模板(三格式 × 随机模型池)→ 公开组 → 账号(分散模板/组/上游)→ 用户(可选 余额/并发区间)→ 逐个登录建 key(可选多 key/并发/额度区间),key 明文写文件 (loadtest -keys 用)。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL