After this guide, your chief of staff can 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.
Listen read aloud, 8 min — or as a conversation, 8 min
Read aloud 8 min · narrated
As a conversation 8 min · two people

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 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.

#long-poll · #capture · #security · #networking · #telegram

Hand this to your chief of staff. Plain Markdown: /guides/contributed/no-webhooks-no-cloud.md. Tell it: "Read this guide and tell me what in our system it would change."