Standard event contract v1
yovision.event/v1 is the only shared representation of an anonymous safety event. It is an immutable fact, not a Bell Alert. Bell owns all rule matching, Alert, acknowledgement, close, notification and user/audit state.
Identity and idempotency
The permanent idempotency key is the exact UTF-8 pair (producer_id, source_event_id). producer_id always names the original producer. A Sense gateway/relay sends its own authenticated transport identity and optional X-YoVision-Relay-ID, but it must forward both key fields and the business payload unchanged. A retry is not a new event.
After schema validation, calculate payload_sha256 from the RFC 8785 JSON Canonicalization Scheme representation of the complete Event. The checked-in vector fixes the expected digest for supported implementations. Bell stores key, digest and Bell event_id permanently:
- absent key: atomically create Event/Receipt and return
201withdisposition=created; - same key and digest: return the original
event_idand digest with200,disposition=duplicate; - same key but another digest: return
409 idempotency_conflict, append an audit fact, and mutate neither Event nor Alert; - identity lookup and insert must share a transaction/unique constraint so concurrent duplicates have the same result.
Canonical timestamps in Event v1 are UTC RFC 3339 with exactly three fractional digits and Z. Optional members are omitted, never sent as null. Producers must reject non-finite numbers before canonicalization.
Mapper responsibilities
| Role | Required responsibility | Must not do |
|---|---|---|
| Brain producer mapper | Convert brain.internal.event-candidate/v1 into stable original identity, logical site/device/profile/rule/region refs, model version and anonymous observation; generate one source_event_id once and persist/reuse it across retries. |
Expose internal candidate fields, face/person identity, camera credentials, file paths, Alert state, or regenerate identity during retry. |
| Sense producer/evidence mapper | When Sense originates an event, apply the same original-identity rule; map its internal evidence record to a logical evidence reference and own later status resolution. | Put local path, RTSP URL, signed URL, credential or Outbox attempt ID into Event. |
| Sense relay | Authenticate as a transport hop, preserve original producer_id, source_event_id and payload, retain retry/audit state outside the Event, and return Bell's response unchanged enough for deterministic retry handling. |
Replace producer identity, create a new source ID, enrich/reorder semantics, or treat 409/422 as a transient retry. |
| Bell consumer mapper | Validate before persistence; canonicalize; enforce permanent idempotency; map the immutable shared Event into Bell's private Event/Receipt and then independently evaluate rules to create an Alert. Unknown evidence becomes degraded evidence, not a rejected Event. | Persist arbitrary extension fields, import producer internals, or accept shared ack/close/notification/user state. |
Field ownership is deliberately narrow:
| Contract fields | Authoritative writer | Relay/Bell responsibility |
|---|---|---|
schema_version, producer_id, source_event_id |
Original Brain or Sense producer mapper | Relay preserves; Bell uses version gate and permanent idempotency key. |
site_ref, device_ref, profile_ref |
Producer mapper from versioned logical configuration | Relay preserves; Bell treats as opaque external refs. |
event_type, occurred_at, severity, rule, model, observation, region |
Brain/Sense mapper at the detection decision | Relay preserves; Bell validates and stores the immutable snapshot. |
evidence[] identity and initial status |
Evidence-owning producer, normally Sense | Relay preserves; Bell stores the Event snapshot and resolves current metadata separately. |
X-YoVision-Relay-ID |
Authenticated Sense transport hop | Bell audits transport metadata outside the immutable Event. |
event_id, disposition, payload_sha256 |
Bell ingest boundary | Producer/relay retain the receipt for deterministic retries. |
Errors, compatibility and fallback
400 invalid_event: schema, canonical form, or sensitive/unknown member violation. Terminal until the producer fixes the payload.409 idempotency_conflict: same permanent key with a different payload. Terminal and audited; never overwrite the first Event.422 unsupported_schema_version: unknown major/revision. Terminal for that payload.- Evidence
pending,processing,successandfailedare valid Event states. Bell keeps the Event and resolves/degrades evidence independently.
v1 is closed (additionalProperties=false). Producers may enable a compatible revision only after all relays and Bell validate it. Any removed/renamed required field, changed meaning, enum narrowing, identity/canonicalization change, or new required member publishes a new major path such as /v2. During the compatibility window Bell keeps the previous version endpoint. Rollback disables the new producer version and resumes the last accepted version; it does not delete Event, Receipt, Outbox or audit facts.
Unknown-version fallback is explicit: Bell returns 422; relay records the terminal rejection without rewriting the payload; producer may remap the same internal candidate into a supported v1 payload only if it has not previously assigned that (producer_id, source_event_id) to a different canonical payload. Otherwise it must stop and require operator reconciliation.
Reproducible verification
No third-party package is needed:
python contracts/tests/events-v1/test_contract.py
python contracts/tests/evidence-v1/test_contract.py
The tests validate Schema/OpenAPI references, mapper fixtures, RFC 8785-compatible canonical vectors used by v1 examples, duplicate/conflict behavior, unknown versions and sensitive-field rejection.