Copyright © 2024-2026, London Stock Exchange Group plc
Copyright © 2024-2026, Federated Knowledge, LLC
Copyright © 2024-2026, agnos.ai UK Ltd.
Copyright © 2024-2026, Quantyca S.p.A.
Copyright © 2024-2026, eccenca GmbH
Copyright © 2024-2026, EDM Council, Inc.
USE OF SPECIFICATION – TERMS, CONDITIONS & NOTICES
The material in this document details an Object Management Group specification in accordance with the terms, conditions and notices set forth below. This document does not represent a commitment to implement any portion of this specification in any company's products. The information contained in this document is subject to change without notice.
LICENSES
Contributions to this specification are made under the terms of the Contributor License Agreement given at https://cla-assistant.io/EKGF/dprod. Each of the copyright holders listed above has agreed that no person shall be deemed to have infringed the copyright in the included material of any such copyright holder by reason of having used the specification set forth herein or having conformed any computer software to the specification.
Subject to all of the terms and conditions below, the owners of the copyright in this specification hereby grant you a fully-paid up, non-exclusive, nontransferable, perpetual, worldwide license (without the right to sublicense), to use this specification to create and distribute software and special purpose specifications that are based upon this specification, and to use, copy, and distribute this specification as provided under the Copyright Act; provided that:
This limited permission automatically terminates without notice if you breach any of these terms or conditions. Upon termination, you will immediately destroy any copies of the specifications in your possession or control.
PATENTS
This specification is made available under the OMG’s Copyright and Non-Assertion Covenant (see https://www.omg.org/cgi-bin/doc.cgi?ipr for details). The attention of adopters is directed to the possibility that compliance with or adoption of OMG specifications may require use of an invention covered by patent rights. OMG shall not be responsible for identifying patents for which a license may be required by any OMG specification, or for conducting legal inquiries into the legal validity or scope of those patents that are brought to its attention. OMG specifications are prospective and advisory only. Prospective users are responsible for protecting themselves against liability for infringement of patents.
GENERAL USE RESTRICTIONS
Any unauthorized use of this specification may violate copyright laws, trademark laws, and communications regulations and statutes. This document contains information which is protected by copyright. All Rights Reserved. No part of this work covered by copyright herein may be reproduced or used in any form or by any means--graphic, electronic, or mechanical, including photocopying, recording, taping, or information storage and retrieval systems--without permission of the copyright owner.
DISCLAIMER OF WARRANTY
WHILE THIS PUBLICATION IS BELIEVED TO BE ACCURATE, IT IS PROVIDED "AS IS" AND MAY CONTAIN ERRORS OR MISPRINTS. THE OBJECT MANAGEMENT GROUP AND THE COMPANIES LISTED ABOVE MAKE NO WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, WITH REGARD TO THIS PUBLICATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTY OF TITLE OR OWNERSHIP, IMPLIED WARRANTY OF MERCHANTABILITY OR WARRANTY OF FITNESS FOR A PARTICULAR PURPOSE OR USE. IN NO EVENT SHALL THE OBJECT MANAGEMENT GROUP OR ANY OF THE COMPANIES LISTED ABOVE BE LIABLE FOR ERRORS CONTAINED HEREIN OR FOR DIRECT, INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL, RELIANCE OR COVER DAMAGES, INCLUDING LOSS OF PROFITS, REVENUE, DATA OR USE, INCURRED BY ANY USER OR ANY THIRD PARTY IN CONNECTION WITH THE FURNISHING, PERFORMANCE, OR USE OF THIS MATERIAL, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
The entire risk as to the quality and performance of software developed using this specification is borne by you. This disclaimer of warranty constitutes an essential part of the license granted to you to use this specification.
RESTRICTED RIGHTS LEGEND
Use, duplication or disclosure by the U.S. Government is subject to the restrictions set forth in subparagraph (c) (1) (ii) of The Rights in Technical Data and Computer Software Clause at DFARS 252.227-7013 or in subparagraph (c)(1) and (2) of the Commercial Computer Software - Restricted Rights clauses at 48 C.F.R. 52.227-19 or as specified in 48 C.F.R. 227-7202-2 of the DoD F.A.R. Supplement and its successors, or as specified in 48 C.F.R. 12.212 of the Federal Acquisition Regulations and its successors, as applicable. The specification copyright owners are as indicated above and may be contacted through the Object Management Group, 9C Medway Rd, PMB 274, Milford, MA 01757, U.S.A.
TRADEMARKS
CORBA®, CORBA logos®, FIBO®, Financial Industry Business Ontology®, FINANCIAL INSTRUMENT GLOBAL IDENTIFIER®, IIOP®, IMM®, Model Driven Architecture®, MDA®, Object Management Group®, OMG®, OMG Logo®, SoaML®, SOAML®, SysML®, UAF®, Unified Modeling Language®, UML®, UML Cube Logo®, VSIPL®, and XMI® are registered trademarks of the Object Management Group, Inc.
For a complete list of trademarks, see: https://www.omg.org/legal/tm_list.htm. All other products or company names mentioned are used for identification purposes only and may be trademarks of their respective owners.
COMPLIANCE
The copyright holders listed above acknowledge that the Object Management Group (acting itself or through its designees) is and shall at all times be the sole entity that may authorize developers, suppliers and sellers of computer software to use certification marks, trademarks or other special designations to indicate compliance with these materials. Software developed under the terms of this license may claim compliance or conformance with this specification if and only if the software compliance is of a nature fully matching the applicable compliance points as stated in the specification. Software developed only partially matching the applicable compliance points may claim only that the software was based on this specification but may not claim compliance or conformance with this specification. In the event that testing suites are implemented or approved by Object Management Group, Inc., software developed using this specification may claim compliance or conformance with the specification only if the software satisfactorily completes the testing suites.
The concept of Data Products has emerged as organizations increasingly recognize the value of data as an asset to be managed and distributed like any other product. As more companies adopt decentralized data architectures, such as Data Mesh, the need for standardized methods to describe and manage data products consistently across platforms has become critical. This is where the [[dprod]] specification, built on W3C Linked Data standards, becomes essential. Without such a standard, organizations face significant challenges: inconsistent metadata across diverse data products, limited discoverability, and interoperability issues that hinder data integration from various sources. As data ecosystems grow, the lack of a common framework also impedes scalability, increases vendor lock-in, and makes it difficult to manage these products effectively.
DPROD offers a solution by providing a clear schema for describing data products, ensuring they are discoverable, interoperable, and treated with the same level of accountability as traditional products.
The W3C tech stack is perfectly suited to address these challenges because it was designed to foster interconnected, decentralized systems, providing a robust framework for creating metadata that is both machine-readable and human-understandable. DPROD enables consistent terminology across different platforms, domains, and organizations, allowing advanced users to semantically enrich their data products and connect them into distributed knowledge graphs.
As more organizations strive to build and scale data products, DPROD provides the standardization needed to ensure interoperability and unlock the full potential of decentralized data ecosystems in a controlled and mature way.
The Data Product (DPROD) specification is a profile of the Data Catalog (DCAT) Vocabulary, designed to describe Data Products. This document defines the schema and provides examples of its use.
DPROD extends DCAT to enable publishers to describe Data Products and data services in a decentralized way. By using a standard model and ontology, DPROD facilitates the consumption and aggregation of metadata from multiple Data Marketplaces. This approach increases the discoverability of products and services, supports decentralized data publishing, and enables federated search across multiple sites using a uniform query mechanism and structure.
DPROD also defines an optional Data Contracts profile of the W3C Open Digital Rights Language [[odrl-model]], with which the provider of a data product can publish the terms on which it is offered, and a consumer can record the contract under which it is used.
The namespace for DPROD terms is https://ekgf.org/dprod/spec/develop/
The suggested prefix for the DPROD namespace is dprod
DPROD follows two basic principles:
The DPROD specification has four main aims:
A data product is conformant with this specification if it satisfies the [[SHACL]] constraints provided in the file dprod-shapes.ttl.
Support for the Data Contracts profile is optional. A data offer, data contract or data use policy is conformant with this specification if it satisfies the [[SHACL]] constraints provided in the file dprod-contracts-shapes.ttl, and an evaluator of such policies is conformant if it produces the results defined by the profile's formal semantics.
All terms introduced in this specification are given definitions in the Data Product Model defined later.
The following acronyms are used in this specification.
Namespaces and prefixes used in normative parts of this Profile are shown in the following table.
| Prefix | Namespace IRI | Source |
|---|---|---|
dprod
|
https://ekgf.org/dprod/spec/develop/
|
[[dprod]] |
dprod-shapes
|
https://ekgf.org/dprod/spec/develop/shapes/
|
[[dprod]] |
dprod-contracts-shapes
|
https://ekgf.org/dprod/spec/develop/contracts/shapes/
|
[[dprod]] |
dcat
|
http://www.w3.org/ns/dcat#
|
[[vocab-dcat-3]] |
dct
|
http://purl.org/dc/terms/
|
[[dcterms]] |
odrl
|
http://www.w3.org/ns/odrl/2/
|
[[odrl-model]] |
owl
|
http://www.w3.org/2002/07/owl#
|
[[owl2-quick-reference]] |
prov
|
http://www.w3.org/ns/prov#
|
[[prov-overview]] |
rdf
|
http://www.w3.org/1999/02/22-rdf-syntax-ns#
|
[[rdf11-primer]] |
rdfs
|
http://www.w3.org/2000/01/rdf-schema#
|
[[rdf-schema]] |
sh
|
http://www.w3.org/ns/shacl#
|
[[shacl]] |
skos
|
http://www.w3.org/2004/02/skos/core#
|
[[skos-reference]] |
xsd
|
http://www.w3.org/2001/XMLSchema#
|
[[xmlschema-2]] |
Data Mesh Architectures [[Data Mesh]] use input and output ports to manage how data enters and leaves a Data Product. These ports can handle different formats, schemas, and protocols. Input ports bring in data, while output ports send data to other Data Products for aggregation, reuse, analysis or reporting, etc.
In the [[[vocab-dcat-3]]] framework, a Data Service is a way to describe services that provide access to data. Data Services give standardized, machine-readable descriptions of how to access one or more datasets or data processing functions.
Data Services specify how to access and download the data. In DPROD, Data Services are connected to Distributions by a property called isAccessServiceOf; on the Distribution one can specify formats (like CSV or JSON etc.) and provide metadata about the "physical model" of the data. Distributions link to Datasets and DCAT has a very rich vocabulary for describing every aspect of a dataset. Finally, Datasets use the conformsTo property to link to the "logical model" where one can specify one's own rich semantic metadata.
By linking Data Product ports to DCAT DataServices ([[vocab-dcat-3]]), DPROD can describe Data Products in a way that machines can read across the organization. This makes it easier for data teams to build and manage their own data products independently, while still working well with the rest of the organization's data.
Using standards like DCAT helps create a strong and clear way to define Data Products. It ensures that as data becomes more complex, the methods for describing, sharing, and using data stay consistent and reliable. It also allows different organizations to share data securely and in a standardized way.
The Profile consists of the following classes:
dcat:Catalog) - The collection of Data Products
dprod:DataProduct) - A data product may have input and output ports, code and
metadata
dcat:DataService) - A digital interface that provides access to a Dataset.
This can be an HTTP URL, a Database or a FileShare, etc.
dcat:Distribution) - A specific representation of a dataset (CSV, JSON, ADLS etc.)
which can conform to a physical model
dcat:Dataset) - A collection of related data that can conform to a logical model
As DCAT Data Services, the DPROD input and output ports can specify connection details, they have distributions that define formats, and link to datasets that conform to shared schemas. In this example, the UK Bonds Data Product includes an output port, which is a RESTful API. This API delivers JSON data conforming to the shared FIBO specification for callable bonds.
{
"@context": "https://ekgf.org/dprod/spec/develop/dprod-simple.jsonld",
"id": "https://y.com/products/uk-bonds",
"type": "DataProduct",
"title": "UK Bonds",
"description": "UK Bonds is your one-stop-shop for all your bonds!",
"dataProductOwner": "https://www.linkedin.com/in/tonyseale/",
"dataProductLifecycleStatus": "https://ekgf.org/dprod/spec/develop/data/lifecycle-status/Consume",
"outputPort": {
"id": "https://y.com/products/uk-bonds/ports/10-year-api",
"type": "DataService",
"endpointURL": "https://y.com/uk-10-year-bonds",
"isAccessServiceOf": {
"id": "https://y.com/products/uk-bonds/distributions/10-year-json",
"type": "Distribution",
"format": "https://www.iana.org/assignments/media-types/application/json",
"isDistributionOf": {
"id": "https://y.com/products/uk-bonds/datasets/10-year",
"type": "Dataset",
"conformsTo": "https://spec.edmcouncil.org/fibo/ontology/SEC/Debt/Bonds/CallableBond"
}
}
}
}
DPROD publishes two JSON-LD contexts that denote the same graph. The examples in this specification
use the simple context, dprod-simple.jsonld, which lets a document use plain terms such
as outputPort, title, id and type. When DPROD
JSON is combined with other JSON-LD contexts, use dprod-context.jsonld and prefixed
terms such as dprod:outputPort and dct:title with the keywords
@id and @type, so that neither side claims generic names such as
title or format. Neither context has an @vocab, so an
undefined term is visibly undefined rather than silently coined in the DPROD namespace.
The JSON above can be pasted into https://json-ld.org/playground
to see the RDF it denotes.
The following sections are driven by the Shapes definitions for DPROD, which represent the properties expected to be used for instances of the above classes. As such, they include properties defined by the DCAT specification which DPROD extends. The prefix in the identifier for each entry indicates the ontology defining the property.
| Identifier: | dprod:DataProduct |
|---|
A rational, managed, and governed collection of data, with purpose, value and ownership, meeting consumer needs over a planned life-cycle.
A data product may have input and output ports, code and metadata
The name given to the data product.
| Identifier: | rdfs:label |
|---|---|
| Label: | data product label shape |
| Domain: | dprod:DataProduct |
| Range: | xsd:string |
A free text description of the data product.
| Identifier: | dct:description |
|---|---|
| Label: | data product description shape |
| Domain: | dprod:DataProduct |
| Range: | xsd:string |
The agent that is accountable overall for the data product, including managing it through its lifecycle.
| Identifier: | dprod:dataProductOwner |
|---|---|
| Label: | data product owner |
| Domain: | dprod:DataProduct |
| Range: | prov:Agent |
The business or information area supported by the data product.
| Identifier: | dprod:domain |
|---|---|
| Label: | domain |
| Comment: | The domain is intended to be a resource in its own right. This specification does not constrain the class to be used. |
| Domain: | dprod:DataProduct |
| Range: |
A set of services exposed by a data product to collect its source data and makes it available for further internal transformation. An input port can receive data from one or more upstream sources in a push (i.e. asynchronous subscription) or pop mode (i.e. synchronous query). Each data product may have one or more input ports.
| Identifier: | dprod:inputPort |
|---|---|
| Label: | input port |
| Domain: | dprod:DataProduct |
| Range: | dcat:DataService |
A set of services exposed by a data product to share the generated data in a way that can be understood and trusted.
| Identifier: | dprod:outputPort |
|---|---|
| Label: | output port |
| Domain: | dprod:DataProduct |
| Range: | dcat:DataService |
The source data made available to the data product through input data services. Depending on the lifecycle of the data product, this may be a stated or inferred relationship aligned with the input ports.
| Identifier: | dprod:inputDataset |
|---|---|
| Label: | input dataset |
| Domain: | dprod:DataProduct |
| Range: | dcat:Dataset |
The data that is exposed by the data product through output data services in a way that can be understood and trusted. Depending on the lifecycle of the data product, this may be a stated or inferred relationship aligned with the output ports.
| Identifier: | dprod:outputDataset |
|---|---|
| Label: | output dataset |
| Domain: | dprod:DataProduct |
| Range: | dcat:Dataset |
A description of the objectives and intended usage of the data product.
| Identifier: | dprod:purpose |
|---|---|
| Label: | purpose |
| Domain: | dprod:DataProduct |
| Range: | xsd:string |
An ODRL conformant policy expressing the rights associated with the data product. This is an inferred relationship based on the rights expressed on the individual datasets of the data product.
| Identifier: | odrl:hasPolicy |
|---|---|
| Label: | data product has policy shape |
| Domain: | dprod:DataProduct |
| Range: | odrl:Policy |
The authored development lifecycle status of the data product.
| Identifier: | dprod:dataProductLifecycleStatus |
|---|---|
| Label: | data product lifecycle status |
| Comment: | Values are SKOS concepts. DPROD supplies dprod:DataProductLifecycleStatus as an optional reference concept scheme; enterprises may use another scheme. |
| Domain: | dprod:DataProduct |
| Range: | skos:Concept |
| Identifier: | dcat:DataService |
|---|
A collection of operations that provides access to one or more datasets or data processing functions.
A site or end-point providing operations related to the discovery of, access to, or processing functions on, data or related resources.
The dataset distribution that is being offered through this data service.
| Identifier: | dprod:isAccessServiceOf |
|---|---|
| Label: | is access service of |
| Domain: | dcat:DataService |
| Range: | dcat:Distribution |
A protocol (possibly one of many options) used to communicate with this data service.
| Identifier: | dprod:protocol |
|---|---|
| Label: | protocol |
| Domain: | dcat:DataService |
| Range: |
The security schema type used for authentication and communication with this Data Service.
| Identifier: | dprod:securitySchemaType |
|---|---|
| Label: | security schema type |
| Domain: | dcat:DataService |
| Range: | dprod:SecuritySchemaType |
The root location or primary endpoint of the service.
| Identifier: | dcat:endpointURL |
|---|---|
| Label: | end-point del servicio |
| Comment: | Kořenové umístění nebo hlavní přístupový bod služby (IRI přístupné přes Web). |
| Domain: | dcat:DataService |
| Range: | rdfs:Resource |
A description of the services available via the end-points, including their operations, parameters etc.
| Identifier: | dcat:endpointDescription |
|---|---|
| Label: | descripción del end-point del servicio |
| Comment: | A description of the service end-point, including its operations, parameters etc. |
| Domain: | dcat:DataService |
| Range: |
| Identifier: | dcat:Distribution |
|---|
A specific representation of a dataset. A dataset might be available in multiple serializations that may differ in various ways, including natural language, media-type or format, schematic organization, temporal and spatial resolution, level of detail or profiles (which might specify any or all of the above).
A data service that gives access to the distribution of the dataset.
| Identifier: | dcat:accessService |
|---|---|
| Label: | data access service |
| Comment: | A site or end-point that gives access to the distribution of the dataset. |
| Domain: | dcat:Distribution |
| Range: | dcat:DataService |
The schema that the distribution conforms to that is format and technology dependent.
| Identifier: | dct:conformsTo |
|---|---|
| Label: | distribution conforms to shape |
| Domain: | dcat:Distribution |
| Range: |
The dataset that this distribution makes available.
| Identifier: | dprod:isDistributionOf |
|---|---|
| Label: | is distribution of |
| Domain: | dcat:Distribution |
| Range: | dcat:Dataset |
The file format of the distribution.
| Identifier: | dct:format |
|---|---|
| Label: | distribution format shape |
| Domain: | dcat:Distribution |
| Range: |
| Identifier: | dcat:Dataset |
|---|
A collection of data, published or curated by a single source, and available for access or download in one or more representations.
The name given to the dataset
| Identifier: | rdfs:label |
|---|---|
| Label: | dataset label shape |
| Domain: | dcat:Dataset |
| Range: | xsd:string |
Free text description of the dataset.
| Identifier: | dct:description |
|---|---|
| Label: | dataset distribution shape |
| Domain: | dcat:Dataset |
| Range: | xsd:string |
The type or genre of the dataset.
| Identifier: | dct:type |
|---|---|
| Label: | dataset type shape |
| Domain: | dcat:Dataset |
| Range: |
An available distribution of the dataset.
| Identifier: | dcat:distribution |
|---|---|
| Label: | distribuce |
| Comment: | An available distribution of the dataset. |
| Domain: | dcat:Dataset |
| Range: | dcat:Distribution |
A model, schema, ontology, view or profile that the dataset conforms to.
| Identifier: | dct:conformsTo |
|---|---|
| Label: | dataset conforms to shape |
| Domain: | dcat:Dataset |
| Range: |
An ODRL conformant policy expressing the rights associated with the resource.
| Identifier: | odrl:hasPolicy |
|---|---|
| Label: | dataset has policy shape |
| Domain: | dcat:Dataset |
| Range: |
More granular classification that indicates the level of control and protection that must be applied to the asset due to the nature of the data and its sensitivity or importance to the organization.
| Identifier: | dprod:informationSensitivityClassification |
|---|---|
| Label: | information sensitivity classification |
| Domain: | dcat:Dataset |
| Range: | dprod:InformationSensitivityClassification |
| Identifier: | dprod:InformationSensitivityClassification |
|---|
A classification of the information within a dataset to indicate the level of control and protection that must be applied.
| Identifier: | dprod:SecuritySchemaType |
|---|
A classification encompassing a set of rules used for authentication and communication.
A data product is only useful once someone can rely on it. The provider of a product commits to deliver data on a schedule, to keep it conformant to a schema, and to give notice before it changes; the consumer, in turn, agrees to use the data only in permitted ways and to meet obligations of its own, such as reporting usage. In a Data Mesh these commitments are made between many autonomous teams, so they need to be recorded in a form that machines can validate and evaluate, not just in prose.
DPROD Data Contracts is a profile of the W3C Open Digital Rights Language (ODRL) 2.2 [[odrl-model]] for exactly this purpose. It uses ODRL's own terms for everything ODRL already defines: a policy, its permissions, prohibitions and duties, the constraints on them, and the parties and assets they refer to. DPROD adds vocabulary only where ODRL leaves behaviour undefined:
dprod:DataOffer is an
odrl:Offer published by a data provider. A
dprod:DataContract is an odrl:Agreement that
records a consumer's acceptance of exactly one offer, through
dprod:acceptsOffer, and binds both parties.
dprod:subjectOfDuty) and the party it is owed to
(dprod:objectOfDuty).
dprod:recurrence, an [[RFC5545]] recurrence rule) and each
instance has a fulfilment window (dprod:deadline).
dprod:dutyState). The two are kept apart
on purpose.
Because it is a proper ODRL profile, every DPROD data contract is a valid ODRL 2.2 policy. An ODRL processor that knows nothing of DPROD can still read the permissions and prohibitions; a DPROD-aware processor additionally enforces duty state, bilateral duties and the evaluation rules.
The profile is defined by three files, published alongside this document: dprod-contracts.ttl (the vocabulary), dprod-contracts-shapes.ttl (the [[SHACL]] constraints an instance document must satisfy) and dprod-contracts-prof.ttl (the profile declaration). The normative evaluation semantics, which define what a conforming evaluator must compute, are maintained in the formal semantics document; authoring guidance for contract writers and policy writers is in the contracts documentation.
The Data Contracts profile is optional. A data product description conforms to DPROD whether or not it carries any policy; an implementation that does process contracts must satisfy the contracts shapes as stated in the Conformance section.
A provider publishes a dprod:DataOffer, an odrl:Offer
setting out the provider's duties, the consumer's rights and what is prohibited. When a consumer accepts
it, the result is a dprod:DataContract, an
odrl:Agreement that binds both parties, may add duties on the consumer, and links back to
the offer it accepts. Each row of the figure names the property that carries it; DPROD terms are shown
in blue. The lower part shows the state an evaluator computes for each duty.
A data product, or any of its datasets, distributions and data services, is an odrl:Asset
when it appears as the odrl:target of a policy. Teams and organisations are
odrl:Party instances. Both may be grouped into ODRL collections using
odrl:partOf; DPROD evaluates membership over the transitive closure of that property, so a
rule on a collection applies to everything in it.
The lifecycle of an offer or a contract is recorded with
dprod:offerLifecycleStatus and
dprod:contractLifecycleStatus. Both are
sub-properties of the core dprod:lifecycleStatus and take any skos:Concept;
DPROD ships dprod:DataContractLifecycleStatus, an optional concept scheme with the values
dprod:Pending, dprod:Active, dprod:Fulfilled and
dprod:Violated, which an enterprise may use, extend or replace.
A DPROD-aware evaluator answers the question "may this agent perform this action on this asset now?"
It never reads a live graph while doing so. Instead the caller assembles one immutable
dprod:EvaluationContext holding the authorisation request,
a snapshot of the state of the world, the requesting agent and the clock. Each
odrl:LeftOperand used in a constraint declares which of those inputs it reads
(dprod:operandSource) and which single property it reads from it
(dprod:operandProperty). A value that is missing, ambiguous or of the wrong type is an
evaluation error rather than a silently false constraint. ODRL's own odrl:dateTime operand
is bound to the context clock, and dprod:currentAgent to the requesting agent.
In this example the Trading Data Team offers its Market Price Data product with a single provider duty (deliver every 15 minutes, within 5 minutes of the scheduled time), one consumer permission and one prohibition. The Analytics Team has accepted the offer for 2026; the contract names both parties and links to the offer it accepts.
{
"@context": [
"https://ekgf.org/dprod/spec/develop/dprod-simple.jsonld",
{ "ex": "https://example.org/" }
],
"id": "ex:tradingMarketData2026",
"type": "DataContract",
"profile": "https://ekgf.org/dprod/spec/develop/",
"assigner": "ex:tradingDataTeam",
"assignee": "ex:analyticsTeam",
"target": "ex:marketPrices",
"contractLifecycleStatus": "Active",
"effectiveDate": "2026-01-15T00:00:00Z",
"expirationDate": "2026-12-31T23:59:59Z",
"permission": {
"type": "Permission",
"assignee": "ex:analyticsTeam",
"action": "display"
},
"acceptsOffer": {
"id": "ex:marketDataFeed",
"type": "DataOffer",
"profile": "https://ekgf.org/dprod/spec/develop/",
"assigner": "ex:tradingDataTeam",
"target": "ex:marketPrices",
"offerLifecycleStatus": "Active",
"obligation": {
"type": "Duty",
"subjectOfDuty": "ex:tradingDataTeam",
"action": "ex:deliver",
"recurrence": "FREQ=MINUTELY;INTERVAL=15",
"deadline": { "@value": "PT5M", "type": "xsd:duration" }
},
"permission": { "type": "Permission", "action": "display" },
"prohibition": { "type": "Prohibition", "action": "distribute" }
}
}
ODRL terms are written as plain names, in the same way DCAT terms are in a data product description.
The context knows that an action, an operator or a status names a vocabulary term, so
"action": "display" denotes odrl:display and
"contractLifecycleStatus": "Active" denotes dprod:Active. The offer is shown nested
inside the contract for readability. In practice a provider publishes the offer once, as its own
resource, and each contract that accepts it refers to it by identifier.
The full version of this example, with the domain vocabulary, the parties and the evaluated duty
instances, is in the Data Offer and Contract worked example.
The following class definitions are driven by the contracts Shapes, in the same way as the core model above. Only classes DPROD defines are listed here; the ODRL classes the profile reuses are defined by [[odrl-model]], and the DPROD properties that extend them follow in the next section.
| Identifier: | dprod:DataOffer |
|---|
A DataOffer is an Offer from a data provider. It defines permissions, duties, and prohibitions for data access. When accepted by a consumer, becomes a DataContract (Agreement).
The ODRL profile token a policy must declare to be recognised as DPROD. Reused by Set, Offer, Agreement, DataOffer, and DataContract shapes.
| Identifier: | odrl:profile |
|---|---|
| Constraint: | DPROD policies must declare odrl:profile <https://ekgf.org/dprod/spec/develop/>. |
| Domain: | dprod:DataOffer |
| Range: |
| Identifier: | odrl:assigner |
|---|---|
| Constraint: | DataOffer must have exactly one assigner. |
| Domain: | dprod:DataOffer |
| Range: |
Optional authored lifecycle status for a DataOffer. The value must be a skos:Concept; DPROD's scheme is optional and profiles may use an enterprise taxonomy.
| Identifier: | dprod:offerLifecycleStatus |
|---|---|
| Label: | offer lifecycle status |
| Comment: | Value is any skos:Concept; dprod:DataContractLifecycleStatus is one example concept scheme shipped with DPROD. |
| Constraint: | offerLifecycleStatus, if present, must be a single skos:Concept value. |
| Domain: | dprod:DataOffer |
| Range: | skos:Concept |
When the offer or contract becomes effective.
| Identifier: | dprod:effectiveDate |
|---|---|
| Label: | effective date |
| Constraint: | effectiveDate, if present, must be a single xsd:dateTime value. |
| Domain: | dprod:DataOffer |
| Range: | xsd:dateTime |
When the offer or contract expires.
| Identifier: | dprod:expirationDate |
|---|---|
| Label: | expiration date |
| Constraint: | expirationDate, if present, must be a single xsd:dateTime value. |
| Domain: | dprod:DataOffer |
| Range: | xsd:dateTime |
| Identifier: | dprod:DataContract |
|---|
A DataContract is an activated DataOffer. - assigner: data provider (from offer) - assignee: data consumer Bilateral: both parties may have active duties.
The ODRL profile token a policy must declare to be recognised as DPROD. Reused by Set, Offer, Agreement, DataOffer, and DataContract shapes.
| Identifier: | odrl:profile |
|---|---|
| Constraint: | DPROD policies must declare odrl:profile <https://ekgf.org/dprod/spec/develop/>. |
| Domain: | dprod:DataContract |
| Range: |
| Identifier: | odrl:assigner |
|---|---|
| Constraint: | DataContract must have exactly one assigner (data provider). |
| Domain: | dprod:DataContract |
| Range: |
| Identifier: | odrl:assignee |
|---|---|
| Constraint: | DataContract must have exactly one assignee (data consumer). |
| Domain: | dprod:DataContract |
| Range: |
The offer this contract activates.
| Identifier: | dprod:acceptsOffer |
|---|---|
| Label: | accepts offer |
| Constraint: | DataContract must reference exactly one DataOffer. |
| Domain: | dprod:DataContract |
| Range: | dprod:DataOffer |
Optional authored lifecycle status for a DataContract. The value must be a skos:Concept; DPROD's scheme is optional and profiles may use an enterprise taxonomy.
| Identifier: | dprod:contractLifecycleStatus |
|---|---|
| Label: | contract lifecycle status |
| Comment: | Value is any skos:Concept; dprod:DataContractLifecycleStatus is one example concept scheme shipped with DPROD. |
| Constraint: | contractLifecycleStatus, if present, must be a single skos:Concept value. |
| Domain: | dprod:DataContract |
| Range: | skos:Concept |
When the offer or contract becomes effective.
| Identifier: | dprod:effectiveDate |
|---|---|
| Label: | effective date |
| Constraint: | effectiveDate, if present, must be a single xsd:dateTime value. |
| Domain: | dprod:DataContract |
| Range: | xsd:dateTime |
When the offer or contract expires.
| Identifier: | dprod:expirationDate |
|---|---|
| Label: | expiration date |
| Constraint: | expirationDate, if present, must be a single xsd:dateTime value. |
| Domain: | dprod:DataContract |
| Range: | xsd:dateTime |
| Identifier: | dprod:EvaluationContext |
|---|
The immutable container for runtime operand resolution. An evaluator constructs one EvaluationContext for each evaluation and supplies the authorization request, an immutable state-of-the-world snapshot, the requesting agent, and the evaluation clock through the four normative properties. The context is a prov:Entity so implementations can retain the exact input provenance needed to reproduce and audit a decision. Evaluation never reads a live global graph or dereferences an undeclared source while resolving an operand binding.
Authorization request supplied for one policy evaluation.
| Identifier: | dprod:request |
|---|---|
| Label: | request |
| Comment: | Selects the normalized request node used by dprod:requestSource operand bindings. |
| Constraint: | An evaluation context must have exactly one dprod:request graph root. |
| Domain: | dprod:EvaluationContext |
| Range: | rdfs:Resource |
Immutable state-of-the-world snapshot supplied for one policy evaluation.
| Identifier: | dprod:state |
|---|---|
| Label: | state |
| Comment: | Selects the normalized snapshot node used by dprod:stateSource operand bindings. The object is supplied by the evaluator, never an instruction to query live external state. |
| Constraint: | An evaluation context must have exactly one immutable dprod:state snapshot, typed as prov:Entity. |
| Domain: | dprod:EvaluationContext |
| Range: | prov:Entity |
Requesting agent supplied for one policy evaluation.
| Identifier: | dprod:agent |
|---|---|
| Label: | agent |
| Constraint: | An evaluation context must have exactly one dprod:agent typed as odrl:Party. |
| Domain: | dprod:EvaluationContext |
| Range: | odrl:Party |
Immutable timestamp at which policy evaluation occurs.
| Identifier: | dprod:clock |
|---|---|
| Label: | clock |
| Constraint: | An evaluation context must have exactly one dprod:clock xsd:dateTime. |
| Domain: | dprod:EvaluationContext |
| Range: | xsd:dateTime |
The properties below are defined by DPROD but apply to ODRL classes. They are what turns a generic ODRL duty into a data-contract obligation, and a generic left operand into one an evaluator can resolve deterministically. The Domain column names the ODRL class each property is used on.
Time constraint for duty fulfillment.
| Identifier: | dprod:deadline |
|---|---|
| Label: | deadline |
| Comment: | Supports multiple forms: - xsd:dateTime: absolute deadline (e.g., 2026-12-31T23:59:59Z) - xsd:duration: relative to activation (e.g., P30D, PT24H) For duration: deadline = activationTime + duration. No rdfs:range declared because the range is a union of datatypes; SHACL enforces the allowed types. |
| Domain: | odrl:Duty |
The latest evaluation result for this duty, computed by an evaluator from observable facts; recomputed on demand rather than authored.
| Identifier: | dprod:dutyState |
|---|---|
| Label: | duty state |
| Comment: | Value is any skos:Concept. The state machine in formal-semantics §5 is one possible interpretation. Implementations are free to use a different state vocabulary and/or state machine. This evaluator-computed property is deliberately separate from the authored dprod:lifecycleStatus hierarchy. |
| Domain: | odrl:Duty |
| Range: | skos:Concept |
Party affected by the duty action.
| Identifier: | dprod:objectOfDuty |
|---|---|
| Label: | object |
| Comment: | The party affected by or receiving the result of the duty action. Sub-property of odrl:function for ODRL processor compatibility. |
| Domain: | odrl:Duty |
| Range: | odrl:Party |
RFC 5545 RRULE defining when duty instances are generated.
| Identifier: | dprod:recurrence |
|---|---|
| Label: | recurrence |
| Comment: | An RFC 5545 RRULE string (e.g., FREQ=DAILY;BYHOUR=6;BYMINUTE=0). Defines the schedule on which duty instances are created. Each instance follows the standard duty lifecycle independently. The deadline property defines the per-instance fulfillment window. Any iCal-compliant library can parse the value. |
| Domain: | odrl:Duty |
| Range: | xsd:string |
Party bearing the duty (must perform the action).
| Identifier: | dprod:subjectOfDuty |
|---|---|
| Label: | subject |
| Comment: | The duty bearer. When omitted, the bearer defaults to the data product owner (dprod:dataProductOwner) of the data product the policy applies to. Sub-property of odrl:function for ODRL processor compatibility. |
| Domain: | odrl:Duty |
| Range: | odrl:Party |
Single property read from an operand's normalized source graph.
| Identifier: | dprod:operandProperty |
|---|---|
| Label: | operand property |
| Comment: | The property MUST be an IRI and is looked up directly on the node selected by dprod:operandSource. Request and state producers MUST normalize every policy-relevant fact onto their respective source node. RDF lists and multi-step traversal are deliberately unsupported. |
| Domain: | odrl:LeftOperand |
Normalized graph from which an operand reads its value.
| Identifier: | dprod:operandSource |
|---|---|
| Label: | operand source |
| Comment: | Exactly one of dprod:requestSource, dprod:stateSource, or dprod:contextSource. The source is selected before the evaluator performs its single property lookup. |
| Domain: | odrl:LeftOperand |
| Range: | dprod:OperandSource |
Logical negation on a constraint.
| Identifier: | dprod:not |
|---|---|
| Label: | not |
| Comment: | ODRL defines odrl:and and odrl:or on LogicalConstraint but lacks negation. DPROD adds dprod:not following the same pattern: _:lc a odrl:LogicalConstraint ; dprod:not _:c1 . |
| Domain: | odrl:LogicalConstraint |
| Range: |
This section contains some worked examples illustrating how to use [[dprod]] for some common use cases. All these examples are provided as accompanying machine-readable files, from which this part of the specification is automatically generated (hence soem formatting variations).
For real world data products, the core data product details will be part of a wider set of metadata that allows the data and data product to be used effectively.
Below is an example of extending the DPROD data product, specifically by adding an agreement to a data product.
In this example, a Data Product Agreement is defined as a subclass of FIBO Agreement.
Definition of a simple Agreement based on FIBO:
{
"@context": [
"https://ekgf.org/dprod/spec/develop/dprod-simple.jsonld",
{
"fibo": "http://spec.edmcouncil.org/fibo/ontology/FND/Agreements/MetadataFNDAgreements/#",
"ex": "http://example.org/dp#"
}
],
"@graph": [
{
"id": "ex:isSubjectToAgreement",
"type": "rdf:Property",
"label": "Data Product is Subject To FIBO Agreement",
"rdfs:domain": { "id": "dprod:DataProduct" },
"rdfs:range": { "id": "ex:DataProductAgreement" }
},
{
"id": "ex:DataProductAgreement",
"type": "rdfs:Class",
"label": "DataProductAgreement",
"rdfs:subClassOf": { "id": "fibo:Agreement" }
}
]
}
A full definition of agreements for data products is likely to be more complex than a single class and may use other information models or their profiles (such as ODRL Policy) or create dedicated definitions.
Below is an example of a Data Product with an associated Data Product Agreement with an effective date.
Using the agreement:
{
"@context": [
"https://ekgf.org/dprod/spec/develop/dprod-simple.jsonld",
{
"fibo": "http://spec.edmcouncil.org/fibo/ontology/FND/Agreements/MetadataFNDAgreements/#",
"ex": "http://example.org/dp#"
}
],
"@graph": [
{
"id": "https://y.com/data-product/company-sales",
"type": "DataProduct",
"outputPort": {
"id": "https://y.com/data-product/company-sales/port/2025-sales",
"type": "DataService",
"label": "Sales",
"endpointURL": "https://y.com/data-product/company-sales/port/2025-sales",
"isAccessServiceOf": {
"id": "https://y.com/data-product/company-sales/distribution/2025-sales",
"type": "Distribution",
"format": "https://www.iana.org/assignments/media-types/application/json",
"isDistributionOf": {
"id": "https://y.com/data-product/company-sales/dataset/2025-sales",
"type": "Dataset",
"label": "Sales",
"conformsTo": "https://y.com/schema/Sale"
}
}
},
"ex:isSubjectToAgreement": { "id": "ex:VVSimpleAgreement" }
},
{
"id": "ex:VVSimpleAgreement",
"type": "ex:DataProductAgreement",
"label": "Very Simple Data Product Agreement",
"fibo:hasEffectiveDate": { "type": "xsd:date", "@value": "2024-08-31" }
}
]
}
It is important to be able to trace the lineage of data. Within DPROD, this can be done in two ways: at a high level from one data product to another and, if desired, at the more detailed level of the underlying datasets.
Data products have input and output ports, and one data product’s input port will point to another data product’s output port.
This allows a user to query the lineage. The data products all have URLs as identifiers, and properties all connect to each other, so a query can walk from one data product to the downstream data products that feed it.
One can follow the path that leads from one data product to another like this:
Data Product >> inputPort >> isAccessServiceOf >> isDistributionOf >> Input Data Product
The following example data has three data products that connect to each other through their input and output ports:
{
"@context": "https://ekgf.org/dprod/spec/develop/dprod-simple.jsonld",
"@graph": [
{
"id": "https://y.com/data-product/company-finance",
"type": "DataProduct",
"inputPort": [
{
"id": "https://y.com/data-product/company-sales/port/2025-sales",
"type": "DataService"
},
{
"id": "https://y.com/data-product/company-hr/port/2025-payroll",
"type": "DataService"
}
],
"outputPort": {
"id": "https://y.com/data-product/company-sales/port/2025-balance-sheet",
"type": "DataService",
"label": "Balance Sheet",
"endpointURL": "https://y.com/data-product/company-sales/port/2025-c",
"isAccessServiceOf": {
"type": "Distribution",
"format": "https://www.iana.org/assignments/media-types/application/json",
"isDistributionOf": {
"id": "https://y.com/data-product/company-sales/dataset/2025-balance-sheet",
"type": "Dataset",
"conformsTo": "https://y.com/schema/BalanceSheet"
}
}
}
},
{
"id": "https://y.com/data-product/company-sales",
"type": "DataProduct",
"outputPort": {
"id": "https://y.com/data-product/company-sales/port/2025-sales",
"type": "DataService",
"label": "Sales",
"endpointURL": "https://y.com/data-product/company-sales/port/2025-sales",
"isAccessServiceOf": {
"type": "Distribution",
"format": "https://www.iana.org/assignments/media-types/application/json",
"isDistributionOf": {
"id": "https://y.com/data-product/company-sales/dataset/2025-sales",
"type": "Dataset",
"label": "Sales",
"conformsTo": "https://y.com/schema/Sale"
}
}
}
},
{
"id": "https://y.com/data-product/company-hr",
"type": "DataProduct",
"outputPort": {
"id": "https://y.com/data-product/company-sales/port/2025-payroll",
"type": "DataService",
"label": "Payroll",
"endpointURL": "https://y.com/data-product/company-hr/port/2025-payroll",
"isAccessServiceOf": {
"type": "Distribution",
"format": "https://www.iana.org/assignments/media-types/text/csv",
"isDistributionOf": {
"id": "https://y.com/data-product/company-sales/dataset/2025-payroll",
"type": "Dataset",
"label": "Payroll",
"conformsTo": "https://y.com/schema/Payroll"
}
}
}
}
]
}
Given this example data, starting at the data product
https://y.com/data-product/company-finance,
one could walk the relationships to find the input data products that feed it:
https://y.com/data-product/company-finance >>
:inputPort >>
:isAccessServiceOf >>
:isDistributionOf >> [
https://y.com/data-product/company-sales,
https://y.com/data-product/company-hr
]
In Linked Data, this would use a query such as:
PREFIX : <https://y.com/data-product/>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX dprod: <https://ekgf.org/dprod/spec/develop/>
SELECT DISTINCT ?input
WHERE
{
:company-finance dprod:inputPort ?inputPort .
?inputPort dprod:isAccessServiceOf/dprod:isDistributionOf/rdfs:label ?input .
}
To track lineage at a more granular level, one can also use PROV (https://www.w3.org/TR/prov-o/) at the dataset level.
@prefix dap: <https://data.csiro.au/dap/> . @prefix dcat: <http://www.w3.org/ns/dcat#> . @prefix dcterms: <http://purl.org/dc/terms/> . @prefix prov: <http://www.w3.org/ns/prov#> . @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> . @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . @prefix xsd: <http://www.w3.org/2001/XMLSchema#> . dap:atnf-P366-2003SEPT rdf:type dcat:Dataset ; dcterms:bibliographicCitation "Burgay, M; McLaughlin, M; Kramer, M; Lyne, A; Joshi, B; Pearce, G; D'Amico, N; Possenti, A; Manchester, R; Camilo, F (2017): Parkes observations for project P366 semester 2003SEPT. v1. CSIRO. Data Collection. https://doi.org/10.4225/08/598dc08d07bb7" ; dcterms:title "Parkes observations for project P366 semester 2003SEPT"@en ; dcat:landingPage <https://data.csiro.au/dap/landingpage?pid=csiro:P366-2003SEPT> ; prov:wasGeneratedBy dap:P366 ; . dap:P366 rdf:type prov:Activity ; dcterms:type <http://dbpedia.org/resource/Observation> ; prov:startedAtTime "2000-11-01"^^xsd:date ; prov:used dap:Parkes-radio-telescope ; prov:wasInformedBy dap:ATNF ; rdfs:label "P366 - Parkes multibeam high-latitude pulsar survey"@en ; rdfs:seeAlso <https://doi.org/10.1111/j.1365-2966.2006.10100.x> ; .
See: https://www.w3.org/TR/vocab-dcat-3/#examples-dataset-provenance.
{
"@context": "https://ekgf.org/dprod/spec/develop/dprod-simple.jsonld",
"@graph": [
{
"id": "https://y.com/derived-quality-measurementA",
"type": "QualityMeasurement",
"value": 1,
"computedOn": { "id": "https://y.com/products/uk-bonds", "type": "DataProduct" },
"isMeasurementOf": {
"id": "https://y.com/metric/stale-dataset-count",
"type": "Metric",
"label": "Number of stale datasets"
}
},
{
"id": "https://y.com/quality-measurement-B",
"type": "QualityMeasurement",
"value": false,
"computedOn": {
"id": "https://y.com/products/uk-bonds/yearlyPrices",
"type": "Dataset"
},
"isMeasurementOf": {
"id": "https://y.com/metric/expected-distribution-frequency-achieved",
"type": "Metric",
"label": "Expected distribution frequency achieved"
}
},
{
"id": "https://y.com/products/uk-bonds",
"type": "DataProduct",
"outputPort": {
"id": "https://y.com/service/uk-bonds-quality-report",
"type": "DataService",
"endpointURL": "https://y.com/uk-bonds/quality-report",
"isAccessServiceOf": {
"id": "https://y.com/distribution/uk-bonds-quality-report",
"type": "Distribution",
"isDistributionOf": {
"id": "https://y.com/dataset/uk-bonds-quality-measurements",
"type": "Dataset",
"conformsTo": "http://www.w3.org/ns/dqv#QualityMeasurement"
}
}
}
}
]
}
ODRL is a W3C standard to describe rights and entitlements. Based on ODRL, data product and dataset publishers can describe policies in a consistent, standard and machine-readable manner. Policies contain permissions and prohibitions on specific actions that are required to be met by stakeholders.
In addition, policies may be limited by constraints (eg. temporal or geographical constraints) and duties (eg. payments) that may be imposed on the permissions.
Policies and their permitted or prohibited actions can be described at different levels, eg. a policy can target a data product, a dataset, a data service or even a column.
Sophisticated engines should interpret and enforce the ODRL policies at the appropriate level eg.:
@prefix odrl: <http://www.w3.org/ns/odrl/2/> . @prefix examplePolicy: <https://data.org/policy/> . @prefix exampleProduct: <https://data.org/data-product/> . @prefix exampleDataset: <https://data.org/dataset/> . examplePolicy:A odrl:target exampleProduct:ProductA . examplePolicy:B odrl:target exampleDataset:DatasetA1 .
An example of an agreement follows, that describes permission to use all the datasets of the product if the user is working inside EMEA or APAC:
{
"@context": [
"https://ekgf.org/dprod/spec/develop/dprod-simple.jsonld",
{ "reg": "https://www.region.taxonomy/v/1/" }
],
"id": "https://data.org/policy/examplePolicyA",
"type": "Agreement",
"permission": [
{
"target": { "id": "https://data.org/data-product/equity-trade-xxx" },
"action": "use",
"assignee": {
"id": "https://example.org/DataDepartment/emea-and-apac-staff",
"type": "PartyCollection",
"refinement": [
{
"leftOperand": "odrl:spatial",
"operator": "isAnyOf",
"rightOperand": [
{ "id": "reg:EMEA" },
{ "id": "reg:APAC" }
]
}
]
}
}
]
}
{
"@context": [
"https://ekgf.org/dprod/spec/develop/dprod-simple.jsonld",
{ "reg": "https://www.region.taxonomy/v/1/" }
],
"id": "https://data.org/policy/56456df-dfg-34535345-5545",
"type": "Agreement",
"permission": [
{
"target": { "id": "https://data.org/data-product/equity-trade-xxx" },
"assigner": { "id": "https://schema.org/person/AdamSmith" },
"assignee": {
"id": "https://example.org/DataDepartment/emea-and-apac-staff",
"type": "PartyCollection",
"source": { "id": "https://example.org/DataDepartment" },
"refinement": [
{
"leftOperand": "odrl:spatial",
"operator": "isAnyOf",
"rightOperand": [
{ "id": "reg:EMEA" },
{ "id": "reg:APAC" }
],
"description": "Permission to read all the datasets of the product if the user is working inside EMEA or APAC"
}
]
},
"action": "use"
}
]
}
The Data Product provides to the consumers (dprod:outputDataset) datasets defined based on DCAT. Datasets should be described (dcat:conforms) with logical models. Logical models describe business entities and their properties (attributes and relationships) with consistent business terms and they are technology independent. Ideally, logical models are based on existing standards eg, FIBO, CDM etc. If a logical model does not exist to describe the dataset, then the dataset publisher can create one, preferably by using SHACL modelling language:
Example of a Dataset conforming to a SHACL Schema:
exampleDataset dcat:conforms exampleSchema:DatasetLogicalSchema. exampleSchema:DatasetLogicalSchema a owl:Ontology, dct:Standard.
Based on SHACL all entities that exist in the dataset are Node Shapes (1). The attributes of the entities are described as Property Shapes with sh:datatype (2) The relationships are also defined as Property Shaped with sh:class the target class of the relationship (3)
@prefix dc: <http://purl.org/dc/elements/1.1/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix example: <https://y.com/schema/> .
@prefix exampleSchema: <https://y.com/schema/logical/> .
# definition of the entity as a Node Shape (1)
example:Account a sh:NodeShape ;
# human readable name of the entity
rdfs:label "Account"@en ;
# description of the entity
dc:description "An Account is..." ;
# an account has a property shape Account Age.
# Definition of the property shape follows (2)
sh:property example:Account-AccountAge ;
# an account has a property shape Account Branch.
# Definition of the property shape follows (3)
sh:property example:Account-AccountBranch ;
rdfs:isDefinedBy exampleSchema:DatasetLogicalSchema;
.
# (2) Definition of the Account-AccountAge property shape
# describing that an account MUST have exactly one
# AccountAge attribute and its datatype is integer
example:Account-AccountAge a sh:PropertyShape ;
sh:path example:AccountAge ;
sh:datatype xsd:integer ;
sh:minCount 1 ;
sh:maxCount 1 ;
rdfs:isDefinedBy exampleSchema:DatasetLogicalSchema ;
.
# (3) Definition of the Account-AccountBranch property shape
# describing than an account must have at least one
# Account Branch which is another entity
example:Account-AccountBranch a sh:PropertyShape ;
sh:path example:AccountBranch ;
sh:class example:Branch ;
sh:minCount 1 ;
rdfs:isDefinedBy exampleSchema:DatasetLogicalSchema ;
.
# definition of the entity Branch as a Node Shape (1)
example:Branch a sh:NodeShape ;
rdfs:label "Branch"@en ;
dc:description "A Branch is.." ;
rdfs:isDefinedBy exampleSchema:DatasetLogicalSchema ;
.
{
"@context": "https://ekgf.org/dprod/spec/develop/dprod-simple.jsonld",
"id": "https://y.com/products/equity-trade-xxx",
"type": "DataProduct",
"title": "Equity Trade XXX",
"description": "Trade data defining the outcome of equity trades between parties in different stock markets, where the terms are primarily reflected in the tradable product. Additionally, Trade includes attributes such as the trade date, transacting parties, and settlement terms. Some attributes, such as the parties, are already defined in the Party Product and are simply referenced in Trade",
"outputPort": {
"id": "https://y.com/service/equity-trade-euronext-paris",
"type": "DataService",
"endpointURL": "abfss://datasetsv1@demo.dfs.core.windows.net/demo/full/trade-euronext",
"isAccessServiceOf": {
"id": "https://y.com/distribution/equity-trade-euronext-paris-parquet",
"type": "Distribution",
"format": "https://www.iana.org/assignments/media-types/application/vnd.apache.parquet",
"isDistributionOf": {
"id": "https://y.com/dataset/equity-trade-euronext-paris",
"type": "Dataset",
"title": "Equity Trade Euronext Paris XXX",
"conformsTo": "https://spec.edmcouncil.org/fibo/ontology/BP/Process/FinancialContextAndProcess/SecuritiesTrade"
}
}
}
}
Example of a data product for Equity Trades.
The Equity Trades Data Product provides two datasets to the consumers: one for trades in London Stock Exchange (LSEG) and one in Euronext.
{
"@context": [
"https://ekgf.org/dprod/spec/develop/dprod-simple.jsonld",
{ "ex": "https://y.com/vocabulary/" }
],
"id": "https://y.com/products/equity-trade-xxx",
"type": "DataProduct",
"identifier": "equity-trade-xxx",
"title": "Equity Trade XXX",
"description": "Trade data defining the outcome of equity trades between parties in different stock markets, where the terms are primarily reflected in the tradable product. Additionally, Trade includes attributes such as the trade date, transacting parties, and settlement terms. Some attributes, such as the parties, are already defined in the Party Product and are simply referenced in Trade",
"dataProductOwner": "https://www.schema.xxx/person/AnnTaylor",
"dataProductLifecycleStatus": "https://ekgf.org/dprod/spec/develop/data/lifecycle-status/Consume",
"outputPort": [
{
"id": "https://y.com/service/equity-trade-euronext-xxx-adls-prod-1",
"type": "DataService",
"identifier": "equity-trade-euronext-xxx-tabular-adls-prod",
"endpointURL": "abfss://datasetsv1@demo.dfs.core.windows.net/demo/full/trade-euronext",
"description": "Details for accessing storage account",
"isAccessServiceOf": {
"id": "https://y.com/distribution/equity-trade-euronext-xxx-tabular",
"type": "Distribution",
"identifier": "equity-trade-euronext-xxx-tabular",
"format": "https://www.iana.org/assignments/media-types/application/vnd.apache.parquet",
"conformsTo": "https://cdm.finos.org/docs/event-model",
"isDistributionOf": {
"id": "https://y.com/dataset/equity-trade-euronext-paris",
"type": "Dataset",
"identifier": "equity-trade-euronext-paris-xxx",
"title": "Equity Trade Euronext Paris XXX",
"ex:datasetOwner": { "id": "https://www.schema.xxx/person/JohnBarks" },
"conformsTo": "https://cdm.finos.org/docs/event-model"
}
}
},
{
"id": "https://y.com/service/equity-trade-lseg-xxx-adls-prod-1",
"type": "DataService",
"identifier": "equity-trade-lseg-xxx-tabular-adls-prod",
"endpointURL": "abfss://datasetsv1@demo.dfs.core.windows.net/demo/full/trade-lseg",
"description": "Details for accessing storage account",
"isAccessServiceOf": {
"id": "https://y.com/distribution/equity-trade-lseg-xxx-tabular",
"type": "Distribution",
"identifier": "equity-trade-lseg-xxx-tabular",
"format": "https://www.iana.org/assignments/media-types/application/vnd.apache.parquet",
"conformsTo": "https://cdm.finos.org/docs/event-model",
"isDistributionOf": {
"id": "https://y.com/dataset/equity-trade-lseg-xxx",
"type": "Dataset",
"identifier": "equity-trade-lseg-xxx",
"title": "Equity Trade LSEG XXX",
"conformsTo": "https://cdm.finos.org/docs/event-model"
}
}
}
]
}
An Observability Port is a designated interface or endpoint in a system or application specifically used for monitoring and diagnostic purposes. It allows external tools or services to collect and analyze data related to the system's performance, health, and behaviour. By exposing metrics, logs, and traces through this port, administrators and developers can gain insights into the system's state, troubleshoot issues, and ensure it operates efficiently and reliably.
DPROD has a schema-first design. The first thing to do is define a schema for the logging information. It could be a schema based on OpenTelemetry, but this uses RLOG (which is a semantic ontology for logging).
To find the Observability Port, query the ports to identify the
ones that return an RLOG:Entry:
outputPort >> isAccessServiceOf >> isDistributionOf >> conformsTo >> rlog:Entry
One can see that the example data product has two ports, one with the data
and one with the logging.
This query will return the URI of the port that returns logging
data: https://y.com/uk-bonds/observability-port.
Here is an example of a data product with an observability port:
{
"@context": "https://ekgf.org/dprod/spec/develop/dprod-simple.jsonld",
"@graph": [
{
"id": "https://y.com/data-product/uk-bonds",
"type": "DataProduct",
"inputPort": [
{
"id": "https://y.com/data-product/uk-bonds/port/2024-data",
"type": "DataService"
}
],
"outputPort": [
{
"id": "https://y.com/data-product/uk-bonds/port/2024-observability",
"type": "DataService",
"label": "Observability Port",
"endpointURL": "https://y.com/data-product/uk-bonds/port/2024-observability",
"isAccessServiceOf": {
"type": "Distribution",
"format": "https://www.iana.org/assignments/media-types/application/json",
"isDistributionOf": {
"id": "https://y.com/data-product/uk-bonds/dataset/2024-observability",
"type": "Dataset",
"conformsTo": "https://y.com/schema/ObservabilityLog"
}
}
},
{
"id": "https://y.com/data-product/uk-bonds/port/2024-data",
"type": "DataService",
"label": "Data Port",
"endpointURL": "https://y.com/data-product/uk-bonds/port/2024-data",
"isAccessServiceOf": {
"type": "Distribution",
"format": "https://www.iana.org/assignments/media-types/application/json",
"isDistributionOf": {
"id": "https://y.com/data-product/uk-bonds/dataset/2024-data",
"type": "Dataset",
"conformsTo": "https://y.com/schema/Data"
}
}
}
]
}
]
}
Given that the schema defines the class for an observation, it can be used to find all observability ports on data product like this:
[https://y.com/data-product/uk-bonds/port/2024-observability] >> isAccessServiceOf >> isDistributionOf >> conformsTo >> https://y.com/schema/ObservabilityLog
In Linked Data a SPARQL query would do that:
SELECT ?port
WHERE
{
?port a dcat:DataService .
?port (dprod:isAccessServiceOf/dprod:isDistributionOf)/dcat:conformsTo rlog:Entry
}
This query will return the URI of the port that provides logging
data: https://y.com/data-product/uk-bonds/port/2024-observability.
Rates for SBA Pool that are Mortgage Backed Securities.
The data product is provided through 3 ports:
{
"@context": [
"https://ekgf.org/dprod/spec/develop/dprod-simple.jsonld",
{ "ex": "https://y.com/vocabulary/" }
],
"id": "https://y.com/products/sba-pool-rates",
"type": "DataProduct",
"identifier": "sba-pool-rates",
"title": "SBA Pool Rates",
"description": "Rates for SBA Pool that are Mortgage Backed Securities. The data product is provided through 3 ports, one of them proving all SBA pool rates through a query to a database, another port providing EMEA only rates through an api and another one providing US only rates through a Kafka topic",
"dataProductOwner": "https://www.schema.xxx/person/johnSmith",
"dataProductLifecycleStatus": "https://ekgf.org/dprod/spec/develop/data/lifecycle-status/Consume",
"outputPort": [
{
"id": "https://y.com/service/sba-pool-rate-tabular-prod1",
"type": "DataService",
"identifier": "sba-pool-rate-tabular-prod1",
"ex:environment": "PROD",
"endpointURL": "jdbc:oracle:thin@sd656-5656-6745.ldn.organiation.com:43534/PGPERG.WORLD",
"ex:sql": "select * from ...",
"isAccessServiceOf": {
"id": "https://y.com/distribution/sba-pool-rate-tabular",
"type": "Distribution",
"identifier": "sba-pool-rate-tabular",
"format": "https://www.iana.org/assignments/media-types/application/sql",
"isDistributionOf": {
"id": "https://y.com/dataset/sba-pool-rate",
"type": "Dataset",
"identifier": "sba-pool-rate",
"conformsTo": "https://spec.edmcouncil.org/fibo/ontology/SEC/Debt/MortgageBackedSecurities/SBA-Pool"
}
}
},
{
"id": "https://y.com/service/sba-pool-rate-emea-api-prod1",
"type": "DataService",
"identifier": "sba-pool-rate-emea-api-prod1",
"ex:environment": "PROD",
"endpointURL": "https://example.org/mbs/SBA-Pool-location-emea",
"conformsTo": "https://y.com/resources/users.yaml",
"isAccessServiceOf": {
"id": "https://y.com/distribution/sba-pool-rate-emea-json1",
"type": "Distribution",
"identifier": "sba-pool-rate-emea-json1",
"format": "https://www.iana.org/assignments/media-types/application/json",
"isDistributionOf": {
"id": "https://y.com/dataset/sba-pool-rate-emea",
"type": "Dataset",
"identifier": "sba-pool-rate-emea",
"description": "SBA pool data that cover EMEA accessed through an api",
"spatial": "https://y.com/country/EMEA",
"conformsTo": "https://spec.edmcouncil.org/fibo/ontology/SEC/Debt/MortgageBackedSecurities/SBA-Pool"
}
}
},
{
"id": "https://y.com/service/sba-pool-rate-json-prod1",
"type": "DataService",
"identifier": "sba-pool-rate-json-prod1",
"endpointURL": "kafka://q1.debt.mbs.dataset.us",
"isAccessServiceOf": {
"id": "https://y.com/distribution/sba-pool-rate-json",
"type": "Distribution",
"identifier": "sba-pool-rate-json",
"format": "https://www.iana.org/assignments/media-types/application/json",
"conformsTo": "http://confluent-registry-y/rates-json-schema.json",
"ex:schemaCompatibility": "backwards compatible",
"isDistributionOf": {
"id": "https://y.com/dataset/sba-pool-rate-us",
"type": "Dataset",
"identifier": "sba-pool-rate-us",
"spatial": "https://y.com/country/US",
"conformsTo": "https://spec.edmcouncil.org/fibo/ontology/SEC/Debt/MortgageBackedSecurities/SBA-Pool"
}
}
}
]
}
This section illustrates the Data Contracts profile. The examples build on
one another: the first shows an offer and the contract that accepts it, and the later ones each pick
out one thing a contract or policy can express. All use the same fictional organisation, and each
document is self-contained, so the domain vocabulary it needs (actions, operands and concept values)
is declared inline under the ex: prefix. Every example validates against
dprod-contracts-shapes.ttl. Like the examples above, this
part of the specification is generated from the accompanying machine-readable files.
A data contract in DPROD is an ODRL 2.2 policy. The provider of a data product publishes a dprod:DataOffer (an odrl:Offer) stating what it commits to deliver, what consumers may do with the data, and what they may not do. A consumer accepts that offer, and the acceptance is recorded as a dprod:DataContract (an odrl:Agreement) that names both parties and points at the offer through dprod:acceptsOffer.
Below, the Trading Data Team offers its market prices with two commitments of its own, a delivery every 15 minutes with a 5-minute deadline and conformance to a published schema, and one duty the consumer takes on, a monthly usage report. Consumers may display and derive from the data but may not redistribute it. The Analytics Team has accepted the offer for 2026.
Three things are worth noticing:
dprod:subjectOfDuty says who must act. The usage report has no subject in the offer because the consumer is not yet known; it becomes the consumer's duty when the contract is made.odrl:target, because it is about the schema rather than the data.odrl:assignee; a contract must carry at least one rule of its own.ODRL terms are written as plain names, just as DCAT terms are in a data product description. The simple context knows that an action, an operator or a status names a vocabulary term, so "action": "display" denotes odrl:display without any wrapping. Only values whose datatype the context cannot know, such as a deadline that may be a duration or a timestamp, carry an explicit type.
{
"@context": [
"https://ekgf.org/dprod/spec/develop/dprod-simple.jsonld",
{ "ex": "https://example.org/" }
],
"id": "ex:tradingMarketData2026",
"type": "DataContract",
"profile": "https://ekgf.org/dprod/spec/develop/",
"assigner": "ex:tradingDataTeam",
"assignee": "ex:analyticsTeam",
"target": "ex:marketPrices",
"contractLifecycleStatus": "Active",
"effectiveDate": "2026-01-15T00:00:00Z",
"expirationDate": "2026-12-31T23:59:59Z",
"permission": {
"type": "Permission",
"assignee": "ex:analyticsTeam",
"action": "display"
},
"prohibition": {
"type": "Prohibition",
"assignee": "ex:analyticsTeam",
"action": "distribute"
},
"acceptsOffer": {
"id": "ex:marketDataFeed",
"type": "DataOffer",
"profile": "https://ekgf.org/dprod/spec/develop/",
"assigner": "ex:tradingDataTeam",
"target": "ex:marketPrices",
"offerLifecycleStatus": "Active",
"obligation": [
{
"type": "Duty",
"subjectOfDuty": "ex:tradingDataTeam",
"action": "ex:deliver",
"recurrence": "FREQ=MINUTELY;INTERVAL=15",
"deadline": { "@value": "PT5M", "type": "xsd:duration" }
},
{
"type": "Duty",
"subjectOfDuty": "ex:tradingDataTeam",
"action": "ex:conformTo",
"target": "ex:marketDataSchema"
},
{
"type": "Duty",
"action": "ex:reportUsage",
"recurrence": "FREQ=MONTHLY;BYMONTHDAY=1",
"deadline": { "@value": "P5D", "type": "xsd:duration" }
}
],
"permission": [
{ "type": "Permission", "action": "display" },
{ "type": "Permission", "action": "derive" }
],
"prohibition": [
{ "type": "Prohibition", "action": "distribute" }
]
}
}
Most of what a data contract promises is expressed as duties on the provider. DPROD needs no new rule type for this: each is an ordinary odrl:Duty in the offer's odrl:obligation, with dprod:subjectOfDuty naming the provider as the party that must act. Four patterns cover almost every service-level commitment:
dprod:recurrence (an RFC 5545 recurrence rule) says when each delivery is due and dprod:deadline how long after that the provider has to deliver.odrl:target instead of inheriting the offer's.odrl:constraint. The constraint's left operand is declared where it is used: dprod:operandSource and dprod:operandProperty tell an evaluator to read ex:timeliness from the authorisation request.Consumer duties use exactly the same construct, minus the subject.
{
"@context": [
"https://ekgf.org/dprod/spec/develop/dprod-simple.jsonld",
{ "ex": "https://example.org/" }
],
"id": "ex:riskMetricsOffer",
"type": "DataOffer",
"profile": "https://ekgf.org/dprod/spec/develop/",
"assigner": "ex:dataPlatformTeam",
"target": "ex:riskMetrics",
"obligation": [
{
"type": "Duty",
"label": "Delivery: every day at 07:00, within 30 minutes",
"subjectOfDuty": "ex:dataPlatformTeam",
"action": "ex:deliver",
"recurrence": "FREQ=DAILY;BYHOUR=7;BYMINUTE=0",
"deadline": { "@value": "PT30M", "type": "xsd:duration" }
},
{
"type": "Duty",
"label": "Schema conformance",
"subjectOfDuty": "ex:dataPlatformTeam",
"action": "ex:conformTo",
"target": "ex:riskMetricsSchema"
},
{
"type": "Duty",
"label": "Change notification: 14 days notice",
"subjectOfDuty": "ex:dataPlatformTeam",
"action": "ex:notify",
"target": "ex:schemaChanges",
"deadline": { "@value": "P14D", "type": "xsd:duration" }
},
{
"type": "Duty",
"label": "Quality: data must be real-time",
"subjectOfDuty": "ex:dataPlatformTeam",
"action": "ex:conformTo",
"constraint": {
"type": "Constraint",
"leftOperand": {
"id": "ex:timeliness",
"operandSource": "requestSource",
"operandProperty": "ex:timeliness"
},
"operator": "eq",
"rightOperand": { "id": "ex:realtime" }
}
}
]
}
DPROD keeps two questions apart. Where is this offer or contract in its administrative life? is answered by a person or a contract-management process and recorded with dprod:offerLifecycleStatus or dprod:contractLifecycleStatus. Has this obligation been met? is answered by an evaluator from observable facts and recorded with dprod:dutyState. Both take any skos:Concept; DPROD ships the optional scheme with Pending, Active, Fulfilled and Violated, and an enterprise may substitute its own.
The example shows both, plus versioning:
prov:wasRevisionOf. The retired offer stays in the graph with its status set to Fulfilled, so contracts that accepted it remain interpretable.Active, with its effective and expiration dates.odrl:Duty with a computed state: the 4 February delivery was made on time, the 5 February one missed its 30-minute window, and the 6 February one is not yet due.Duty state is deliberately not a sub-property of dprod:lifecycleStatus: it is derived, and re-deriving it must never change an authored status.
{
"@context": [
"https://ekgf.org/dprod/spec/develop/dprod-simple.jsonld",
{ "ex": "https://example.org/" }
],
"id": "ex:analyticsCustomerData2026",
"type": "DataContract",
"profile": "https://ekgf.org/dprod/spec/develop/",
"assigner": "ex:dataPlatformTeam",
"assignee": "ex:analyticsTeam",
"contractLifecycleStatus": "Active",
"effectiveDate": "2026-02-01T00:00:00Z",
"expirationDate": "2026-12-31T23:59:59Z",
"acceptsOffer": {
"id": "ex:customerApiV2",
"type": "DataOffer",
"profile": "https://ekgf.org/dprod/spec/develop/",
"assigner": "ex:dataPlatformTeam",
"target": "ex:customerData",
"offerLifecycleStatus": "Active",
"effectiveDate": "2026-01-01T00:00:00Z",
"obligation": {
"type": "Duty",
"subjectOfDuty": "ex:dataPlatformTeam",
"action": "ex:deliver",
"recurrence": "FREQ=DAILY;BYHOUR=7;BYMINUTE=0",
"deadline": { "@value": "PT30M", "type": "xsd:duration" }
},
"wasRevisionOf": {
"id": "ex:customerApiV1",
"type": "DataOffer",
"profile": "https://ekgf.org/dprod/spec/develop/",
"assigner": "ex:dataPlatformTeam",
"target": "ex:customerData",
"offerLifecycleStatus": "Fulfilled",
"expirationDate": "2025-12-31T23:59:59Z",
"permission": { "type": "Permission", "action": "use" }
}
},
"obligation": [
{
"id": "ex:delivery-2026-02-04",
"type": "Duty",
"subjectOfDuty": "ex:dataPlatformTeam",
"objectOfDuty": "ex:analyticsTeam",
"action": "ex:deliver",
"deadline": { "@value": "2026-02-04T07:30:00Z", "type": "xsd:dateTime" },
"dutyState": "Fulfilled"
},
{
"id": "ex:delivery-2026-02-05",
"type": "Duty",
"subjectOfDuty": "ex:dataPlatformTeam",
"objectOfDuty": "ex:analyticsTeam",
"action": "ex:deliver",
"deadline": { "@value": "2026-02-05T07:30:00Z", "type": "xsd:dateTime" },
"dutyState": "Violated"
},
{
"id": "ex:delivery-2026-02-06",
"type": "Duty",
"subjectOfDuty": "ex:dataPlatformTeam",
"objectOfDuty": "ex:analyticsTeam",
"action": "ex:deliver",
"deadline": { "@value": "2026-02-06T07:30:00Z", "type": "xsd:dateTime" },
"dutyState": "Pending"
}
]
}
Not every rule about data is a contract between two parties. An organisation also has standing rules: who may read employee data and for what purpose, what must be logged, what may never be done. These are expressed as an odrl:Set, an ODRL policy with no assigner or assignee, and evaluated by the same DPROD evaluator as a contract. The profile's fixed conflict rule applies: a prohibition always wins over a permission.
The HR data access policy shows the constraint patterns a policy writer typically needs:
odrl:purpose operand.odrl:assignee.odrl:isNoneOf and a list of values.odrl:LogicalConstraint with odrl:and, and DPROD's dprod:not for negation: model training is forbidden unless the environment is the isolated one.Every operand a policy reads must say where its value comes from. Each is declared the first time it is used, with one dprod:operandSource and one dprod:operandProperty; later uses just name it.
{
"@context": [
"https://ekgf.org/dprod/spec/develop/dprod-simple.jsonld",
{ "ex": "https://example.org/" }
],
"id": "ex:hrDataAccessPolicy",
"type": "Set",
"profile": "https://ekgf.org/dprod/spec/develop/",
"target": "ex:employeeData",
"permission": [
{
"type": "Permission",
"label": "HR Analytics may read, for analytics",
"assignee": "ex:hrAnalyticsTeam",
"action": "read",
"constraint": {
"type": "Constraint",
"leftOperand": {
"id": "odrl:purpose",
"operandSource": "requestSource",
"operandProperty": "odrl:purpose"
},
"operator": "eq",
"rightOperand": { "id": "ex:analytics" }
}
},
{
"type": "Permission",
"label": "Compliance may read, for compliance",
"assignee": "ex:complianceTeam",
"action": "read",
"constraint": {
"type": "Constraint",
"leftOperand": "odrl:purpose",
"operator": "eq",
"rightOperand": { "id": "ex:compliance" }
}
}
],
"obligation": [
{
"type": "Duty",
"label": "Access to confidential data must be logged within an hour",
"action": "ex:log",
"target": "ex:accessLog",
"constraint": {
"type": "Constraint",
"leftOperand": {
"id": "ex:classification",
"operandSource": "requestSource",
"operandProperty": "ex:classification"
},
"operator": "eq",
"rightOperand": { "id": "ex:confidential" }
},
"deadline": { "@value": "PT1H", "type": "xsd:duration" }
},
{
"type": "Duty",
"label": "Delete after 7 years",
"action": "delete",
"constraint": {
"type": "Constraint",
"leftOperand": {
"id": "ex:retentionPeriod",
"operandSource": "requestSource",
"operandProperty": "ex:retentionPeriod"
},
"operator": "gt",
"rightOperand": { "@value": "P7Y", "type": "xsd:duration" }
}
}
],
"prohibition": [
{
"type": "Prohibition",
"label": "No external sharing",
"action": "distribute"
},
{
"type": "Prohibition",
"label": "No use outside production or staging",
"action": "use",
"constraint": {
"type": "Constraint",
"leftOperand": {
"id": "ex:environment",
"operandSource": "requestSource",
"operandProperty": "ex:environment"
},
"operator": "isNoneOf",
"rightOperand": {
"@list": [
{ "id": "ex:production" },
{ "id": "ex:staging" }
]
}
}
},
{
"type": "Prohibition",
"label": "No model training, unless in an isolated environment",
"action": "use",
"constraint": {
"type": "LogicalConstraint",
"and": {
"@list": [
{
"type": "Constraint",
"leftOperand": {
"id": "ex:processingMode",
"operandSource": "requestSource",
"operandProperty": "ex:processingMode"
},
"operator": "eq",
"rightOperand": { "id": "ex:modelTraining" }
},
{
"type": "LogicalConstraint",
"not": {
"type": "Constraint",
"leftOperand": "ex:environment",
"operator": "eq",
"rightOperand": { "id": "ex:isolated" }
}
}
]
}
}
}
]
}
Two ODRL mechanisms keep large contracts short, and DPROD gives both a precise meaning.
Target inheritance. A rule with no odrl:target of its own inherits every target of its policy. The bundle offer names three products at policy level. Its blanket rules, read permitted and redistribution prohibited, say nothing about targets and so apply to all three. The aggregation permission is about the bundle as a whole, so it names that asset explicitly.
Collections. Assets and parties are grouped with odrl:partOf into an odrl:AssetCollection or odrl:PartyCollection, and a rule addressed to a collection applies to every member. DPROD evaluates membership over the transitive closure of odrl:partOf. So the contract's assignee is the whole Trading Division and both of its teams are covered, and a rule on the reference data collection covers the country-codes table inside it. Only explicit membership is supported; ODRL's derived collections (odrl:source with odrl:refinement) are rejected by the profile.
Membership is stated on the member, pointing up at its collection, so the members cannot be nested inside the contract. This is the one example that needs a @graph: the contract first, then the collection memberships.
A contract accepts exactly one offer. If a consumer wants a subset of what a provider publishes, the provider publishes an offer for that subset, or a multi-target offer such as this one.
{
"@context": [
"https://ekgf.org/dprod/spec/develop/dprod-simple.jsonld",
{ "ex": "https://example.org/" }
],
"@graph": [
{
"id": "ex:tradingDivisionBundle2026",
"type": "DataContract",
"profile": "https://ekgf.org/dprod/spec/develop/",
"assigner": "ex:dataPlatformTeam",
"assignee": "ex:tradingDivision",
"permission": {
"type": "Permission",
"label": "Every team in the Trading Division may read",
"assignee": "ex:tradingDivision",
"action": "read"
},
"acceptsOffer": {
"id": "ex:marketDataBundle",
"type": "DataOffer",
"profile": "https://ekgf.org/dprod/spec/develop/",
"assigner": "ex:dataPlatformTeam",
"target": ["ex:marketPrices", "ex:referenceData", "ex:riskMetrics"],
"permission": [
{
"type": "Permission",
"label": "Read: applies to all three targets",
"action": "read"
},
{
"type": "Permission",
"label": "Aggregate: applies to the bundle as a whole",
"action": "aggregate",
"target": "ex:marketDataBundleAsset"
}
],
"prohibition": {
"type": "Prohibition",
"label": "No redistribution: applies to all three targets",
"action": "distribute"
}
}
},
{ "id": "ex:tradingDivision", "type": "PartyCollection" },
{
"id": "ex:tradingDataTeam",
"type": "Party",
"partOf": "ex:tradingDivision"
},
{
"id": "ex:analyticsTeam",
"type": "Party",
"partOf": "ex:tradingDivision"
},
{ "id": "ex:referenceData", "type": "AssetCollection" },
{
"id": "ex:countryCodes",
"type": "Asset",
"partOf": "ex:referenceData"
}
]
}
A DPROD evaluator answers one question: may this agent perform this action on this asset, now, under these policies? To make the answer reproducible, it never consults a live graph. The caller hands it one immutable dprod:EvaluationContext with four parts: the normalised authorisation request, an immutable state-of-the-world snapshot (a prov:Entity, so its provenance can be kept), the requesting agent, and the clock.
Each odrl:LeftOperand a policy uses declares where its value comes from with dprod:operandSource (the request, the state snapshot or the context itself) and which single property to read with dprod:operandProperty. Resolution is one lookup; there is no path traversal. A missing, multiple or wrongly typed value is an evaluation error, never a quietly false constraint.
This policy permits internal recipients to use market prices while the market is open. ex:recipientType is read from the request and ex:marketOpen from the state snapshot:
{
"@context": [
"https://ekgf.org/dprod/spec/develop/dprod-simple.jsonld",
{ "ex": "https://example.org/" }
],
"id": "ex:marketHoursPolicy",
"type": "Set",
"profile": "https://ekgf.org/dprod/spec/develop/",
"target": "ex:marketPrices",
"permission": {
"type": "Permission",
"action": "use",
"constraint": {
"type": "LogicalConstraint",
"and": {
"@list": [
{
"type": "Constraint",
"leftOperand": {
"id": "ex:recipientType",
"operandSource": "requestSource",
"operandProperty": "ex:recipientType"
},
"operator": "eq",
"rightOperand": { "id": "ex:internal" }
},
{
"type": "Constraint",
"leftOperand": {
"id": "ex:marketOpen",
"operandSource": "stateSource",
"operandProperty": "ex:marketOpen"
},
"operator": "eq",
"rightOperand": true
}
]
}
}
}
}
The document below is the evaluation context an evaluator would be given for Alice's request at 09:15 on 2 March. Against the policy above it yields a permit. DPROD's dprod:currentAgent and ODRL's standard odrl:dateTime are built-in operands bound to the context's agent and clock, and need no declaration.
{
"@context": [
"https://ekgf.org/dprod/spec/develop/dprod-simple.jsonld",
{ "ex": "https://example.org/" }
],
"id": "ex:evaluation-2026-03-02-0915",
"type": "EvaluationContext",
"agent": { "id": "ex:alice", "type": "Party" },
"clock": "2026-03-02T09:15:00Z",
"request": {
"assignee": "ex:alice",
"action": "use",
"target": "ex:marketPrices",
"ex:recipientType": { "id": "ex:internal" }
},
"state": { "type": "Entity", "ex:marketOpen": true }
}
The Open Data Contract Standard (ODCS) describes a data contract as a YAML document that bundles schema, quality rules, service levels, team and infrastructure. DPROD Contracts covers a narrower question, the rights and obligations, and leaves schema to DCAT and SHACL and infrastructure to deployment descriptors. The two are complementary. This example translates the policy parts of the ODCS reference document into a DPROD data offer; each rule's label names the ODCS field it came from.
| ODCS | DPROD |
|---|---|
| kind: DataContract, status: active, version | dprod:DataOffer, dprod:offerLifecycleStatus, dct:hasVersion |
| team owner | odrl:assigner |
| slaProperties frequency and timeOfAvailability | a delivery duty with dprod:recurrence and dprod:deadline |
| slaProperties generalAvailability and endOfSupport | dprod:effectiveDate and dprod:expirationDate |
| quality nullValues mustBe 0, scheduled nightly | a conformance duty constrained on completeness, with dprod:recurrence |
| slaProperties retention | a duty to odrl:delete beyond the retention period |
| roles with access: read | an odrl:Permission for the role's party |
| description.limitations | an odrl:Prohibition |
ODCS fields that are workflow or operations rather than policy, such as approver chains, severity and servers, have no counterpart and stay in ODCS.
{
"@context": [
"https://ekgf.org/dprod/spec/develop/dprod-simple.jsonld",
{ "ex": "https://example.org/" }
],
"id": "ex:sellerDataContract",
"type": "DataOffer",
"description": "Views built on top of the seller tables. Predict sales over time.",
"hasVersion": "1.1.0",
"profile": "https://ekgf.org/dprod/spec/develop/",
"assigner": "ex:sellerDataTeam",
"target": "ex:sellerData",
"offerLifecycleStatus": "Active",
"effectiveDate": "2022-05-12T09:30:10-08:00",
"expirationDate": "2032-05-12T09:30:10-08:00",
"obligation": [
{
"type": "Duty",
"label": "slaProperties frequency + timeOfAvailability: daily, by 09:00",
"subjectOfDuty": "ex:sellerDataTeam",
"action": "ex:deliver",
"recurrence": "FREQ=DAILY;BYHOUR=9;BYMINUTE=0",
"deadline": { "@value": "PT1H", "type": "xsd:duration" }
},
{
"type": "Duty",
"label": "quality nullValues mustBe 0, checked nightly",
"subjectOfDuty": "ex:sellerDataTeam",
"action": "ex:conformTo",
"recurrence": "FREQ=DAILY;BYHOUR=20;BYMINUTE=0",
"constraint": {
"type": "Constraint",
"leftOperand": {
"id": "ex:completeness",
"operandSource": "requestSource",
"operandProperty": "ex:completeness"
},
"operator": "eq",
"rightOperand": { "@value": "100", "type": "xsd:decimal" }
}
},
{
"type": "Duty",
"label": "slaProperties retention: delete after 3 years",
"action": "delete",
"constraint": {
"type": "Constraint",
"leftOperand": {
"id": "ex:retentionPeriod",
"operandSource": "requestSource",
"operandProperty": "ex:retentionPeriod"
},
"operator": "gt",
"rightOperand": { "@value": "P3Y", "type": "xsd:duration" }
}
}
],
"permission": {
"type": "Permission",
"label": "roles: microstrategy_user_opr, access: read",
"assignee": "ex:reportingUsers",
"action": "read"
},
"prohibition": {
"type": "Prohibition",
"label": "description.limitations: no buyer information",
"action": "derive"
}
}
The editors gratefully acknowledge the feedback and contributions made by individuals who have participated in EDM Council's CDMC team.