diff --git a/cloudevents/extensions/README.md b/cloudevents/extensions/README.md index 0b66ea88d..b22e5b2a5 100644 --- a/cloudevents/extensions/README.md +++ b/cloudevents/extensions/README.md @@ -45,6 +45,7 @@ for more information. - [Data Classification](data-classification.md) - [Dataref (Claim Check Pattern)](dataref.md) - [Deprecation](deprecation.md) +- [Descriptor Reference](descriptors.md) - [Distributed Tracing](distributed-tracing.md) - [Expiry Time](expirytime.md) - [OPC UA](opcua.md) @@ -53,3 +54,4 @@ for more information. - [Sampling](sampledrate.md) - [Sequence](sequence.md) - [Severity](severity.md) +- [Web of Things](wot.md) (Core attribute mapping profile) diff --git a/cloudevents/extensions/descriptors.md b/cloudevents/extensions/descriptors.md new file mode 100644 index 000000000..efe2c36be --- /dev/null +++ b/cloudevents/extensions/descriptors.md @@ -0,0 +1,375 @@ +# Descriptor Reference + +The OPTIONAL `describedby` extension attribute identifies a Descriptor +containing shared information for interpreting an event. A Descriptor can +reference schemas, WoT Thing Descriptions or other +representations, and can contain typed metadata such as a publisher identity. It +describes information relevant to the event, not necessarily only the event's +source or subject. + +This proposal is developed with the Descriptor Registry revision of +[xregistry/spec#796][Descriptor Registry]. That domain defines the metadata-only +Descriptor Resource, its Asset, Actor and Context specializations, named +References, selectors and OPTIONAL bundle. These are drafts, not released +standards. This document defines their CloudEvents binding; it does not require +a Producer to host or query a Registry. + +A Publisher publishes Descriptors and their referenced representations. A +Producer emits events, and a Consumer processes them. A publication profile is +an agreement defining message interpretation and REQUIRED inputs. Producers and +Consumers establish that agreement through a specification or configuration, not +by trusting a profile asserted in untrusted metadata. + +## Notational Conventions + +As with the main [CloudEvents specification](../spec.md), the key words "MUST", +"MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", +"RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as +described in [RFC 2119](https://tools.ietf.org/html/rfc2119). + +The scope of these key words is limited to events using this extension or a +profile that explicitly uses its description-selection rules. + +## Attributes + +### describedby + +- Type: `URI` +- Description: The absolute URL of the Descriptor selected for interpreting + this event. +- Constraints: + - OPTIONAL. + - If present, MUST identify a Descriptor Resource or Descriptor Version + and MUST NOT contain a fragment. + - SHOULD identify a Descriptor Version. Profiles MAY require one. + - Consumers MUST use the value unchanged as the request URL and as the URL + component of the cache key. Cache selection MUST also account for the + selected representation and authorization context. + - Consumers MUST NOT derive other URLs from the value. In particular, they + MUST NOT append `$details`, a view path or query parameter, or remove + `/versions/`. + - An empty string or otherwise invalid set value is invalid, not absent. + +In the JSON event format, `describedby` is a top-level string member. In HTTP +binary content mode, it is the `ce-describedby` header, encoded and decoded +according to the [HTTP binding](../bindings/http-protocol-binding.md). Other +bindings use their normal extension-attribute mapping. + +Consumers MUST apply the event format's decoding rules before testing attribute +presence. In particular, the [JSON +format](../formats/json-format.md#22-type-system-mapping) treats `"describedby": +null` as unset, equivalent to omission. That is not a failed explicit reference +and does not prohibit a profile-defined route for events without `describedby`. +The same presence rule applies to the migration check for simultaneous old and +new attributes. + +The name expresses a descriptive relationship, consistent with the +[`describedby` Web link relation][Link Relations]; it does not define an HTTP +`Link` header or a list of links. + +## Core Attributes and Description Selection + +Core attributes retain their meaning: + +| Attribute | Meaning | +| --- | --- | +| `source` | Identifies the context in which the event occurred. It is not necessarily one physical Producer. | +| `subject` | Identifies what the event concerns within that originating context. It is a String, not inherently a retrieval URL. | +| `dataschema` | Identifies the schema that the event's `data` adheres to. | +| `describedby` | Identifies additional shared information for interpreting the event. It does not replace any of the above. | + +A Producer SHOULD omit `describedby` when the event and agreed profile already +supply all REQUIRED inputs. If only an external payload schema is needed, +`dataschema` supplies that relationship without a Descriptor. + +### Profile-defined source and subject retrieval + +A profile MAY use a Descriptor URL as `source` when that URL also identifies the +originating context. It MAY define a subject that identifies a Thing or other +entity and permits retrieval of its description. Such a profile MUST define the +selected representation, any fragment processing, REQUIRED inputs and failure +behavior. An arbitrary processing metadata URL MUST NOT replace `subject` if it +no longer identifies what the event concerns under that profile. + +Generic Consumers MUST NOT infer retrieval from an HTTPS scheme, a URL path +containing `descriptors`, or the absence of `describedby`. They MUST NOT resolve +arbitrary subject strings against `source`. Core URNs and relative source +references remain valid; a profile needing direct retrieval MUST define an +appropriate retrievable value or use `describedby`. + +The [WoT profile](wot.md#description-selection) defines both Descriptor-relative +subjects and self-contained TD URL subjects. The latter can select a TD without +any Descriptor. This extension does not define a universal syntax for following +references from an entity record into another document. + +### One selected processing description + +| Condition | Processing | +| --- | --- | +| `describedby` is present and valid | Select that Descriptor, whether or not source or subject descriptions are also available. | +| `describedby` is present but invalid, unavailable or incomplete | Report the condition. Do not fall back to source or subject descriptions. | +| `describedby` is absent and the agreed profile defines source or subject retrieval | Use the profile's selected route. | +| `describedby` is absent and no retrieval mapping applies | This extension supplies no description. Do not probe source or subject. | +| `source` and `describedby` have the same URL | One retrieval per representation and authorization context can serve both uses; event identity is unchanged. | + +When present, `describedby` MUST select a Descriptor supplying all Descriptor +inputs REQUIRED by the profile. It need not duplicate information already +supplied by the event. A Consumer MUST NOT merge missing fields from a source or +subject description, including when both descriptions identify the same entity. +The Source Descriptor and Processing Descriptor can describe different entities, +such as an actor and a device; differing entity identities alone are not a +metadata conflict. + +When `describedby` is absent, a profile MUST distinguish its retrieval routes +before access. There is no general "try source, then subject" algorithm. A +failure in the selected route MUST NOT cause an implicit switch to another +route. OPTIONAL descriptive lookups for display do not supply missing processing +inputs. + +For example, a source description might contain PublisherId. If an explicit +Processing Descriptor and the event omit that REQUIRED OPC UA value, the +Consumer reports missing metadata rather than borrowing it from the source +description. Conversely, an unavailable source description does not prevent +processing when the explicit Descriptor and event supply all REQUIRED inputs. + +## Descriptor Representation and Processing + +An ordinary `GET` of the selected Descriptor URL with `Accept: application/json` +returns Descriptor metadata directly. Its `hasdocument` model aspect is `false`; +no `$details` request or separate Descriptor document is needed. + +The [Descriptor Registry][Descriptor Registry] defines these members: + +| Member | Use | +| --- | --- | +| `references` | REQUIRED map of local Reference names to objects containing `class` and `url`. An empty map is valid. | +| `entityid`, `entitytype` | Describe a primary entity when one exists; not replacements for event source identity or authenticated principals. | +| `schemaref` | Selects a named payload-schema Reference. | +| Profile attributes | Typed shared metadata whose meaning and relation to event fields are defined by the applicable profile. | + +Representation classes include `schema`, `thingdescription`, +`thingmodel` and `usdasset`. Vendor classes use URI identifiers. Multiple +References can have the same class. Reference names do not establish emitter, +target, authority or execution roles. + +A Consumer MUST check response status, media type and Descriptor validity +against that domain. A missing or incorrectly typed `references` map is invalid. +An empty map can be valid but insufficient for a profile requiring a TD or +schema. An explicit `schemaref` MUST name an existing Reference of class +`schema`, even when the event supplies `dataschema`. + +For a valid Descriptor, the Consumer retrieves only the representations it +needs, subject to its access and content-trust policies. It MUST use their URLs +unchanged. A Reference's `class` identifies a representation, not a requirement +to retrieve it or permission to execute anything it describes. An unknown class +can be ignored if the profile does not need it; an unsupported REQUIRED input +makes interpretation unavailable. Entity identity and class URIs are not +automatic retrieval instructions. + +### Schema selection + +The Consumer uses the Descriptor Registry's schema-selection rule: an explicit +`schemaref` names one Reference of class `schema`; without that selector, a +unique Reference of class `schema` is selected. Zero candidates means absence; +multiple candidates mean ambiguity, not permission to select the first entry. + +The Consumer selects the first applicable payload schema source: + +1. The event's `dataschema`. +2. The Processing Descriptor's selected `schema` Reference. + +Only absence permits proceeding to the next source. Ambiguity, an invalid +selector, unavailable selected content or an unsupported selected format MUST +NOT cause fallback to a different schema. With no Processing Descriptor, the +second candidate is absent. If neither source supplies a schema REQUIRED by the +profile, the Consumer MUST report that missing input; it MUST NOT derive another +schema from a TD or other unselected representation. + +When `dataschema` is present, otherwise unselected schema inventory is +irrelevant to precedence. Explicit Descriptor selector validity is still +checked. Publishers and Producers SHOULD supply one applicable schema source, or +identify the same schema when several are supplied. Consumers apply the +precedence without comparing schema documents. + +The Producer MUST ensure that the selected schema describes the actual `data` +representation, including any protocol envelope. A TD containing several +DataSchemas is not automatically the payload schema. Consumers MUST NOT use +event-type matching or Reference-name conventions to resolve an ambiguous +schema selection. + +### Failure outcomes + +| Condition | Result | +| --- | --- | +| REQUIRED inputs obtained and selected | Processing context available; this does not itself validate the payload or authorize an operation. | +| Missing, ambiguous, unavailable or unsupported REQUIRED input | Processing context unavailable. | +| Invalid shape, selector or profile value | Invalid metadata. | +| Repeated profile-defined values disagree after REQUIRED decoding | Metadata conflict. | +| Applicable validation completed | Payload valid or invalid, according to that validator. | +| Validation not performed or could not complete | Payload not checked; not a successful validation. | + +These are processing outcomes, not new event attributes or HTTP error codes. The +Consumer SHOULD record a failure. Unless a profile requires an outcome, local +policy determines whether to defer, reject, or retain/forward the event without +that interpretation. It MUST NOT claim successful interpretation or validation +when the REQUIRED inputs are missing. Retaining an uninterpreted event does not +permit bypassing a failed explicit `describedby`. + +## Publication, Identity and Replay + +The Descriptor Registry defines publication and Version stability for all +processing-significant fields, including References, selectors and typed +metadata. A Publisher MUST make the representations REQUIRED by the profile +available before configuring Producers to emit the selected Descriptor URL. A +Producer MUST select information applicable to the event and SHOULD reuse +configured Version URLs when that information is unchanged. Producers are not +REQUIRED to perform runtime validation of each event by this extension. + +A Descriptor Resource URL selects its current default Version, which can change. +Consumers SHOULD bound the freshness of cached Resource responses. Under the +Descriptor Registry's publication rule, the Publisher MUST keep referenced +representations stable for the intended processing and replay period. +Publishers SHOULD pin representation Versions and retain them with the +Descriptor for that period. A Version identifier alone is not evidence that +the Publisher has met the stability requirement. There is no lookup of the +Version intended at event `time`, and an ETag does not reconstruct that history. + +`source` plus `id` retains its Core uniqueness rule. Changing `source`, +including a Version segment or query, changes the identity and source-wide +Sequence scope. Changing only `describedby` does not. Producers MUST NOT reuse +event identities or restart a claimed source-wide sequence when returning to an +earlier source. + +Changing `subject` can change subscription filtering and correlation; it does +not change the source-plus-id identity scope. A profile using a versioned +description URL as subject MUST account for that coupling. A stable subject with +an independently versioned `describedby` avoids it. + +## OPTIONAL Descriptor Bundle + +A supporting endpoint can return the Descriptor and its direct referenced +representations at the same URL: + +```http +Accept: application/vnd.xregistry.descriptor-bundle+json, application/json;q=0.5 +``` + +The Consumer MUST inspect `Content-Type`. Ordinary `application/json` returns +metadata; the bundle media type returns `descriptor` and `documents` as defined +by the [Descriptor Registry][Descriptor Registry]. Each document entry uses the +same Reference name and records its exact URL, content type, content or base64 +bytes, and redirect base URL when applicable. + +The bundle covers each direct `references.*.url` or returns an error. Additional +representations to be included MUST be listed as named References. An empty +Reference map has an empty document map. Relative dependencies use the +referenced representation's recorded retrieval base, not the Descriptor's URL. +Stable published inputs do not make the bundle an atomic snapshot across +Registries. + +This behavior is additional to Core metadata serving. Content negotiation alone +does not collect referenced URLs. A Publisher can prebuild a bundle for a +suitably configured endpoint, but the same completeness, access, freshness and +representation requirements apply. + +A Consumer MAY retry ordinary JSON at the same unchanged Descriptor URL after a +bundle error and retrieve only its REQUIRED representations. This is a +representation retry, not fallback to another Descriptor or source. Consumers +and caches MUST distinguish negotiated representations and authorization +contexts, following the bundle's `Vary` and freshness rules. + +## Examples + +### Actor and processing description + +An application profile reports an actor's maintenance decision about Pump 7. It +defines `pump7` as the subject identifier within that actor's context; this +generic example does not assert the WoT or OPC UA profile. + +```json +{ + "specversion": "1.0", + "id": "decision-47", + "source": "https://registry.example/descriptorgroups/actors/descriptors/planner7", + "subject": "pump7", + "type": "com.example.maintenance.decision.v1", + "describedby": "https://registry.example/descriptorgroups/streams/descriptors/pump7-maintenance/versions/1", + "datacontenttype": "application/json", + "data": { + "inspectionNeeded": true + } +} +``` + +The Processing Descriptor contains the following metadata; Core bookkeeping is +omitted. Its schema describes the decision payload, not the TD document. + +```json +{ + "descriptorid": "pump7-maintenance", + "versionid": "1", + "schemaref": "payload", + "references": { + "pump7": { + "class": "thingdescription", + "url": "https://registry.example/thingdescriptiongroups/factory/thingdescriptions/pump7/versions/3" + }, + "payload": { + "class": "schema", + "url": "https://schemas.example/maintenance-decision/versions/1" + } + } +} +``` + +The actor's independently retrieved description can have `references: {}` and +actor identity metadata. It is not merged with the Processing Descriptor. +Neither description is an authorization to inspect or operate the pump. + +### Without an additional link + +For the same application profile, if the event and an explicit `dataschema` +already supply every REQUIRED input, no `describedby` is needed. A profile can +also select a sufficient Descriptor through `source`, or retrieve a subject +description through a defined mapping. Neither case is inferred by a generic +Consumer. + +The [WoT examples](wot.md#examples) show a concrete direct TD subject without a +Descriptor and a source-selected Descriptor. The [OPC UA +mapping](opcua.md#shared-stream-metadata-in-a-descriptor) defines when a +selected Descriptor Version supplies native stream metadata. + +## Migration from Event Context + +This revision replaces the draft `contexturl` extension and Role-keyed Context +representation. Existing events retain their original attribute and profile +semantics; Consumers MUST NOT silently reinterpret them as the new binding. +Deployments retaining such events need the old reader or an explicit conversion +policy. A profile using this revision MUST reject an event with both +`contexturl` and `describedby` unless a separate migration agreement defines +that combination. + +Do not rewrite historical sources merely to adopt registry-based identity. Names +discussed only during design, such as `contracturl`, `refset` and `contextref`, +are not aliases defined by this extension. + +## Security Considerations + +Consumers and bundle endpoints MUST apply authorization, URL, redirect and +content-trust policies before retrieving or using Descriptors or their +representations. An event-provided URL MUST NOT authorize arbitrary network +access or credential forwarding. Implementations SHOULD bound response sizes, +decompression, retrieval time, redirects and dependency processing. + +Read access to a Descriptor does not grant access to its targets. Caches MUST +NOT share representations across authorization contexts unless the applicable +access policy permits it. A bundle does not bypass trust checks. Versions do not +authenticate content or prove unchanged bytes. + +Descriptive entity IDs, TD security declarations and references to actions are +not authenticated principals, execution grants or current readiness. Device +admission and operation outcomes remain governed by their own protocols and +trusted authorities. + +[Descriptor Registry]: https://github.com/xregistry/spec/pull/796 +[Link Relations]: https://www.iana.org/assignments/link-relations/ diff --git a/cloudevents/extensions/opcua.md b/cloudevents/extensions/opcua.md index 95954fb23..afbb8db9b 100644 --- a/cloudevents/extensions/opcua.md +++ b/cloudevents/extensions/opcua.md @@ -1,12 +1,22 @@ # OPC UA -This extension defines the mapping of [OPC UA](https://reference.opcfoundation.org/Core/Part1/v105/docs/) -[PubSub](https://reference.opcfoundation.org/Core/Part14/v105/docs/) dataset to -CloudEvents to allow seamless routing of OPC UA dataset messages via different -protocols, it therefore provides a recommendation to map known REQUIRED and -OPTIONAL attributes using other extensions as well as defines its own extension +This extension maps [OPC UA](https://reference.opcfoundation.org/Core/Part1/v105/docs/) +[PubSub](https://reference.opcfoundation.org/Core/Part14/v105/docs/) +DataSetMessages to CloudEvents for routing over supported protocols. It defines +mappings for Core and other extension attributes and specifies OPC UA extension attributes. +When used with the [Descriptor Reference extension](descriptors.md) and the +xRegistry Descriptor model, metadata that stays constant for a message stream is +preferably stored in the selected Descriptor rather than repeated in each event. +The existing OPC UA attributes remain defined and usable. A Producer MUST supply +publisher and writer metadata REQUIRED by this mapping in the `source` query +when neither the full Part 14 NetworkMessage envelope nor the selected +Descriptor supplies it. The [shared metadata +rules](#shared-stream-metadata-in-a-descriptor) describe how a Consumer obtains +these values. A direct WoT TD subject can identify its description without a +Descriptor, but does not supply these native identities. + ## Notational Conventions As with the main [CloudEvents specification](../spec.md), the key words "MUST", @@ -29,15 +39,54 @@ field `MessageId`. ### source -MUST either map to [Application +An OPC UA Producer MAY identify the server using its [Application Description](https://reference.opcfoundation.org/Core/Part4/v104/docs/7.1) field -`applicationUri` of the OPC UA server or to a customer configured identifier -like a unified namespace path. +`applicationUri` or a customer-configured identifier such as a unified namespace +path. For data and event messages, a Producer MUST include each publisher or +writer query parameter REQUIRED by the mapping unless the full OPC UA Part 14 +NetworkMessage envelope or the Descriptor supplies its corresponding value. ### type -MUST map to [Data Set Message Header](https://reference.opcfoundation.org/Core/Part14/v105/docs/7.2.5.4#Table164) -field `MessageType`. +An OPC UA Producer MAY map `type` to the +[Data Set Message Header](https://reference.opcfoundation.org/Core/Part14/v105/docs/7.2.5.4#Table164) +field `MessageType`. A Producer using the WoT profile MUST follow its semantic +event-type rules. When the `type` identifies a WoT event contract, a Producer +MUST carry the native `MessageType` as `opcuamessagetype` when `data` omits +that field from the DataSetMessage header. When a retained NetworkMessage +envelope includes the field, the Producer MUST NOT repeat it as an event +attribute. The [WoT mapping section](#wot-mapping) specifies the combined +mapping. + +### WoT mapping + +When a CloudEvent uses both this extension and the +[WoT profile](./wot.md), the following mappings apply to data and event +messages: + +- `type` MUST follow the WoT profile's semantic event-type rules instead of + mapping to the native `MessageType`. +- `subject` MUST follow the WoT profile's Descriptor-relative or direct-TD + subject rules. +- The native PublisherId and at least one DataSetWriter identifier MUST be + available from the full Part 14 NetworkMessage envelope, `source`, or the + selected Descriptor, as defined in [Publisher and Writer + Metadata](#publisher-and-writer-metadata). +- Nothing MUST be appended to `subject`. For an event message, the `EventId` + remains in the OPC UA event payload. +- `opcuasequence` MUST carry the native `SequenceNumber`. The CloudEvents + `sequence` attribute, when present, MUST satisfy the independent ordering + requirements of the Sequence extension and MUST NOT be inferred from + `opcuasequence`. + +The [publisher and writer metadata rules](#publisher-and-writer-metadata) +define how OPC UA publisher and writer identifiers are supplied. The Core +attribute mappings above remain unchanged. + +These conditional mappings replace only the conflicting OPC UA mappings above. +All other requirements in this extension remain applicable. A profile that +uses only part of this extension MUST NOT claim conformance to it unless the +profile satisfies every applicable requirement in this document. ## Mapping of OPTIONAL Attributes @@ -55,15 +104,25 @@ separate format like [JSON schema](https://json-schema.org/specification) or [Avro schema](https://avro.apache.org/docs/1.11.1/specification/) or others. For those cases the attribute references the schema and is used for versioning. +When the Descriptor Reference selection rules are used, a Producer MAY select a +Descriptor whose selected schema Reference supplies this schema +instead. The Consumer follows that extension's [schema +precedence](descriptors.md#schema-selection), including its selector +and ambiguity rules. The Producer SHOULD omit `dataschema` when the selected +Descriptor supplies the schema. The selected schema MUST describe the actual +`data`, including the full NetworkMessage envelope when one is retained. + ### subject -For metadata, event and data messages (type one of `ua-metadata`, `ua-keyframe`, -`ua-deltaframe`, `ua-event`), `subject` MUST map to either [Data Set Message -Header](https://reference.opcfoundation.org/Core/Part14/v105/docs/7.2.5.4#Table164) -field `DataSetWriterId` or `DataSetWriterName`. +For metadata, data, and event messages, a Producer MAY map `subject` to +`DataSetWriterId` or `DataSetWriterName` from the [Data Set Message +Header](https://reference.opcfoundation.org/Core/Part14/v105/docs/7.2.5.4#Table164). +A Producer using the WoT profile MUST instead follow its Descriptor-relative or +direct-TD subject mapping. -For event messages (type equals to `ua-event`) `subject` MUST be appended with -"/" and [Base Event Type](https://reference.opcfoundation.org/Core/Part5/v104/docs/6.4.2) +For an OPC UA event message (`type` equals `ua-event`), a Producer using the +OPC UA-specific `subject` mapping MUST append "/" and the +[Base Event Type](https://reference.opcfoundation.org/Core/Part5/v104/docs/6.4.2) field `EventId`. ### time @@ -74,14 +133,35 @@ field `Timestamp`. ## Mapping for other extensions -The following well-known extensions attributes MUST be used for data messages and -event messages (type one of `ua-keyframe`, `ua-deltaframe`, `ua-event`). +The following well-known extension attributes apply to data and event +messages. When the WoT profile is used, determine the native message type +from `opcuamessagetype` when present or from the retained NetworkMessage's +DataSetMessage header when it contains `MessageType`. ### sequence -Attribute as defined by [sequence extensions](./sequence.md) MUST map to [Data -Set Message Header](https://reference.opcfoundation.org/Core/Part14/v105/docs/7.2.5.4#Table164) -field `SequenceNumber`. +Without the WoT profile, a `sequence` attribute, when present, MUST map to +the native DataSetMessage `SequenceNumber`. The OPC UA Publisher assigns and +increments this counter for messages from a DataSetWriter, as defined in +[OPC UA Part 14, Section 7.2.3][UA SequenceNumber]. This is the existing +OPC UA mapping for the [Sequence extension](./sequence.md), but it does not by +itself establish an ordering across multiple DataSetWriters that share one +CloudEvents `source`. A Publisher using this mapping MUST scope `source` to the +DataSetWriter's ordering scope; otherwise, it MUST NOT present the per-writer +counter as the CloudEvents `sequence`. + +The Sequence extension compares String values lexicographically. The Producer +MUST format the mapped counter so that string comparison preserves the event +order; padding the unsigned decimal value to ten digits is one way to do so. +A reset or rollover can reverse that order within the same source scope. The +Producer MUST NOT use the counter for `sequence` when it cannot preserve the +ordering defined by the Sequence extension. + +When the WoT profile is also used, `subject` identifies a TD or affordance, not +the DataSetWriter. `opcuasequence` therefore carries the native OPC UA counter; +a CloudEvents `sequence`, if present, MUST satisfy the Sequence extension's +ordering requirements independently and MUST NOT be inferred from +`opcuasequence`. ### traceparent @@ -118,6 +198,15 @@ field `MetaDataVersion`. - Constraints - OPTIONAL but MUST NOT be present if `dataschema` is used +Both metadata-version event attributes use the CloudEvents `Integer` type. +Their native [VersionTime][UA VersionTime] values are unsigned, so the +representable event-attribute range is `0` to `2147483647`, inclusive. +A Producer encoding a native value in either event attribute MUST preserve +that value exactly. If it exceeds this range, the Producer MUST report an +encoding error rather than truncate, wrap or reinterpret it as a negative +value. The full native range can be carried in the selected Descriptor +metadata described below, or in an applicable native payload representation. + ### opcuastatus - Type: `Integer` @@ -128,18 +217,287 @@ field `MetaDataVersion`. - REQUIRED if status is not _Good_ - MAY be omitted if status is _Good_ +CloudEvents `Integer` is limited to the signed 32-bit range. If a native +status value cannot be represented, the Producer MUST report an error rather +than omit the status, wrap the value, or report it as _Good_. + +### opcuamessagetype + +- Type: `String` +- Description: Carries the OPC UA PubSub DataSetMessage type when the + DataSetMessage header is omitted from `data`. +- Constraints + - OPTIONAL + - In combination with the WoT profile, the value is REQUIRED for data and + event messages when `data` omits the DataSetMessage header. + - MUST NOT be present when `data` retains a full NetworkMessage envelope + that includes the DataSetMessage `MessageType`. + - MUST map to the Data Set Message Header `MessageType`. + - The value MUST be the native OPC UA message type, such as `ua-keyframe`, + `ua-deltaframe` or `ua-event`. + +### opcuasequence + +- Type: `String` +- Description: Carries the native DataSetMessage sequence counter without + assigning CloudEvents Sequence extension ordering semantics to it. +- Constraints + - OPTIONAL + - If present, MUST be a canonical unsigned decimal representation in the + range `0` to `4294967295`, inclusive, with no leading zero except for `0`. + - In combination with the WoT profile, REQUIRED for keyframe, deltaframe + and event messages. + - MUST map to the Data Set Message Header `SequenceNumber`. The + [OPC UA JSON mapping][UA DataSetWriterName] defines this field as `UInt32`; + the String representation retains the full range of that value. + - MUST NOT be interpreted as a source-wide sequence unless it also + independently satisfies the Sequence extension's requirements. + +## Publisher and Writer Metadata + +The following values identify the OPC UA Publisher and DataSetWriter. They are +not CloudEvents extension attributes. A Producer MUST supply each REQUIRED value +as a query parameter in `source` or an extension attribute of the applicable +Descriptor Version unless the full Part 14 NetworkMessage envelope supplies it. +The names are the same in the query and Descriptor representations. + +| Name | Value | +| --- | --- | +| `opcuapublisherid` | The native PublisherId, represented as a string. | +| `opcuadatasetwriterid` | The native DataSetWriterId, represented as an integer. | +| `opcuadatasetwritername` | The configured DataSetWriterName, represented as a string. | + +In a full [Part 14 NetworkMessage][UA NetworkMessage], the Consumer obtains the +PublisherId from the NetworkMessage header and the DataSetWriterId or +DataSetWriterName from its single DataSetMessage header. Those fields retain +their native names and encodings. The following representation rules apply to +their equivalents in `source` and the Descriptor. + +`opcuapublisherid` MUST use `:`. The type MUST be `Byte`, +`UInt16`, `UInt32`, `UInt64`, or `String`, as defined for the native +[PublisherId][UA PublisherId]. For an unsigned integer type, the value MUST be +the nonzero unsigned decimal representation within that type's range, without +a sign or leading zeros. For `String`, the value MUST be the unpadded base64url +encoding of the non-empty PublisherId's UTF-8 bytes. If the message encoding +does not preserve the PublisherId's type, the Producer obtains it from +configuration. If the type cannot be determined, the Producer MUST report an +error rather than assign one. + +`opcuadatasetwriterid` MUST map to the native DataSetWriterId. It MUST be in +the range 1 to 65535, inclusive; [OPC UA reserves zero][UA DataSetWriterId] +as a null value. +`opcuadatasetwritername` MUST be non-empty and match the DataSetWriter's +configured name. For JSON message mapping, this is the +[`DataSetWriterName` field][UA DataSetWriterName] when present in the +DataSetMessage; otherwise the Producer obtains it from configuration. + +The native PublisherId and at least one of DataSetWriterId and DataSetWriterName +are REQUIRED for all data and event messages, whether or not the WoT profile is +used. Each value MUST be supplied by the envelope or by the corresponding query +parameter or Descriptor attribute. A full envelope that omits an identifier does +not satisfy that identifier's presence requirement. If both writer values are +supplied, they MUST identify the same DataSetWriter. A writer name MUST uniquely +identify the DataSetWriter within the PublisherId scope. Consumers MUST compare +names exactly, without case folding or Unicode normalization. + +When an identity is supplied in more than one place, the representations MUST +identify the same native Publisher or DataSetWriter. To compare a query or +Descriptor value with an envelope field, the Consumer MUST decode that value and +apply the field's native Part 14 encoding. For example, `UInt16:12` is encoded +as `"12"` in a JSON NetworkMessage. The Consumer MUST report a metadata conflict +and MUST NOT interpret the payload as a conforming OPC UA message if the values +disagree. + +A Consumer MUST report invalid metadata and MUST NOT interpret the payload as +a conforming OPC UA message if a supplied publisher or writer value violates +these representation or range constraints. + +### Source query syntax + +When these values are supplied in `source`, the query parameters carry +stream-constant metadata. The Producer SHOULD omit a query parameter when the +full OPC UA Part 14 NetworkMessage envelope or the selected Descriptor supplies +its value. For each REQUIRED publisher or writer value that neither supplies, +the Producer MUST include the corresponding parameter in `source`. + +The Producer MUST use `name=value` for each of these parameters, with a +non-empty value. It MUST separate them from each other and from preceding +query content with `;`, and place the query before any URI fragment. The three +names are case-sensitive and MUST appear literally, at most once each. Other +parameters MAY appear in any order; a Consumer MUST NOT reject the source +solely because it contains parameters not defined by this extension. + +For these parameter values, the unescaped characters are the [RFC 3986][URI] +`unreserved` characters and `!`, `$`, `'`, `(`, `)`, `*`, `,`, `:`, `@`, +`/`, and `?`. A Producer MUST leave these characters unescaped and +percent-encode the UTF-8 bytes of all other characters using uppercase +hexadecimal digits. In particular, a literal `%`, `;`, `=`, `&`, `+`, or `#` +MUST be encoded. A Producer MUST NOT use form-query encoding or encode a space +as `+`. + +A Consumer MUST split the query at `;` before decoding these values. It MUST +reject an empty or duplicate recognized parameter, an invalid percent escape, +invalid UTF-8, or an unescaped character outside the permitted set. It MUST +percent-decode each value exactly once and MUST NOT interpret `+` as a space. +The writer ID uses unsigned decimal notation without a sign or leading zeros in +the query and an integer in the Descriptor; the other two values use strings in +both places. + +For example, the following source supplies the PublisherId and writer name when +neither the envelope nor the Descriptor supplies them. The writer name is +`Pump&Process`. + +```text +mqtt://site/line/cell/device/sensor?opcuapublisherid=UInt16:12;opcuadatasetwritername=Pump%26Process;site=north +``` + +Changing `source`, including its query parameters, changes the CloudEvents +event-identity and [Sequence extension](sequence.md) scope. A Producer MUST +NOT reuse event identities or restart a claimed source-wide sequence when it +returns to a previously used `source`. + ## General Constraints - OPC UA messages MUST use `binary-mode` of CloudEvents. - OPC UA PubSub JSON messages MUST be encoded using non-reversible encoding as the decoding information is contained in metadata messages or by schema -referenced via `dataschema` attribute. -- Payload of OPC UA PubSub JSON messages MUST NOT contain Network Message Header -and Data Set Header as that information is mapped into CloudEvents attributes. -- OPC UA PubSub JSON messages MUST contain exactly one dataset message. +referenced via `dataschema` attribute or selected through a Descriptor. +- A Producer MAY retain the full Part 14 NetworkMessage envelope in `data`. + Otherwise, the payload MUST omit the NetworkMessage and DataSetMessage + headers and supply their applicable information through the mappings in + this extension. +- OPC UA PubSub JSON messages MUST contain exactly one DataSetMessage, + including when a full NetworkMessage envelope is retained. + +A retained envelope does not replace the REQUIRED CloudEvents attributes or +the per-event `opcuasequence` attribute when the WoT mapping applies. +`opcuamessagetype` is REQUIRED only when `data` omits the DataSetMessage +header; when the envelope includes that field, the attribute MUST be absent. +The NetworkMessage's `ua-data` type is not the DataSetMessage type. Any +duplicated native values MUST agree after applying the specified mappings. +The Consumer MUST report a conflict and MUST NOT interpret a message as +conforming to this extension when they disagree. + +## Shared Stream Metadata in a Descriptor + +A message stream here is a configured flow from one Publisher and +DataSetWriter with a defined payload contract. It is not necessarily a broker +topic or a CloudEvents `source`; either can carry several streams. + +When Producers and Consumers agree to use this extension with the [Descriptor +Reference selection rules](descriptors.md#one-selected-processing-description), +the Publisher SHOULD store metadata that is constant for the stream as +Descriptor Version extension attributes. The agreed binding selects the +Descriptor through `describedby` or a profile-defined source or subject mapping. +Storing stream-constant values once avoids repeating them in event attributes or +the `source` query. The Descriptor has no document body: these values are +members of its metadata object, alongside `references`. + +The Publisher MAY store the following values in the Descriptor when they remain +constant for every event using that Version. When a Descriptor supplies any of +these values, its selected URL MUST identify a Descriptor Version, whether +selected explicitly or through a profile-defined mapping. A Resource URL can +select a different default Version when a delayed or replayed event is +processed. + +| Descriptor attribute | Meaning and conditions | +| --- | --- | +| `opcuapublisherid` | The stream's native PublisherId. | +| `opcuadatasetwriterid` | The stream's native DataSetWriterId, when used. | +| `opcuadatasetwritername` | The stream's configured DataSetWriterName, when used. | +| `opcuadatasetname` | The non-empty `Name` of the applicable DataSet metadata; descriptive, not a writer identifier. | +| `opcuametadatamajorversion` | The native MajorVersion as a JSON integer in `0` to `4294967295`, when no payload schema is selected. | +| `opcuametadataminorversion` | The native MinorVersion as a JSON integer in `0` to `4294967295`, when no payload schema is selected. | + +Publisher and writer members MUST use the constraints and mappings defined +in [Publisher and Writer Metadata](#publisher-and-writer-metadata). For +example, a Descriptor's `opcuadatasetwriterid` is a JSON integer, not a string. +The Descriptor metadata-version members MUST preserve the full native +`VersionTime` (`UInt32`) range, `0` to `4294967295`, inclusive. These are +Descriptor members, not CloudEvents `Integer` attributes. Consumers MUST +compare duplicate event and Descriptor values without narrowing either value. +Out-of-range Descriptor values are invalid metadata. + +The Descriptor's selected schema Reference identifies an external payload +schema. Native metadata-version attributes MUST NOT be supplied in the event +or Descriptor when a schema is selected through `dataschema` or the Descriptor. + +`opcuadatasetname` is an OPTIONAL Descriptor attribute, not a new CloudEvents +attribute or source query parameter. If supplied, it MUST be a non-empty String +equal to `DataSetMetaDataType.Name` in the applicable [DataSet metadata][UA +DataSet Metadata], obtained from that metadata or its approved configuration. A +Producer MUST NOT infer it from a writer name, topic or TD affordance name. It +need not be unique and MUST NOT substitute for a REQUIRED PublisherId or +DataSetWriter identity. Repeated dataset-name representations, when the profile +supplies them, MUST compare exactly without case folding or Unicode +normalization. A mismatch is a metadata conflict. This attribute does not permit +removal of native fields REQUIRED by the selected encoding. + +For the listed values, an applicable Descriptor satisfies this extension's +presence requirements for the corresponding field, including those of the +combined WoT mapping. The dataset name satisfies no publisher or writer identity +requirement. A Producer MAY omit the corresponding event attribute or source +query parameter. Other event attributes, including `opcuamessagetype`, MUST NOT +be replaced by Descriptor members. + +If a publisher or writer value appears in both the `source` query and the +Descriptor, the values MUST agree after decoding and type conversion. If an +event attribute and the Descriptor both supply a metadata version, their typed +values MUST agree. The same consistency requirement applies to publisher and +writer identities retained in the envelope. On a conflict, the Consumer MUST +report a metadata conflict and MUST NOT interpret the payload as a conforming +OPC UA message. If a REQUIRED value is absent from all applicable locations, the +Consumer MUST report missing metadata and MUST NOT interpret the payload as a +conforming OPC UA message. + +A Consumer MUST use only the Descriptor selected by the agreed binding. If +`describedby` is present, missing values MUST NOT be taken from a source or +subject description. If its Descriptor cannot be retrieved, the Consumer MAY +interpret the native payload using the envelope, source query and explicit event +attributes when they supply every native requirement, but MUST NOT report +successful Descriptor-based interpretation or WoT correlation. The WoT profile's +explicit-link failure rules still apply. + +Without `describedby`, the revised WoT profile can select a direct TD subject +without a Descriptor. Native publisher and writer requirements then have to be +satisfied by the retained envelope or the existing source query. A direct TD +MUST NOT be treated as a source of missing OPC UA metadata. Consumers MUST NOT +retrieve a source Descriptor as an implicit supplement. + +The Descriptor Publisher MUST NOT change these values in a Descriptor Version +after any Producer is configured with its URL. A change requires a new +Descriptor Version and reconfigured Producers. Consumers MUST NOT retain values +from a previous Descriptor when processing an event that selects another +Descriptor. A changed payload contract also requires a changed WoT `type` +revision when that profile is used; changing only `describedby` is not +sufficient. + +Values that vary by occurrence remain in each event. In particular, +`opcuamessagetype`, `opcuasequence`, `opcuastatus` when REQUIRED, `id`, and +`time` MUST NOT be supplied by the Descriptor as replacements for their event +attributes. Tracing, recording times, and a CloudEvents `sequence`, when used, +also remain in the event. A Consumer MUST NOT use Descriptor members with those +names as event values. Core attributes retain their normal placement and meaning +even when their values happen to be constant for a stream. + +When the agreed route selects no Descriptor, the Producer MUST supply all +REQUIRED OPC UA values through the other locations defined above. An absent +`describedby` alone does not determine whether a Descriptor was selected. +Producers SHOULD use Descriptor metadata only when the intended Consumers +support the agreed mapping. + +When `source` is itself a Descriptor URL, Producers SHOULD supply shared +identity metadata in the selected versioned Descriptor instead of appending +query parameters to that URL. Appending a query changes the event's source +identity and can change the retrieved resource. Consumers MUST NOT strip a query +to make source-based retrieval work. ## Examples +The examples preceding the WoT section use publisher `UInt16:12` and writer name +`energy-consumption-asset`. Their payloads omit the native headers, so their +`source` queries supply both identifiers. + ### Metadata message The metadata message helps Cloud applications to understand the semantics and @@ -157,7 +515,7 @@ specversion: 1.0 type: ua-metadata time: 2024-03-28T21:56:24Z id: 1234-1234-1234 -source: urn:factory:aggregationserver:opcua +source: https://factory.example.com/aggregationserver/opcua?opcuapublisherid=UInt16:12;opcuadatasetwritername=energy-consumption-asset datacontenttype: application/json; charset=utf-8 subject: energy-consumption-asset .... further attributes ... @@ -201,10 +559,10 @@ specversion: 1.0 type: ua-deltaframe time: 2024-03-28T21:56:42Z id: 1235-1235-1235 -source: urn:factory:aggregationserver:opcua +source: https://factory.example.com/aggregationserver/opcua?opcuapublisherid=UInt16:12;opcuadatasetwritername=energy-consumption-asset datacontenttype: application/json; charset=utf-8 subject: energy-consumption-asset -sequence: 7 +sequence: 0000000007 traceparent: 4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00000011 recordedtime: 2024-03-28T21:56:43Z opcuametadatamajorversion: 672338910 @@ -276,10 +634,10 @@ specversion: 1.0 type: ua-event time: 2024-03-28T21:57:01Z id: 1236-1237-1238 -source: urn:factory:aggregationserver:opcua +source: https://factory.example.com/aggregationserver/opcua?opcuapublisherid=UInt16:12;opcuadatasetwritername=energy-consumption-asset datacontenttype: application/json; charset=utf-8 subject: energy-consumption-asset/444321 -sequence: 18 +sequence: 0000000018 traceparent: caffef3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00000011 recordedtime: 2024-03-28T21:57:01Z opcuametadatamajorversion: 672338910 @@ -318,11 +676,11 @@ specversion: 1.0 type: ua-keyframe time: 2024-03-28T23:59:59Z id: 6235-7235-8235 -source: bottling-company/amsterdam/FillingArea1/FillingLine9/Cell1/Conveyor +source: bottling-company/amsterdam/FillingArea1/FillingLine9/Cell1/Conveyor?opcuapublisherid=UInt16:12;opcuadatasetwritername=energy-consumption-asset datacontenttype: application/avro subject: energy-consumption-asset dataschema: http://example.com/schemas/energy-consumption-asset/v1.8 -sequence: 3141 +sequence: 0000003141 traceparent: 22222f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00000011 recordedtime: 2024-03-28T23:59:59Z .... further attributes ... @@ -334,3 +692,159 @@ recordedtime: 2024-03-28T23:59:59Z ----------------------------------------------- ``` + +### WoT and OPC UA telemetry message + +This binary-mode keyframe uses the WoT event type and named affordance subject. +Its Descriptor supplies the resolved TD, payload schema, PublisherId, +DataSetWriter identity and dataset name. The native OPC UA message type and +sequence remain in the event because they can vary within this stream. The +example uses writer ID `17` within publisher `UInt16:12`. The CloudEvents +`sequence` attribute is absent because this example does not define a +source-wide ordering across DataSetWriters. + +```text +------------------ PUBLISH ------------------- + +Topic Name: opcua/json/DataSetMessage/pump +Content Type: application/json + +------------- User Properties ---------------- + +specversion: 1.0 +id: ua-message-25460 +source: https://registry.example/descriptorgroups/devices/descriptors/pump7 +type: com.example.models.tm.pump.properties.processData.v1 +subject: pump7#/properties/processData +time: 2026-10-29T12:00:00Z +describedby: https://registry.example/descriptorgroups/streams/descriptors/pump7-telemetry/versions/1 +opcuamessagetype: ua-keyframe +opcuasequence: 4294967295 + +------------------ payload ------------------- + +{ + "Speed": 1500, + "Pressure": 4.5 +} + +----------------------------------------------- +``` + +The referenced Descriptor Version has the following metadata. Other Core +metadata is omitted from this excerpt. + +```json +{ + "descriptorid": "pump7-telemetry", + "versionid": "1", + "schemaref": "payload", + "references": { + "pump7": { + "class": "thingdescription", + "url": "https://registry.example/thingdescriptiongroups/factory/thingdescriptions/pump7/versions/3" + }, + "gateway1": { + "class": "thingdescription", + "url": "https://registry.example/thingdescriptiongroups/factory/thingdescriptions/gateway1/versions/1" + }, + "payload": { + "class": "schema", + "url": "https://schemas.example/pump-process-data/versions/3" + }, + "geometry": { + "class": "usdasset", + "url": "https://assets.example/usdassetgroups/line1/usdassets/pump7/versions/2" + } + }, + "opcuapublisherid": "UInt16:12", + "opcuadatasetwriterid": 17, + "opcuadatasetwritername": "pump7-process", + "opcuadatasetname": "PumpProcessData" +} +``` + +The Consumer uses the selected Descriptor's PublisherId and writer identity with +the event's `opcuamessagetype` and `opcuasequence`. The subject's `pump7` prefix +selects the TD among two candidates; `schemaref` selects the payload schema. The +dataset name and writer name deliberately differ. + +With an agreed source-Descriptor binding, a Producer can instead use the +stream's Descriptor Version URL as `source` and omit `describedby`. This changes +the source identity; it is not an identity-preserving rewrite of already +published events. + +#### Direct TD subject with the NetworkMessage envelope + +This event retains the full Part 14 NetworkMessage envelope. Its PublisherId +and DataSetWriterId satisfy the identity requirements, so `source` needs no +publisher or writer query parameters. Part 14 JSON encodes the configured +PublisherId `UInt16:12` as the string `"12"`. + +The direct subject supplies the TD URL and affordance pointer; `dataschema` +supplies the envelope's schema. No Descriptor is REQUIRED, so `describedby` is +omitted. The event type uses revision `v2` because retaining the envelope +changes the payload contract. + +```text +------------------ PUBLISH ------------------- + +Topic Name: opcua/json/DataSetMessage/pump +Content Type: application/json + +------------- User Properties ---------------- + +specversion: 1.0 +id: ua-message-25461 +source: mqtt://site/line/cell/device/sensor?site=north +type: com.example.models.tm.pump.properties.processData.v2 +subject: https://registry.example/thingdescriptiongroups/factory/thingdescriptions/pump7/versions/3#/properties/processData +time: 2026-10-29T12:00:01Z +dataschema: https://schemas.example/pump-process-data-envelope/versions/1 +opcuasequence: 0 + +------------------ payload ------------------- + +{ + "MessageId": "ua-message-25461", + "MessageType": "ua-data", + "PublisherId": "12", + "Messages": [ + { + "DataSetWriterId": 17, + "MessageType": "ua-keyframe", + "SequenceNumber": 0, + "Timestamp": "2026-10-29T12:00:01Z", + "Payload": { + "Speed": 1500, + "Pressure": 4.5 + } + } + ] +} + +----------------------------------------------- +``` + +The native sequence has rolled over from `4294967295` to `0`; it remains +`opcuasequence`, not a CloudEvents `sequence`. The subject selects its TD +directly without a source lookup. The DataSetMessage's `MessageType` remains in +the retained NetworkMessage envelope instead of being repeated as +`opcuamessagetype`. + +## Descriptor Retrieval Security + +The Descriptor Reference extension's retrieval, authorization and cache rules +apply to this mapping. Stable writer metadata in a Descriptor does not +authenticate the Publisher or authorize access to the referenced TD or schema. A +Consumer MUST establish trust in the Descriptor before using it as OPC UA +metadata. + +[UA DataSetWriterName]: https://reference.opcfoundation.org/Core/Part14/v105/docs/7.2.5.4#Table185 +[UA NetworkMessage]: https://reference.opcfoundation.org/specs/OPC-10000-14/v1.05.06/7.2.5.3 +[UA DataSetWriterId]: https://reference.opcfoundation.org/specs/OPC-10000-14/v1.05.06/6.2.4.1 +[UA PublisherId]: https://reference.opcfoundation.org/specs/OPC-10000-14/v1.05.06/6.2.7.1 +[UA SequenceNumber]: https://reference.opcfoundation.org/Core/Part14/v105/docs/7.2.3 +[UA DataSet Metadata]: https://reference.opcfoundation.org/specs/OPC-10000-14/v1.05.06/6.2.3.2.3 +[UA VersionTime]: https://reference.opcfoundation.org/specs/OPC-10000-4/v1.05.07/7.43 +[URI]: https://www.rfc-editor.org/rfc/rfc3986 diff --git a/cloudevents/extensions/wot.md b/cloudevents/extensions/wot.md new file mode 100644 index 000000000..b5cba94cc --- /dev/null +++ b/cloudevents/extensions/wot.md @@ -0,0 +1,482 @@ +# Web of Things + +This profile maps a [Web of Things Thing Description][TD] (TD) to CloudEvents +Core context attributes and optionally the [Descriptor Reference +extension](descriptors.md). The REQUIRED `subject` identifies a Thing or +affordance, either through a selected Descriptor or through a self-contained TD +document URL. The `type` names the event contract. + +The profile defines no new CloudEvents extension attributes. Producers and +Consumers agree on its use through a publication profile or configuration. It +does not define a transport binding, provision a Thing, or require a particular +registry implementation. A direct TD subject can supply the REQUIRED description +without a Descriptor; an explicit `describedby` can supply additional shared +information when needed. + +## Notational Conventions + +As with the main [CloudEvents specification](../spec.md), the key words "MUST", +"MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", +"RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as +described in [RFC 2119](https://tools.ietf.org/html/rfc2119). + +The scope of these key words is limited to events using this profile. An +attribute marked REQUIRED here is not REQUIRED in every CloudEvent. + +## Mapping Core Context Attributes + +### source + +The Core [`source`](../spec.md#source) identifies the context in which the event +occurred and follows Core's URI-reference rules. It does not have to identify +the Thing selected by `subject`; an actor can emit an event concerning a +different device. + +For example, `https://factory.example.com/line1/pump7` and +`mqtt://site/line/cell/device/sensor` are valid sources. + +An agreed source-Descriptor binding MAY use an absolute, fragment-free +Descriptor URL as `source` when it identifies the originating context. Consumers +MUST NOT infer that binding merely from URL-like spelling. The +[description-selection rules](#description-selection) determine whether the +source is retrieved; a direct TD subject does not require that lookup. + +#### Event identity + +Core requires `source` and `id` together to identify each distinct event. +Changing the `source` changes the event identity and +[Sequence extension](sequence.md) scope. +Returning to an earlier `source` returns to its existing scope: Producers MUST +NOT reuse event identities or restart a claimed source-wide sequence within +that scope. + +### type + +The Core [`type`](../spec.md#type) identifies the event contract. The contract +is the payload representation defined by the publication profile or protocol +binding, including its members, member types, and semantics. Two Things can +produce events of the same type when their event contracts are the same. This +profile uses a dot-separated symbol so a Consumer can map the type to a +code-generation symbol. + +The type MUST contain one or more segments separated by single dots. Each +segment MUST start with an ASCII letter or underscore and contain only ASCII +letters, digits, or underscores thereafter. These character restrictions apply +only to `type`; they do not change the Thing identifier, TD member names, +`source`, `subject`, or payload. A code generator remains responsible for +language-specific keywords. The final segment MUST identify the event-contract +revision as `vN`, where `N` is a positive decimal integer without leading zeros. + +A Producer MAY configure the complete type. Otherwise, it derives the type +from a governing [Thing Model][TM] (TM) and, for an affordance event, the +corresponding model-affordance path. When composition or derivation does not +identify that mapping unambiguously, the publication configuration MUST +select it explicitly. A Producer MUST NOT infer the mapping solely from +matching names or select the first TM link. + +For an eligible HTTPS TM identifier, derive the type as follows: + +1. Require an ASCII DNS hostname without empty labels or a trailing root dot. + The identifier cannot contain user information, a port, a query, or a + fragment for this derivation. +2. Lowercase the hostname, reverse its labels, and join them with dots. +3. Append the TM identifier's path, replacing each `/` with `.`. +4. For an affordance event, append the model-affordance JSON Pointer path. + Decode its [RFC 6901][JSON Pointer] token escaping, then replace each `/` + with `.`. +5. Append the event-contract revision as the final segment. + +For example, TM `https://models.example.com/tm/pump` and model affordance +`/properties/processData` with event-contract revision 1 produce: + +```text +com.example.models.tm.pump.properties.processData.v1 +``` + +For a whole-Thing event, use the TM-derived type without an affordance path, +then append the revision. The publication profile defines how interaction data +is aggregated into the payload. + +The publication profile MUST assign the same revision to the same event +contract. A Producer MUST use a new revision when the contract changes, +including when a member is added or removed, a member's type or meaning +changes, or the payload envelope changes. This applies even when the change is +backwardly compatible. A Producer MUST NOT reuse a revision for a different +contract with the same base type. + +For example, adding a `quality` member to +`com.example.models.tm.pump.properties.processData.v1` requires +`com.example.models.tm.pump.properties.processData.v2`, even if Consumers can +ignore the new member. + +A change to an individual value or the presence of a member already defined as +OPTIONAL does not change the contract revision. A change to `describedby`, the +selected TD URL or the selected schema alone also does not change the revision; +if it changes the payload contract, the Producer MUST use a new revision. + +The Producer MUST configure a valid complete type when the derivation is +unavailable, produces an invalid symbol, or collides with a different event +contract in the Producer's type namespace. A slash-to-dot conversion is not +reversible: `/tm/pump.v1` and `/tm/pump/v1` produce the same suffix. The +Producer MUST disambiguate such contracts by configuration rather than +silently rewriting identifiers or member names. + +### subject + +The [`subject`](../spec.md#subject) is REQUIRED. It MUST be an [RFC 6901][JSON +Pointer] URI-fragment pointer with one of the prefixes defined below. The +pointer is evaluated against the selected ordinary, resolved TD, not Descriptor +metadata or an unresolved projection plan. + +| Subject form | Meaning | +| --- | --- | +| `#` or `#/properties/processData` | Select the unique TD Reference in the selected Descriptor, then the whole Thing or affordance. | +| `pump7#/properties/processData` | Select the Descriptor's named `thingdescription` Reference `pump7`, then its affordance. | +| `https://example.com/td/pump7/versions/3#/properties/processData` | Identify the Thing's affordance through a TD document URL; description selection follows the rules below. | + +The Consumer MUST split the value at the first literal `#`. A missing delimiter +is invalid. It recognizes a direct-URL candidate by an `http://` or `https://` +prefix, case-insensitively, and MUST validate that prefix as a complete absolute +HTTP(S) URL. Otherwise, the prefix MUST be empty or a Descriptor Reference name. +Reference names follow the Descriptor Registry's map-key rules and cannot +contain `/`; `http:pump7` can therefore be a name, but is not an HTTP URL. +Invalid input MUST NOT be retried through another form or resolved against +`source`. + +The suffix including `#` MUST be a valid JSON Pointer URI fragment. For the +whole Thing, the Producer MUST use an empty fragment, such as `#`, `pump7#` or +`https://example.com/td/pump7/versions/3#`. For an affordance event, the pointer +MUST identify the concrete affordance in that TD, such as +`#/properties/processData`, `#/actions/setSetpoint` or `#/events/alarm`. + +The Producer MUST escape JSON Pointer tokens before applying URI-fragment +encoding. For example, property `pressure/bar` is addressed by +`#/properties/pressure~1bar`; a literal `#` in a name is percent-encoded. The +Consumer MUST apply URI-fragment decoding once, then JSON Pointer token decoding +as specified by RFC 6901. It MUST report a correlation failure if the encoding +or pointer is invalid or the pointer does not select the intended Thing or +affordance. + +The subject identifies the Thing or affordance, not an occurrence. Different +events can have the same subject and different CloudEvents `id` values. A +binding-specific writer identifier or native occurrence identifier is not +appended to this pointer. + +The concrete TD path and corresponding TM declaration path can differ. This +profile does not infer a declaration-lineage mapping between them. + +Changing a direct subject's TD Version URL changes the subject string used for +filtering and correlation, not the source-plus-id event identity. A +Descriptor-relative subject with an independently versioned `describedby` can +avoid that coupling. + +### dataschema and datacontenttype + +Core [`dataschema`](../spec.md#dataschema) identifies the schema that the +CloudEvent's actual `data` adheres to. +[`datacontenttype`](../spec.md#datacontenttype) identifies that data's media +type. Neither identifies the TD document's format. + +The Consumer follows the Descriptor Reference extension's [schema +precedence](descriptors.md#schema-selection): `dataschema`, then the +selected Descriptor's schema. Without a Descriptor, the second source is +absent. The +Producer SHOULD omit `dataschema` when a selected Descriptor already supplies +the schema. + +A TD describes interactions and can contain several DataSchemas. The +Producer MUST NOT use the whole TD as `dataschema` merely because the TD +contains a schema for the selected affordance. A TD's retrieval URL is a +valid `dataschema` only if the identified representation is itself the +schema applicable to `data`. + +The publication profile or protocol binding defines how the selected +affordance's DataSchema maps to the wire payload. An object containing several +property values, an event payload and a protocol envelope can need different +schemas. When `dataschema` is present, the Producer MUST ensure that it +identifies the schema of the representation actually sent, including any +encoding or envelope selected by the binding. The same requirement applies to a +schema supplied by a Descriptor. + +An xRegistry Schema Resource's document-view URL can identify that external +schema. It is not the WoT Resource's metadata-view URL. A profile MAY require +an exact schema Version independently of the selected TD Version. + +## Description Selection + +This profile uses the [Descriptor Reference selection +rules](descriptors.md#one-selected-processing-description), including when +`describedby` is absent. Apply event-format decoding before testing presence; +JSON `null` means unset. Selection proceeds as follows: + +1. If `describedby` is present, retrieve and validate its Descriptor. It is + authoritative; a failure MUST NOT cause fallback to source or direct TD + subject retrieval. +2. Otherwise, if `subject` has a direct HTTP(S) TD URL prefix, retrieve that + document. Use the prefix unchanged, including its query; the fragment is + evaluated locally and is not sent in HTTP. Do not retrieve a source + Descriptor or construct a synthetic Descriptor from the TD. +3. Otherwise, use the agreed source-Descriptor binding. Retrieve the exact + `source` URL, which MUST be an absolute URL without a fragment for this + binding. If no such binding was agreed, report a correlation failure. + +When a Descriptor is selected, select its TD Reference as follows: + +- For an empty subject prefix, the Descriptor MUST have exactly one + `thingdescription` Reference. +- For a Reference-name prefix, that entry MUST exist and its `class` MUST be + `thingdescription`. +- For a direct URL prefix, exactly one `thingdescription` Reference MUST + have a `url` equal to that prefix. Consumers MUST compare the strings exactly, + without URI normalization. Missing or duplicate matches are correlation + failures. A named subject can disambiguate duplicate URLs. + +The selected `url` identifies the ordinary TD document, not its Registry +metadata. The Consumer uses that representation directly or from an accepted +Descriptor bundle. It MUST NOT construct another URL from the Descriptor URL or +Reference. Redirect destinations, TD `id` and equal `entityid` values MUST NOT +be used to repair a subject-to-Reference mismatch. + +A `thingmodel` Reference cannot replace an instance TD. The selected +representation MUST be an ordinary resolved TD. The +[WoT Descriptor profile][WoT Descriptor Profile] defines the TD/TM admission +and provenance rules, including profile-specific `planurl` and plan/view +pairing. Consumers using that profile MUST apply its admission and provenance +rules. Failure to obtain or validate a REQUIRED profile input MUST cause a +reported correlation failure. Unavailable OPTIONAL provenance does not prevent +correlation with an otherwise usable TD. The generic Descriptor model does not +define those provenance attributes. + +The Producer MUST supply all REQUIRED inputs through the selected route, the +event and the agreed publication profile. A direct TD subject without a +Descriptor does not supply an external payload schema or native publisher +metadata. A Producer needing shared inputs can use a complete Descriptor +with `describedby`. A Consumer MUST NOT merge them from source descriptions. + +If selection, retrieval or correlation fails, the Consumer MUST report the +failure and leave the event uncorrelated. It MAY retain or forward the event +without TD-based interpretation, but MUST NOT switch routes or substitute +another TD. This does not authenticate the originating entity or authorize an +action described by the TD. + +The Publisher MUST make the selected TD available before configuring Producers +with its URL or a Descriptor that references it. The Publisher SHOULD publish an +explicit TD Version. A change to the resolved logical view requires a new TD +Version; the Publisher MUST NOT change that view at a previously published +Version URL. Descriptor publication and Version stability follow the Descriptor +Registry rules. + +Changing only `describedby` does not change identity or sequence scope. A +Producer MUST NOT reuse an `id` or restart a source-wide sequence merely because +a description changes. An exact Version or successful resolution does not itself +prove that retrieved bytes are unchanged. + +## Relationship to Other Extensions + +This profile does not redefine native message types, sequence counters, or +writer identities. An event that also uses the [OPC UA extension](opcua.md) +MUST follow that extension's conditional WoT mapping. + +## Examples + +### Single Affordance + +This CloudEvent identifies a pump and its `processData` property. Its Descriptor +selects version `3` of the resolved TD and supplies the payload schema. Another +TD in the same Descriptor describes a gateway. + +```json +{ + "specversion": "1.0", + "id": "sample-4711", + "source": "https://registry.example/descriptorgroups/devices/descriptors/pump7", + "type": "com.example.models.tm.pump.properties.processData.v1", + "subject": "pump7#/properties/processData", + "datacontenttype": "application/json", + "describedby": "https://registry.example/descriptorgroups/streams/descriptors/pump7-telemetry/versions/1", + "data": { + "Speed": 1500, + "Pressure": 4.5 + } +} +``` + +The Descriptor has the following metadata. Core bookkeeping and any +profile-specific shared metadata are omitted from this excerpt. + +```json +{ + "descriptorid": "pump7-telemetry", + "versionid": "1", + "schemaref": "payload", + "references": { + "pump7": { + "class": "thingdescription", + "url": "https://registry.example/thingdescriptiongroups/factory/thingdescriptions/pump7/versions/3" + }, + "gateway1": { + "class": "thingdescription", + "url": "https://registry.example/thingdescriptiongroups/factory/thingdescriptions/gateway1/versions/1" + }, + "payload": { + "class": "schema", + "url": "https://schemas.example/pump-process-data/versions/3" + }, + "geometry": { + "class": "usdasset", + "url": "https://assets.example/usdassetgroups/line1/usdassets/pump7/versions/2" + } + } +} +``` + +The subject prefix selects `pump7`, not the first TD in the map. The suffix +selects its property. The payload directly contains the property's object value +without a `processData` wrapper. The selected schema describes that object, not +the TD document. The USD asset does not have to be retrieved for +this correlation. + +### Direct TD Subject + +This event needs only the TD and an external payload schema, so it omits +`describedby`. The source identifies an actor's originating context rather than +the pump; it is not retrieved. + +```json +{ + "specversion": "1.0", + "id": "sample-4712", + "source": "urn:example:actor:telemetry-service", + "type": "com.example.models.tm.pump.properties.processData.v1", + "subject": "https://registry.example/thingdescriptiongroups/factory/thingdescriptions/pump7/versions/3#/properties/processData", + "dataschema": "https://schemas.example/pump-process-data/versions/3", + "datacontenttype": "application/json", + "data": { + "Speed": 1500, + "Pressure": 4.5 + } +} +``` + +The Consumer retrieves the subject's fragment-free TD URL and evaluates +`#/properties/processData`. The URI fragment is not sent in HTTP. + +### Direct Subject with a Descriptor + +The preceding event can instead select the same telemetry Descriptor: + +```json +{ + "specversion": "1.0", + "id": "sample-4714", + "source": "urn:example:actor:telemetry-service", + "type": "com.example.models.tm.pump.properties.processData.v1", + "subject": "https://registry.example/thingdescriptiongroups/factory/thingdescriptions/pump7/versions/3#/properties/processData", + "describedby": "https://registry.example/descriptorgroups/streams/descriptors/pump7-telemetry/versions/1", + "datacontenttype": "application/json", + "data": { + "Speed": 1500, + "Pressure": 4.5 + } +} +``` + +The direct subject matches exactly the `pump7` Reference. The Descriptor now +supplies the schema. Changing just the subject's TD Version to `4` would fail +correlation because this Descriptor names Version `3`. Adding another TD +Reference with that same Version `3` URL would also make the direct match +ambiguous; a named subject would then be REQUIRED. + +### Source-selected Descriptor + +When the agreed source-Descriptor binding is enabled, the Single Affordance +event can use the following complete envelope without `describedby`: + +```json +{ + "specversion": "1.0", + "id": "sample-4715", + "source": "https://registry.example/descriptorgroups/streams/descriptors/pump7-telemetry/versions/1", + "type": "com.example.models.tm.pump.properties.processData.v1", + "subject": "pump7#/properties/processData", + "datacontenttype": "application/json", + "data": { + "Speed": 1500, + "Pressure": 4.5 + } +} +``` + +This intentionally identifies the stream Descriptor as the originating context. +Its source-plus-id namespace differs from the device-source example. A new +Version in `source` changes that namespace; a new Version in `describedby` alone +does not. + +### Whole Thing + +This CloudEvent carries aggregated data for the same pump. Its Descriptor +selects version `3` of the resolved TD and the aggregate payload schema. The +publication profile defines a payload containing the `processData` and `running` +property values. The subject selects the whole TD. + +```json +{ + "specversion": "1.0", + "id": "sample-4713", + "source": "https://factory.example.com/line1/pump7", + "type": "com.example.models.tm.pump.v1", + "subject": "#", + "datacontenttype": "application/json", + "describedby": "https://registry.example/descriptorgroups/streams/descriptors/pump7-state/versions/1", + "data": { + "processData": { + "Speed": 1500, + "Pressure": 4.5 + }, + "running": true + } +} +``` + +The referenced Descriptor Version contains the following metadata. + +```json +{ + "descriptorid": "pump7-state", + "versionid": "1", + "schemaref": "payload", + "references": { + "pump7": { + "class": "thingdescription", + "url": "https://registry.example/thingdescriptiongroups/factory/thingdescriptions/pump7/versions/3" + }, + "payload": { + "class": "schema", + "url": "https://schemas.example/pump-state/versions/3" + } + } +} +``` + +The empty subject prefix selects the unique TD Reference. The TD document itself +is not the payload. `schemaref` selects the schema of the aggregate JSON object, +not the TD or either property's DataSchema alone. A direct whole-Thing subject +would end in `#` after its TD document URL. + +## Security Considerations + +Resolvers MUST apply their configured trust, authorization and retrieval +policies before accessing documents. An untrusted identity or schema URI +MUST NOT authorize arbitrary network access, redirects or credential +forwarding. Implementations SHOULD bound document sizes and dependency +resolution. + +The Producer controls the context attributes unless another mechanism +authenticates them. A version does not establish that a TD, its model lineage, +or a schema is authorized for the Consumer's use. It does not grant access to +referenced documents or make their dependencies available. + +[TD]: https://www.w3.org/TR/wot-thing-description11/ +[TM]: https://www.w3.org/TR/wot-thing-description11/#thing-model +[JSON Pointer]: https://www.rfc-editor.org/rfc/rfc6901 +[WoT Descriptor Profile]: https://github.com/marcschier/xregistry-drafts/blob/28b6d245fb4f634bb172e0505ae8e64fdf882428/workingdrafts/models/wot/spec.md#66-wot-descriptor-profile