Sounds the wake-up call for sleeping AI agents

Wake your agents on events,
not polling loops.

Bugler is an open-source event relay. Point GitHub, Stripe, Sentry, CI, any webhook at it. Agents in any harness subscribe with filters and sleep until a matching event arrives. Zero polling, zero tokens while idle, and a queryable event history to catch up from.

Self-hosting works today, free forever. The hosted relay is in early access: join the waitlist, we onboard in small batches.

Condensed from the recorded proof run: real GitHub events on a real repository, timestamps from machine logs.

4 s

from GitHub sending a PR-opened event to a fully stopped agent's written wake summary.

Machine-logged timeline
8 s

from a GitHub comment landing to a parked Codex CLI agent's completed reply, via the in-session MCP wait.

Machine-logged timeline
0

GitHub API calls between events, corroborated by GitHub's own delivery log: push-only traffic, three deliveries, all HTTP 200.

Externally corroborated
0

inference tokens while waiting. No model request is in flight while an agent sleeps; the bill starts at the wake.

By construction, not measured

Latencies measured on real GitHub events with machine-log timestamps. The token figure is structural, true by construction rather than benchmarked: there is simply no request to bill.

01 · How it works

Webhooks in. Filters in the middle. Agents woken at the end.

One relay, three moving parts. Events are stored and delivered as CloudEvents 1.0 envelopes, so every source and every subscriber speaks the same shape.

STEP 1

Point webhooks at it

Verifier presets for GitHub, Stripe, Sentry, GitLab (Standard Webhooks), CircleCI, and generic token sources. A dozen-line bridge covers SQS, Pub/Sub, NATS, or Redis. Poll-only systems get the bundled poll agent: one poll, shared by fifty subscribers.

POST /ingest/github
X-Hub-Signature-256: sha256=9f2c…
→ verified, appended as a
  CloudEvents 1.0 envelope

STEP 2

Subscribe with a filter

CloudEvents filter dialects, exact, prefix, and suffix composed with all, any, and not, evaluated relay-side. A subscriber's entire delivery state is a filter plus one integer bookmark in a shared append-only log. Committing the cursor is the ack.

{ "all": [
  { "exact": { "type":
      "com.github.pull_request" } },
  { "prefix": { "subject":
      "myteam/" } }
] }
state: this filter + cursor #1041

STEP 3

Sleep until it matters

The waker holds one ordinary outbound long-poll, so nothing ever connects inward to your machine. It relaunches your stopped CLI, or resolves an in-session wait_for_event call. The cursor advances only on exit 0; a crash means redelivery, never a lost event.

$ bugler waker -- \
    claude -p "review the new PR"
or, in-session MCP:
    wait_for_event · check_events
exit 0 → cursor commits
crash  → redelivered

Two harnesses, two wake modes, both proven on real GitHub events: Claude Code relaunched from fully stopped, and Codex CLI parked in-session on the bounded MCP wait. The waker daemon can relaunch any CLI; native push adapters cover OpenClaw hooks and Claude Code Routines; a generic signed webhook feeds Temporal, Inngest, Restate, LangGraph, and Cloudflare Workflows through a small bridge.

02 · The economics

Stop paying your agent to ask if anything happened yet.

Polling every 5 minutes is 288 wake-ups a day, almost all of them empty, and every empty one still spins up a model to hear "nothing yet." A subscribed agent wakes only when a matching event arrives, in seconds.

PR opened
review submitted
Polling every 5 minutes 288 wakes a day, almost all empty
Subscribed to Bugler 2 wakes, seconds after each event
One day, two real events: 288 polling wakes versus 2 event-driven wakes A 24-hour timeline. The top track shows 288 evenly spaced gray ticks, one for every 5-minute poll. The bottom track shows just two accent-colored wake marks, at the moments a PR was opened and a review was submitted.
An illustrative day with two real events. Each gray tick is one scheduled 5-minute poll; each accent dot is one event-driven wake. The exact count varies with your day; the asymmetry is structural.

While asleep

No model request in flight, so waiting costs zero inference tokens by construction. No consumer-side API calls to the source either: during the proof run, GitHub's own delivery log showed push-only traffic, and the only standing connection was one outbound long-poll to the relay.

At the wake

The agent receives the matching event and can query the history API to catch up from its cursor. Non-matching events cost an agent nothing, because filters run relay-side. Babysit a PR for hours; spend tokens only when it moves.

03 · Runs anywhere

One relay, eight documented homes, most of them $0.

Five storage drivers ship today: bun:sqlite, pure memory, Cloudflare Durable Object SQLite, DynamoDB, and Cosmos DB NoSQL. All pass one shared conformance suite that defines correctness for every driver, and no storage behavior leaks outside src/storage.

Local single process

bun run src/cli.ts init, then serve. One process, no external dependencies. bun run smoke drives a full signed GitHub flow locally.

Storage SQLite file (WAL, synchronous=FULL)$0, no third party

Docker / Compose

docker compose up --build -d. First start initializes the database and prints a show-once admin key in the service logs; rotate it right away, since container logs persist.

Storage SQLite on one persistent volume$0 on your own machine

