eyeling 1.24.29 → 1.24.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,221 @@
1
+ # ======================================================================================
2
+ # odrl-benefits.n3
3
+ #
4
+ # A very small example showing why ODRL policies are useful.
5
+ #
6
+ # The same machine-readable policy can:
7
+ # - permit a good use case (medical research),
8
+ # - block an unwanted use case (marketing),
9
+ # - attach an operational duty (delete the data after use), and
10
+ # - produce an explainable audit trail.
11
+ # ======================================================================================
12
+
13
+ @prefix : <https://example.org/odrl-benefits#> .
14
+ @prefix odrl: <http://www.w3.org/ns/odrl/2/> .
15
+ @prefix dct: <http://purl.org/dc/terms/> .
16
+ @prefix log: <http://www.w3.org/2000/10/swap/log#> .
17
+ @prefix string:<http://www.w3.org/2000/10/swap/string#> .
18
+ @prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
19
+
20
+ # -------------------
21
+ # Parties and assets
22
+ # -------------------
23
+
24
+ :ResearchLab dct:title "Example Research Lab" .
25
+ :HealthDataset dct:title "Pseudonymised health dataset" .
26
+ :MedicalResearch dct:title "medical research" .
27
+ :Marketing dct:title "marketing" .
28
+ :Allowed dct:title "Allowed" .
29
+ :Blocked dct:title "Blocked" .
30
+
31
+ # -----------------------------
32
+ # One compact ODRL policy
33
+ # -----------------------------
34
+
35
+ :Policy a odrl:Policy ;
36
+ dct:title "Health-data sharing policy" ;
37
+ odrl:permission :PermitResearchUse ;
38
+ odrl:prohibition :ProhibitMarketingUse .
39
+
40
+ :PermitResearchUse a odrl:Permission ;
41
+ odrl:assignee :ResearchLab ;
42
+ odrl:target :HealthDataset ;
43
+ odrl:action odrl:use ;
44
+ odrl:constraint [
45
+ odrl:leftOperand odrl:purpose ;
46
+ odrl:operator odrl:eq ;
47
+ odrl:rightOperand :MedicalResearch
48
+ ] ;
49
+ odrl:duty :DeleteAfterUse .
50
+
51
+ :DeleteAfterUse a odrl:Duty ;
52
+ odrl:action odrl:delete ;
53
+ odrl:target :HealthDataset ;
54
+ odrl:constraint [
55
+ odrl:leftOperand odrl:dateTime ;
56
+ odrl:operator odrl:lteq ;
57
+ odrl:rightOperand "2026-12-31"^^xsd:date
58
+ ] .
59
+
60
+ :ProhibitMarketingUse a odrl:Prohibition ;
61
+ odrl:assignee :ResearchLab ;
62
+ odrl:target :HealthDataset ;
63
+ odrl:action odrl:use ;
64
+ odrl:constraint [
65
+ odrl:leftOperand odrl:purpose ;
66
+ odrl:operator odrl:eq ;
67
+ odrl:rightOperand :Marketing
68
+ ] .
69
+
70
+ # -----------------
71
+ # Two access requests
72
+ # -----------------
73
+
74
+ :RequestResearch a :Request ;
75
+ dct:title "Use the dataset for medical research" ;
76
+ odrl:assignee :ResearchLab ;
77
+ odrl:target :HealthDataset ;
78
+ odrl:action odrl:use ;
79
+ :purpose :MedicalResearch .
80
+
81
+ :RequestMarketing a :Request ;
82
+ dct:title "Use the dataset for marketing" ;
83
+ odrl:assignee :ResearchLab ;
84
+ odrl:target :HealthDataset ;
85
+ odrl:action odrl:use ;
86
+ :purpose :Marketing .
87
+
88
+ # ---------------------------
89
+ # Minimal policy evaluation
90
+ # ---------------------------
91
+
92
+ # Benefit 1: permissions are machine-readable.
93
+ {
94
+ ?request a :Request ;
95
+ odrl:assignee ?party ;
96
+ odrl:target ?asset ;
97
+ odrl:action ?action ;
98
+ :purpose ?purpose .
99
+
100
+ :Policy odrl:permission ?permission .
101
+ ?permission odrl:assignee ?party ;
102
+ odrl:target ?asset ;
103
+ odrl:action ?action ;
104
+ odrl:constraint [
105
+ odrl:leftOperand odrl:purpose ;
106
+ odrl:operator odrl:eq ;
107
+ odrl:rightOperand ?purpose
108
+ ] .
109
+ }
110
+ =>
111
+ {
112
+ ?request :decision :Allowed ;
113
+ :benefit :MachineReadablePermission ;
114
+ :why "A matching ODRL permission grants this purpose-specific use." .
115
+ } .
116
+
117
+ # Benefit 2: prohibitions make unwanted use cases explicit.
118
+ {
119
+ ?request a :Request ;
120
+ odrl:assignee ?party ;
121
+ odrl:target ?asset ;
122
+ odrl:action ?action ;
123
+ :purpose ?purpose .
124
+
125
+ :Policy odrl:prohibition ?prohibition .
126
+ ?prohibition odrl:assignee ?party ;
127
+ odrl:target ?asset ;
128
+ odrl:action ?action ;
129
+ odrl:constraint [
130
+ odrl:leftOperand odrl:purpose ;
131
+ odrl:operator odrl:eq ;
132
+ odrl:rightOperand ?purpose
133
+ ] .
134
+ }
135
+ =>
136
+ {
137
+ ?request :decision :Blocked ;
138
+ :benefit :PurposeLimitation ;
139
+ :why "A matching ODRL prohibition blocks this purpose-specific use." .
140
+ } .
141
+
142
+ # Benefit 3: permitted use can carry operational duties.
143
+ {
144
+ ?request :decision :Allowed ;
145
+ odrl:assignee ?party ;
146
+ odrl:target ?asset ;
147
+ odrl:action ?action ;
148
+ :purpose ?purpose .
149
+
150
+ :Policy odrl:permission ?permission .
151
+ ?permission odrl:assignee ?party ;
152
+ odrl:target ?asset ;
153
+ odrl:action ?action ;
154
+ odrl:constraint [
155
+ odrl:leftOperand odrl:purpose ;
156
+ odrl:operator odrl:eq ;
157
+ odrl:rightOperand ?purpose
158
+ ] ;
159
+ odrl:duty ?duty .
160
+
161
+ ?duty odrl:action odrl:delete ;
162
+ odrl:constraint [
163
+ odrl:leftOperand odrl:dateTime ;
164
+ odrl:operator odrl:lteq ;
165
+ odrl:rightOperand ?date
166
+ ] .
167
+ }
168
+ =>
169
+ {
170
+ ?request :duty "Delete the dataset after the approved research use." ;
171
+ :deleteBy ?date ;
172
+ :benefit :OperationalDuty .
173
+ } .
174
+
175
+ # ----------------------------
176
+ # Markdown report output
177
+ # ----------------------------
178
+
179
+ :out01 log:outputString "# odrl-benefits\n\n## Source files\n\n- [N3 rules](../odrl-benefits.n3)\n\n" .
180
+ :out02 log:outputString "## Why ODRL helps\n\n" .
181
+ :out03 log:outputString "This example uses one small ODRL policy to make data-use decisions explicit, machine-checkable, and auditable.\n\n" .
182
+ :out04 log:outputString "### Policy in plain language\n\n" .
183
+ :out05 log:outputString "- The research lab **may use** the pseudonymised health dataset for **medical research**.\n" .
184
+ :out06 log:outputString "- The research lab **must delete** the dataset by `2026-12-31`.\n" .
185
+ :out07 log:outputString "- The research lab **must not use** the dataset for **marketing**.\n\n" .
186
+ :out08 log:outputString "### Evaluated requests\n\n" .
187
+ :out09 log:outputString "| Request | Decision | Explanation |\n" .
188
+ :out10 log:outputString "| --- | --- | --- |\n" .
189
+
190
+ {
191
+ :RequestResearch dct:title ?title ;
192
+ :decision ?decision ;
193
+ :why ?why ;
194
+ :deleteBy ?date .
195
+ ?decision dct:title ?decisionLabel .
196
+
197
+ ("| %s | `%s` | %s Duty: delete by `%s`. |\n" ?title ?decisionLabel ?why ?date) string:format ?line .
198
+ }
199
+ =>
200
+ {
201
+ :out11 log:outputString ?line .
202
+ } .
203
+
204
+ {
205
+ :RequestMarketing dct:title ?title ;
206
+ :decision ?decision ;
207
+ :why ?why .
208
+ ?decision dct:title ?decisionLabel .
209
+
210
+ ("| %s | `%s` | %s |\n" ?title ?decisionLabel ?why) string:format ?line .
211
+ }
212
+ =>
213
+ {
214
+ :out12 log:outputString ?line .
215
+ } .
216
+
217
+ :out13 log:outputString "\n### Benefits illustrated\n\n" .
218
+ :out14 log:outputString "1. **Clarity:** the policy says who may do what, to which asset, and for which purpose.\n" .
219
+ :out15 log:outputString "2. **Automation:** requests can be allowed or blocked by rules instead of manual reading.\n" .
220
+ :out16 log:outputString "3. **Purpose limitation:** research use and marketing use are treated differently.\n" .
221
+ :out17 log:outputString "4. **Accountability:** duties such as deletion are part of the same policy and can be audited.\n" .
@@ -0,0 +1,29 @@
1
+ # odrl-benefits
2
+
3
+ ## Source files
4
+
5
+ - [N3 rules](../odrl-benefits.n3)
6
+
7
+ ## Why ODRL helps
8
+
9
+ This example uses one small ODRL policy to make data-use decisions explicit, machine-checkable, and auditable.
10
+
11
+ ### Policy in plain language
12
+
13
+ - The research lab **may use** the pseudonymised health dataset for **medical research**.
14
+ - The research lab **must delete** the dataset by `2026-12-31`.
15
+ - The research lab **must not use** the dataset for **marketing**.
16
+
17
+ ### Evaluated requests
18
+
19
+ | Request | Decision | Explanation |
20
+ | --- | --- | --- |
21
+ | Use the dataset for medical research | `Allowed` | A matching ODRL permission grants this purpose-specific use. Duty: delete by `2026-12-31`. |
22
+ | Use the dataset for marketing | `Blocked` | A matching ODRL prohibition blocks this purpose-specific use. |
23
+
24
+ ### Benefits illustrated
25
+
26
+ 1. **Clarity:** the policy says who may do what, to which asset, and for which purpose.
27
+ 2. **Automation:** requests can be allowed or blocked by rules instead of manual reading.
28
+ 3. **Purpose limitation:** research use and marketing use are treated differently.
29
+ 4. **Accountability:** duties such as deletion are part of the same policy and can be audited.
@@ -3,10 +3,10 @@
3
3
  ## Source files
