Skip to content

ump_spec.events

module ump-spec

CloudEvents 1.0.2 envelopes with UMP extension attributes (spec §19).

Events carry IDs and states only, never data values (claim-check pattern): make_event rejects floats anywhere in data. Delivery is at least once, so consumers deduplicate on (source, id). Ordering is per artifact: the producer stamps artifactseq, an integer that increases strictly for every event about one artifact whatever its type, and a consumer MAY discard an event whose artifactseq is at or below the last one it applied for that artifactid (is_newer). It never discards an event only because its artifactversion (the content revision, which may repeat) was already seen. Events with no artifactid are not ordered and are processed on arrival.

Webhooks follow Standard Webhooks: webhook-id equals the CloudEvents id, the signature covers webhook-id.webhook-timestamp.body and is v1 (HMAC-SHA256) or v1a (Ed25519); several space-delimited signatures may ride one header during key rotation.

function

def make_event(
type: str,
source: str,
*,
artifact_id: str | None = None,
artifact_version: int | None = None,
artifact_seq: int | None = None,
scope: str | None = None,
subject: str | None = None,
data: dict[str, Any] | None = None,
extensions: dict[str, str | int | bool] | None = None,
id: str | None = None,
time_: datetime | None = None,
strict_type: bool = True,
) -> CloudEvent

Build a CloudEvents 1.0.2 envelope. scope is usually client:<id> or global.

artifact_seq is the producer’s per-artifact sequence (needs artifact_id); extensions are vendor attributes under a registered prefix (spec §21).

Source: packages/ump-spec/src/ump_spec/events.py, line 102

function

def is_newer(event: CloudEvent, last_applied_seq: int | None) -> bool

Ordering rule (§19): an event about an artifact is newer when its artifactseq is above the last one applied for that artifactid. Events with no artifactid or no artifactseq are processed on arrival. artifactversion is never used: it may repeat across events.

Source: packages/ump-spec/src/ump_spec/events.py, line 144

function

def webhook_signature(
secret: bytes,
msg_id: str,
timestamp: int,
body: str | bytes,
) -> str

Standard Webhooks v1 HMAC-SHA256 over id.timestamp.body; returns v1,<base64>.

Source: packages/ump-spec/src/ump_spec/events.py, line 164

function

def webhook_signature_v1a(
private_key: Ed25519PrivateKey,
msg_id: str,
timestamp: int,
body: str | bytes,
) -> str

Standard Webhooks v1a Ed25519 over id.timestamp.body; returns v1a,<base64>.

Source: packages/ump-spec/src/ump_spec/events.py, line 170

function

def webhook_headers(
secret: bytes,
msg_id: str,
body: str | bytes,
timestamp: int | None = None,
) -> dict[str, str]

Standard Webhooks headers for one delivery. For a CloudEvent, msg_id MUST be the event id.

Source: packages/ump-spec/src/ump_spec/events.py, line 175

function

def verify_webhook(
secret: bytes | None,
headers: dict[str, str],
body: str | bytes,
now: int | None = None,
tolerance: int = WEBHOOK_TOLERANCE_SECONDS,
public_keys: Iterable[Ed25519PublicKey] = (),
) -> bool

True when the timestamp is within tolerance and any space-delimited signature verifies: a v1 one under secret (HMAC) or a v1a one under any of public_keys (Ed25519).

Source: packages/ump-spec/src/ump_spec/events.py, line 182

NameTypeValueDescription
SPEC_EVENT_TYPESdict[str, str]{'dataset.registered': 'A dataset delivery was registered (G-DATA-01)', 'mode...The sixteen core event types from spec §19.
POC_EVENT_TYPESdict[str, str]{'client.created': 'A client was onboarded', 'artifact.created': 'An artifact...Extra POC event types (lifecycle plumbing the spec leaves to the implementation).
EVENT_TYPESdict[str, str]{**SPEC_EVENT_TYPES, **POC_EVENT_TYPES}
WEBHOOK_TOLERANCE_SECONDS300Replay tolerance, proposed (§19).