#Ingress
Also played Ingress and war drove a bit, 2.76 km setting up (mostly) distinct Resistance fields and logging 195 Wi-Fi & 215 Bluetooth. The clicking and numeration in through my hearing aids is soothing.
October 17, 2026 at 7:55 PM
2026.10.14 #日本縦断旅 145日目
スパ・アルプスで朝ウナをキメてから #黒部峡谷トロッコ 列車へ!宇奈月駅から欅平駅まで、エメラルドグリーンに輝く宇奈月湖や大自然の絶景と名物の富山ブラックラーメンも堪能してきました。
#Ingress #No_Ingress_No_Life
#ハイエースのある生活
October 17, 2026 at 7:47 PM
Ingress-nginx rewrite-target + use-regex turns regex capture groups into path rewrites: /v1/users/123 -> /users/123. Build versioned API gateways without extra hops. https://www.valtersit.com/vault/rewrite-and-redirect-rules-with-regex-capture-groups-e509da/ #nginx #ingress #ValtersIT
Rewrite and redirect rules with regex capture groups
www.valtersit.com
October 17, 2026 at 7:40 PM
This is my third visit to Lane Stadium, and it reliably has the worst ingress of any stadium I've been to.
October 17, 2026 at 7:01 PM
The Contra Costa County Board's recent decision to move forward with the I‑80/San Pablo Dam Road interchange project has sparked controversy, as property owners raise concerns about safety and access.

Read the full story

#ContraCostaCounty #CA #CitizenPortal #CommunityNegotiation
Board adopts resolution of necessity for I‑80/San Pablo Dam Road interchange; owner objects
The board adopted Resolution of Necessity No. 2026-385 to authorize eminent domain for the I‑80/San Pablo Dam Road interchange improvement (phase 2) after a public hearing; the property owner's representative urged a 30‑day deferral, saying the current design would eliminate the site's sole ingress/egress.
citizenportal.ai
October 17, 2026 at 3:15 PM
Swarm on one box still runs a Raft store and an ingress overlay network for a cluster of one. Compose with restart: unless-stopped covers what most single VPS setups wanted from it.
October 17, 2026 at 3:00 PM
Bianca Bailey of @pickuptrucktalk.bsky.social +SUV Talk - PUTT reports Stellantis is recalling over 265,000 Ram ProMaster vans are being recalled for a power steering issue.

pickuptrucktalk.com/2026/10/ram-...
Ram ProMaster Recall: 265K Vans May Lose Power Steering Assist
A recall of 265,512 Ram ProMaster models for power steering that may fail due to wiring harness water ingress.
pickuptrucktalk.com
October 17, 2026 at 2:57 PM
Indeed. I would have left my tomato plants in longer but needed space in greenhouse to overwinter plants, not wanting to take risk of possible night frosts catching me out. My cellar is exceptionally dry as no significant water ingress so far this yr. Usually have some days of 1" after heavy rain.
October 17, 2026 at 12:45 PM
今日もまたメダルもろた。
今更ですかと言われそうなヤツだけども。何年越しよ。
#ingress
October 17, 2026 at 12:39 PM
Some stories hook you with action. Others get you because you want to see how things turn out.

A woman with the ability to travel between parallel r...

Watch Ingress on Amazon prime (Affiliate link) 👇🏽
https://www.amazon.com/gp/video/detail/B0F236ZY8R?tag=wtsr0f-20

#Filmsky #MovieSky #Movie #📽️
October 17, 2026 at 11:51 AM
Air to Ground Message:

Water ingress rear exit sign in fligh cc video of and will continue to ncl

Area: Bedford, UK
Type: Boeing 737-800
A: #a4456bfe3dc
F: #f5ff70434d4
October 17, 2026 at 11:25 AM
Air to Ground Message:

WATER INGRESS REAR EXIT SIGN IN FLIGH CC VIDEO OF AND WILL CONTINUE TO NCL

Area: Bedford, UK
Type: Boeing 737-800
A: #a4456bfe3dc
F: #f5ff70434d4
October 17, 2026 at 11:25 AM
📢 New #updates · 17 Oct #7.1

· nginx-ui v2.9.0 — Added Ingress management and MFA enforcement

Browse everything at https://selfhost.directory

#privacymatters #auth #dns #opensource
October 17, 2026 at 8:05 AM
これ程ないあのまり練習はないので(実際やってることは同じなので)、他人がいる前提のingress、を理解するのにはとてもよい機会。

支度はそんなにいりませんが、準備できるならresonatorsを持てるだけ持っていくと楽しめます(XMPは最悪他人が使えばいいから気にしない)。XMPは他人が使えばいい、と思うと、自分のレベルも突っ込むresonatorsのレベルも気にしない。
FW: X IRK_Osaka

10/18(日)は梅田スカイビルに集合!
🎖️メダル獲得🍻交流⚔️ガチバトル

🦢両陣営共催のお約束🕊️
①シールド投入禁止
②引用元記載チャット参加(任意)
③ビーコン投入はチャット参加者のみ
④休戦タイム(攻撃中止)あり

下記からIRK案内所チャット(RES限定)参加もぜひ🐥
https://t.co/MBOoJ6xqjY https://t.co/Ps4tzrkMZJ
Ingressレジスタンス大阪/IRK (@IRK_Osaka) on X
10/18(日)は梅田スカイビルに集合! 🎖️メダル獲得🍻交流⚔️ガチバトル 🦢両陣営共催のお約束🕊️ ①シールド投入禁止 ②引用元記載チャット参加(任意) ③ビーコン投入はチャット参加者のみ ④休戦タイム(攻撃中止)あり 下記からIRK案内所チャット(RES限定)参加もぜひ🐥 https://t.co/MBOoJ6xqjY
x.com
October 17, 2026 at 5:51 AM
Ingressやってましたがその発想はなかったですね🤣
October 17, 2026 at 4:24 AM
鳥取砂丘でIngressやってエラい目に遭ったことがある。歩けなかった。
October 17, 2026 at 4:06 AM
let the hair seas part oh great one over the pit of peculiar smells to allow ingress of past particles of clothing for future reaping 🎶ammmmmmmmmeeeeeeeennnn🎶
October 17, 2026 at 3:26 AM
I'd say LFL; Ingress is a good measure of population density.
October 17, 2026 at 3:12 AM
Fairly, original Ingress had really interesting player incentive structures that encouraged incredible off-line community building in ways that contributed to disaster response and have been studied. That was awesome too.

(Niantic has killed these, but they used to be there)
October 17, 2026 at 2:57 AM
Naturally, from what I've seen, I wouldn't expect a smooth ingress!
October 17, 2026 at 2:45 AM
Routing Webhooks Through Cloudflare Workers KV and Durable Objects: Building a Globally Distributed Ingress Buffer
Inbound webhooks are deceptively hard to handle at scale. Providers such as Stripe, GitHub, and Shopify don’t know about your database connection pool, your autoscaling lag, or a brief microservice outage. They send events when things happen, and a busy period for one of your customers can turn into a burst of requests against your origin. If the origin sits directly behind a load balancer, one of those bursts can exhaust database connections and memory and cause timeouts. Some senders then retry, which adds more load while you are already struggling. One way to protect the origin is an **ingress buffer** at the edge. With **Cloudflare Workers** , **Workers KV** , and **Durable Objects** , you can verify signatures, deduplicate, rate-limit, and durably queue incoming webhooks before a single request reaches your infrastructure. Cloudflare runs Workers in more than 330 cities and says that puts code within roughly 50 ms of about 95% of the world’s Internet-connected population. One caveat up front: only the entrypoint Worker is “everywhere”. Each tenant’s Durable Object is a single globally unique instance that coordinates all requests sent to it. The buffer is therefore distributed across tenants, not replicated for any one tenant. ## Why Webhook Ingestion Fails Each of these failure modes shapes the design below. 1. **Retry behavior differs by provider.** Stripe retries failed live-mode deliveries with exponential backoff for up to three days and disables endpoints that keep failing. Shopify retries 8 times over the following 4 hours. GitHub does not automatically redeliver failed deliveries at all. A short outage can therefore produce a burst of retries from some senders and permanently lost events from others. You can’t assume one retry contract. 2. **Senders give up quickly.** Shopify allows a one-second connection timeout and a five-second timeout for the whole request. If your origin does real work before responding, slow responses are treated as failures and trigger the retries you were trying to avoid. 3. **Duplicates are normal.** Providers deliver at least once. Shopify says its app “might receive the same webhook more than once”, for example after a network timeout or a retry, and recommends deduplicating on the `X-Shopify-Webhook-Id` header. Stripe events carry a stable event ID that stays the same across retries. 4. **One tenant can starve the rest.** A misconfigured integration that fires thousands of events can consume shared processing capacity unless you isolate and limit it per tenant. Acknowledging quickly after a durable write handles the first two, deduplication handles the third, and a per-tenant limiter handles the fourth. ## Architecture [Webhook sender] │ ▼ [Worker: validate tenant, verify HMAC] │ │ │ └──► [Workers KV: cached tenant config, secrets, limits] ▼ [Durable Object (one per tenant, SQLite-backed)] ├─ dedupe by event ID ├─ per-tenant rate limit ├─ durable event queue └─ alarm → batched delivery to origin (or to Cloudflare Queues) * **Worker (entrypoint):** Parses the path, loads tenant config, verifies the signature, and forwards verified events to the tenant’s Durable Object. Anycast routing and Cloudflare’s scheduling typically place it close to the sender. * **Workers KV:** A read-optimized, eventually consistent cache for data that changes rarely, such as tenant config and rate limits. Cloudflare cites sub-5 ms hot read latency. Writes can take 60 seconds or more to be visible in other locations. * **Durable Objects:** Provide per-tenant coordination with a private, transactional SQLite database. Because each object processes one tenant’s events, dedupe checks and rate-limit counters don’t need distributed locks. * **Alarms:** Each object can schedule a single alarm, which lets it drain its buffer in batches without waiting for another incoming request. ## Step 1: The Entrypoint Worker The Worker reads the raw bytes, verifies the HMAC over exactly those bytes, and only then touches the Durable Object. Several details differ from naive implementations: * **Fail closed.** If there is no secret for the tenant, the request is rejected. A shared fallback secret would let anyone who knows it forge events for every tenant. * **Constant-time comparison.** Workers provide `crypto.subtle.timingSafeEqual`. Cloudflare’s documentation notes it is not constant-time with respect to length, which is harmless here because an HMAC-SHA256 digest is always 32 bytes. * **Meaningful dedupe keys.** If the sender provides no event ID, the code falls back to a hash of the body. A random UUID would defeat deduplication, because every retry would look new. * **Minimal forwarding.** Only what the origin needs is passed on, not every header. Forwarding all headers would persist signatures and other sensitive values. // src/index.ts import type { TenantWebhookBuffer } from "./tenant-webhook-buffer"; export interface Env { TENANT_DO: DurableObjectNamespace<TenantWebhookBuffer>; WEBHOOK_KV: KVNamespace; ORIGIN_BASE_URL: string; // e.g. https://api.example.com ORIGIN_TOKEN: string; // set with `wrangler secret put ORIGIN_TOKEN` } interface TenantConfig { active: boolean; rateLimit: number; // max accepted events per 10-second window secrets: string[]; // current + previous secret during rotation } const TENANT_ID = /^[A-Za-z0-9_-]{1,64}$/; const encoder = new TextEncoder(); export default { async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> { if (request.method !== "POST") { return new Response("Method Not Allowed", { status: 405, headers: { Allow: "POST" } }); } // Expected path: /webhook/<tenantId> const [, prefix, tenantId] = new URL(request.url).pathname.split("/"); if (prefix !== "webhook" || !tenantId || !TENANT_ID.test(tenantId)) { return new Response("Not Found", { status: 404 }); } const config = await getTenantConfig(env, ctx, tenantId); if (!config?.active) { return new Response("Unknown tenant", { status: 404 }); } // Verify against the exact bytes the sender signed. const bodyBytes = new Uint8Array(await request.arrayBuffer()); const signature = request.headers.get("X-Signature") ?? ""; let valid = false; for (const secret of config.secrets) { if (await verifyHmacHex(bodyBytes, signature, secret)) valid = true; } if (!valid) { return new Response("Invalid signature", { status: 401 }); } const eventId = request.headers.get("X-Event-ID") ?? (await sha256Hex(bodyBytes)); try { const stub = env.TENANT_DO.getByName(tenantId); const result = await stub.ingestWebhook( { eventId, receivedAt: Date.now(), contentType: request.headers.get("Content-Type") ?? "application/octet-stream", rawBody: new TextDecoder().decode(bodyBytes), // assumes UTF-8 payloads (JSON) }, config.rateLimit, ); return Response.json(result.body, { status: result.status, headers: result.retryAfter ? { "Retry-After": String(result.retryAfter) } : undefined, }); } catch { // The event was NOT durably stored, so ask the sender to retry. return new Response("Temporarily unavailable", { status: 503, headers: { "Retry-After": "30" } }); } }, }; async function verifyHmacHex(body: Uint8Array, headerSig: string, secret: string): Promise<boolean> { const provided = hexToBytes(headerSig); if (!provided) return false; const key = await crypto.subtle.importKey( "raw", encoder.encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"], ); const expected = new Uint8Array(await crypto.subtle.sign("HMAC", key, body)); return provided.length === expected.length && crypto.subtle.timingSafeEqual(provided, expected); } function hexToBytes(hex: string): Uint8Array | null { if (hex.length % 2 !== 0 || !/^[0-9a-fA-F]*$/.test(hex)) return null; const out = new Uint8Array(hex.length / 2); for (let i = 0; i < out.length; i++) out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16); return out; } async function sha256Hex(data: Uint8Array): Promise<string> { const digest = await crypto.subtle.digest("SHA-256", data); return Array.from(new Uint8Array(digest), (b) => b.toString(16).padStart(2, "0")).join(""); } This example assumes a generic sender that sends a hex HMAC-SHA256 of the body in `X-Signature`. Real providers differ, so adapt the verification step: * GitHub sends `sha256=<hex>` in `X-Hub-Signature-256`. * Shopify sends a base64-encoded digest in `X-Shopify-Hmac-SHA256`. * Stripe signs a timestamp plus the payload, which also gives you replay protection. For providers with a stable delivery ID, use it as the dedupe key: `X-Shopify-Webhook-Id` for Shopify, `X-GitHub-Delivery` for GitHub, and the event `id` in the body for Stripe. The Worker calls the Durable Object through Workers RPC. Public methods on a class that extends `DurableObject` are callable through a stub. Arguments and return values must be serializable, so this example returns a plain object and builds the `Response` in the Worker. ## Step 2: Per-Tenant State in a Durable Object Each tenant’s Durable Object holds four things in its SQLite database: * processed event IDs, for deduplication * a short rate-limit window * the pending event queue * a small counter for delivery backoff SQLite-backed Durable Objects are Cloudflare’s recommended storage backend for new classes, and the SQL API is synchronous. Writes made without an intervening `await` are committed together, and an output gate holds the response until the writes are confirmed. The 202 therefore means the event has been durably stored, not just accepted into memory. Keeping deduplication and rate limiting in separate tables matters. Pruning old rate-limit rows from the same table that records processed IDs would erase your dedupe history after seconds. // src/tenant-webhook-buffer.ts import { DurableObject } from "cloudflare:workers"; import type { Env } from "./index"; export interface WebhookEvent { eventId: string; receivedAt: number; contentType: string; rawBody: string; } export interface IngestResult { status: 200 | 202 | 429; body: Record<string, unknown>; retryAfter?: number; } const RATE_WINDOW_MS = 10_000; const DEDUPE_RETENTION_MS = 4 * 24 * 60 * 60 * 1000; // longer than Stripe's 3-day retry window const BATCH_SIZE = 50; const FLUSH_DELAY_MS = 2_000; const MAX_BACKOFF_MS = 5 * 60 * 1000; export class TenantWebhookBuffer extends DurableObject<Env> { constructor(ctx: DurableObjectState, env: Env) { super(ctx, env); ctx.blockConcurrencyWhile(async () => { const sql = ctx.storage.sql; sql.exec(`CREATE TABLE IF NOT EXISTS processed_events ( event_id TEXT PRIMARY KEY, received_at INTEGER NOT NULL)`); sql.exec(`CREATE TABLE IF NOT EXISTS rate_window (received_at INTEGER NOT NULL)`); sql.exec(`CREATE INDEX IF NOT EXISTS idx_rate_window ON rate_window (received_at)`); sql.exec(`CREATE TABLE IF NOT EXISTS event_queue ( id INTEGER PRIMARY KEY AUTOINCREMENT, event_id TEXT NOT NULL, payload TEXT NOT NULL, received_at INTEGER NOT NULL)`); sql.exec(`CREATE TABLE IF NOT EXISTS flush_state ( id INTEGER PRIMARY KEY CHECK (id = 1), failures INTEGER NOT NULL DEFAULT 0)`); sql.exec(`INSERT OR IGNORE INTO flush_state (id, failures) VALUES (1, 0)`); }); } async ingestWebhook(event: WebhookEvent, limit = 100): Promise<IngestResult> { const sql = this.ctx.storage.sql; const now = Date.now(); // 1. Deduplicate. Duplicates are acknowledged with 200 and cost almost nothing. const seen = sql.exec("SELECT 1 FROM processed_events WHERE event_id = ?", event.eventId).toArray(); if (seen.length > 0) { return { status: 200, body: { status: "deduplicated", eventId: event.eventId } }; } // 2. Sliding-window rate limit over accepted events. sql.exec("DELETE FROM rate_window WHERE received_at < ?", now - RATE_WINDOW_MS); const { count } = sql.exec<{ count: number }>("SELECT COUNT(*) AS count FROM rate_window").one(); if (count >= limit) { return { status: 429, body: { status: "rate_limited" }, retryAfter: 10 }; } // 3. Record and enqueue atomically. this.ctx.storage.transactionSync(() => { sql.exec("INSERT INTO processed_events (event_id, received_at) VALUES (?, ?)", event.eventId, now); sql.exec("INSERT INTO rate_window (received_at) VALUES (?)", now); sql.exec( "INSERT INTO event_queue (event_id, payload, received_at) VALUES (?, ?, ?)", event.eventId, JSON.stringify(event), now, ); }); // Schedule a flush only if one isn't pending. Calling setAlarm() again would // overwrite the pending alarm and keep pushing it back under steady traffic. if ((await this.ctx.storage.getAlarm()) === null) { await this.ctx.storage.setAlarm(now + FLUSH_DELAY_MS); } return { status: 202, body: { status: "buffered", eventId: event.eventId } }; } async alarm(): Promise<void> { const sql = this.ctx.storage.sql; // Housekeeping: expire old dedupe records. sql.exec("DELETE FROM processed_events WHERE received_at < ?", Date.now() - DEDUPE_RETENTION_MS); const rows = sql .exec<{ id: number; payload: string }>("SELECT id, payload FROM event_queue ORDER BY id LIMIT ?", BATCH_SIZE) .toArray(); if (rows.length === 0) return; let delivered = false; try { const response = await fetch(`${this.env.ORIGIN_BASE_URL}/internal/webhooks/batch`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${this.env.ORIGIN_TOKEN}`, }, body: JSON.stringify({ events: rows.map((r) => JSON.parse(r.payload)) }), signal: AbortSignal.timeout(10_000), }); delivered = response.ok; } catch { delivered = false; } if (delivered) { sql.exec("DELETE FROM event_queue WHERE id <= ?", rows[rows.length - 1].id); sql.exec("UPDATE flush_state SET failures = 0 WHERE id = 1"); const { n } = sql.exec<{ n: number }>("SELECT COUNT(*) AS n FROM event_queue").one(); if (n > 0) await this.ctx.storage.setAlarm(Date.now() + FLUSH_DELAY_MS); // keep draining } else { const { failures } = sql.exec<{ failures: number }>("SELECT failures FROM flush_state WHERE id = 1").one(); sql.exec("UPDATE flush_state SET failures = ? WHERE id = 1", failures + 1); const delay = Math.min(FLUSH_DELAY_MS * 2 ** failures, MAX_BACKOFF_MS); await this.ctx.storage.setAlarm(Date.now() + delay); } } } A few design points are worth explaining: * **Alarm retries are limited.** Cloudflare retries a throwing `alarm()` handler with exponential backoff starting at 2 seconds, up to 6 retries. After that the alarm is not retried. That is not enough to ride out a long origin outage, so the handler above catches errors and manages its own backoff, capped at five minutes. * **Delivery to the origin is at-least-once.** If the Durable Object is interrupted after the origin accepted a batch but before the rows are deleted, the batch is sent again. Each event carries its `eventId`, so the origin should deduplicate on it. * **A permanently failing batch blocks the head of the queue.** In production, cap per-event attempts and move poison events to a dead-letter table. * **The 429 is a safety valve, not a guarantee.** Providers that retry, such as Stripe and Shopify, will treat a non-2xx as a failure and try again later. Don’t rely on every sender honoring `Retry-After`, and GitHub won’t redeliver automatically. ## Step 3: Workers KV for Tenant Config KV suits data that is read often and changed rarely, such as tenant status, rate limits, and signing secrets. It is eventually consistent. A write is visible immediately in the location that made it, but can take 60 seconds or more to propagate elsewhere, and a missing key can be cached for up to that long too. KV also enforces a minimum TTL of 60 seconds and a maximum of one write per second to the same key. That has practical consequences: * **Use short TTLs.** A 300-second `expirationTtl` is a reasonable default for rules that change occasionally. * **Accept old and new secrets during rotation.** The `secrets` array in the config above does this, because a rotated secret can lag at some edge locations. * **Don’t write on every cache miss.** Concurrent misses for one key would hit the write limit, so the write happens in `ctx.waitUntil` and failures are ignored. * **Don’t cache unknown tenants.** Otherwise anyone can fill your namespace with junk keys by inventing tenant IDs. async function getTenantConfig(env: Env, ctx: ExecutionContext, tenantId: string): Promise<TenantConfig | null> { const key = `config:${tenantId}`; const cached = await env.WEBHOOK_KV.get<TenantConfig>(key, { type: "json", cacheTtl: 60 }); if (cached) return cached; // Cache miss: load from the system of record. Only known tenants are cached. const response = await fetch(`${env.ORIGIN_BASE_URL}/internal/tenants/${tenantId}/webhook-config`, { headers: { Authorization: `Bearer ${env.ORIGIN_TOKEN}` }, signal: AbortSignal.timeout(3_000), }); if (!response.ok) return null; const fresh = (await response.json()) as TenantConfig; ctx.waitUntil( env.WEBHOOK_KV.put(key, JSON.stringify(fresh), { expirationTtl: 300 }).catch(() => {}), ); return fresh; } Treat KV as a cache of data you can reload from your system of record. Never hardcode secrets in source. ## Wrangler Configuration Cloudflare now documents a declarative `exports` field for Durable Object class lifecycle in new Workers. Older projects use the `migrations` array with `new_sqlite_classes`, and a Worker can use only one of the two. Cloudflare recommends the SQLite backend for all new namespaces. // wrangler.jsonc { "name": "webhook-ingress", "main": "src/index.ts", "compatibility_date": "2026-10-01", "vars": { "ORIGIN_BASE_URL": "https://api.example.com" }, "kv_namespaces": [{ "binding": "WEBHOOK_KV", "id": "<KV_NAMESPACE_ID>" }], "durable_objects": { "bindings": [{ "name": "TENANT_DO", "class_name": "TenantWebhookBuffer" }] }, "exports": { "TenantWebhookBuffer": { "type": "durable-object", "storage": "sqlite" } } } If you are on the legacy flow, replace `exports` with `"migrations": [{ "tag": "v1", "new_sqlite_classes": ["TenantWebhookBuffer"] }]`. ## Failure Modes and Mitigations Component | Consistency model | Primary failure mode | Mitigation ---|---|---|--- Worker | Stateless | Uncaught exception or timeout | Wrap the Durable Object call in `try/catch` and return a deliberate 503 so retrying senders try again Workers KV | Eventually consistent | Stale config or secrets for 60 seconds or more | Short TTLs, accept old and new secrets during rotation, don’t cache unknown tenants Durable Object | Strongly consistent per object | Per-object limits: 10 GB storage on Workers Paid (1 GB on Free), soft limit of about 1,000 requests per second | Alert on backlog depth, split very large tenants across several objects, and shard dedupe by event ID Alarm | At-least-once | Handler keeps failing after the built-in retries | Custom backoff with a cap; origin deduplicates by `eventId` Origin | Yours | Outage or slow responses | The buffer absorbs it; drain in controlled batches ### Backpressure and Origin Outages If the origin goes down, each tenant’s Durable Object keeps accepting and storing events up to its storage limit, and the sender sees fast 202 responses. When the origin recovers, the alarm drains the backlog in batches of 50 with a pause between them, which avoids a thundering herd against your database. Buffered events are only safe while there is space. When a SQLite-backed Durable Object hits its storage cap, writes fail. In this design that surfaces as a 503, and providers that retry will redeliver later. Providers that don’t retry, GitHub among them, will have lost the event. Monitor queue depth and age so you can act well before the cap. ## Alternative: Cloudflare Queues If you prefer to hand batches to a managed queue, Cloudflare Queues offers at-least-once delivery, consumer batches of up to 100 messages, and dead-letter queues. A single message is limited to 128 KB, so large webhook bodies need to go to object storage with only a reference queued. A Durable Object buffer, as built above, gives you per-tenant ordering, deduplication, and rate limiting in one place, and Queues can still sit behind it as the delivery mechanism. ## Production Checklist 1. **Fail closed on authentication.** No fallback secrets, constant-time comparison, and provider-specific verification. Check timestamps where the provider signs them. 2. **Deduplicate on the provider’s ID and retain it long enough.** Keep dedupe records for longer than the longest provider retry window. Stripe’s is three days, hence four above. 3. **Make the origin idempotent.** Delivery from the buffer is at-least-once, so the origin must tolerate repeats. 4. **Set limits deliberately.** Choose per-tenant rate limits from real traffic, and plan for tenants that approach a single object’s throughput ceiling. 5. **Add a dead-letter path** for events the origin permanently rejects, so they don’t block a tenant’s queue. 6. **Alert on backlog depth and oldest-event age** per tenant. 7. **Test the failure paths** : origin down, KV stale, duplicate floods, and a rotated secret. ## Further Reading * Durable Objects alarms * Durable Objects limits * Durable Object class exports * Workers RPC * Workers KV: writing key-value pairs * Protecting against timing attacks with timingSafeEqual * Shopify: verifying webhook deliveries * GitHub: handling failed webhook deliveries * Hookdeck: guide to Stripe webhooks _Originally published at https://instawebhook.com/blog/routing-webhooks-through-cloudflare-workers-kv-and-durable-objects-building-a-gl_
dev.to
October 17, 2026 at 2:14 AM
2026.10.13 #日本縦断旅 144日目
白川郷へ!1月の雪景色も最高だったけど、黄金色の稲穂に囲まれた合掌造り同じ場所とは思えない、季節の魔法にかけられた。富山へ移動、TVで見た名水の水風呂のスパ•アルプスへ 冷水が体に染み渡る気分! #サ活
#Ingress #No_Ingress_No_Life
#ハイエースのある生活
October 16, 2026 at 10:57 PM
Evaluating real estate by combining the Little Free Library & Ingress metrics is surprisingly informative.
Lien Twining Novel Ingress
Alt: Lien Twining Novel Ingress
static.klipy.com
October 16, 2026 at 10:42 PM