Home lab + tunnel

The same binary or container on any spare machine. Webhooks arrive through a free Cloudflare Tunnel or Tailscale Funnel: outbound-only daemons, no open ports.

Storage SQLite on local disk$0, zero billing relationships

GCP e2-micro VM

deploy/gcp/setup-vm.sh cross-compiles a Linux binary, creates one always-free e2-micro, and installs it under systemd. Never touches billing settings.

Storage SQLite on the 30 GB persistent disk$0 always-free (IPv4 ~$3/mo, erasable with a free tunnel)
Driver queued

GCP Cloud Run

The standard container on request-based billing, scale to zero, sub-10 s cold starts. This is the ingest-and-push profile, queued behind the Firestore driver (P5d).

Storage Firestore free tier (driver queued)$0 at low volume within always-free grants

Cloudflare Workers

deploy/cloudflare/setup.sh: one named SQLite Durable Object owns the log, delivery, and a persisted single-alarm scheduler. Hard caps fail rather than bill.

Storage Durable Object SQLite (free plan)$0 within the published free-plan budget

AWS: DynamoDB + your compute

deploy/aws/setup.sh provisions only a DynamoDB table and a least-privilege IAM user; you run the container on compute you already operate. AWS has no always-free home for a long-polling core, and we say so.

Storage DynamoDB single table + one GSIStorage $0 within the always-free allowance

Azure Container Apps + Cosmos

deploy/azure/setup.sh forces an explicit choice: scale-to-zero at exactly $0 idle with documented cold-start risk on ingest, or always-on to keep every GitHub delivery inside its 10-second deadline.

Storage Cosmos DB NoSQL free tier (lifetime)$0 idle, or ~$3 to $5/mo always-on

04 · The gap

Good tools stop exactly where agents start.

An 8-agent research sweep with roughly 235 web fetches found no existing project that combines a permissive license, a single-process zero-dependency footprint, durability, and a harness-neutral agent wake contract. Here is where the nearest ones stop.

smee.io · webhook forwarding

  • Fire-and-forget SSE to currently connected clients only: a disconnected client loses the event.
  • Channels are unauthenticated capability URLs; anyone with the ID can read payloads.
  • No persistence, no filtering, no at-least-once, no wake action, and the maintainers say it is not for production.

Authenticated, durable, filtered, and built around a wake contract.

Hookdeck · event gateway

  • The Event Gateway is not self-hostable.
  • Free tier caps at 10,000 events/month with 3-day retention.
  • The consumer is an HTTP endpoint or CLI tunnel, with no notion of waking a sleeping agent. Hookdeck's own "put Hookdeck in front of OpenClaw" guides confirm demand for exactly the half it does not cover.

Self-host free forever, and the consumer is an agent, not an endpoint.

Svix · webhook platform

  • Platform-scale multi-tenant infrastructure; self-hosting needs Redis plus Postgres.
  • Consumers are HTTP endpoints, not long-poll or SSE subscribers with cursors, and there is no agent-wake concept.
  • The free-to-paid cliff jumps from $0 to roughly $490/month.

A single process with zero dependencies, and a $0 → $12 → $29 ladder instead of a cliff.

Claude Code Routines · vendor trigger

  • Requires a claude.ai subscription and runs only Claude Code, in Anthropic's cloud.
  • Covers two GitHub event categories (pull_request and release); hourly caps drop excess events rather than delivering at-least-once.
  • No local trigger layer, no generic event sources, no subscription-cursor model for an agent that is already awake.

The same trigger idea, generalized across harnesses and event sources, delivered at-least-once.

All four are good at what they aim for; none of them aim at waking agents. Based on each product's public docs and pricing: competitor prices and limits checked 2026-08-23.

05 · Pricing

Self-host free forever. Hosted tiers in early access.

Every hosted limit is volume, never capability: the full engine ships in the open-source build with no feature deltas. The hosted product sells operations, ingress URLs, uptime, and retention storage, not withheld code.

Early access The hosted service is not live yet. These are the prices we intend to charge at launch; joining the waitlist costs nothing and commits you to nothing.
Open source

Self-host

$0 forever

  • Full engine, no feature deltas vs paid tiers
  • Unlimited events, sources, subscriptions, tokens
  • Retention set by you, the operator
  • No phone-home
Read the self-host docs
Early access

Solo

$0/mo

  • 5,000 delivered events/mo
  • 3 sources, 10 active subscriptions
  • 3-day retention, 1 admin token
  • All transports, full filtering, history API
  • Hard stop with gap notification, no surprise billing
Join the waitlist
Early access

Team

$29/mo

  • 250,000 delivered events/mo, then $0.75 per 10,000
  • Unlimited sources and subscriptions
  • 30-day retention
  • Unlimited team tokens + token-level audit log
  • Delivery guarantees for fleets of concurrent agents
Join the waitlist

What counts: a delivered event is a verified webhook matched and delivered to a subscription. Unmatched requests and retries are free.

Why these numbers: outgrowing the free tier costs $12 here, not a $490 cliff. Pro's included volume equals Svix's entire free tier; Team stays under Hookdeck's $39 entry with 25x its included events, and 30-day retention at $29 matches what Svix gives away free. Each step in the ladder is 2.4x or less. Heavy free users are not a problem to solve: they have self-host.

