diff --git a/src/pages/learn/persistent-stable-webhook-endpoint-url.astro b/src/pages/learn/persistent-stable-webhook-endpoint-url.astro new file mode 100644 index 0000000..38288fa --- /dev/null +++ b/src/pages/learn/persistent-stable-webhook-endpoint-url.astro @@ -0,0 +1,150 @@ +--- +import BlogLayout from "../../layouts/BlogLayout.astro"; + +const bodyContent = ` +

How to Set Up a Persistent, Stable Webhook Endpoint URL That Doesn't Change on Restart

+ +
+
+

TL;DR:

+ +
+
+ +

If you are building anything that receives webhooks, you will eventually hit this exact question: how do I set up a persistent, stable webhook endpoint URL that does not change every time I restart? The local server restarts, the tunnel reconnects, the URL is different, and every provider that was POSTing to the old address is now failing silently. This article walks through the options that actually fix the URL — static IPs, pinned-domain tunnels, and agent-native permanent addressing — with the commands to set each up.

+ +

The problem is not exotic: any process that receives inbound HTTP needs an address that stays put, and a surprising number of common setups hand you a new one on every restart. The fix is to move stability into the right layer of your stack — deliberately, instead of discovering it when an integration goes dark.

+ +

Why Your Webhook URL Changes on Every Restart

+ +

A webhook is an HTTP POST from a provider to a URL you own. For that URL to be useful it must satisfy two requirements at once: it has to be routable from the internet, and it has to stay the same. Restarts break the second requirement in three common ways.

+ + + +

Whatever the cause, the consequence is the same: every webhook provider you configured — the payment dashboard, the GitHub app settings, the chat integration — now points at an address that no longer answers, and you get to re-enter the URL by hand. The rest of this article is about making that stop.

+ +

Option 1: Static IP and DNS

+ +

The classic fix: give the machine an address that does not change, then point a DNS name at it. On any major cloud provider you can reserve a static IP and attach it to your instance; restarts — and in many cases even instance replacements — keep the same address. A DNS record maps a hostname you control to that IP, so the URL your providers see is a name rather than a raw address. Put a reverse proxy in front of your app and the endpoint is stable by construction: https://hooks.yourdomain.com/events keeps working no matter how often the process behind it restarts.

+ +

This option works well when you control the infrastructure and the machine has a public presence. It does nothing for a laptop behind a home router, a container behind a corporate firewall, or any machine that has no routable address to pin in the first place. If your webhook receiver runs somewhere without a public IP, a static IP is not available to you — which is most machines running AI agents.

+ +

Option 2: Tunnel Services With a Pinned Domain

+ +

Tunnel services solve the routability problem for machines behind NAT: a process on your machine opens an outbound connection to the tunnel provider, and the provider forwards incoming requests to you over that connection. For webhooks, everything hinges on the URL. Free tiers typically generate a new public URL for every session — acceptable for a quick demo, fatal for a webhook that must stay configured in a provider dashboard. The stable path is a pinned domain: reserve a fixed subdomain on the provider, or route a hostname from a domain you own through the service. Configured that way, the tunnel can disconnect and reconnect across restarts while the URL stays the same.

+ +

These services are legitimate and widely used, and they are the right answer for many setups. Two trade-offs are worth naming. First, a third-party service sits in the path of every request your endpoint receives. Second, the stable-URL options are typically the paid tier — a reasonable price for a lot of people. If you are building agent-to-agent systems rather than integrating a public SaaS provider, there is a third option that does not route through a third-party relay at all.

+ +

Option 3: A Persistent Webhook Endpoint URL That Survives Restarts — Permanent Addressing for Agents

+ +

AI agents change the equation. Agents move between machines, run on laptops behind NAT, get restarted by supervisors, and are expected to keep working unattended. A URL that changes on restart is not an annoyance for them — it is a blocker, because peers and services cannot track an address that moves. Pilot Protocol solves this at the addressing layer rather than the DNS or tunnel layer. Every agent on the network is assigned a 48-bit virtual address (format N:NNNN.HHHH.LLLL) by a registry, and that address is permanent: it survives restarts, IP changes, and moves between clouds. Agents can also register a human-readable hostname with pilotctl set-hostname. When another agent wants to reach you, it uses the address — which does not change — and the overlay handles the rest: encrypted UDP tunnels, automatic NAT traversal (STUN discovery, hole-punching, relay fallback), and a mutual trust handshake before anything flows. You are joining an open network of 243k+ agents and users.

+ +

Getting started is one command:

+ +
curl -fsSL https://pilotprotocol.network/install.sh | sh
+ +

Receive daemon events at a stable local URL

+ +

The most direct answer to "webhook endpoint that does not change" for a Pilot agent: the daemon can POST events to a webhook URL you choose, and delivery is outbound. Nothing needs to reach you from the internet, so there is no public URL to keep stable. Configure it at daemon startup or at runtime:

+ +
# at daemon startup
+pilotctl daemon start --webhook http://localhost:8080/events
+
+# or at runtime — persists to ~/.pilot/config.json and applies immediately
+pilotctl set-webhook http://localhost:8080/events
+
+# clear it when you no longer need it
+pilotctl clear-webhook
+ +

The daemon POSTs a JSON event for things like connections, trust changes, messages received, and pub/sub activity. Delivery is asynchronous and non-blocking: treat the webhook as a fast signal to react to, not a durable message bus — events are retried with bounded backoff, but there is no durable redelivery queue, so pair it with your own persistence if you need a record. The URL is localhost, so it is stable across restarts by definition. Your receiver is a plain HTTP server, and the documented integration patterns are the useful ones: auto-approve handshakes when the justification matches your criteria, dispatch a task when a message arrives, maintain a live connection dashboard, and alert on security events.

+ +

Expose your own HTTP service over the overlay

+ +

The other direction — when peers need to reach an HTTP service you run — is the gateway. The gateway (a separate binary, published as the pilot-protocol/gateway Go library) bridges standard TCP clients to a service on a remote Pilot node. Run a local HTTP server, then start the gateway for the matching port:

+ +
# on the server: run your app, then find your address
+python3 -m http.server 8080 &
+pilotctl info        # Address: 0:0000.0000.xxxx — share this with the peer
+
+# on the peer's machine: trust the server, then bridge the port
+pilotctl handshake 0:0000.0000.xxxx
+sudo pilotctl extras gateway start --ports 8080 0:0000.0000.xxxx
+curl http://10.4.0.1:8080/
+ +

The gateway maps each pilot address to a local loopback address (10.4.0.1 for the first mapping, and so on) and forwards connections through the encrypted overlay. Ports are not translated: the local port must match the remote service's port. Because the overlay handles traversal, there is no port forwarding, VPN, or firewall configuration. The address you share is the permanent one, so the endpoint peers use never changes. For a full walkthrough of running an HTTP server directly on a Pilot virtual port with the Go driver — including curl and fetch() against an agent's service — see HTTP services over the encrypted overlay.

+ +

Which Option Should You Use?

+ +

Match the fix to where your receiver actually runs.

+ + + +

The three are not mutually exclusive. Many agent setups combine them: an overlay address for agent-to-agent traffic, and a pinned-domain tunnel for the one SaaS webhook you must accept. What you should not do is depend on a URL that is regenerated per session — that is a reconfiguration task with a timer on it, and it will eventually fire at the worst moment. If you would rather not run a webhook endpoint at all, Replace Webhooks With Persistent Agent Tunnels makes the case for event streams instead. For the complete event list and the gateway reference, see the webhooks documentation.

+ +

If you want the changing-URL problem to stop recurring, the one-command start is:

+ +
curl -fsSL https://pilotprotocol.network/install.sh | sh
+ +

Frequently Asked Questions

+ +

Why does my webhook URL change every time I restart?

+

Because one of the three things your URL depends on moved: a dev server bound an unpinned port, a cloud instance was assigned a new public IP, or a tunnel tool generated a fresh public URL for the new session. The URL is only stable when the layer that provides it — the port, the IP, or the tunnel mapping — is pinned.

+ +

What is the difference between a static IP and a permanent virtual address?

+

A static IP is infrastructure you reserve and keep attached to a specific machine. A permanent virtual address is assigned to an agent by a registry and stays with that agent regardless of what machine it runs on — restart it, move it to another cloud, and the address is unchanged. Static IPs pin the machine; virtual addresses pin the agent.

+ +

Can I receive webhooks on a laptop behind NAT?

+

Yes, two ways. A tunnel service with a pinned domain gives you a stable public URL that forwards into your laptop. On Pilot Protocol, the daemon delivers its events to a webhook URL you configure, and delivery is outbound — nothing needs to reach your laptop from the internet, so NAT is not a problem.

+ +

Does Pilot Protocol provide a public webhook URL that SaaS providers can reach?

+

No. Pilot Protocol is an overlay network for agent-to-agent communication and daemon-event delivery — it is not a public-ingress service that accepts connections from the open internet. If a public SaaS provider must POST to your endpoint, pair Pilot with a pinned-domain tunnel or a static IP for that specific integration, and keep the overlay for agent traffic.

+ +

What events can the Pilot daemon deliver to a webhook?

+

Daemon lifecycle, connection, tunnel, and security events: connections established and closed, trust changes, messages received, pub/sub activity, key rotation, and more. The webhook configuration accepts a URL at startup or at runtime, and the full event list is in the webhooks documentation.

+`; + +const faqItems = [ + { + question: "Why does my webhook URL change every time I restart?", + answer: "Because one of the three things your URL depends on moved: a dev server bound an unpinned port, a cloud instance was assigned a new public IP, or a tunnel tool generated a fresh public URL for the new session. The URL is only stable when the layer that provides it — the port, the IP, or the tunnel mapping — is pinned.", + }, + { + question: "What is the difference between a static IP and a permanent virtual address?", + answer: "A static IP is infrastructure you reserve and keep attached to a specific machine. A permanent virtual address is assigned to an agent by a registry and stays with that agent regardless of what machine it runs on — restart it, move it to another cloud, and the address is unchanged. Static IPs pin the machine; virtual addresses pin the agent.", + }, + { + question: "Can I receive webhooks on a laptop behind NAT?", + answer: "Yes, two ways. A tunnel service with a pinned domain gives you a stable public URL that forwards into your laptop. On Pilot Protocol, the daemon delivers its events to a webhook URL you configure, and delivery is outbound — nothing needs to reach your laptop from the internet, so NAT is not a problem.", + }, + { + question: "Does Pilot Protocol provide a public webhook URL that SaaS providers can reach?", + answer: "No. Pilot Protocol is an overlay network for agent-to-agent communication and daemon-event delivery — it is not a public-ingress service that accepts connections from the open internet. If a public SaaS provider must POST to your endpoint, pair Pilot with a pinned-domain tunnel or a static IP for that specific integration, and keep the overlay for agent traffic.", + }, + { + question: "What events can the Pilot daemon deliver to a webhook?", + answer: "Daemon lifecycle, connection, tunnel, and security events: connections established and closed, trust changes, messages received, pub/sub activity, key rotation, and more. The webhook configuration accepts a URL at startup or at runtime, and the full event list is in the webhooks documentation.", + }, +]; +--- + + +