Drop-in integration
Guard the call, keep your code. Axiom sits between your existing agent and your model provider — you keep your orchestrator, your prompts, and your deployment pipeline. Three paths, ordered by how fast you can be live. None of them require an architectural rewrite.
Every path gives you the same three things on day one:
- Zero-call blocks — prompt injections and constitutional violations
are stopped at preflight, before inference. A blocked request reports
usage.total_tokens: 0because nothing ever reached a model. The preflight check is regex + arithmetic (the ORVL-016 intent gate plus domain agents), not another LLM call — overhead is measured in microseconds, not seconds. - Tamper-evident audit trail — every decision produces an
HMAC-SHA256 signed manifest recording what was checked, what was
blocked, and why. Manifests are retrievable by id
(
GET /guard/manifest/{id}) and every verdict also lands in the Flight Recorder for search, replay, and SIEM export. - Immutable boundaries —
CANNOT_MUTATEfields in the.axiomspec (and frozen-module enforcement in the runtime) mean an agent cannot rewrite its own identity, goals, or trust levels at runtime.
Path A — reroute your base_url (fastest)
If your agent speaks the OpenAI API, integration is one line: point the client at Axiom instead of the provider.
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:8001/v1", # was: your provider
api_key="anything", # or your AXIOM_GUARD_API_TOKEN
)
resp = client.chat.completions.create(
model="your-model",
messages=[{"role": "user", "content": "Hello"}],
)
The proxy runs the intent gate and domain agents on the prompt, forwards allowed requests to your configured backend, then screens the completion on the way back. The backend is set by environment on the Axiom side:
NIM_API_KEY(+ optionalNIM_BASE_URL,NIM_MODEL) — any OpenAI-compatible provider: NVIDIA NIM, vLLM, Ollama's/v1shimANTHROPIC_API_KEY— Anthropic
A blocked call comes back as a normal OpenAI response with
finish_reason: "content_filter", zero token usage, and an axiom
block carrying the signed manifest id — your agent loop does not crash,
and the audit trail already has the receipt:
{
"choices": [{"finish_reason": "content_filter",
"message": {"role": "assistant", "content": "BLOCKED: ..."}}],
"usage": {"total_tokens": 0},
"axiom": {"blocked_at": "INPUT", "verdict": "BLOCKED",
"manifest_id": "GUARD-1a2b3c4d-IN",
"signature": "hmac-sha256:..."}
}
GET /v1/models answers client discovery probes (LiteLLM, OpenWebUI).
Set AXIOM_GUARD_API_TOKEN to require callers to present it as their
bearer key; leave it unset for local development.
stream: true streams token-by-token with an incremental output
guard: every delta is appended to a buffer and re-screened before it
is forwarded, so a violating pattern is cut off within one delta of
appearing and the stream closes with finish_reason: "content_filter".
(Tokens already delivered cannot be recalled — that residue is inherent
to any streaming guard; the signed manifest always covers the full
buffered text.)
Tool calls are gated whole: when a request carries tools, the
completion is buffered server-side (even under stream: true, where the
result arrives as a single SSE chunk) and every call the model wants to
make — name plus arguments — passes the intent gate and domain agents
before your agent can execute it. A blocked call suppresses the entire
tool_calls turn and returns finish_reason: "content_filter" with the
signed manifest naming the offending call.
Prefer richer verdict detail over strict OpenAI shape? POST
/guard/proxy returns the full manifest pair, and POST /guard/input /
POST /guard/output let you keep your own provider call and just wrap
it — that is the sidecar pattern: your orchestrator (LangChain,
LlamaIndex, custom) calls the guard before and after its own LLM call.
See the API reference for those shapes.
Path B — the Docker sidecar (most control)
One container, no cloud dependency:
docker run -d -p 8001:8001 \
-e AXIOM_MASTER_KEY=$(openssl rand -hex 32) \
orivaeldev/axiom-guard
curl http://localhost:8001/guard/status
From docker pull to a live constitutional review API in under five
minutes — the image is published on Docker Hub as
orivaeldev/axiom-guard,
so there is nothing to clone and nothing to build. The container ships
a healthcheck on /guard/status, and the full production stack (guard
API + firewall + observability console behind Caddy TLS) is one compose
file: deploy/firewall/docker-compose.yml — see
Self-hosting for the production checklist.
Your SIEM plugs in via the Flight Recorder export — five formats from one endpoint:
# Splunk HEC, Datadog Logs, OTLP/HTTP JSON, JSON lines, or CSV
curl "http://localhost:8001/flight_recorder/export?fmt=otlp"
The otlp payload is OpenTelemetry LogsData JSON — POST it unmodified
to any OTel collector's /v1/logs endpoint. splunk and datadog
match those platforms' native ingestion shapes.
Path C — edge and on-device (privacy-first)
The guard stack is pure Python over regex + arithmetic — no GPU, no network calls, no model needed for verdicts. For clinical, industrial, and air-gapped deployments it runs where the data lives.
Measured on a Jetson Orin (15 W mode), running the full quantized-model
pipeline alongside the guard: Llama-3.2-1B at 31.8–34.0 tok/s, 12.5 W
peak, 3.8 GB RAM peak (research/quant/results/jetson_llama32_1b_15w.json
— reproducible, with the harness in research/quant/). The guard's own
verdict path adds microseconds and works with zero model calls, so a
device that never leaves the premises still produces the same signed
audit trail.
Licensing
The self-hosted guard container is free to run and nothing in it is gated.
If your deployment is covered by a commercial agreement, set
AXIOM_LICENSE_KEY (and optionally AXIOM_LICENSE_TIER) and the posture
shows up in /guard/status and in the startup banner:
{ "licensed": true, "tier": "enterprise", "key_id": "bc1fcc406bb2" }
Only a digest of the key is reported, never the key itself. This is a
record, not a check — the value is not verified and no verdict
changes with or without it. Unset, you get
{ "licensed": false, "tier": "community" } and an identical guard.
When you outgrow the proxy
The signed manifests tell you what was blocked. When you want to see why an agent loop went wrong — step through runs, set breakpoints, watch a critic loop stall — that is NodeXLoop, the visual debugger for agent loops (nodexloop.orivael.dev). It embeds the same guard engine (generated from the same Axiom modules, verified by a parity corpus), so verdicts match what your proxy would say. Adopt it when you are ready; the proxy keeps working either way.
What's next
- Quickstart — hosted firewall: sign up, get a key, first signed verdict in five minutes
- Self-hosting — production checklist, TLS, backups
- API reference — every endpoint, request and response shapes