Failure Behavior
Applies to DynamoGuard. For the platform layer beneath it, see Platform Reliability.
High availability and disaster recovery describe how the platform stays available. This page describes what happens to a single guardrail request when something it depends on is not.
Read this before integrating. The most important configuration decision for a protected workload is not made in the platform: it is made in the calling application, in how that application handles an error from the analyze endpoint.
The Platform Returns An Error, Not A Decision
When the platform cannot evaluate a policy, the analyze endpoint returns an HTTP error. It does not return an allow, and it does not return a block.
There is no fail-open or fail-closed setting. The calling application decides the posture by how it handles that error. An application that treats an error as a pass permits ungoverned content. An application that treats an error as a block does not.
For any workload where unchecked content is unacceptable, treat every non-2xx response from
POST /v1/moderation/analyze as a block. This is the only way to achieve fail-closed
behavior in the current release.
if (response.status < 200 || response.status >= 300) {
// Treat as BLOCK. Do not forward the content.
}
A successful analyze returns 201. Check for a 2xx range; equality with 200 fails.
Dependencies On The Request Path
A single analyze call depends on all of the following. Any one of them being unavailable fails the request.
| Dependency | Used for | Cached |
|---|---|---|
| Keycloak | Authentication and per-resource authorization | Keycloak-side only |
| MongoDB | Policy lookup, guardrail configuration, keyword lists | No. Read on every request |
| Moderation worker | Dispatching each policy to its model server | No |
| Guardrail model servers | One inference per policy in the request | No |
| Application PostgreSQL | The moderation log write, which precedes the response | No |
Object storage is not on the request path. Guardrail model weights load when a model pod starts. An object store outage does not fail requests that are already being served. It prevents new model pods from starting. That affects recovery and scaling, not serving. See Object Storage Availability.
What A Request Returns When A Dependency Fails
| Unavailable | Response | Recorded in the moderation log |
|---|---|---|
| Keycloak | 401 or 5xx | No |
| MongoDB | 5xx | No |
| Moderation worker | 5xx | No |
| One guardrail model server | 5xx for the whole request, including policies that succeeded | No |
| Application PostgreSQL | 5xx, after the verdict was computed | No |
Two consequences follow from this table and are worth stating plainly.
The policy fan-out is all-or-nothing. A request naming several policies dispatches one inference per policy. If any one of them fails, the whole request fails, including the policies that returned a result. Across separate requests the policies are independent.
A failed request produces no moderation log entry. The moderation log records completed evaluations. A period during which evaluation was failing appears in the log as an absence of entries, and that is indistinguishable from an absence of traffic. Detect degraded enforcement from the platform's request metrics. The moderation log will not show it. See Monitoring And Targets.
The Moderation Log Write Precedes The Response
The moderation log write is synchronous and happens before the response returns. A log store outage therefore fails a request whose evaluation already succeeded.
Two request-level controls exist:
| Field | Effect |
|---|---|
loggingControl.enabled | Defaults to true. When false, the log write is skipped and a log store outage does not fail the request |
loggingControl.forceLogOnFinalAction | A list of final actions the server logs regardless of the setting above |
Setting enabled to false trades the audit record for availability. For a regulated
workload, the combination below is usually the correct one: keep a record of every decision
that restricted content, and allow unrestricted content to proceed unlogged if the log store
is unavailable.
{
"loggingControl": {
"enabled": false,
"forceLogOnFinalAction": ["BLOCK", "SANITIZE", "REDACT"]
}
}
Confirm the behavior you configured before relying on it, and record the choice: it changes what the audit trail contains.
A Policy That Cannot Be Resolved Is Omitted
This is the most important limitation on this page.
The analyze endpoint resolves each identifier in policyIds against the policy store. An
identifier that does not resolve is omitted from the evaluation, and the request returns
success with the remaining policies applied. The response does not warn that a policy was
omitted.
A policy identifier fails to resolve when the policy has been deleted, or when the policy store the serving region reads does not hold it — for example a standby region whose policy set is behind the primary. A disabled policy behaves differently and correctly: the request fails with a 400 naming the policy.
Detect this in the calling application. The response carries appliedPolicies. Compare
it against the identifiers sent:
POST /v1/moderation/analyze
{"policyIds": ["<id-a>", "<id-b>"], ...}
Response 201
{"finalAction": "NONE", "appliedPolicies": [ { "policy": { "id": "<id-a>" }, ... } ]}
Here <id-b> was not applied. Treat any request where appliedPolicies does not account for
every identifier sent as an enforcement failure, and handle it as you would a 5xx.
policyIds accepts an empty list, and a request that names no policies returns 201 with a
final action of NONE, having applied nothing. Validate in the calling application that at
least one policy identifier is present before sending, and apply the appliedPolicies check
above to every response.
Cross-region behavior for this case, including why a policy count is not a parity check on its own, is in Traffic Routing.
Guardrail Serving Under Load And During Updates
| Property | Behavior |
|---|---|
| Replicas per guardrail model | One by default. Configurable through replicas on the model |
| Disruption budget | Created automatically when a model runs two or more replicas |
| Cold start | 60 to 300 seconds while a model loads, so capacity is pre-provisioned rather than autoscaled |
| Update strategy | Rolling. A new pod starts before the old one stops, which requires headroom for a second pod |
A model running a single replica is unavailable for the duration of a restart, and every request naming its policy fails during that window. Run two or more replicas for any policy whose availability matters, which also creates the disruption budget.
Custom policy models are managed by the platform and run a single replica in the current release.
Related
- High Availability: in-region redundancy and per-component behavior
- Disaster Recovery: recovery objectives and the failover procedure
- Configuration Requirements: the complete list, with status