Need SSO, an SLA, or VPC peering? That is the contact-us Scale line above Team. Write to us from the waitlist form below, or email hello@bugler.dev.

Honesty note: hosted tiers are not live yet. This is early-access pricing, published so you can decide before you commit, and subject to refinement before general availability. hello@bugler.dev is the contact address used across this page and is live; if you prefer, open a GitHub issue instead.

06 · Honest engineering

Claims you can audit, limits we say out loud.

a. At-least-once, bounded honestly

Delivery is at-least-once within retention. An agent that stays away past the window gets an explicit gap notification telling it to reconcile, never a silent hole in history. Because GitHub never auto-retries a failed delivery, Bugler acks only after a durable commit, dedupes on GitHub's delivery GUID, and can run GitHub's own list-and-redeliver recovery on a schedule.

b. Verified never means obey

Signature verification is recorded as provenance: verified means authentic, never safe to obey. Payload text is always delivered as labeled untrusted data and never interpolated into instruction fields. The waker scrubs the subscriber token from every spawned handler's environment, so a prompt-injected handler cannot acknowledge events away.

c. Reviewed across vendors, to zero

The design survived five independent review rounds before a line of product code, converging 22 → 19 → 11 → 1 → 0 findings. Every build phase then closed its loop at zero blocking, zero material, zero minor findings: code built under a Claude orchestrator, independently reviewed by GPT-5.6 Sol at high effort. Core tests grew 38 → 89 through those rounds, plus 17 DynamoDB, 14 Cosmos, and 11 workerd tests.

d. Secrets fail closed

Tokens are shown once, carry CRC32 typo checks, are stored only as SHA-256 hashes, and are rejected in query strings. Signing secrets stay env: or file: references resolved at use time and fail closed when missing. Deploy scripts are consent-first: tested --dry-run transcripts, explicit --yes for external changes, secrets via stdin, ShellCheck 0.11.0 at zero findings.

50.004 s. The MCP server exposes exactly two tools: wait_for_event (45 s default, hard-clamped to 50 s to stay under the universal 60 s client timeout floor) and non-blocking check_events. The clamp was measured at 50.004 s under live review probes.
1.7M → 48. Review quantitatively confirmed a Workers fix that took an idle-subscription hot loop from roughly 1.7 million alarm invocations per day to a 30-minute cadence, keeping the free plan's budget math honest. Oversized payloads are rejected with 413 at the HTTP boundary, with caps derived from measured envelope overhead per storage engine.

07 · FAQ

Practical questions, direct answers.

Is the hosted relay live?

Not yet. Hosted Bugler is in early access: you join the waitlist, we onboard in small batches, and the published prices are early-access pricing. None of this gates the open-source relay, which works today and is free forever.

What does zero tokens while idle actually mean?

It is a structural property, not a benchmark: while an agent is stopped, or parked inside a bounded wait_for_event tool call, no model request is in flight, so no inference tokens are consumed. The bill starts only at the wake. We label this "by construction" rather than "measured" because that is the honest description.

Does my machine need an open inbound port?

No. Nothing ever connects inward to your machine. The waker holds one ordinary outbound long-poll, the same pattern GitHub's self-hosted Actions runners use, so it works from a laptop behind NAT, a container, or a locked-down corporate network.

Which agents and harnesses can it wake?

Any CLI the waker daemon can relaunch. Claude Code (wake-from-stopped) and Codex CLI (in-session MCP wait) are proven against real GitHub events. Native push adapters cover OpenClaw hooks and Claude Code Routines, and a generic signed webhook feeds Temporal, Inngest, Restate, LangGraph, and Cloudflare Workflows through a small bridge.

What happens if my agent is away longer than retention?

It receives an explicit gap notification telling it to reconcile, never a silent hole in history. Inside the window, delivery is at-least-once: the cursor advances only when your handler exits zero, so a crashed or failing handler means redelivery, not a lost event, with consecutive failures backing off to a cap.

Is the open-source version feature-limited?

No. There are no feature deltas between self-host and the paid tiers, and every hosted limit is volume, never capability. The hosted product sells operations: uptime, ingress URLs, and retention storage. There is no phone-home in the open-source build.

What about sources that cannot push?

A bundled polling agent turns poll-only systems into events at the relay, so fifty subscribers share one poll instead of running fifty polling loops. On the push side, verifier presets cover GitHub, Stripe, Sentry, GitLab (Standard Webhooks), CircleCI, and generic token sources, and a dozen-line bridge covers SQS, Pub/Sub, NATS, or Redis.

First light for your agents

Let your agents sleep.
That is the point.

Self-host tonight from GitHub, or join the early-access waitlist for the hosted relay: we run it, you sign in and point webhooks at us.

Your mail client should now hold a pre-filled note. Send that email to finish joining. If nothing opened, write to hello@bugler.dev yourself and you are on the list all the same.

This opens a pre-filled email to hello@bugler.dev. One reply when your slot opens, nothing else. Prefer self-hosting? The relay is on GitHub.