Run Claude Code, Codex, Gemini CLI, Hermes Agent, and OpenCode in your browser — zero setup, wired to your Databricks workspace.
Please note that this project is provided for your exploration only and is not formally supported by Databricks with Service Level Agreements (SLAs). It is provided AS-IS, and we do not make any guarantees. Please do not submit a support ticket relating to any issues arising from the use of this project.
Any issues discovered through the use of this project should be filed as GitHub Issues on this repository.
See LICENSE.md for full terms, including the warranty disclaimer and limitation of liability. See NOTICE.md for third-party software attribution.
Presenting.CoDA.mp4
CoDA runs as a hosted Databricks App inside your tenancy, alongside Genie Code — Databricks' in-product AI coding agent that lives in notebooks, the SQL editor, and dashboards. Genie Code is the interactive in-product surface; CoDA is the always-on hosted-app surface where Developers brief the agents through the browser and Claude Code, Codex, Gemini CLI, and OpenCode execute alongside the Hermes orchestrator. Both surfaces share the same access plane: every model call routes through Foundation Model APIs (no third-party egress) and every tool call routes through Governed MCP Servers (Unity Catalog ACLs + MLflow trace + named human identity). The result: agentic coding for legacy migration, application development, multi-repo refactor, production monitoring, code modernisation, and CI/CD deployments — all governed like any other workload.
🟠 Claude Code — Anthropic's coding agent with 39 Databricks skills + 2 MCP servers
🟣 Codex — OpenAI's coding agent, pre-configured for Databricks
🔵 Gemini CLI — Google's coding agent with shared skills
🟡 Hermes Agent — NousResearch's multi-provider AI CLI with tool-calling and skills
🟢 OpenCode — Open-source agent with multi-provider support
Every agent installs at boot and connects to your Databricks AI Gateway — on first terminal session, paste a short-lived PAT and all CLIs are configured automatically. Token auto-rotates every 10 minutes.
Want to see CoDA installed and running end-to-end? Click the thumbnail to watch the full walkthrough on YouTube.
This isn't just a terminal in the cloud. Running coding agents on Databricks gives you enterprise-grade infrastructure out of the box:
| Benefit | What you get | |
|---|---|---|
| 🔐 | Unity Catalog Integration | All data access governed by UC permissions — agents can only touch what your identity allows |
| 🤖 | AI Gateway | Route all LLM calls through a single control plane — swap models, set rate limits, and manage API keys centrally |
| 🔀 | Multi-AI & Multi-Agent | Switch between Claude, GPT, Gemini, and open-source models on the fly — change the model or agent without redeploying |
| 📊 | Consumption Monitoring | Track token usage, cost, and latency per user and per model via the AI Gateway control center dashboard |
| 🔍 | MLflow Tracing | Every Claude Code session is automatically traced — review prompts, tool calls, and outputs in your MLflow experiment |
| 🧬 | Assess Traces with Genie | Point Genie at your MLflow traces to ask natural-language questions about agent behavior, cost patterns, and session quality |
| 📝 | App Logs to Delta | Optionally route application logs to Delta tables for long-term retention, querying, and dashboarding |
New browser PTYs are admitted against both the configured session cap and the container's own cgroup memory budget. Reclaimable active and inactive file cache is excluded from pressure accounting, while in-flight launches consume a reserve so concurrent requests cannot oversubscribe the budget.
Browser terminals receive a reviewed, non-secret environment. Workspace credentials, client secrets, registry tokens, bootstrap secrets, and unsafe credential-bearing URL values are removed before the shell starts; the loopback token-broker coordinate is the only intentional capability exception.
| 🎨 8 Themes | Dracula, Nord, Solarized, Monokai, GitHub Dark, and more |
| ✂️ Split Panes | Run two sessions side by side with a draggable divider |
| 🌐 WebSocket I/O | Real-time terminal output over WebSocket — zero-latency, eliminates polling delay |
| 🔁 HTTP Polling Fallback | Automatic fallback via Web Worker when WebSocket is unavailable |
| 🚀 Parallel Setup | 7 agent setups run in parallel (~5x faster startup) |
| 🔍 Search | Find anything in your terminal history (Ctrl+Shift+F) |
| 🎤 Voice Input | Dictate commands with your mic (Option+V) |
| 📋 Image Paste | Paste or drag-and-drop images into the terminal — saved to ~/uploads/, path inserted automatically |
| ⌨️ Customizable | Fonts, font sizes, themes — all persisted across sessions |
| 🔄 Workspace Sync | Every git commit auto-syncs to /Workspace/Shared/coda/{app-name}/ |
| ✏️ Micro Editor | Modern terminal editor, pre-installed |
| ⚙️ Databricks CLI | Installed at boot, configured interactively on first session |
| 📊 MLflow Tracing | Every Claude Code session is automatically traced to your Databricks MLflow experiment |
Claude Code and Codex sessions can both be automatically traced to a single Databricks MLflow experiment — flip one switch to turn them on.
Claude Code can also export native OpenTelemetry signals to Unity Catalog tables. Set CLAUDE_CODE_OTEL_ENABLED=true and CLAUDE_CODE_OTEL_CATALOG_SCHEMA=<catalog>.<schema> to send spans, logs, and metrics to claude_otel_spans, claude_otel_logs, and claude_otel_metrics in that schema. Create those three target tables before enabling the flag.
Set MLFLOW_TRACING_ENABLED=true in app.yaml (or your shell for local dev). That single variable enables tracing for both CLIs. Tracing is off by default to keep deploys lightweight — opt in when you want it.
# app.yaml
env:
- name: MLFLOW_TRACING_ENABLED
value: "true"MLFLOW_TRACING_ENABLED=true
│
├──► Claude Code: Stop hook fires on session end →
│ mlflow.claude_code.hooks.stop_hook_handler() logs the transcript
│
└──► Codex: @mlflow/codex notify hook fires after each turn →
trace appended to the experiment
Both land in the same MLflow experiment, so you can compare runs across agents side by side.
/Users/{your-email}/{app-name}
For example, if you're jane@company.com and your app is named coding-agents:
/Users/jane@company.com/coding-agents
View them in the Databricks UI: Workspace > Machine Learning > Experiments.
Tracing is wired up during app startup:
| Setting | Value | Purpose |
|---|---|---|
MLFLOW_TRACING_ENABLED |
true/false (default false) |
Master switch for Claude + Codex |
MLFLOW_CLAUDE_TRACING_ENABLED |
mirrors MLFLOW_TRACING_ENABLED |
Gates Claude's Stop hook at runtime |
MLFLOW_TRACKING_URI |
databricks |
Routes traces to the Databricks backend |
MLFLOW_EXPERIMENT_NAME |
/Users/{owner}/{app} |
Target experiment path |
MLFLOW_EXPERIMENT_ID |
resolved from name | Set in ~/.codex/.env (Codex needs an ID) |
Tracing setup is skipped gracefully when APP_OWNER is not set (e.g., local dev without Databricks) or when MLFLOW_TRACING_ENABLED is left at its default false.
CoDA can register itself as a persistent Omnigent agent host — an always-on target the Omnigent server can drive coding-agent sessions into. Those sessions run inside this container and use the same filesystem as browser terminals. They authenticate to Databricks as the CoDA app service principal, not as the interactive browser user, so their Unity Catalog authority may differ. A deployed CoDA app becomes both an interactive terminal and a headless host that survives restarts and redeploys.
Off by default. With OMNIGENTS_SERVER_URL unset, none of this runs and CoDA behaves exactly as before. This is opt-in, environment-specific wiring — the committed app.yaml keeps it commented out.
Set three variables in your deployed app.yaml (see app.yaml.workshop for a ready-to-copy overlay template):
# app.yaml
env:
# The Omnigent server this app registers against on boot.
- name: OMNIGENTS_SERVER_URL
value: "https://<your-omnigent-app>.<region>.databricksapps.com"
# UC Volume holding the omnigent host wheels (app SP needs READ_VOLUME).
- name: OMNIGENTS_WHEEL_SPEC
value: "/Volumes/<catalog>/<schema>/artifacts/wheels"
# Optional: force-reinstall the host CLI on boot while rolling out a new wheel.
- name: OMNIGENTS_FORCE_REINSTALL
value: "1"Before deploying, grant the CoDA app service principal CAN_USE on the
Omnigent server app plus USE_CATALOG, USE_SCHEMA, READ_VOLUME, and
WRITE_VOLUME on the wheel-volume path. The repository's grant target applies
the complete prerequisite set:
make grant-omnigent-host PROFILE=<profile> APP_NAME=<coda-app>On boot, initialize_app() calls start_host(), which — only when OMNIGENTS_SERVER_URL is set — installs the omnigents host CLI from the wheel volume and launches it as a supervised background process that dials the server over an outbound WSS tunnel.
The non-obvious part of this design is that the host uses two separate credentials (see omnigents_host.py):
┌─────────────────────── CoDA container ───────────────────────┐
│ │
│ omnigents host ──WSS tunnel──► Omnigent server │
│ │ (auth: app-SP OAuth token) │
│ │ │
│ └── spawns runner ──► AI Gateway │
│ (auth: CoDA's ANTHROPIC_* creds) │
└──────────────────────────────────────────────────────────────┘
- Host tunnel and runners authenticate to the server through short-lived app-SP OAuth tokens. CoDA captures the SP credentials before stripping them from the environment, keeps the client secret only in Flask process memory, and exposes fresh tokens through a loopback-only broker. The on-disk
[omnigents-host]profile contains only the workspace host; spawned Omnigent runners receive a refresh command, not a static bearer or client secret. - Harness LLM — the runner the host spawns authenticates to AI Gateway via CoDA's already-injected
ANTHROPIC_*env. No new LLM credential is minted.
Beyond boot registration, the host can be driven at runtime:
| Endpoint | Method | Purpose |
|---|---|---|
/api/omnigents-status |
GET | Host-integration state (FR-9 observability) |
/api/omnigent-host/status |
GET | Current runtime host state |
/api/omnigent-host/connect |
POST | Start a host tunnel for a supplied server_url |
/api/omnigent-host/disconnect |
POST | Stop the active host tunnel |
/api/omnigent-host/share |
POST | Share the SP-owned host with a connecting user |
ENABLE_SP_APIKEYHELPER=true enables the same loopback-broker boundary for agent gateway calls: helpers fetch short-lived app-SP OAuth tokens without persisting the SP client secret or a static token in terminal-visible configuration.
- Click Use this template to create your own repo
- Go to Databricks → Apps → Create App
- Choose Custom App and connect your new repo
- From your checkout, run
make configure-gateway-resources PROFILE=<profile> APP_NAME=<app> - Deploy, then open the app and paste a short-lived PAT if prompted
Required: a plain Apps UI deployment cannot grant Unity AI Gateway v3 model-service privileges. The configuration target grants the app SP
USE CATALOG,USE SCHEMA, and least-privilegeEXECUTE; without it Pi and Claude report404 '<model>' does not exist. The repository's normalmake deploy,make redeploy, andmake deploy-gitworkflows run it automatically.
→ Full deployment guide — environment variables, gateway config, and advanced options.
- Click Use this template to create your own repo
- Clone your new repo and run:
git clone https://github.com/<you>/<your-repo>.git
cd <your-repo>
uv run python app.pyOpen http://localhost:8000 — type claude, codex, gemini, or opencode to start coding.
🧠 All 41 Skills
Databricks Skills (27) — ai-dev-kit
| Category | Skills |
|---|---|
| AI & Agents | agent-bricks, ai-functions, genie, mlflow-eval, model-serving |
| Analytics | aibi-dashboards, unity-catalog, metric-views |
| Data Engineering | declarative-pipelines, jobs, structured-streaming, synthetic-data-gen, zerobus-ingest |
| Development | bundles, app-apx, apps-python, python-sdk, config, execution-compute, spark-python-data-source |
| Storage | iceberg, lakebase-autoscale, lakebase-provisioned, vector-search |
| Reference | docs, dbsql, pdf-generation |
| Meta | refresh-databricks-skills |
Superpowers Skills (14) — obra/superpowers
| Category | Skills |
|---|---|
| Build | brainstorming, writing-plans, executing-plans |
| Code | test-driven-dev, subagent-driven-dev |
| Debug | systematic-debugging, verification |
| Review | requesting-review, receiving-review |
| Ship | finishing-branch, git-worktrees |
| Meta | dispatching-agents, writing-skills, using-superpowers |
🔌 2 MCP Servers
| Server | What it does |
|---|---|
| DeepWiki | Ask questions about any GitHub repo — gets AI-powered answers from the codebase |
| Exa | Web search and code context retrieval for up-to-date information |
🏗️ Architecture
┌─────────────────────┐ WebSocket ┌─────────────────────┐
│ Browser Client │◄═══════════►│ Gunicorn + Flask │
│ (xterm.js) │ (primary) │ + Flask-SocketIO │
│ │───────────►│ (PTY Manager) │
│ │ HTTP Poll │ │
│ │ (fallback) │ │
└─────────────────────┘ └─────────────────────┘
│ │
│ on first load │ on startup
▼ ▼
┌─────────────────────┐ ┌─────────────────────┐
│ Setup Progress │ │ Background Setup │
│ (inline UI) │ │ (11 steps, 5→6 ║) │
└─────────────────────┘ └─────────────────────┘
│
▼
┌─────────────────────┐
│ Shell Process │
│ (/bin/bash) │
└─────────────────────┘
- Gunicorn starts, calls
initialize_app()viapost_worker_inithook - App serves the terminal UI with inline setup progress
- Background thread runs setup: 5 sequential steps (git config, micro editor, GitHub CLI, Databricks CLI upgrade, content-filter proxy), then 6 agent setups (Claude, Codex, OpenCode, Gemini, Databricks CLI config, MLflow) run in parallel via
ThreadPoolExecutor /api/setup-statusendpoint reports progress to the UI- Once complete, the terminal becomes interactive
| Endpoint | Method | Description |
|---|---|---|
/ |
GET | Terminal UI with inline setup progress |
/health |
GET | Liveness probe. Exempt from the SSO gate so the platform can reach it. Unauthenticated callers get only {"status": "healthy"|"degraded"}; the owner additionally gets version, session count, setup status and PAT-rotator state |
/api/setup-status |
GET | Setup progress for the UI (owner-gated) |
/api/app-state |
GET | Persisted app state (owner, last rotation) (owner-gated) |
/api/version |
GET | App version |
/api/sessions |
GET | List active (non-exited) sessions with metadata |
/api/pat-status |
GET | Whether a valid, usable PAT is currently configured (owner-gated) |
/api/configure-pat |
POST | Interactive first-session PAT setup (owner-gated via SSO) |
/api/inject-pat |
POST | Programmatic PAT injection for scripted provisioning (shared-secret gated; disabled unless CODA_BOOTSTRAP_SECRET is set). Also requires a workspace OAuth bearer for the Apps edge — a PAT bearer 401s at the platform edge before reaching the app |
/api/session |
POST | Create new terminal session |
/api/session/attach |
POST | Reattach to an existing session (replays buffered output) |
/api/input |
POST | Send input to terminal |
/api/output |
POST | Poll for terminal output (single session) |
/api/output-batch |
POST | Batch poll output for multiple sessions |
/api/heartbeat |
POST | Lightweight keepalive (no buffer drain) |
/api/resize |
POST | Resize terminal dimensions |
/api/upload |
POST | Upload file (clipboard image paste) |
/api/session/close |
POST | Close terminal session |
| Event | Direction | Description |
|---|---|---|
join_session |
Client → Server | Join session room for output delivery |
leave_session |
Client → Server | Leave session room |
terminal_input |
Client → Server | Send keystrokes to PTY |
terminal_resize |
Client → Server | Resize terminal |
heartbeat |
Client → Server | Keepalive for idle sessions |
terminal_output |
Server → Client | Push PTY output in real time |
session_exited |
Server → Client | Shell process exited |
session_closed |
Server → Client | Session terminated by server |
shutting_down |
Server → Client | Server restarting (SIGTERM) |
⚙️ Configuration
Single-user app — the owner is resolved via the app's service principal and Apps API (app.creator), with no PAT required at deploy time. Authorization checks X-Forwarded-Email against app.creator. On first terminal session, the user pastes a short-lived PAT interactively. Tokens auto-rotate every 10 minutes (15-minute lifetime), with old tokens proactively revoked. On restart, the user re-pastes (no persistence by design).
Each GitHub Release ships a signed CycloneDX SBOM — see docs/SECURITY.md for verification steps.
Production uses workers=1 (PTY state is process-local), threads=16 (concurrent polling + WebSocket), gthread worker class, timeout=60 (long-lived WebSocket connections).
📁 Project Structure
coding-agents-databricks-apps/
├── app.py # Flask backend + PTY management + setup orchestration
├── app_state.py # Shared app state (setup progress, session registry)
├── app.yaml.template # Databricks Apps deployment config template
├── cli_auth.py # Interactive PAT setup + CLI credential writer
├── content_filter_proxy.py # Proxy that sanitises empty-content blocks for OpenCode
├── gunicorn.conf.py # Gunicorn production server config
├── pat_rotator.py # Background PAT auto-rotation (10-min cycle)
├── pyproject.toml # Package metadata + uv config (supply-chain guardrails)
├── requirements.txt # Compiled from pyproject.toml (Dependabot compatibility)
├── requirements.lock # Hash-pinned lockfile (auto-regenerated by CI)
├── Makefile # Deploy, redeploy, status, and cleanup targets
├── setup_claude.py # Claude Code CLI + MCP configuration
├── setup_codex.py # Codex CLI configuration
├── setup_gemini.py # Gemini CLI configuration
├── setup_opencode.py # OpenCode configuration
├── setup_databricks.py # Databricks CLI configuration
├── setup_mlflow.py # MLflow tracing auto-configuration
├── setup_proxy.py # Content-filter proxy startup
├── sync_to_workspace.py # Post-commit hook: sync to Workspace
├── install_micro.sh # Micro editor installer
├── install_gh.sh # GitHub CLI installer (OS/arch-aware)
├── install_databricks_cli.sh # Databricks CLI upgrade script
├── utils.py # Utility functions (ensure_https)
├── static/
│ ├── index.html # Terminal UI (xterm.js + split panes + WebSocket)
│ ├── favicon.svg # App favicon
│ ├── poll-worker.js # Web Worker for HTTP polling fallback
│ └── lib/
│ ├── xterm.js # xterm.js terminal emulator
│ └── socket.io.min.js # Vendored Socket.IO client
├── .claude/
│ └── skills/ # 39 pre-installed skills
├── .github/
│ └── workflows/
│ ├── dependency-audit.yml # Weekly CVE audit + lockfile drift check
│ └── update-lockfile.yml # Auto-regenerate requirements.lock on push
└── docs/
├── deployment.md # Full Databricks Apps deployment guide
├── prd/ # Product requirement documents
└── plans/ # Design documentation
Flask · Flask-SocketIO · Socket.IO · Gunicorn · xterm.js · Python PTY · uv · Databricks SDK · Databricks AI Gateway · MLflow


