Orivael

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: 0 because 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_MUTATE fields in the .axiom spec (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 (+ optional NIM_BASE_URL, NIM_MODEL) — any OpenAI-compatible provider: NVIDIA NIM, vLLM, Ollama's /v1 shim
  • ANTHROPIC_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