Skip to content

ARMS Storage

Persist guardrail run data through the ARMS Backend API, with automatic routing to MongoDB, DynamoDB, or ClickHouse depending on your deployment.

Overview

Guardrails can buffer check results, tool authorization events, rate-limit events, and LLM generate metadata, then flush them to the ARMS Backend at the end of each run. The Backend writes to whichever database your ARMS deployment uses — you do not configure a database driver in guardrails YAML.

Key points:

  • Guardrails alone (storage.enabled: false) never writes to a database.
  • Storage requires ARMS Backend credentials (API_BASE_URL, ELSAI_ARMS_API_KEY).
  • The active database type is resolved automatically via GET /api/v1/db_type.
  • Run data is posted to POST /api/v1/guardrails/{db_type} where db_type is mongodb, dynamodb, or clickhouse.

Supported Databases

DatabaseBackend routeNotes
MongoDB/api/v1/guardrails/mongodbDefault for many ARMS deployments
DynamoDB/api/v1/guardrails/dynamodbAWS-hosted ARMS stacks
ClickHouse/api/v1/guardrails/clickhouseAnalytics-oriented deployments

The guardrails SDK discovers the active type from the Backend. You do not pass a backend: mongodb setting in policy — direct database clients were removed in v0.1.5.

How It Works

  1. Enable storage in guardrails policy and provide Backend credentials.
  2. GuardrailsStorageHook buffers events in memory during a logical run.
  3. Input checks, output checks, generate results, tool auth, and rate-limit events are collected.
  4. On end_run() (or when LLMRails.generate() completes), the hook builds a run document and POSTs it to the Backend.
  5. The Backend persists the document to the configured database using your ARMS project context.

Persisted Run Document

Each flushed run includes correlation ids and check outcomes:

  • project, project_id, run_id, session_id, trace_id
  • user_input, final_response, llm_response (optionally redacted)
  • input_check, output_check, pii_input_check, pii_output_check
  • exfiltration_output_check
  • rate_limit_check, token_budget_* checks
  • tool_authorization_events, rate_limit_events
  • blocked, block_reason, latency_ms, arms_correlated

Set store_raw_text: false to store SHA-256 digests instead of raw prompt/response text.

Configuration

Enable Storage

yaml
guardrails:
  storage:
    enabled: true
    project: my-app
    store_raw_text: true
    fail_soft: true
    unique_run_per_project: false
    arms_correlation: true

Backend credentials are read from the same environment variables as ARMS, not duplicated in guardrails YAML:

VariableRequiredDescription
API_BASE_URLYesARMS Backend base URL
ELSAI_ARMS_API_KEYYesAPI key for Backend authentication
ARMS_MASTER_KEYNoOptional master key sent as X-MASTER-KEY

You may override credentials in policy when needed:

yaml
guardrails:
  storage:
    enabled: true
    api_base_url: "https://arms-backend.example.com"
    api_key: "your-api-key"
    master_key: "optional-master-key"

Storage Options

OptionTypeDefaultDescription
enabledboolfalseEnable persistence via ARMS Backend
projectstr"default"Logical project name stored on run documents
store_raw_textbooltrueStore full text; false stores SHA-256 digests
fail_softbooltrueLog warnings on write failure instead of raising
unique_run_per_projectboolfalseBackend upsert behavior for run ids
arms_correlationbooltrueAuto-link run_id / project_id from ARMS env vars

Environment Overrides

VariableDescription
GUARDRAILS_STORAGE_ENABLEDForce storage on/off (true / false)
GUARDRAILS_ARMS_RUN_IDARMS run id for correlation
GUARDRAILS_ARMS_PROJECT_IDARMS project id for correlation
GUARDRAILS_ARMS_CORRELATIONEnable/disable automatic ARMS correlation
GUARDRAILS_PROJECTDefault project name

ARMS Correlation

Storage writes require a project_id. Link guardrails to an active ARMS run using one of these approaches:

Call immediately after creating both clients, before any guardrail checks:

python
from elsai_guardrails.guardrails import LLMRails
from elsai_arms import ElsaiARMS  # optional companion package