4
4
 
5
5
  - [N3 rules](../rdf-message-flow.n3)
6
- - [Input TriG](../input/rdf-message-flow.trig)
6
+ - [Input RDF Message Log](../input/rdf-message-flow.trig)
7
7
 
8
8
  ## Answer
9
- Continuous RDF Message flow accepted: 5 ordered message envelopes moved through the ingest → validate → interpret → route → sink pipeline. The threshold was 26, so results 21 and 22 were archived, the heartbeat kept the stream alive, and results 28 and 29 were emitted as alerts.
9
+ Continuous RDF Message flow accepted: 5 ordered parser-replayed message envelopes moved through the ingest → validate → interpret → route → sink pipeline. The threshold was 26, so results 21 and 22 were archived, the empty heartbeat kept the stream alive, and results 28 and 29 were emitted as alerts.
10
10
 
11
11
  ## Explanation
12
- The input is a single runnable example split across an N3 rule file and a TriG sidecar. The TriG file uses example-local envelope facts for stream order and processing state, while each named payload graph is treated as an atomic RDF Message dataset. Only :m001 starts at ingress; each envelope must reach :sink before the continuous-flow rule releases its flow:nextEnvelope. Observation payloads are inspected with log:includes inside their own payload formula, and the empty heartbeat advances without a payload graph. This keeps message boundaries visible to the reasoner instead of merging all payload triples into one global graph.
12
+ The input sidecar is now a true RDF Message Log using VERSION \"1.2-messages\" and MESSAGE delimiters. Eyeling parses the log internally into an eymsg: replay view with ordered envelopes, offsets, next-envelope links, and one payload graph per non-empty message. Only the first replayed envelope starts at ingress; each envelope must reach :sink before the continuous-flow rule releases its eymsg:nextEnvelope. Observation payloads are inspected with log:includes inside their own message formula, and the delimiter-only third message is replayed as an empty heartbeat. This keeps message boundaries in the parser/runtime instead of modeling them by hand in the TriG sidecar.
@@ -9,28 +9,23 @@
9
9
  # Motivation
