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 can end with a
signed receipt anyone can verify offline with just a public key
(out of the box sessions record unsigned trajectories; set
default_workflow = "signed" or promote a session via /promote to
mint the receipt). 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.
- Console — the product, clickable in your browser: setup, one-line integration, a live capped session, the fleet overview, and offline receipt verification. Client-side simulation with representative data; nothing leaves the page.
- 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 · Conformance status — going deeper.
- Reference docs — configuration, operations, OpenAI compatibility, limits, and offline receipt verification.
- API docs — Rust crate reference. Skip unless you're embedding AgentVisor AI as a library.
curl -fsSL https://agentvisorai.me/install.sh | sh # installs `agentvisord` + `avctl`
avctlOr install straight from this repository (same thing the script runs):
cargo install --locked --git https://github.com/AgentVisorAI/agentvisor av-harness
cargo install --locked --git https://github.com/AgentVisorAI/agentvisor av-cli
avctl
cargo installbuilds default features only: the embedded bridge, in-memory state, hash embedder and in-memory vector store all work, butredis/kafka/nats/onnx/qdrant/otelare compiled out. To use those backends add--features full(or the specific feature) to theav-harnessinstall line. Both pre-flight tools (avctl config-validate,avctl doctor) fail loudly when a config selects a backend the build cannot run.
Plain
cargo install av-harness av-cli(crates.io, no--git) starts working when the first tagged release is published; until then the registry does not know these crates — use the--gitlines above or Install from source.
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.
curl -fsSL https://agentvisorai.me/install.sh | sh # or the --git lines aboveavctl 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. Trajectories and receipts land under spool/atif/ (receipts in spool/atif/receipts/); bridge events land under data/bridge.
Note: context compression is on by default and may rewrite long or repetitive message histories before forwarding (duplicate collapse from ~512 tokens, middle summarization from ~50k). Stubs record the pruned token count and content hash in the audit trail. Set
compression_enabled = falseto forward payloads verbatim — details in docs/reference/OPENAI-COMPATIBILITY.md.
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/AgentVisorAI/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/v1beta/openai | GEMINI_API_KEY |
groq |
api.groq.com/openai | GROQ_API_KEY |
mistral |
api.mistral.ai | MISTRAL_API_KEY |
openrouter |
openrouter.ai/api | 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, budget posture) 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~/.agentvisor/agentvisor.toml(written by theavctlguided setup)- built-in defaults (requires
AV_UPSTREAM_URL)
config/harness.example.toml is documentation only — it is NOT
searched. Copy it to one of the paths above to use it.
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.
cp config/harness.example.toml config/harness.toml # the example is a template, never auto-loaded
OPENAI_API_KEY=sk-... cargo run -p av-harness --bin agentvisord
curl http://127.0.0.1:8484/health
curl http://127.0.0.1:8484/metricsThe copy is deliberate: config/harness.toml is on the search path,
the example file is not (so editing documentation can never
reconfigure a running deployment). Uncomment upstream_api_key_env
in the copy — or point upstream_url at a local keyless server and
uncomment ignore_client_authorization — before sending traffic.
POST /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-locate my-session-id --spool spool/atif # session id -> artifact paths
avctl spool-prune --spool spool/atif --retention-days 30 # one-off sealed-ATIF retention sweep
avctl receipt-verify receipt.json --public-key-hex "$TRUSTED_ED25519_PUBLIC_KEY_HEX" # repeatable; hex or base64
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 secret create signing_seed /path/to/signing.seed
docker compose -f docker/docker-compose.yml up --buildBoth external secrets are required by the production profile — the compose file declares identity_hmac and signing_seed as external, and docker compose up refuses to start if either is missing. 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.
A single AgentVisor AI process is the default and the tested-at-scale
configuration (10k concurrent streams, p95 0.865 ms — see
BENCHMARKS.md). Running two or more replicas is safe only when
every backend below is external — the embedded defaults are strictly
single-instance:
| Subsystem | Single-instance default | Multi-instance requirement |
|---|---|---|
| Signer seed | file on disk | same seed mounted at each replica (or a rotation you accept per replica) |
| State store (budgets, ratelimits) | state_backend = "memory" |
state_backend = "redis" with state_endpoint = "redis://..." and identical AV_STATE_ENDPOINT at each replica |
| Bridge (event bus) | bridge_backend = "embedded" (per-pod data-dir) |
bridge_backend = "kafka" or "nats" with shared endpoints |
| ATIF spool | pod-local atif_spool_dir (enforced: the daemon holds an exclusive lock on .agentvisord.lock in the spool and a second instance refuses to boot) |
one replica per spool volume — sharing a spool is refused at startup because the reconciler's per-file lifecycle lock is process-local and two replicas would race on close |
| Session registry | in-memory only | client stickiness (LB session affinity on X-AV-Session) OR accept that a session's audit chain lives on one pod for its lifetime and doesn't survive that pod's eviction |
Concretely: a two-replica deployment with state_backend = "memory"
lets a client rotate through both pods and effectively bypass every
per-session budget. A two-replica deployment sharing an ATIF spool
without session affinity will race on session close and land one
audit event on the broker twice. If you're not sure which subsystem
is on which side of the line, run one replica.
See docs/reference/OPERATIONS.md for the full per-subsystem checklist and docs/reference/CONFIGURATION.md for the exact TOML keys.
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.
