use examples

BareProxy Inbox and Drift Watcher Use Examples

Six short recipes for the two features that arrived in BareProxy 0.16.0, the webhook inbox and the drift watcher. Each one was run on the released Linux binary and the output below is copied from that run. The tested configs used a temporary folder, local ports and names under .test; the configs here use the usual paths and example names, and the output is left as it came, test names included. The webhooks were signed with test secrets in Stripe’s and GitHub’s formats, and the drift recipes ran against a local stand-in for DNS and HTTPS, because the machine had no public DNS.

1. An inbox in front of an app that restarts on every deploy

site hooks.example.com
  route /hooks/stripe -> inbox stripe

inbox stripe
  deliver app
  signature stripe /etc/bareproxy/stripe-secret
  retry 5s 2m

pool app
  backend 127.0.0.1:8080
  health /healthz

bareproxy check on the tested file said ok, 1 site, 1 pool, 2 rules. The app was stopped, one Stripe webhook came in and got 202 in 20 ms, and the app came back 15.4 seconds later. The event reached the app 19.7 seconds after that, on its fourth attempt.

That gap is the backoff at work. Attempts came at 5 and 15 seconds after the webhook arrived, both while the app was down, and the third retry was set for 35 seconds. The health check tells the pool when the app is back, but it doesn’t wake up an event that is already waiting. So pick retry for how long your deploys take. With 5s 2m no event waits more than two minutes between tries; the default ceiling is an hour.

2. Checking Stripe signatures

signature stripe FILE is all it takes. The file holds the endpoint’s signing secret (whsec_...) and nothing else. In the run, a correctly signed webhook got 202, one signed with the wrong secret got 401, and one with the right secret but a timestamp ten minutes old got 401. Neither rejected webhook was stored.

One thing to watch. The inbox keeps the request as it came, Stripe-Signature included, and its t= value is the time Stripe signed it. If your app also checks that header with Stripe’s library, a delivery that waited out an outage will fail the library’s five minute tolerance. Let BareProxy do the check, or widen the tolerance in the app. For repeats, keep the evt_ IDs you have handled: a Stripe retry is a new request to BareProxy, as the duplicates case study shows.

3. Replaying what failed during an outage

When the app was broken long enough for events to pass give-up (15 seconds in this test, three days by default), they were marked failed and kept:

$ bareproxy inbox list github --status failed
Inbox github (line 15), to pool app, in /work/state/inbox/github
  0 pending, 0 delivering, 0 delivered, 2 failed
  18e39758786c365152c1d6  2026-10-31 10:44:42  failed   /hooks/github, 158 B, 5 attempts, last: 500
  18e39758638e4c637f884c  2026-10-31 10:44:42  failed   /hooks/github, 158 B, 5 attempts, last: 500
$ bareproxy inbox replay github --failed
Replayed 2 events; each delivery carries BareProxy-Inbox-Replay: operator.

Both reached the fixed app at once, marked operator, and were processed. inbox show keeps the whole story of an event, with the sender’s own ID when the inbox has a key-header:

$ bareproxy inbox show github 18e39758638e4c637f884c
Event 18e39758638e4c637f884c in inbox github: delivered
Received 2026-10-31 10:44:42.506 UTC: POST hooks.test:56529/hooks/github, 158 B
Sender's ID: 155493cd-d51f-43f0-bad5-fd661337a3fc
  ...
Attempt 1 at 2026-10-31 10:44:42: 500
Attempt 2 at 2026-10-31 10:44:44: 500
Attempt 3 at 2026-10-31 10:44:48: 500
Attempt 4 at 2026-10-31 10:44:53: 500
Attempt 5 at 2026-10-31 10:44:58: 500
Attempt 6 at 2026-10-31 10:44:59: 200 (marked operator)
Delivered 2026-10-31 10:44:59 UTC

--body adds the payload, and --json works on all three commands. To replay a handful by hand, list their IDs after the inbox name instead of --failed.

4. Drift in CI, failing the job on alerts

A config with only a drift block is enough for a CI job, and no server needs to run:

global
  state /var/lib/bareproxy-ci

drift sites
  domains www.example.com shop.example.com

The job runs a small script around the exit code:

#!/bin/sh
bareproxy drift -c drift-ci.conf
case $? in
  0) echo "drift: all clear" ;;
  3) echo "drift: some parts couldn't be checked, see above" ;;
  4) echo "drift: alerts, failing the job"; exit 1 ;;
  *) echo "drift: the check couldn't run"; exit 1 ;;
esac

In the run, the shop certificate had 11 days left:

ALERT      shop.example.test  certificate ends in 11 days (2026-11-12 10:45 UTC), inside the 21-day window
Result: 1 alert, 0 unchecked
drift: alerts, failing the job
(job exit code 1)

With expiry 7d added to the block, the same certificate was outside the window, the result was 0 alerts, 0 unchecked and the job passed. Code 3 is left as a warning here, so a flaky resolver doesn’t fail a build; make it fail too if you’d rather.

Keep the state folder between CI runs, in a cache or an artifact. Without it every run is a first look, and a first look reports standing problems but can’t see a change.

5. Drift every hour inside the server, with a webhook alert

Add every and webhook to a drift block in the server’s own config:

drift sites
  domains www.example.com shop.example.com
  every 1h
  webhook https://alerts.example.com/bareproxy

The server ran its first check five seconds after it started, since the watch had no state yet. After that it waits for every to pass since the last check, across restarts. The alert receiver got the report as JSON, which jq boils down to:

{"watch":"sites","domains":2,"alerts":[{"domain":"shop.example.test","text":"certificate ends in 11 days (2026-11-12 10:45 UTC), inside the 21-day window"}],"unchecked":0}

And bareproxy events showed the check and its alert:

2026-10-31T10:45:07.519Z  drift        drift sites: 2 domains, 1 alert, 0 unchecked, webhook sent, answered 204
2026-10-31T10:45:07.519Z  drift        drift sites: shop.example.test certificate ends in 11 days (2026-11-12 10:45 UTC), inside the 21-day window

The webhook only fires when there is something to say, alerts or unchecked parts. A clean check stays in events.

6. Reading the history

After www moved to a new address, the next check alerted once, and the history kept the change:

$ bareproxy drift -c drift-ci.conf --history
Drift watch sites, history:
2026-10-31 10:45:01  shop.example.test  first look, recorded: A 127.0.0.1; ...
2026-10-31 10:45:01  www.example.test  first look, recorded: A 127.0.0.1; ...
2026-10-31 10:45:07  www.example.test  A changed
                     before: 127.0.0.1
                     after:  127.0.0.2

As JSON it is one list per watch, and each change has kind, part, before and after. To pull out only the changes:

$ bareproxy drift -c drift-ci.conf --history --json | jq -c '.. | objects | select(has("before") and has("after"))'
{"time":"2026-10-31T12:45:07.671489159+02:00","domain":"www.example.test","kind":"record-changed","part":"A","text":"A changed","before":["127.0.0.1"],"after":["127.0.0.2"]}

The script that ran all six recipes and its full log are in the repository: use-examples.sh and use-examples.log. Longer runs of the same features are in the case studies on an app deploy, a crash mid-delivery, duplicates and a bad deploy and a DNS move.