10
10
  # ----------
11
11
  # The RDF Messages draft describes an RDF Message as an RDF dataset interpreted
12
- # atomically, and an RDF Message Stream as an ordered sequence of such messages.
13
- # It also defines RDF Message Logs with explicit MESSAGE delimiters. This Eyeling
14
- # example keeps the same message-level discipline while staying in ordinary
15
- # N3/TriG that Eyeling can reason over today.
12
+ # atomically, an RDF Message Stream as an ordered sequence of messages, and an
13
+ # RDF Message Log as a static replayable representation of that stream.
16
14
  #
17
- # The companion TriG file therefore separates two layers:
15
+ # The companion sidecar is now a true RDF Message Log: it uses
16
+ # VERSION "1.2-messages" and MESSAGE delimiters. In RDF compatibility mode,
17
+ # Eyeling parses those delimiters internally and materializes a replay view using
18
+ # the eymsg: vocabulary: a stream resource, ordered envelopes, offsets,
19
+ # next-envelope links, and one named payload graph per non-empty message.
18
20
  #
19
- # 1. example-local envelope facts in the default graph, such as order,
20
- # processing stage, and payload graph; and
21
- # 2. one named graph per non-empty message payload, treated here as the RDF
22
- # dataset/message that must be inspected atomically.
23
- #
24
- # The rules below deliberately do not merge all payload graphs into one global
25
- # graph. Instead, each observation is checked with log:includes inside the named
26
- # payload formula. The empty heartbeat has no payload graph but still advances
27
- # through the same pipeline. The next message is released only after the current
28
- # one reaches the sink, making the example a small back-pressure / flow-control
29
- # story rather than a batch merge of all input triples.
21
+ # The rules below therefore no longer invent envelope facts in the TriG file.
22
+ # They consume the replay view that Eyeling exposes, inspect each payload with
23
+ # log:includes inside its own message formula, and let the empty heartbeat flow
24
+ # through without a payload graph.
30
25
 
