Codara is a stateful gateway that lets OpenAI-compatible clients talk to CLI-native agents such as Codex, Gemini, and OpenCode while reusing sessions, managing workspace state, and routing across a quota-aware account pool.
- Maintains persistent sessions behind
/v1/chat/completions - Reuses workspace-aware context to reduce repeated prompt cost
- Stores provider credentials in the vault/SQLite registry instead of treating local CLI auth files as inventory
- Selects accounts automatically based on health, cooldown state, and remaining quota headroom
- Exposes an operator dashboard and management API for accounts, sessions, usage, and audit logs
uv sync --extra dev
pip install -e .If you want the dashboard, install the UI dependencies once:
cd ui && npm installcodara serve --host 0.0.0.0 --port 8000codara serve no longer blocks on an implicit UI build. If ui/dist is missing, the API still starts and the CLI prints the command you need for the dashboard. Build the dashboard explicitly when you need it:
cd ui && npm run build
# or
codara serve --build-uiIf you change files under ui/src, rebuild the dashboard before using the served /dashboard bundle. codara serve warns when the checked-in ui/dist build is older than the current UI source tree.
The gateway serves dashboard client-side routes with a history fallback. After ui/dist exists, refreshing /dashboard/workspaces, /dashboard/accounts, or another dashboard page returns the React shell instead of a FastAPI JSON 404.
cp .env.example .env
# Edit API_TOKEN before exposing the service.
docker compose up --buildThe image builds the dashboard bundle during docker build, installs the Codex, Gemini, and OpenCode CLIs, then starts codara serve inside the container. Runtime state is persisted in named volumes for /data, /config, /logs, and /workspaces; see docker-compose_README.md for deployment details.
The GitHub workflow publishes container images to GitHub Container Registry:
docker pull ghcr.io/chency87/codara:latestTagged releases such as v0.1.0 publish matching semver tags. If the package is not publicly pullable after the first publish, mark the GHCR package visibility as public in the repository package settings.
Check the installed framework version locally:
codara version
codara version --checkcodara version --check uses the GitHub latest-release API only when [release].enabled = true and [release].repository is configured in codara.toml.
Runtime defaults come from codara.toml, which is organized into blocks such as [server], [workspace], [providers.*], and [channels.*]. Environment variables still override config values, including:
Start from the sanitized example when creating a local config:
cp codara.toml.example codara.tomlAPI_TOKENfor operator login (UAG_MGMT_SECRETremains a supported fallback alias)UAG_WORKSPACES_ROOTfor provisioned user workspacesUAG_CODEX_STALL_TIMEOUT_SECONDS,UAG_GEMINI_STALL_TIMEOUT_SECONDS, andUAG_OPENCODE_STALL_TIMEOUT_SECONDSfor killing provider CLIs that are alive but no longer producing stdout/stderr progressUAG_CODEX_AUTH_PATH,UAG_GEMINI_AUTH_PATH,UAG_OPENCODE_AUTH_PATHfor CLI auth materialization targetsUAG_RELEASE_REPOSITORYandUAG_RELEASE_CHECK_ENABLEDfor GitHub release update checks
Telegram channels support both:
receive_mode = "webhook"for public HTTPS webhook deliveryreceive_mode = "polling"for long-polling via TelegramgetUpdateswhen you do not have a webhook endpoint
In polling mode, Codara disables any existing Telegram webhook for the bot at startup and stores the per-bot update offset locally so polling can resume after restarts.
Linked Telegram users can create and switch project workspaces directly:
/projects
/project_create news-pulse python
/project_info news-pulse
/project news-pulse
The visible account pool comes from the vault-backed registry. UAG does not treat ~/.codex/auth.json or ~/.gemini/oauth_creds.json as standalone accounts to display in the dashboard.
Codex is the only provider with managed account registration. Gemini and OpenCode use the locally installed CLI login on the host system and are not imported into the managed account pool.
Examples:
# Codex OAuth session from an existing auth.json
codara account add \
--id codex-main \
--provider codex \
--auth-type OAUTH_SESSION \
--label "Codex Main" \
--credential-file ~/.codex/auth.json
# Raw API key account
printf 'sk-example\n' > /tmp/codex.key
codara account add \
--id codex-api \
--provider codex \
--auth-type API_KEY \
--label "Codex API" \
--credential-file /tmp/codex.keyWhen an operator selects a Codex account as CLI-primary, Codara marks it as the preferred managed identity for Codex routing. The managed credential stays in the vault and is injected into the isolated Codex runtime home during execution instead of overwriting the host machine's ~/.codex/auth.json. Automatic routing still falls back to another ready account when the primary one is expired, cooling down, or close to depletion.
project is the user-facing name for a managed workspace. Internally, projects still use the workspace safe-zone, session binding, diff tracking, and reset/delete behavior.
codara project create news-pulse
codara project create agent-lab --template python --provider codex
codara project list
codara project info news-pulseThe default template creates:
README.md
docs/
src/
scripts/
tests/
.codara/project.toml
Available templates are default, python, docs, and empty.
- Set
UAG_MGMT_SECRET - Start the gateway
- Open
/dashboard - Enter the
API_TOKENvalue from.envon the login screen to exchange it for a management token
The dashboard is a thin client over /management/v1/*. Useful endpoints:
POST /management/v1/auth/tokenGET /management/v1/overviewGET /management/v1/version?check_updates=trueGET /management/v1/projectsPOST /management/v1/projectsGET /management/v1/workspacesGET /management/v1/accountsGET /management/v1/tracesGET /management/v1/logsGET /management/v1/providers/modelsPOST /management/v1/playground/chatPOST /management/v1/usage/refreshGET /management/v1/audit
Usage/auth refresh activity is written into the audit log, and the audit page supports text search plus actor/action/target filters.
Runtime logs are emitted as structured JSON and trace events are persisted as datetime-partitioned JSONL files under the configured telemetry trace root. The management plane exposes trace roots and trace-event details for cross-component debugging.
The dashboard includes an Observability explorer that lets operators search trace roots and runtime messages together, then reconstruct a trace-centric timeline from correlated events and log lines.
Use [logging].retention_days and [telemetry].trace_retention_days to control how long file-backed runtime logs and trace shards are kept. The Observability explorer also supports quick and custom time-range filters.
The dashboard Playground runs as a dedicated Dashboard Admin user inside the provisioned workspaces root. That default user is managed like any other provisioned user, remains visible in the Users panel, and Playground turns bind to that user's active API-key identity and workspace_id instead of an arbitrary absolute path.
The dashboard also includes a dedicated Workspaces page that inventories the managed workspaces tree, shows git metadata for repo-backed workspaces, and lets operators inspect or wipe bound sessions before optionally deleting a workspace directory from disk.
curl -X POST http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-5-codex",
"messages": [{"role": "user", "content": "Review this repo."}],
"provider": "codex",
"workspace_root": "/absolute/path/to/project",
"client_session_id": "thread-1"
}'Provision a user from the dashboard or management API, then
call the same endpoint with that user API key. User requests normally only need
the standard bearer header plus provider and optional workspace_id and
client_session_id:
curl -X POST http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer uagk_live_..." \
-d '{
"model": "gemini-2.5-pro",
"messages": [{"role": "user", "content": "Review my changes."}],
"provider": "gemini",
"workspace_id": "project-a/feature-x",
"client_session_id": "thread-1"
}'UAG namespaces the real session as user_id::workspace_id::client_session_id so each user workspace gets stable session reuse.
For user-key requests:
workspace_rootis injected by the gateway from the provisioned workspace and should usually be omitted.workspace_idis only needed when the user wants multiple isolated sub-workspaces; otherwise the default workspace is used.manual_modeis an advanced runtime control and can usually be omitted.modelmay be either an explicit provider runtime model (for examplegpt-5-codex,gemini-2.5-pro, oropencode/big-pickle) or auag-*alias; aliases resolve to the configured provider default model fromcodara.toml.GET /v1/user/providers/modelsreturns the current provider model listings; operators can query the same inventory viaGET /management/v1/providers/models.
Responses include normal OpenAI-compatible fields plus:
modified_filesdiffactions(normalized exact workspace operations such as search/replace patches, unified diffs, or JSON file-write actions)dirty
If quota data has not been observed yet, the management API returns null for limits and reset timestamps instead of inventing defaults.
Common commands:
pytest -q
cd ui && npm run buildFor design details, start with: