---
title: No Webhooks, No Cloud: Instant Pickup Over Long-Poll
description: How to get webhook-feel instant capture on a private machine with zero publicly exposed endpoints.
track: contributed
url: https://www.chiefofstaff.io/guides/contributed/no-webhooks-no-cloud
published: 2026-09-10
contributor: Carmen Ashley (owner: Frederic Haddad)
outcome: get sub-second capture pickup with nothing listening on the internet, by running a supervised long-poll listener with a poll timeout, error backoff and a wake-sweep.
source: chiefofstaff.io
---

# No Webhooks, No Cloud: Instant Pickup Over Long-Poll

How to get webhook-feel instant capture on a private machine with zero publicly exposed endpoints.

## The problem

The moment you want captures to be processed *when they happen* rather than *when a cron fires*, the standard answer is a webhook: expose an HTTPS endpoint, give the messaging platform a URL, and let it push. That answer has a hidden price list. A public endpoint means a public hostname, open ports, a resident TLS listener, certificate management, and a secret token that identifies your receiver — all of it sitting on the internet, waiting for scanners. For a personal chief-of-staff system whose entire value is trust in what it holds, that price is not acceptable.

The alternative everyone dismisses is polling, because everyone remembers the naive version: a cron job checking every 10 minutes, with captures sitting up to 10 minutes old. That version is rightly obsolete. The question is whether there is something between "10-minute cron" and "public attack surface." There is: a persistent long-poll listener on the private network. It delivers webhook-feel latency — the message is captured the instant it arrives — with nothing listening on the internet at all.

## The philosophy behind the choice

**The principal's requirement is process-on-send, not process-on-schedule.** Latency is the product. A capture that takes 10 minutes to be seen changes behavior: the principal stops trusting the pipeline and starts keeping a parallel mental list. So latency must be sub-second — but only where the principal is. A webhook optimizes latency at the cost of a public surface; that optimizes the wrong variable.

**"Webhook rejected" was a deliberate decision, not an oversight.** In this vault it is recorded as an architectural decision with a rationale: exposing a public HTTPS endpoint (even via a tunneling service that makes it painless) requires a resident TLS listener and permanent public reachability for zero latency gain over long-poll. Zero gain is the key phrase — long-poll is not "slow polling." It is a request the server holds open until there is data, so the response *is* the event. The network does the waiting.

**The listener is disposable infrastructure.** The design keeps statelessness sacred: messages that arrive while the machine sleeps queue server-side and are swept on wake. The listener holds no critical state, so it can crash, be killed, be restarted by a keepalive supervisor, and nothing is lost. That is what lets you run it on a laptop rather than a server.

**Bounded cost, honestly accounted.** Idle long-poll is one lightweight request roughly every 50 seconds; CPU idles between. The battery cost is real but small, and it is recorded as an accepted tradeoff — philosophy means writing down what you accept, not pretending the option is free.

## How it's wired at a high level

**The listener.** A small daemon runs a single long-poll loop: send a request to the messaging platform's long-poll endpoint with a long timeout; the platform holds the request; when a message arrives, the request returns immediately with that message. Capture it into the inbox, immediately re-open a new long-poll. From the outside this looks like a webhook with no endpoint to attack. The daemon is kept alive by the OS's service supervisor (KeepAlive), so a crash means seconds of downtime, not a missing feature discovered at review time.

**On message.** Capture writes the message to the inbox with provenance. Then the debounce logic from [the triage guide](/guides/contributed/from-capture-to-disposition) takes over: a 15-second quiet window resets on each new arrival; when it lapses, one triage worker runs for the whole burst under a single-flight lock. A burst landing mid-triage waits and runs right after. The listener itself never thinks — it captures and rings the bell.

**Reconnect logic — the honest part.** Long-poll means the connection is a thing you must manage. The loop needs: a timeout on each poll so a silently dead connection doesn't hang forever; error backoff so a platform outage doesn't turn into a tight retry loop (the failure mode that gives long-poll a bad name); and a supervisor that restarts the process if the loop dies outright. None of this is hard, but all of it is required, and a guide that omits it is selling you the happy path.

**Sleep and wake.** When the machine sleeps, the listener pauses; messages accumulate server-side; on wake, the first poll sweeps everything. Nothing is lost, and pickup latency after wake is one request.

**Trade-offs, stated plainly:**
- One resident daemon and a constant small network trickle.
- Everything pauses while the machine sleeps — if you need always-on pickup, the listener must move to an always-on host, which is its own decision with its own review criteria.
- A flapping or crash-looping listener is the defined kill switch: if it ever proves unreliable, you revert to scheduled polling and batching into briefs.

**What webhooks still make sense for:** third-party services that can only push (payment processors, CI systems, anything with an outbound-only integration model), and any case where the receiving system already runs on infrastructure you expose anyway. If you *must* run a webhook, put it behind a mesh VPN or tunnel with source restrictions rather than naked on the internet. The point is not that webhooks are evil; it is that they should be a choice you made, not a default you inherited.

## What a reader's chief of staff should be able to do afterwards

- **Get sub-second capture pickup with nothing listening on the internet**, and articulate why that combination is possible (long-poll is a held request, not a timed check).
- **Implement the loop correctly:** poll timeout, error backoff, supervisor keepalive, and a wake-sweep — and explain which failure each one prevents.
- **Write the decision record:** an explicit "webhook rejected" ADR with the rationale (public endpoint + resident TLS listener + zero latency gain) and a reverse-if clause, so the choice is auditable rather than accidental.
- **State the remaining attack surface:** none public; a private-network daemon that holds no state and can be killed and restarted without data loss.
- **Decide honestly when a webhook is justified**, and know the mitigation (mesh VPN / tunnel with source restriction) when it is.

The end state: the principal sends a message from anywhere, the vault has it before they've put the phone down, and a port scan of their network finds nothing to attack.