31
26
  @prefix : <https://eyereasoner.github.io/eyeling/examples/rdf-message-flow#>.
32
27
  @prefix flow: <https://eyereasoner.github.io/eyeling/examples/rdf-message-flow/vocab#>.
33
- @prefix prov: <http://www.w3.org/ns/prov#>.
28
+ @prefix eymsg: <https://eyereasoner.github.io/eyeling/vocab/message#>.
34
29
  @prefix sosa: <http://www.w3.org/ns/sosa/>.
35
30
  @prefix xsd: <http://www.w3.org/2001/XMLSchema#>.
36
31
  @prefix math: <http://www.w3.org/2000/10/swap/math#>.
@@ -38,29 +33,49 @@
38
33
  @prefix log: <http://www.w3.org/2000/10/swap/log#>.
39
34
  @prefix string: <http://www.w3.org/2000/10/swap/string#>.
40
35
 
36
+ # Eyeling's parser-level replay exposes the first envelope; that starts the
37
+ # consumer-visible flow. Later envelopes are released only by the sink rule.
38
+ {
39
+ ?Stream a eymsg:RDFMessageStream;
40
+ eymsg:firstEnvelope ?Envelope.
41
+ } => {
42
+ ?Envelope :atStage :ingest.
43
+ }.
44
+
45
+ # Stream configuration is carried by the first RDF Message payload, not by a
46
+ # hand-written sidecar envelope. The rule lifts only the threshold needed by the
47
+ # routing rules into the ordinary reasoning context.
48
+ {
49
+ ?Envelope eymsg:payloadGraph ?Payload.
50
+ ?Payload log:nameOf ?PayloadContext.
51
+ ?PayloadContext log:includes { :temperatureFlow :highThreshold ?Threshold. }.
52
+ } => {
53
+ :temperatureFlow :highThreshold ?Threshold.
54
+ }.
55
+
41
56
  # Stage 1: once an envelope has entered the stream, validate it.
42
57
  { ?Envelope :atStage :ingest. } => {
43
58
  ?Envelope :atStage :validate.
44
59
  }.
45
60
 
46
- # Stage 2: validation records that the envelope keeps an explicit message
47
- # boundary before the payload is interpreted.
61
+ # Stage 2: validation records that the parser gave the reasoner an explicit
62
+ # RDF Message boundary before the payload is interpreted.
48
63
  {
49
64
  ?Envelope :atStage :validate;
50
- a flow:MessageEnvelope;
51
- flow:offset ?Offset.
65
+ a eymsg:MessageEnvelope;
66
+ eymsg:offset ?Offset.
52
67
  } => {
53
68
  ?Envelope flow:boundaryExplicit true.
54
69
  ?Envelope :atStage :interpret.
55
70
  }.
56
71
 
57
- # Stage 3a: observation payloads are inspected inside their own payload graph.
58
- # This models the RDF Messages idea that messages are separate atomic datasets.
72
+ # Stage 3a: observation payloads are inspected inside their own replayed message
73
+ # graph. This keeps the RDF Messages default discipline: messages are atomic
74
+ # datasets and are not silently merged.
59
75
  {
60
76
  ?Envelope :atStage :interpret;
61
- flow:payloadKind :observation;
62
- flow:expectedResult ?Result;
63
- flow:payloadGraph ?Payload.
77
+ eymsg:payloadKind eymsg:nonEmpty;
78
+ eymsg:payloadGraph ?Payload.
64
79
  ?Payload log:nameOf ?PayloadContext.
65
80
  ?PayloadContext log:includes { ?Observation sosa:hasSimpleResult ?Result. }.
66
81
  } => {
@@ -69,10 +84,10 @@
69
84
  }.
70
85
 
71
86
  # Stage 3b: empty heartbeats contain no quads, but RDF Messages explicitly allow
72
- # empty messages, so the envelope still moves through the flow.
87
+ # empty messages, so the parser-level envelope still moves through the flow.
73
88
  {
74
89
  ?Envelope :atStage :interpret;
75
- flow:payloadKind :heartbeat.
90
+ eymsg:payloadKind eymsg:empty.
76
91
  } => {
77
92
  ?Envelope flow:emptyMessageAllowed true.
78
93
  ?Envelope :atStage :route.
@@ -112,46 +127,52 @@
112
127
  :heartbeatSink :received ?Envelope.
113
128
  }.
114
129
 
115
- # Continuous-flow rule: reaching the sink releases the next envelope into ingress.
116
- # This mirrors a consumer-visible ordered stream: later messages are not processed
117
- # until the earlier message has completed the pipeline.
130
+ # Continuous-flow rule: reaching the sink releases the next replayed envelope
131
+ # into ingress. The ordering link comes from Eyeling's internal MESSAGE replay,
132
+ # not from the sidecar data.
118
133
  {
119
134
  ?Envelope :atStage :sink;
120
- flow:nextEnvelope ?Next.
121
- ?Next a flow:MessageEnvelope.
135
+ eymsg:nextEnvelope ?Next.
136
+ ?Next a eymsg:MessageEnvelope.
122
137
  } => {
123
138
  ?Envelope :releases ?Next.
124
139
  ?Next :atStage :ingest.
125
140
  }.
126
141
 
