OGuardAI
Operations

Service Level Objectives

How to set availability, latency, and throughput objectives for an OGuardAI deployment, and the hard limits and security properties the runtime enforces

An SLO is a target you commit to for your own deployment, on your own hardware. OGuardAI publishes no latency or throughput figures, and this page does not invent any: see Benchmarks for the rule and for the harnesses that produce numbers from a run you execute. What this page does give you is the axes worth setting a target on, and the limits and properties the runtime enforces regardless of what you target.

Availability

Availability targets are yours to set. These are common starting points, not vendor commitments.

MetricMeasurement
API availability/readyz returns 200
Transform endpointNon-5xx responses / total requests
Rehydrate endpointNon-5xx responses / total requests

Rehydrate deserves the tighter target of the two: a failed transform can be retried from the original text, while a failed rehydrate strands output the caller already paid an LLM to produce.

Latency

Set the latency objective per detector mode, because the two modes are bounded by different things:

  • Builtin-only is in-process CPU work. It scales with payload length and match count, and it has no external dependency to wait on.
  • Builtin + NER (both or advanced) adds a synchronous sidecar round trip and a model forward pass per detection. Its worst case is bounded by detector.timeout_secs, so that value, not a target you write down, is what actually caps the tail.
  • Rehydrate performs local replacement and runs no detection, unless output NER scanning or a Redis-backed session or revocation backend puts it on the network. See Performance.

Measure your own baseline before committing to a number. The guardai_transform_duration_seconds and guardai_rehydrate_duration_seconds histograms give you the quantiles:

histogram_quantile(0.95, sum(rate(guardai_transform_duration_seconds_bucket[5m])) by (le))

Throughput

Throughput has two independent capacity axes, and sizing only one of them is the common mistake:

  • The API instances handle the builtin path and scale horizontally, because sealed sessions carry state with the request and instances do not coordinate on the transform path.
  • The NER sidecars handle model inference. A NER-heavy workload needs proportional sidecar capacity; adding API instances does not add NER throughput.

Optional shared backends (Redis sessions, replay protection) introduce a coordination point a purely sealed deployment does not have. Shared revocation is not optional in a multi-replica deployment, so count it in.

Payload Limits

These are enforced by the runtime, not targets.

LimitDefaultConfigurable
Max request body (non-file routes)10 MBserver.max_body_size_bytes
Max file/image upload50 MBfile_upload.max_size_bytes
Max batch items100limits.max_batch_size (lower only; capped at 100)
Max detection spans per request10000limits.max_detected_spans (GUARDAI_MAX_DETECTED_SPANS, 1..=10000; over-cap request refused 413, over-cap config value fails startup)
PDF extraction address-space cap256 MiBlimits.pdf_extract_memory_mib (lower only)
PDF extraction wall clock10slimits.pdf_extract_timeout_secs (lower only)
Session TTL3600s (1 hour)session.ttl_seconds, no enforced ceiling; keep it short, it bounds how long a captured blob restores
Max concurrent streams (proxy)100GUARDAI_PROXY_STREAM_MAX_CONCURRENT (0 disables the cap)
Max concurrent streams (server streaming endpoints)100limits.max_concurrent_streams (GUARDAI_MAX_CONCURRENT_STREAMS), 1..=1024
Server stream total duration3600s (1 hour)limits.stream_max_duration_secs (GUARDAI_STREAM_MAX_DURATION_SECS), 1..=86400
Server stream total egress64 MiBlimits.stream_max_total_bytes (GUARDAI_STREAM_MAX_TOTAL_BYTES), 1..=1073741824
RAG ingest wall clock (per document)3600s (1 hour)rag.max_ingest_duration_secs (GUARDAI_RAG_MAX_INGEST_DURATION_SECS), 1..=86400; over-budget ingest refused 503 GUARDAI_DEADLINE_EXCEEDED with nothing sealed
SSE heartbeat interval15s (proxy only)Proxy SSE keep-alive; the server stream endpoints send none

A document refused by a size ceiling returns 413 GUARDAI_PAYLOAD_TOO_LARGE, not a 400: the document may be perfectly valid. See PDF Support. The server streaming endpoints add three fail-closed bounds: a request past the concurrency ceiling is refused 503 GUARDAI_STREAM_CAPACITY_EXHAUSTED with Retry-After: 1; a response whose total serialized egress exceeds the byte ceiling is refused 413 GUARDAI_PAYLOAD_TOO_LARGE before the stream opens; and a stream whose client drain outlasts the duration budget is cut with a single terminal GUARDAI_STREAM_BUDGET_EXCEEDED event (never a complete event, so the truncation is detectable).

Error Budget

Budget on the axes you can actually observe:

Error TypeWhere to read it
5xx responsesguardai_errors_total for the coarse signal, then the request failed log events for the error_code breakdown
Token repair failuresGUARDAI_TOKEN_REPAIR_FAILED in the structured log
Session expiryGUARDAI_SESSION_EXPIRED in the structured log. Expected at the TTL boundary; a sustained spike means the TTL is misconfigured or clients are replaying stale blobs

Detection accuracy is deliberately absent from this table. Precision and recall depend on the model, the language, and your own corpus, so they are not a number we can set on your behalf. Run benchmarks/ner_bakeoff/ against your own labelled data to get per-language and per-entity-type precision, recall, and F1, then budget from that.

Degraded Mode

When the NER sidecar is unavailable:

MetricBehavior
both modeThe request still succeeds via builtin regex, after waiting up to detector.timeout_secs
advanced modeA sidecar outage fails the request closed with GUARDAI_DETECTION_UNAVAILABLE (503, retriable). NER is required in this mode
Entity coverage in bothBuiltin types only; person, company, and location are not detected
Policy detection.required_forFails closed in either mode. A type declared required never passes raw because NER did not run
Data safetyUnaffected. Nothing detected is left raw in safe_text

Session Security

PropertyGuarantee
EncryptionAES-256-GCM (AEAD)
Key strength256-bit minimum
Nonce uniquenessRandom 12-byte per seal (cryptographically random)
Tamper detectionAuthentication tag verified on every unseal
Replay protectionEnforced for transform continuations via a per-session-id strict-monotonic counter store (replay_backend: memory default, or redis for HA). Rehydrate and RAG context are exempt by design and remain TTL-bounded bearer tokens. Memory backend has a cold-start window on restart; Redis closes it across replicas. Running redis replay across replicas is a multi-replica deployment, so it also requires revocation_backend: redis, otherwise a value revoked on one replica stays restorable through another and the server refuses to start
Cross-tenant isolationTenant ID validated during unseal

Revocation

PropertyGuarantee
Revocation effectApplies to future rehydrate calls. Output already delivered cannot be clawed back
Revocation persistenceSurvives server restart (file backend)
Revocation consistencyPer-instance with the file backend; shared across replicas with revocation_backend: redis
Future restore suppressionA revoked value always resolves to [DELETED], even from a valid session blob

The binding version of these, including what the person cascade does and does not cover, is the Revocation Contract.

Monitoring

# Prometheus alerts (see deploy/prometheus/alerting-rules.yml)
- guardai_transforms_total          # request rate
- guardai_errors_total              # error rate
- guardai_transform_duration_seconds # latency
- guardai_rate_limit_rejections_total # capacity
- guardai_prompt_security_triggers_total # security

The shipped alerting rules carry starting thresholds, not tuned ones. See Monitoring.