AI agents you can hand to an auditor.
AgentVisor AI is a small Rust server you drop in front of your agent's LLM and tool calls. Every request is recorded, every limit you set is enforced before the call goes out, and every session ends with a signed receipt anyone can verify offline with just a public key. Change one line in your OpenAI client. Nothing else in your app moves.
client = OpenAI(
api_key=os.environ["OPENAI_API_KEY"],
base_url="http://127.0.0.1:8484/v1", # <-- the only change
)- Landing page — the story, in three concrete examples.
- Releases — pre-built binaries with SHA-256 checksums.
- Security model — trust boundaries, controls, and how to deploy safely. Report vulnerabilities privately via the advisory form.
- Architecture · Benchmarks · Verification protocol — going deeper.
- API docs — Rust crate reference. Skip unless you're embedding AgentVisor AI as a library.
cargo install av-harness av-cli # installs `agentvisord` + `avctl` binaries
avctlThe two crates land on crates.io from
v0.1.0onwards; until that release is published, use the--pathvariants shown in Install from source below.
Bare avctl launches a guided setup: pick your AI provider from a
numbered list, paste your API key once (typed hidden, stored
owner-only under ~/.agentvisor/keys/), and it writes
~/.agentvisor/agentvisor.toml for you. Answer "yes" at the end —
or run avctl start later — and AgentVisor AI is running with a
friendly banner telling you exactly what URL to paste into your app.
Ctrl-C stops it cleanly. No files to edit, no environment
variables, no terminal knowledge beyond typing a number.
Pick the row that matches your provider, then run two commands.
cargo install av-harness av-cli # or see "Install from source" belowavctl init --preset openai # writes agentvisor.toml + prints next steps
export OPENAI_API_KEY=sk-...
agentvisord # http://127.0.0.1:8484Point any OpenAI-compatible SDK at http://127.0.0.1:8484/v1 and use it as normal. Receipts, events, and trajectories appear under data/.
No config file at all also works — built-in defaults plus one env var:
AV_UPSTREAM_URL=http://127.0.0.1:11434 agentvisord # e.g. OllamaCloning the repo works too — useful for development, or before the first crates.io release lands:
git clone https://github.com/stef41/agentvisor-ai && cd agentvisor-ai
cargo install --path crates/av-harness # installs `agentvisord`
cargo install --path crates/av-cli # installs `avctl`| Preset | Endpoint | Key env |
|---|---|---|
openai |
api.openai.com | OPENAI_API_KEY |
azure |
your-resource.openai.azure.com | AZURE_OPENAI_API_KEY |
anthropic |
api.anthropic.com | ANTHROPIC_API_KEY |
gemini |
generativelanguage.googleapis.com | GEMINI_API_KEY |
groq |
api.groq.com | GROQ_API_KEY |
mistral |
api.mistral.ai | MISTRAL_API_KEY |
openrouter |
openrouter.ai | OPENROUTER_API_KEY |
together |
api.together.xyz | TOGETHER_API_KEY |
deepseek |
api.deepseek.com | DEEPSEEK_API_KEY |
xai |
api.x.ai | XAI_API_KEY |
ollama |
127.0.0.1:11434 | none |
lmstudio |
127.0.0.1:1234 | none |
vllm |
127.0.0.1:8000 | none |
llamacpp |
127.0.0.1:8080 | none |
litellm |
127.0.0.1:4000 | LITELLM_MASTER_KEY |
custom |
--upstream-url ... |
--key-env NAME |
avctl doctor diagnoses the environment (config resolution, key presence, upstream reachability, data dirs, backends) without printing secrets. avctl health probes a running instance. avctl start launches the server for you (logs to ~/.agentvisor/agentvisor-ai.log) and waits until it answers.
AV_CONFIG=/path/to.toml(error if missing)./agentvisor.toml./config/harness.toml./config/harness.example.toml~/.agentvisor/agentvisor.toml(written by theavctlguided setup)- built-in defaults (requires
AV_UPSTREAM_URL)
Environment overrides beat file values: AV_LISTEN, AV_UPSTREAM_URL, AV_UPSTREAM_CHAT_PATH, AV_UPSTREAM_AUTH_HEADER, AV_UPSTREAM_AUTH_SCHEME, AV_STATE_ENDPOINT, AV_BRIDGE_ENDPOINT, AV_QDRANT_URL. Exporting AV_UPSTREAM_API_KEY selects itself as the key source when the file doesn't name one; AV_UPSTREAM_KEY_FILE=/run/secrets/api_key points at a mounted secret file (Docker/Kubernetes secrets) instead. Key values are only ever read from the environment or 0600 files — never from the command line.
The proxy injects the provider credential itself (clients never hold provider keys):
upstream_api_key_env = "OPENAI_API_KEY" # or upstream_api_key_file = "/run/secrets/key"
upstream_auth_header = "authorization" # azure: "api-key"
upstream_auth_scheme = "Bearer" # azure: "" (raw key)
upstream_chat_path = "/v1/chat/completions" # azure/gemini: custom pathsAlternatively upstream_authorization_passthrough = true relays each client's own Authorization header (multi-tenant gateways; mutually exclusive with static keys and require_identity). MCP tool upstreams take tool_upstream_bearer_env/_file.
cargo run -p av-harness --bin agentvisord # finds config/harness.example.toml
curl http://127.0.0.1:8484/health
curl http://127.0.0.1:8484/metricsPOST /v1/chat/completions: OpenAI-compatible streaming proxy.POST /v1/mcpandPOST /mcp: JSON-RPC tool interception and optional forwarding.POST /v1/sessions/{id}/close: finalize a signed receipt or unsigned ATIF file.POST /v1/sessions/{id}/promote: issue a retroactive receipt for an ATIF trajectory.GET /health: liveness.GET /metrics: Prometheus text exposition.GET /dashboard: read-only operator dashboard (HTML). Disable viadashboard_enabled = falsein the harness config.GET /api/v1/dashboard/{stats,sessions,sessions/{id}}: JSON view of the in-memory session registry the dashboard consumes.
The dashboard shows sessions currently in the registry, their cost, tokens,
tool-call allow/block counts, and stop reason, with a session drawer that
renders the latest receipt as JSON. It is unauthenticated — front the
harness with the same ingress control you already use for /metrics, or
turn it off.
Request headers:
X-AV-Session: caller-selected header-safe id; UUIDv7 is generated when absent.X-AV-Workflow:signedorunsigned.Authorization: Bearer <JWT>: required when identity enforcement is enabled.
avctl init --preset ollama # write a provider config
avctl doctor # diagnose the environment
avctl health # probe a running harness
avctl keygen --output config/signing.seed
avctl config-validate agentvisor.toml
avctl manifest-validate manifests/bridge.example.yaml
avctl bridge-provision --manifest manifests/bridge.example.yaml --data-dir data/bridge
avctl atif-validate trajectory.json
avctl receipt-verify receipt.json --public-key-hex "$TRUSTED_ED25519_PUBLIC_KEY_HEX"
avctl loadgen --connections 500When identity enforcement is enabled, pass --bearer-token-file /path/to/token to session-promote and loadgen, or set AV_BEARER_TOKEN_FILE. Token contents are never accepted as command-line values.
Minimal (one container, embedded bridge, in-memory state):
AV_UPSTREAM_URL=https://api.openai.com AV_UPSTREAM_API_KEY=sk-... \
docker compose -f docker/docker-compose.minimal.yml up --buildFull reference stack (Redpanda, AOF-backed Redis, Qdrant, Vector/OTLP):
docker secret create identity_hmac /path/to/identity-hmac-secret
docker compose -f docker/docker-compose.yml up --buildThe external identity_hmac secret is required by the production profile. Bridge data, cold exports, Redis state, and Qdrant data use persistent volumes. The embedded Bridge remains available for single-binary and air-gapped deployments. Container healthchecks use avctl health (real liveness, not config parsing).
The production image checksum-pins sentence-transformers/all-MiniLM-L6-v2 at revision 1110a243fdf4706b3f48f1d95db1a4f5529b4d41, validates the model/tokenizer hashes during build, and selects the ONNX backend. Air-gapped builds can mirror those immutable URLs or mount equivalent verified artifacts and update the configured paths.
deploy/kubernetes/agentvisor-ai.yaml: single-replica starter (ConfigMap + PVC + probes + Secret-mounted key). Scale horizontally by switching to kafka/nats + redis backends.deploy/systemd/agentvisor-ai.service: hardened unit withEnvironmentFilefor the key; install steps in the file header.
make ci
make test-all
make slaSee ARCHITECTURE.md, SECURITY.md, EVOLUTION.md, and BENCHMARKS.md.
The MVP intentionally excludes a web analytics UI, multi-region consensus, base-model training, and a general-purpose SFT/RL consumer.