diff --git a/banditcallback/banditcallback.go b/banditcallback/banditcallback.go index 1b2d9bfd..13d0006d 100644 --- a/banditcallback/banditcallback.go +++ b/banditcallback/banditcallback.go @@ -51,11 +51,17 @@ type Emitter struct { // New returns an emitter. token and callbackURL come from the daemon's // INI (banditcallbacktoken / banditcallbackurl). ttl is the per-device -// dedup window — typically matches the API-side ProbeTTLForPollInterval -// at the daemon's expected poll. Zero ttl uses 10m as a sensible default. +// dedup window — also the heartbeat cadence: while a device keeps +// using the proxy, it triggers a callback at most once per ttl. The +// API side uses absence of those heartbeats as its only available +// signal for censorship blocking (the proxy itself can't observe a +// blocked connection because the client never reaches it), so ttl +// must stay short enough that an absence is detected before users +// suffer. Zero ttl uses 60s as a sensible default. Keep this in lock- +// step with bandit.ArmCallbackHeartbeatWindow on the API side. func New(token, callbackURL string, ttl time.Duration) *Emitter { if ttl <= 0 { - ttl = 10 * time.Minute + ttl = 60 * time.Second } return &Emitter{ token: token, diff --git a/http-proxy/main.go b/http-proxy/main.go index 9c82b633..d3baacce 100644 --- a/http-proxy/main.go +++ b/http-proxy/main.go @@ -95,7 +95,7 @@ var ( // the same binary without firing any callbacks. banditCallbackToken = flag.String("banditcallbacktoken", "", "Per-arm bandit callback token plumbed by the provisioner") banditCallbackURL = flag.String("banditcallbackurl", "", "Full URL of the /v1/bandit/callback endpoint") - banditCallbackTTL = flag.Duration("banditcallbackttl", 10*time.Minute, "Per-device dedup window for bandit callback emission") + banditCallbackTTL = flag.Duration("banditcallbackttl", 60*time.Second, "Per-device dedup window and heartbeat cadence for bandit callback emission. The API's absence-reaper expects a callback within ArmCallbackHeartbeatWindow (~90s) of the previous one; values much smaller than that just amplify callback traffic, values larger risk false-positive negative rewards on idle clients.") throttleRefreshInterval = flag.Duration("throttlerefresh", throttle.DefaultRefreshInterval, "Specifies how frequently to refresh throttling configuration from redis. Defaults to 5 minutes.")