1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
use super::*;
pub(super) fn check_result(
record: &Record,
prior: &Prior<'_>,
rules: &VerifierRules,
state: &State,
) -> Option<ReasonCode> {
let data: ResultData = dec!(&record.data, ResultData);
// Must reference an open action in same thread/space
if !state.open_actions.contains(&data.action_id) {
return Some(ReasonCode::ActionClosed);
}
let Some(action) = prior.find(data.action_id) else {
return Some(ReasonCode::ActionClosed);
};
if action.thread != record.thread || action.space != record.space {
return Some(ReasonCode::ActionClosed);
}
let action_data: ActionData = dec!(&action.data, ActionData);
// Schema must match exec mode
let external_schema = schema_id(SCHEMA_RESULT_EXTERNAL);
match action_data.exec_mode {
ExecMode::External => {
if record.schema != external_schema {
return Some(ReasonCode::ExternalReceiptRequired);
}
// `Verified` evidence is earned, never asserted: an external
// result is Verified because it is a signed attestation from a
// key-bound executor. It must carry a signature (verified
// strictly by the generic checks above) and its author must
// have pinned keys in `author_keys` - otherwise the signature
// proves key possession, not executor identity, which is the
// Reported class, and the record rejects.
if record.author.signature.is_none() {
return Some(ReasonCode::SignatureMissing);
}
if !rules.author_keys.contains_key(&record.author.id) {
return Some(ReasonCode::SignatureInvalid);
}
}
ExecMode::Internal => {
if record.schema == external_schema {
return Some(ReasonCode::InvalidPayload);
}
}
}
// Must include exactly one Cause ref to action_id
let cause_refs: Vec<&Ref> = record
.refs
.iter()
.filter(|r| r.type_ == RefType::Cause)
.collect();
if cause_refs.len() != 1 || cause_refs[0].target != data.action_id {
return Some(ReasonCode::InvalidPayload);
}
None
}
/// Refusal checks.
pub(super) fn check_refusal(
record: &Record,
prior: &Prior<'_>,
state: &State,
) -> Option<ReasonCode> {
let data: RefusalData = dec!(&record.data, RefusalData);
let target = prior.find(data.target_id);
match target {
None => return Some(ReasonCode::RequestMissing),
Some(t) => {
// target_kind must match actual kind
match data.target_kind {
RefusalTarget::Action => {
if t.kind != Kind::Action {
return Some(ReasonCode::InvalidPayload);
}
// Must be open action in same thread/space
if !state.open_actions.contains(&data.target_id) {
return Some(ReasonCode::ActionClosed);
}
if t.thread != record.thread || t.space != record.space {
return Some(ReasonCode::ActionClosed);
}
}
RefusalTarget::Request => {
if t.kind != Kind::Request {
return Some(ReasonCode::InvalidPayload);
}
// Must be active request in same thread/space
if !state.active_requests.contains(&data.target_id) {
return Some(ReasonCode::RequestMissing);
}
if t.thread != record.thread || t.space != record.space {
return Some(ReasonCode::RequestMissing);
}
}
RefusalTarget::VerifiedEffect => {
if t.kind != Kind::Result
|| t.schema != schema_id(SCHEMA_RESULT_EFFECT_CONFIRMATION)
{
return Some(ReasonCode::InvalidPayload);
}
if !state.accepted_records.contains(&data.target_id) {
return Some(ReasonCode::InvalidPayload);
}
if t.thread != record.thread || t.space != record.space {
return Some(ReasonCode::InvalidPayload);
}
}
}
}
}
// Must include exactly one Cause ref to target_id
let cause_refs: Vec<&Ref> = record
.refs
.iter()
.filter(|r| r.type_ == RefType::Cause)
.collect();
if cause_refs.len() != 1 || cause_refs[0].target != data.target_id {
return Some(ReasonCode::InvalidPayload);
}
None
}
/// Usage checks.
pub(super) fn check_usage(record: &Record, prior: &Prior<'_>, state: &State) -> Option<ReasonCode> {
let data: UsageData = dec!(&record.data, UsageData);
// The payload's actor must be the envelope author: usage feedback is
// attributed, and a record cannot report usage on another actor's
// behalf.
if data.actor != record.author.id {
return Some(ReasonCode::InvalidPayload);
}
// used_record must resolve
if prior.find(data.used_record).is_none() {
return Some(ReasonCode::InvalidPayload);
}
// consuming_record must resolve
let consuming = match prior.find(data.consuming_record) {
None => return Some(ReasonCode::InvalidPayload),
Some(r) => r,
};
// consuming_record must be accepted Result or Refusal in the same
// thread as the usage report.
if !state.accepted_records.contains(&data.consuming_record) {
return Some(ReasonCode::InvalidPayload);
}
if consuming.kind != Kind::Result && consuming.kind != Kind::Refusal {
return Some(ReasonCode::InvalidPayload);
}
if consuming.thread != record.thread {
return Some(ReasonCode::InvalidPayload);
}
// Must include one Use ref to used_record
let use_refs: Vec<&Ref> = record
.refs
.iter()
.filter(|r| r.type_ == RefType::Use)
.collect();
if use_refs.len() != 1 || use_refs[0].target != data.used_record {
return Some(ReasonCode::InvalidPayload);
}
None
}
/// Summary checks.
pub(super) fn check_summary(record: &Record, rules: &VerifierRules) -> Option<ReasonCode> {
let data: SummaryData = dec!(&record.data, SummaryData);
// summary_type must be in allowed_summary_types
if !rules.allowed_summary_types.contains(&data.summary_type) {
return Some(ReasonCode::InvalidPayload);
}
// A summary *rests on* its sources, so it must say so with `Use` refs
// (epistemic dependence): its evidence derives from the weakest source
// and retraction of a source taints it. A summary with no sources at
// all would be an unfounded claim.
let use_count = record
.refs
.iter()
.filter(|r| r.type_ == RefType::Use)
.count();
if use_count == 0 {
return Some(ReasonCode::InvalidPayload);
}
None
}
/// Response checks.
pub(super) fn check_response(
record: &Record,
prior: &Prior<'_>,
state: &State,
) -> Option<ReasonCode> {
let data: ResponseData = dec!(&record.data, ResponseData);
// request_id must resolve to accepted active request in same
// thread/space. The prior-log lookup is mandatory, not best-effort: a
// fabricated State containing a nonexistent request id must not let a
// Response for that phantom request be accepted.
if !state.active_requests.contains(&data.request_id) {
return Some(ReasonCode::RequestMissing);
}
let Some(request) = prior.find(data.request_id) else {
return Some(ReasonCode::RequestMissing);
};
if request.thread != record.thread || request.space != record.space {
return Some(ReasonCode::RequestMissing);
}
// Turns are gap-free and in order: turn_index must equal the count of
// previously accepted responses for this request, so a conversation
// cannot be silently reordered or have turns dropped. The counter's
// ceiling is a hard stop: at u32::MAX prior responses no further
// response is accepted (a saturated counter must never let one
// turn_index be accepted twice).
let expected_turn = state
.response_turns
.get(&data.request_id)
.copied()
.unwrap_or(0);
if expected_turn == u32::MAX || data.turn_index != expected_turn {
return Some(ReasonCode::InvalidPayload);
}
// A closing response is the request's explicit terminal event; it is
// only coherent when no actions remain open and no plan is still
// running (a plan must reach Completed or Abandoned first - a final
// response must not silently strand in-flight work).
if data.closes_request {
if state
.request_open_action_count
.get(&data.request_id)
.copied()
.unwrap_or(0)
> 0
{
return Some(ReasonCode::InvalidPayload);
}
if state.active_plans.contains_key(&data.request_id) {
return Some(ReasonCode::InvalidPayload);
}
}
None
}
// Plan checks.