arms = ElsaiARMS(project_name="my-app")
rails = LLMRails.from_config("config.yml")

rails.guardrail_system.link_arms(arms)

response = rails.generate(
    messages=[{"role": "user", "content": "Hello"}]
)
rails.guardrail_system.end_run()

Pass ids explicitly when you manage correlation yourself:

python
rails.guardrail_system.link_run_context(
    run_id="abc123",
    project_id="proj-456",
    project="my-app",
)

3. Environment variables

Set before starting the process:

bash
export GUARDRAILS_ARMS_RUN_ID=abc123
export GUARDRAILS_ARMS_PROJECT_ID=proj-456

When arms_correlation: true (default), the storage hook picks up these ids automatically.

Usage Patterns

LLMRails (automatic buffering)

LLMRails wires the storage hook when storage is enabled. Each generate() call buffers results; flushing happens according to your run lifecycle:

python
from elsai_guardrails.guardrails import LLMRails

rails = LLMRails.from_config("config.yml")
rails.guardrail_system.link_run_context(
    run_id="run-1",
    project_id="project-1",
)

with rails.guardrail_system.storage_run_context(session_id="sess-1"):
    rails.generate(messages=[{"role": "user", "content": "Hi"}])

GuardrailSystem (manual lifecycle)

python
from elsai_guardrails.guardrails import GuardrailSystem, GuardrailConfig
from elsai_guardrails.guardrails.guardrail_policy import GuardrailPolicy

policy = GuardrailPolicy.from_yaml("config.yml")
guardrail = GuardrailSystem(
    config=GuardrailConfig(),
    guardrail_policy=policy,
)._with_storage_hook()

guardrail.link_run_context(run_id="run-1", project_id="project-1")
guardrail.begin_run()
guardrail.check_input("user message")
guardrail.end_run()  # flushes to Backend

Fail-soft vs fail-hard

With fail_soft: true (default), Backend write failures are logged and the application continues. Set fail_soft: false to raise StorageWriteError when persistence fails.

Database Selection Flow

Troubleshooting

IssueCauseFix
Storage silently disabledMissing API_BASE_URL or ELSAI_ARMS_API_KEYSet env vars or policy credentials
Cannot persist without project_idNo ARMS correlationCall link_arms() or set env ids
Legacy backend: mongodb warningOld direct-DB configMigrate to Backend storage (see below)
Unsupported db_typeBackend returned unknown typeUpgrade Backend or contact ARMS admin

Migration from Direct Database Storage

In v0.1.5, direct guardrails-to-database clients were removed. Policies that set storage.backend: mongodb|dynamodb|clickhouse with connection strings no longer persist data — the SDK logs a warning and storage stays disabled.

Before (removed):

yaml
guardrails:
  storage:
    backend: mongodb
    connection_string: "mongodb://..."

After (ARMS Backend):

yaml
guardrails:
  storage:
    enabled: true
    project: my-app
    arms_correlation: true
bash
export API_BASE_URL=https://your-arms-backend
export ELSAI_ARMS_API_KEY=your-api-key
export GUARDRAILS_ARMS_PROJECT_ID=your-project-id

Link each run with link_arms(), link_run_context(), or the correlation environment variables before calling end_run().

Examples

See Basic Examples and Integration Examples for complete code samples.

python
import os
from elsai_guardrails.guardrails import GuardrailConfig, GuardrailSystem
from elsai_guardrails.guardrails.guardrail_policy import GuardrailPolicy

os.environ["API_BASE_URL"] = "https://your-arms-backend"
os.environ["ELSAI_ARMS_API_KEY"] = "your-api-key"

guardrails = GuardrailSystem(
    config=GuardrailConfig(),
    guardrail_policy=GuardrailPolicy.from_file("config.yaml"),
)._with_storage_hook()

guardrails.link_run_context(
    run_id="demo-run-001",
    project_id="demo-project-001",
    project="demo-app",
)
guardrails.begin_run(session_id="sess-1")
guardrails.check_input("Hello")
guardrails.check_output("Hi there!")
saved_run_id = guardrails.end_run()  # POST to Backend

Next Steps

Copyright © 2026 elsai foundry.