Skip to main content
Turn on store_backend: redis and every replica of your gateway admits the same webhook exactly once, so a duplicate delivery never runs your agent twice.

Quick Start

1

Single replica — nothing to do

One gateway process dedups with the durable SQLite default. Nothing to set.
2

Multiple replicas — set store_backend: redis

Two or more gateway processes need a shared claim so the same webhook fanned to both is admitted only once. Set it in gateway.yaml and make sure a Redis URL is configured — the store reuses the gateway’s push RedisConfig.
3

Or wire it in Python

Pass the same store_backend through the gateway’s hooks config where you construct it.

How It Works

Each replica claims the key (platform, account, channel_id, message_id) with a single SET NX PX; the first wins and the duplicate is rejected.

Fail-Open on Redis Outage

The store never wedges inbound delivery — it degrades to best-effort and surfaces the fact, never a silent green. Two failure modes, both surfaced under the durability:idempotency degraded owner:
On a build-time fallback to SQLite, if a message is fanned to two replicas that do not share the SQLite state file, the agent turn can run twice. Watch health()["degraded_owners"] and restore Redis.

When to Enable


Configuration Options

The operator selects the backend and the store reuses the gateway’s push RedisConfig — no separate idempotency Redis knob.

Gateway Turn Lock

The sibling shared-state knob that wires the same RedisConfig.

Best Practices

For a multi-replica gateway, set both turn_lock.backend: redis and hooks.idempotency.store_backend: redis — one serialises a session’s turns, the other admits each webhook exactly once.
The store auto-shares push.redis. Configure Redis once; do not duplicate the connection for idempotency.
A durability:idempotency entry in gateway doctor / GET /health means dedup fell back or is best-effort. Alert on it the same way you alert on a degraded channel.
Switch to store_backend: redis before scaling past one replica, so a fanned-out webhook is deduped from the first extra pod.

Gateway Turn Lock

Sibling shared state — a distributed lease that serialises a session’s turns.

Gateway State Durability

The default SQLite dedup path and the degraded surface it joins.

Gateway Inbound Hooks

Where store_backend is configured on the inbound hook surface.

Degraded Capabilities

Where the durability:idempotency fact surfaces.