The system of record for companies running AI agents. Ingests OpenTelemetry traces from any agent platform and gives operators a unified view of their AI workforce.
No Postgres needed locally — runs on SQLite by default. Just:
pip install -r requirements.txt
export ANTHROPIC_API_KEY=sk-ant-... # required for /describe; everything else works without it
uvicorn main:app --reload --port 8080The server creates trovis.db (SQLite) on first run. To use Postgres locally instead, export DATABASE_URL=postgresql://... before starting — the storage layer flips automatically.
- Install the Railway CLI:
npm install -g @railway/cli
- Log in:
railway login
- Initialize a project from this repo:
railway init
- Add a Postgres plugin — this also sets
DATABASE_URLautomatically:railway add --plugin postgresql
- Set your Anthropic API key:
railway variables set ANTHROPIC_API_KEY=sk-ant-... - Deploy:
railway up
- Get the public URL:
railway domain
DATABASE_URL is wired into the runtime by Railway when the Postgres plugin is added — no manual config. The build is driven by railway.toml and runtime.txt; the dyno entry point is Procfile.
| Method | Path | Description |
|---|---|---|
| GET | /health |
Liveness probe. Returns {"status": "ok", "version": "..."}. |
| POST | /v1/traces |
OTLP/JSON trace ingest. Accepts the standard OTEL export body. |
| GET | /agents |
List every agent that has reported telemetry, with their latest description. |
| GET | /agents/{service_name}/summary |
Aggregate stats for one agent. 404 if unknown. |
| GET | /agents/{service_name}/spans |
Recent spans for one agent. ?limit=N (1–200, default 50). |
| POST | /agents/{service_name}/describe |
Ask Claude to generate a fresh plain-English description. 503 if API key missing. |
| GET | /agents/{service_name}/description |
Most recent saved description for the agent. 404 if none yet. |
Most agent SDKs ship with an OTLP/HTTP exporter — point it at http://localhost:8080/v1/traces and you're done. To smoke-test the endpoint by hand:
curl -X POST http://localhost:8080/v1/traces \
-H "Content-Type: application/json" \
-d '{
"resourceSpans": [{
"resource": {
"attributes": [
{"key": "service.name", "value": {"stringValue": "demo-agent"}}
]
},
"scopeSpans": [{
"spans": [{
"traceId": "5b8aa5a2d2c872e8321cf37308d69df2",
"spanId": "051581bf3cb55c13",
"name": "summarize_document",
"kind": 1,
"startTimeUnixNano": "1700000000000000000",
"endTimeUnixNano": "1700000001500000000",
"status": {"code": 1},
"attributes": [
{"key": "model", "value": {"stringValue": "claude-opus-4-7"}},
{"key": "input_tokens", "value": {"intValue": "1342"}}
]
}]
}]
}]
}'Then:
curl http://localhost:8080/agents
curl http://localhost:8080/agents/demo-agent/summary
curl http://localhost:8080/agents/demo-agent/spans- FastAPI server (
main.py) — routing + OTLP/JSON parsing - SQLite storage (
database.py) — singlespanstable, Postgres-shaped schema - Pydantic response models (
models.py)
No auth, no rate limiting, no config. This is the demo foundation; those land later.