Skip to main content

Module push

Module push 

Source
Expand description

Push notifications: telling a peer’s webhook that its task moved.

Streaming holds a connection open; push does not. A peer that asked for a task at 09:00 and disconnected wants to know at 14:00 that it finished, and neither polling forever nor holding a socket for five hours is a reasonable way to arrange that.

§The URL comes from the caller, which is the whole problem

Every other outbound destination in this crate is granted by an operator. A webhook URL is supplied by whoever created the task — so push is the one feature where an untrusted party names an address this plane will connect to, with a payload describing somebody’s task.

Three controls, and none of them is sufficient alone:

  • An operator grant. The host must be on an allowlist. A caller may pick any URL under a host the deployment permits, and no host it does not. This is the primary control; the rest are the second lock.
  • Public addresses only. Every destination is checked with netguard before dispatch, and every name the client resolves is checked again as it resolves — so a host that resolves inward, or answers differently the second time, reaches nothing.
  • HTTPS only. The payload describes a task; sending it in clear to an address chosen by the recipient is a disclosure with extra steps.

§What is delivered, and what is not

The payload is a StreamResponse — the same status/artifact union streaming sends, so a receiver parses one thing and A2A’s two delivery mechanisms do not disagree. Registering a destination is therefore authorization to send that task’s output there: task-level policy and the operator host grant are both checked rather than treating the allowlist as sufficient authority.

What that union is called on the wire is the PushMessage’s to say, not this module’s: the same loop also carries an operator’s structured-mode CloudEvents envelope, and one hard-coded media type would make whichever of the two lost a body no conformant receiver routes.

§The journal is the outbox

A registration stores the first journal sequence it has not acknowledged. Workers derive payloads from those records and advance only after HTTP 2xx. A crash after POST but before cursor persistence duplicates an event instead of losing it, which is A2A’s at-least-once contract — and why every delivery carries a HEADER_ID the receiver can recognise a repeat by.

A receiver that answers permanently, or that stays silent past the retry ceiling, has its registration parked rather than deleted. The cursor is the only record of how far that receiver got; discarding it turns a receiver outage into an unrecoverable gap, where keeping it turns the same outage into a list an operator can re-arm.

Structs§

BodySigning
A shared signing key.
DeliveryWorker
Durable outbound delivery, driven by an operator’s scheduler.
Destination
One place the deployment sends its own events.
DueBatch
One namespace’s due registrations, with the other namespace’s backlog counted rather than silently skipped.
Outbox
The operator’s destinations, and the registration that puts a run in front of them.
PushAuthentication
Authentication information from A2A’s push configuration.
PushConfig
Where a peer wants to be told about a task.
PushMessage
One POST’s worth of body: what to send, and what it is.
PushPolicy
Which webhook destinations this deployment permits.
PushRegistration
One durable delivery cursor.
PushSender
Delivers notifications, under the controls in the module docs.
PushSweepReport
Outcome of one bounded delivery sweep.
RunCompleted
One CloudEvents message per completed run.
VerifiedDelivery
A delivery that verified.
WebhookVerifier
The check that BodySigning is one half of.

Enums§

Delivered
What happened to one delivery attempt.
PushError
Why a webhook was not accepted or not delivered.
PushNamespace
Which of the two id namespaces a worker serves.
SigningKeyError
A signing secret this deployment wrote about itself that cannot be used.
WebhookRejected
Why a delivery was not accepted.

Constants§

DEFAULT_TOLERANCE
How far a delivery’s timestamp may sit from now, in either direction.
HEADER_A2A_TOKEN
A2A’s opaque per-task token, echoed on every delivery for the receiver that registered it to validate against.
HEADER_ID
The message’s own identity, and the receiver’s idempotency key.
HEADER_SIGNATURE
v1,<base64> over {id}.{timestamp}.{body} — one element per configured key, space-separated.
HEADER_TIMESTAMP
Unix seconds at which this attempt was made.
MIN_KEY_BYTES
The shortest key this accepts, in bytes.
OPERATOR_PREFIX
The marker that tells an operator destination from a caller’s webhook.
SCHEME
The one signature label this build produces and checks.

Traits§

Projection
What one journal record should be delivered as.
PushStore
Durable storage for webhook registrations.
PushTransport
Delivery transport used by the durable worker.

Functions§

is_operator_id
Whether a registration id names an operator destination.