← Back to list
01Issue
FeatureShippedSwamp CLIPublic
Assigneeszack
Relationships
#3211 Deliver a signal by key through swamp serve
Opened by zack · 10/9/2026· Shipped 10/9/2026
Problem
Part 3 of 6 of keyed signal waits. swamp-club#3210 lets the local command signal by workflow and key. Over the network a signal still needs a wait ID, and the only way to find one is workflow.waits, which needs read. A device or service that holds a credential granted only signal therefore cannot signal a run it was not told about, which is the case a fixed key exists for.
Proposal
- The WebSocket request
workflow.signalaccepts a workflow and a key in place ofwaitId. An HTTP form does the same.swamp workflow signal --serverpasses the key form. Every transport callsdeliverSignalForCaller(src/serve/signal_delivery.ts) and so the one acceptance use case. - Authorization comes first. The caller needs
signalon the named workflow, and that is decided before any claim, registration or workflow definition is read. By ID the registration says which workflow a wait belongs to; by key the request names it, so nothing has to be looked up to authorize. - An unknown workflow, an undeclared key and a caller who may not signal get the same answer:
not_foundover WebSocket and 404 over HTTP, with one fixed message. The denial is audited with the workflow's name; the reply never carries it. - A declared key with no open wait is answered to an authorized caller with its own refusal (
no_open_wait). A caller who may signal the workflow already learns whether a wait is open from whether a delivery succeeds, so this gives away nothing more. - The reply carries the wait ID and the receipt. The workflow, run, job and step,
awaitingResumeand the resume command are added only for a caller who may alsoreadthe workflow, as today. - The by-key forms pass the same gates as the by-ID route: the rate limits, the payload and body caps,
--restricted-commands, the audit fail-secure check and--signal-requires-explicit-grant. - The audit event
workflow.signal.deliveredcarries the key. The payload is never logged or audited. - Serve resolves a key from claim records only. It does not scan run records, as it does not for a wait ID.
- Continuation after a delivery is unchanged (
continueAfterSignal). - Against a server that predates the key form, the CLI reports that the server needs an upgrade (
requestNewerServerResponse).
Open decisions
- The HTTP route's shape. Workflow names may contain
@and/, so the workflow and key probably travel in the body, not the path. - The HTTP status of
no_open_wait.
Acceptance criteria
- A credential granted only
signalon the workflow delivers by key over HTTP and over WebSocket with no earlier lookup, and the run resumes when the workflow's auto-resume policy allows. - The same credential gets one and the same answer for another workflow's key, an undeclared key and an unknown workflow.
- Each refusal has a documented WebSocket error and HTTP status.
- With
--signal-requires-explicit-grant, arungrant alone does not deliver by key. - Signalling by wait ID through serve is unchanged.
Tests
- Additions to
integration/serve_signal_test.tsfor both transports: delivery, each refusal, the same-answer rule, the reply split between a signal-only caller and a reader. integration/signal_wait_records_rules_test.tsholds the new forms to the one use case.
Depends on
swamp-club#3210 (and through it swamp-club#3209).
References
swamp-club#3094, the Signal paragraphs of design/primitives/serve.md, the signal action in design/enablers/access-control.md.
02Bog Flow
Shipped
Click a lifecycle step above to view its details.
03Sludge Pulse
Sign in to post a ripple.