127
- # The Eyeling verdict is emitted only after all five envelopes have flowed
128
- # through ingest, validation, interpretation, routing, and sink while preserving
129
- # their message boundaries.
142
+ # The verdict is emitted only after all five parser-replayed envelopes have
143
+ # flowed through ingest, validation, interpretation, routing, and sink while
144
+ # preserving message boundaries.
130
145
  {
131
- :temperatureFlow flow:orderedEnvelopes ?Envelopes;
132
- :highThreshold ?Threshold.
146
+ ?Stream a eymsg:RDFMessageStream;
147
+ eymsg:orderedEnvelopes ?Envelopes;
148
+ eymsg:firstEnvelope ?M1.
133
149
  ?Envelopes list:length ?Count.
134
- :m001 :atStage :sink;
150
+ :temperatureFlow :highThreshold ?Threshold.
151
+ ?M1 :atStage :sink;
135
152
  flow:payloadResult ?FirstResult;
136
153
  :route :archiveSink;
137
- :releases :m002.
138
- :m002 :atStage :sink;
154
+ eymsg:nextEnvelope ?M2;
155
+ :releases ?M2.
156
+ ?M2 :atStage :sink;
139
157
  flow:payloadResult ?SecondResult;
140
158
  :route :archiveSink;
141
- :releases :m003.
142
- :m003 :atStage :sink;
159
+ eymsg:nextEnvelope ?M3;
160
+ :releases ?M3.
161
+ ?M3 :atStage :sink;
143
162
  flow:emptyMessageAllowed true;
144
163
  :route :heartbeatSink;
145
- :releases :m004.
146
- :m004 :atStage :sink;
164
+ eymsg:nextEnvelope ?M4;
165
+ :releases ?M4.
166
+ ?M4 :atStage :sink;
147
167
  flow:payloadResult ?FourthResult;
148
168
  :route :alertSink;
149
- :releases :m005.
150
- :m005 :atStage :sink;
169
+ eymsg:nextEnvelope ?M5;
170
+ :releases ?M5.
171
+ ?M5 :atStage :sink;
151
172
  flow:payloadResult ?FifthResult;
152
173
  :route :alertSink.
153
- ("# rdf-message-flow\n\n## Source files\n\n- [N3 rules](../rdf-message-flow.n3)\n- [Input TriG](../input/rdf-message-flow.trig)\n\n## Answer\nContinuous RDF Message flow accepted: %d ordered message envelopes moved through the ingest → validate → interpret → route → sink pipeline. The threshold was %d, so results %s and %s were archived, the heartbeat kept the stream alive, and results %s and %s were emitted as alerts.\n\n## Explanation\nThe input is a single runnable example split across an N3 rule file and a TriG sidecar. The TriG file uses example-local envelope facts for stream order and processing state, while each named payload graph is treated as an atomic RDF Message dataset. Only :m001 starts at ingress; each envelope must reach :sink before the continuous-flow rule releases its flow:nextEnvelope. Observation payloads are inspected with log:includes inside their own payload formula, and the empty heartbeat advances without a payload graph. This keeps message boundaries visible to the reasoner instead of merging all payload triples into one global graph." ?Count ?Threshold ?FirstResult ?SecondResult ?FourthResult ?FifthResult) string:format ?Block.
174
+ ("# rdf-message-flow\n\n## Source files\n\n- [N3 rules](../rdf-message-flow.n3)\n- [Input RDF Message Log](../input/rdf-message-flow.trig)\n\n## Answer\nContinuous RDF Message flow accepted: %d ordered parser-replayed message envelopes moved through the ingest → validate → interpret → route → sink pipeline. The threshold was %d, so results %s and %s were archived, the empty heartbeat kept the stream alive, and results %s and %s were emitted as alerts.\n\n## Explanation\nThe input sidecar is now a true RDF Message Log using VERSION \\\"1.2-messages\\\" and MESSAGE delimiters. Eyeling parses the log internally into an eymsg: replay view with ordered envelopes, offsets, next-envelope links, and one payload graph per non-empty message. Only the first replayed envelope starts at ingress; each envelope must reach :sink before the continuous-flow rule releases its eymsg:nextEnvelope. Observation payloads are inspected with log:includes inside their own message formula, and the delimiter-only third message is replayed as an empty heartbeat. This keeps message boundaries in the parser/runtime instead of modeling them by hand in the TriG sidecar." ?Count ?Threshold ?FirstResult ?SecondResult ?FourthResult ?FifthResult) string:format ?Block.
154
175
  } => {
155
176
  :rdfMessageFlowExample log:outputString ?Block.
156
- :rdfMessageFlowExample :demonstrates :ContinuousFlow, :BackPressureRelease, :AtomicMessageContext, :HeartbeatInFlow, :ThresholdRouting.
177
+ :rdfMessageFlowExample :demonstrates :ContinuousFlow, :BackPressureRelease, :AtomicMessageContext, :HeartbeatInFlow, :ThresholdRouting, :RDFMessageLogReplay.
157
178
  }.