Repository navigation
Description
Problem
-
Patterns aren't supported.
hookdeck listen 4000 'deepgram-*'fails with a raw API error:error: name with value deepgram-* fails to match the required pattern: /^[A-z0-9-_]+$/Only the literal
*is special (pkg/listen/source.go#L33). Any other token is sent asGET /sources?name=<token>. -
A running
listennever picks up sources added after it starts, even with'*'. A glob like'deepgram-*'suggests you'll get events from any matching source that's connected to the CLI, including ones created later. Today you don't: the source list is resolved once, at startup.
Current behavior
Observed with the CLI at 98cc142 and API version 2026-09-01.
-
Resolved once:
listenresolves its source argument once at startup.*lists all sources at that moment, and each name is looked up withGET /sources?name=<name>. -
Session holds connection IDs only: it creates its session with:
POST /cli-sessions { "webhook_ids": ["web_a", "web_b"], "filters": { ... } }
That's the connection IDs only, with no record of what was asked for (
'*', names or a pattern). -
Same list on every reconnect: each websocket connect and reconnect sends
X-Webhook-Ids: web_a,web_b. -
New connections aren't routed: a request to a source whose CLI connection isn't in the session's list isn't delivered to the running
listen. The request is stored and verified, but gets an ignored event with causeCLI_DISCONNECTED(GET /requests/{id}/ignored_events). That happens even if the new connection uses the same CLI destination as one the session already serves, and even iflistenwas started with'*'.
To reproduce:
- Create source A and a connection from A to a CLI destination D.
- Run
hookdeck listen 4000 A. - Create source B and a connection from B to D (or to a new CLI destination E).
- Send a request to B. Nothing reaches
listen, and the request is ignored asCLI_DISCONNECTED. - Restart
listen(withA,Bor'*'), and requests to B are delivered.
Use case
The MCP Events bridge gives local agents a public MCP Events webhook endpoint through Event Gateway and the CLI:
-
Setup: each subscription gets its own
MCP_EVENTSsource, because the spec makes the webhook secret per subscription and a source holds one secret. Each source has a connection to the agent's CLI destination. -
The goal: an agent adds subscriptions while running, and we'd like
hookdeck listen 4300 'agent-laptop-*'to keep receiving events for new ones. -
Workaround today: restart
listenwhenever a source is added, then replay the requests that arrived during the restart:POST /requests/{id}/retry { "webhook_ids": ["<connection id>"] }
Proposal
1. Glob patterns for source names
Support glob patterns, e.g. listen 4000 'deepgram-*' or 'deepgram-*,stripe'.
- Why:
- Projects often group sources by a prefix per service or demo (
deepgram-tts,deepgram-stt). - A pattern avoids listing every name and the 10-name limit (
pkg/listen/listen.go#L258).
- Projects often group sources by a prefix per service or demo (
- How:
GET /sourcessupportsname[contains], a case-insensitive substring match. Send the longest literal run asname[contains], then match client-side withpath.Match. - Behavior:
- Treat a pattern as multi-source mode, so
--pathis disallowed. - Error if nothing matches.
- Never auto-create a source from a pattern.
- Treat a pattern as multi-source mode, so
2. Sessions follow newly matching sources
Make the selector part of the session, so new connections that match it are added while listen runs.
-
Session creation carries the selector. The raw source argument and any connection filter are sent alongside the resolved IDs:
POST /cli-sessions { "webhook_ids": ["web_a", "web_b"], "filters": { ... }, "selector": { "sources": ["deepgram-*", "stripe"], "connection": null } }
-
Matching connections join live sessions. When a connection with a CLI destination is created or updated (
POST/PUT /connections), and its source name matches the selector of an active session in the same project:-
The ser 9025 ver adds the connection to that session before responding, so a request sent straight afterwards isn't ignored as
CLI_DISCONNECTED. -
The server pushes a websocket message to the session:
{ "event": "session_updated", "body": { "added": [{ "webhook_id": "web_c", "source": { "id": "src_c", "name": "deepgram-tts" }, "cli_path": "/webhooks" }], "removed": [] } } -
removedcovers connections that are deleted, disabled or changed so they no longer match.
-
-
The CLI applies the update:
- It adds the connection to the list it sends as
X-Webhook-Idson reconnect, so the addition survives reconnects and session expiry. - It prints e.g.
Now also listening on deepgram-tts → http://localhost:4000/webhooks.
- It adds the connection to the list it sends as
-
Scope: only matching sessions.
- The selector is the opt-in:
'*'matches every source, a pattern matches its sources, and an explicit name matches only that source. - Other sessions in the project, such as a teammate's
listenon different sources, are unaffected.
- The selector is the opt-in:
-
Compatibility with older CLIs:
- They log unknown websocket message types at debug level and carry on.
- They forward any attempt they receive.
- They resend their original
X-Webhook-Idson reconnect, so the server should add that list to the session's, not replace it.
Smaller fix, worth doing regardless
Validate source names client-side against ^[A-Za-z0-9_-]+$ and print a helpful error, e.g. Invalid source name "deepgram-*": use letters, digits, - or _ (or '*' for all sources), instead of the raw 422. This also covers typos such as src.a.
Related: #305
Activity
[-]listen: support glob patterns for source names, and a clear error for invalid names[/-][+]listen: glob patterns for source names, and sessions that pick up newly matching sources[/+]leggetter commentedon Oct 7, 2026
Some anonymous, aggregate evidence on how often part 2 (sessions picking up new sources) would matter:
hookdeck gateway connection create/upsert.listenis already running.listensessions our telemetry can see:listenseconds to minutes later" appears a few dozen times across about 16 teams;listenwasn't restarted afterwards. Requests to those connections would have been ignored asCLI_DISCONNECTED.Our visibility into
listensessions has been limited, so we've temporarily added telemetry to measure the create-then-restart pattern more directly. I'll update this issue with what it shows.leggetter commentedon Oct 27, 2026
Update from the temporary telemetry (8–27 Oct):
listenhad started in the previous 2 hours, andlistenwas started again 15 seconds to 60 minutes later. The median gap was 4–6 minutes.listenwasn't restarted within an hour. If it was still running, requests to the new connection were ignored asCLI_DISCONNECTED.listenfor the first time.We've now removed the temporary telemetry.