Gofannon is the agent framework used for building and running the security audit pipeline. It provides a web UI for creating agents, a Python execution sandbox, CouchDB-backed persistent storage, and API endpoints for invoking deployed agents.
This guide covers platform setup and the full agent development lifecycle — it is not specific to any particular audit or repository.
.env below)bedrock:InvokeModel and bedrock:InvokeModelWithResponseStream permissionsQuickstart with screenshots
git clone https://github.com/The-AI-Alliance/gofannon.git cd gofannon/webapp/infra/docker
Create .env from the example:
cp example.env .env
Edit .env with your credentials. Only one LLM provider is required:
# --- LLM Provider Keys (at least one required) --- # Anthropic (direct API) ANTHROPIC_API_KEY=sk-ant-your-key # OpenAI OPENAI_API_KEY=sk-proj-your-key # AWS Bedrock (for Claude via Bedrock) AWS_BEARER_TOKEN_BEDROCK=your-token # Or use standard AWS credentials: # AWS_ACCESS_KEY_ID=your-key # AWS_SECRET_ACCESS_KEY=your-secret # AWS_DEFAULT_REGION=us-east-1 # Google Gemini # GEMINI_API_KEY=... # --- CouchDB (local persistence, used automatically) --- COUCHDB_USER=admin COUCHDB_PASSWORD=password
docker-compose up --build
First build takes a few minutes. Subsequent starts are faster.
cd gofannon/webapp/infra/docker docker-compose down && docker-compose up -d --build
CouchDB data persists across rebuilds (stored in Docker volume couchdb-data). Agent code changes take effect immediately due to uvicorn's --reload flag.
| Service | Port | URL | Purpose |
|---|---|---|---|
| webui | 3000 | http://localhost:3000 | Web UI for creating and managing agents |
| api | 8000 | http://localhost:8000 | API service that executes agents |
| couchdb | 5984 | http://localhost:5984 | Document store for agents, data, and caches |
| minio | 9000/9001 | http://localhost:9001 | Object storage (used internally by Gofannon) |
curl -s -u admin:password http://localhost:5984/_all_dbs | python3 -m json.tool
run_asvs_security_audit)Each agent has two model slots:
call_llm() at runtime. Set this to whatever the agent needs (can be overridden in code).To set a model:
bedrock, anthropic, openai)us.anthropic.claude-sonnet-4-5-20250929-v1:0)Under the Tools menu, you can give the agent access to:
When you select another agent as a tool, it becomes callable via gofannon_client.call() in your agent's code.
Click “Generate” to have the compose model generate Python code from your prompt. The generated code follows this pattern:
from agent_factory.remote_mcp_client import RemoteMCPClient from services.llm_service import call_llm import httpx async def run(input_dict, tools): mcpc = { url : RemoteMCPClient(remote_url = url) for url in tools.keys() } http_client = httpx.AsyncClient() try: # ... agent logic here ... return {"outputText": "result"} finally: await http_client.aclose()
The generated code is a starting point. For production agents, you'll typically need to:
The code editor in the web UI supports direct editing. Changes are saved when you click “Save” or “Update”.
Click “Sandbox” to open the testing interface:
The sandbox uses the same execution environment as production — same data store, same LLM access, same tool connections. This means sandbox runs will write to the same CouchDB and can call the same external APIs.
POST http://localhost:8000/api/agents/{agent_name}/runA deployed agent can be called by other agents via gofannon_client.call() or directly via the API.
In the agent page, click the delete button on the agent card. This removes the agent configuration from CouchDB. It does not delete any data the agent wrote to the data store.
The data store is a CouchDB-backed key-value store shared across all agents for a user. It enables workflows where one agent produces data that another consumes.
files:apache/tooling-trusted-releases, asvs, audit-cache:analysis:...)Data store operations are synchronous (no await needed):
# Switch to a namespace ns = data_store.use_namespace("files:apache/tooling-trusted-releases") # Basic operations value = ns.get("path/to/file.py") # returns value or None ns.set("path/to/file.py", "file contents") # create or update ns.delete("path/to/file.py") # delete # Bulk operations keys = ns.list_keys() # all keys in namespace keys = ns.list_keys(prefix="atr/api/") # keys with prefix all_data = ns.get_all() # dict of all key:value pairs ns.set_many({"key1": val1, "key2": val2}) # bulk write # Discovery namespaces = data_store.list_namespaces() # all namespaces with data
Each data store record is a CouchDB document:
{ "_id": "{userId}:{namespace}:{base64(key)}", "_rev": "5-abc123...", "userId": "local-dev-user", "namespace": "asvs", "key": "asvs:requirements:10.4.1", "value": { ... }, "metadata": {}, "createdByAgent": "ingest_asvs_standard", "lastAccessedByAgent": "run_asvs_security_audit", "accessCount": 4, "createdAt": "2026-03-04T01:06:36.525056", "updatedAt": "2026-03-04T01:06:36.525056", "lastAccessedAt": "2026-03-22T20:58:39.982262" }
Useful for debugging and cache management:
# List all namespaces and counts curl -s -u admin:password "http://localhost:5984/agent_data_store/_find" \ -H "Content-Type: application/json" \ -d '{"selector":{},"fields":["namespace"],"limit":10000}' \ | python3 -c " import sys, json from collections import Counter docs = json.load(sys.stdin)['docs'] for ns, count in sorted(Counter(d['namespace'] for d in docs).items()): print(f' {count}\t{ns}')" # Look up a specific document curl -s -u admin:password "http://localhost:5984/agent_data_store/_find" \ -H "Content-Type: application/json" \ -d '{"selector":{"namespace":"asvs","key":"asvs:requirements:7.2.1"},"limit":1}' \ | python3 -m json.tool # Delete documents matching a pattern (stop running agents first) curl -s -u admin:password "http://localhost:5984/agent_data_store/_find" \ -H "Content-Type: application/json" \ -d '{"selector":{"namespace":{"$regex":"^audit-cache:analysis"}},"fields":["_id","_rev"],"limit":10000}' \ | python3 -c " import sys, json, requests docs = json.load(sys.stdin).get('docs', []) for d in docs: requests.delete(f'http://admin:password@localhost:5984/agent_data_store/{d[\"_id\"]}?rev={d[\"_rev\"]}')"
/ (CouchDB interprets as path separator), whitespace, or quotes_id field uses base64-encoded keys, so the actual CouchDB document ID is {userId}:{namespace}:{base64(key)}These are available in every agent's run() function without importing:
| Global | Type | Description |
|---|---|---|
call_llm | async function | Call any LLM: content, thoughts = await call_llm(provider, model, messages, parameters, timeout=...) |
count_tokens | function | Count tokens: count_tokens(text, provider, model) |
count_message_tokens | function | Count tokens for full messages list: count_message_tokens(messages, provider, model) |
get_context_window | function | Get model context window: get_context_window(provider, model) → 200000 |
data_store | AgentDataStoreProxy | Persistent key-value store (see above) |
gofannon_client | GofannonClient | Call other deployed agents: await gofannon_client.call(agent_name, input_dict) |
asyncio | module | Python asyncio (for asyncio.gather, asyncio.Semaphore, etc.) |
call_llm usagecontent, thoughts = await call_llm( provider="bedrock", model="us.anthropic.claude-sonnet-4-5-20250929-v1:0", messages=[{"role": "user", "content": "Analyze this code..."}], parameters={"temperature": 0.7, "max_tokens": 16384}, timeout=300, )
The thoughts return value contains extended thinking output (if the model supports it), otherwise None.
Always use count_tokens() and get_context_window() when building prompts with variable-length content. Never use character-based estimation (len(text) / 3) — it undercounts by 30-50% for code.
CONTEXT_WINDOW = get_context_window(provider, model) SAFE_LIMIT = int(CONTEXT_WINDOW * 0.80) # leave 20% headroom prompt_tokens = count_message_tokens(messages, provider, model) if prompt_tokens > SAFE_LIMIT: # split into batches ...
Gofannon runs uvicorn with --reload, which creates two worker processes. Both log the same output. This is cosmetic — there's only one actual execution.
Agent code changes are picked up automatically by uvicorn. You don't need to restart the stack after editing code in the web UI and deploying.
# Follow API logs docker-compose logs -f api # Follow with filtering docker-compose logs -f api 2>&1 | grep -E "Step|Done|FAILED|ERROR"
CouchDB has a built-in admin interface at http://localhost:5984/_utils/ (login with COUCHDB_USER/COUCHDB_PASSWORD). Useful for browsing the agent_data_store database directly.