1use schemars::JsonSchema;
5use serde::{Deserialize, Serialize};
6use stack_ids::{
7 AttestationEnvelopeId, AttestationRevocationId, AttestationSupersessionId,
8 CrossRuntimeReplayTicketId, DisputeBundleId, RemoteOracleLeaseId, RemoteSliceRequestId,
9 RemoteSliceResultId, TrustRootSetId,
10};
11
12pub use stack_ids::V25ConstitutionCitation;
13
14pub const ATTESTATION_REVOCATION_V1_SCHEMA: &str = "attestation_revocation_v1";
15pub const ATTESTATION_SUPERSESSION_V1_SCHEMA: &str = "attestation_supersession_v1";
16pub const REMOTE_ORACLE_LEASE_V1_SCHEMA: &str = "remote_oracle_lease_v1";
17pub const REMOTE_SLICE_REQUEST_V1_SCHEMA: &str = "remote_slice_request_v1";
18pub const REMOTE_SLICE_RESULT_V1_SCHEMA: &str = "remote_slice_result_v1";
19pub const CROSS_RUNTIME_REPLAY_TICKET_V1_SCHEMA: &str = "cross_runtime_replay_ticket_v1";
20
21fn require_non_empty(value: &str, field: &'static str) -> Result<(), &'static str> {
25 if value.trim().is_empty() {
26 return Err(field);
27 }
28 Ok(())
29}
30
31fn require_non_empty_slice<T>(values: &[T], field: &'static str) -> Result<(), &'static str> {
32 if values.is_empty() {
33 return Err(field);
34 }
35 Ok(())
36}
37
38fn require_schema_version(
39 found: &str,
40 expected: &'static str,
41 field: &'static str,
42) -> Result<(), &'static str> {
43 if found != expected {
44 return Err(field);
45 }
46 Ok(())
47}
48
49fn require_citation(citation: &V25ConstitutionCitation) -> Result<(), &'static str> {
50 require_non_empty(
51 citation.applicability_context_id.as_str(),
52 "applicability_context_id",
53 )?;
54 require_non_empty(citation.profile_set_id.as_str(), "profile_set_id")?;
55 require_non_empty(
56 citation.composition_receipt_id.as_str(),
57 "composition_receipt_id",
58 )?;
59 require_non_empty(
60 citation.effective_constitution_id.as_str(),
61 "effective_constitution_id",
62 )?;
63 require_non_empty(
64 citation.compiled_obligation_set_id.as_str(),
65 "compiled_obligation_set_id",
66 )?;
67 Ok(())
68}
69
70#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
71#[serde(rename_all = "snake_case")]
72pub enum RemoteExactnessClassV1 {
73 Exact,
74 BoundedExact,
75}
76
77#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
78#[serde(rename_all = "snake_case")]
79pub enum RemoteDisclosureClassV1 {
80 #[serde(rename = "non-sensitive")]
81 NonSensitive,
82 #[serde(rename = "redacted_structured_only")]
83 RedactedStructuredOnly,
84}
85
86#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
87#[serde(rename_all = "snake_case")]
88pub enum RemoteReplayObligationV1 {
89 MustReturnReplayTicketOrNonreplayableReason,
90}
91
92#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
93#[serde(rename_all = "snake_case")]
94pub enum AttestationReplayImpactV1 {
95 #[serde(rename = "replay ticket unchanged")]
96 ReplayTicketUnchanged,
97}
98
99#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
100#[serde(rename_all = "snake_case")]
101pub enum LocalAdmissionRecommendationV1 {
102 Eligible,
103 AdmitIfTransparencyReceiptPresent,
104 AdmitWithDisclosureConstraints,
105}
106
107#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
108#[serde(rename_all = "snake_case")]
109pub enum ReplayFailureBehaviorV1 {
110 Retry,
111 DowngradeToAdvisoryAndEmitDisputeIfMandatory,
112}
113
114#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
115pub struct AttestationRevocationV1 {
116 pub schema_version: String,
117 pub attestation_revocation_id: AttestationRevocationId,
118 #[serde(default, skip_serializing_if = "Vec::is_empty")]
119 pub affected_refs: Vec<String>,
120 pub revocation_reason: String,
121 pub effective_time: String,
122 pub blast_radius: String,
123 pub required_local_invalidation_behavior: String,
124 #[serde(default, skip_serializing_if = "Option::is_none")]
125 pub dispute_linkage: Option<DisputeBundleId>,
126}
127
128impl AttestationRevocationV1 {
129 #[allow(clippy::too_many_arguments)]
130 pub fn new(
131 attestation_revocation_id: AttestationRevocationId,
132 affected_refs: Vec<String>,
133 revocation_reason: impl Into<String>,
134 effective_time: impl Into<String>,
135 blast_radius: impl Into<String>,
136 required_local_invalidation_behavior: impl Into<String>,
137 dispute_linkage: Option<DisputeBundleId>,
138 ) -> Result<Self, &'static str> {
139 let value = Self {
140 schema_version: ATTESTATION_REVOCATION_V1_SCHEMA.to_string(),
141 attestation_revocation_id,
142 affected_refs,
143 revocation_reason: revocation_reason.into(),
144 effective_time: effective_time.into(),
145 blast_radius: blast_radius.into(),
146 required_local_invalidation_behavior: required_local_invalidation_behavior.into(),
147 dispute_linkage,
148 };
149 value.validate()?;
150 Ok(value)
151 }
152
153 pub fn validate(&self) -> Result<(), &'static str> {
154 require_schema_version(
155 &self.schema_version,
156 ATTESTATION_REVOCATION_V1_SCHEMA,
157 "schema_version",
158 )?;
159 require_non_empty(
160 self.attestation_revocation_id.as_str(),
161 "attestation_revocation_id",
162 )?;
163 require_non_empty(&self.revocation_reason, "revocation_reason")?;
164 require_non_empty(&self.effective_time, "effective_time")?;
165 require_non_empty(&self.blast_radius, "blast_radius")?;
166 require_non_empty(
167 &self.required_local_invalidation_behavior,
168 "required_local_invalidation_behavior",
169 )?;
170 Ok(())
171 }
172}
173
174#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
175pub struct AttestationSupersessionV1 {
176 pub schema_version: String,
177 pub attestation_supersession_id: AttestationSupersessionId,
178 pub prior_ref: String,
179 pub replacement_ref: String,
180 pub semantic_delta_summary: String,
181 pub effective_time: String,
182 pub replay_impact: AttestationReplayImpactV1,
183 pub requires_re_admission: bool,
184}
185
186impl AttestationSupersessionV1 {
187 #[allow(clippy::too_many_arguments)]
188 pub fn new(
189 attestation_supersession_id: AttestationSupersessionId,
190 prior_ref: impl Into<String>,
191 replacement_ref: impl Into<String>,
192 semantic_delta_summary: impl Into<String>,
193 effective_time: impl Into<String>,
194 replay_impact: AttestationReplayImpactV1,
195 requires_re_admission: bool,
196 ) -> Result<Self, &'static str> {
197 let value = Self {
198 schema_version: ATTESTATION_SUPERSESSION_V1_SCHEMA.to_string(),
199 attestation_supersession_id,
200 prior_ref: prior_ref.into(),
201 replacement_ref: replacement_ref.into(),
202 semantic_delta_summary: semantic_delta_summary.into(),
203 effective_time: effective_time.into(),
204 replay_impact,
205 requires_re_admission,
206 };
207 value.validate()?;
208 Ok(value)
209 }
210
211 pub fn validate(&self) -> Result<(), &'static str> {
212 require_schema_version(
213 &self.schema_version,
214 ATTESTATION_SUPERSESSION_V1_SCHEMA,
215 "schema_version",
216 )?;
217 require_non_empty(
218 self.attestation_supersession_id.as_str(),
219 "attestation_supersession_id",
220 )?;
221 require_non_empty(&self.prior_ref, "prior_ref")?;
222 require_non_empty(&self.replacement_ref, "replacement_ref")?;
223 require_non_empty(&self.semantic_delta_summary, "semantic_delta_summary")?;
224 require_non_empty(&self.effective_time, "effective_time")?;
225 if self.prior_ref == self.replacement_ref {
226 return Err("replacement_ref");
227 }
228 Ok(())
229 }
230}
231
232#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
233pub struct RemoteOracleLeaseV1 {
234 pub schema_version: String,
235 pub remote_oracle_lease_id: RemoteOracleLeaseId,
236 pub oracle_identity: String,
237 #[serde(default, skip_serializing_if = "Vec::is_empty")]
238 pub allowed_artifact_families: Vec<String>,
239 #[serde(default, skip_serializing_if = "Vec::is_empty")]
240 pub allowed_graph_or_slice_kinds: Vec<String>,
241 pub exactness_class_ceiling: RemoteExactnessClassV1,
242 pub budget_ceiling: String,
243 pub disclosure_ceiling: RemoteDisclosureClassV1,
244 pub replay_obligation: RemoteReplayObligationV1,
245 pub lease_expiry: String,
246 #[serde(default, skip_serializing_if = "Vec::is_empty")]
247 pub policy_owner_refs: Vec<String>,
248}
249
250impl RemoteOracleLeaseV1 {
251 #[allow(clippy::too_many_arguments)]
252 pub fn new(
253 remote_oracle_lease_id: RemoteOracleLeaseId,
254 oracle_identity: impl Into<String>,
255 allowed_artifact_families: Vec<String>,
256 allowed_graph_or_slice_kinds: Vec<String>,
257 exactness_class_ceiling: RemoteExactnessClassV1,
258 budget_ceiling: impl Into<String>,
259 disclosure_ceiling: RemoteDisclosureClassV1,
260 replay_obligation: RemoteReplayObligationV1,
261 lease_expiry: impl Into<String>,
262 policy_owner_refs: Vec<String>,
263 ) -> Result<Self, &'static str> {
264 let value = Self {
265 schema_version: REMOTE_ORACLE_LEASE_V1_SCHEMA.to_string(),
266 remote_oracle_lease_id,
267 oracle_identity: oracle_identity.into(),
268 allowed_artifact_families,
269 allowed_graph_or_slice_kinds,
270 exactness_class_ceiling,
271 budget_ceiling: budget_ceiling.into(),
272 disclosure_ceiling,
273 replay_obligation,
274 lease_expiry: lease_expiry.into(),
275 policy_owner_refs,
276 };
277 value.validate()?;
278 Ok(value)
279 }
280
281 pub fn validate(&self) -> Result<(), &'static str> {
282 require_schema_version(
283 &self.schema_version,
284 REMOTE_ORACLE_LEASE_V1_SCHEMA,
285 "schema_version",
286 )?;
287 require_non_empty(
288 self.remote_oracle_lease_id.as_str(),
289 "remote_oracle_lease_id",
290 )?;
291 require_non_empty(&self.oracle_identity, "oracle_identity")?;
292 require_non_empty_slice(&self.allowed_artifact_families, "allowed_artifact_families")?;
293 require_non_empty_slice(
294 &self.allowed_graph_or_slice_kinds,
295 "allowed_graph_or_slice_kinds",
296 )?;
297 require_non_empty(&self.budget_ceiling, "budget_ceiling")?;
298 require_non_empty(&self.lease_expiry, "lease_expiry")?;
299 require_non_empty_slice(&self.policy_owner_refs, "policy_owner_refs")?;
300 Ok(())
301 }
302}
303
304#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
305pub struct RemoteSliceRequestV1 {
306 pub schema_version: String,
307 pub remote_slice_request_id: RemoteSliceRequestId,
308 pub requested_slice_definition: String,
309 #[serde(default, skip_serializing_if = "Vec::is_empty")]
310 pub required_artifact_refs: Vec<String>,
311 pub allowed_disclosure_policy: String,
312 pub exactness_target: RemoteExactnessClassV1,
313 pub trust_root_set_id: TrustRootSetId,
314 #[serde(flatten)]
315 pub citation: V25ConstitutionCitation,
316 #[serde(default, skip_serializing_if = "Vec::is_empty")]
317 pub challenge_expectations: Vec<String>,
318 pub remote_oracle_lease_id: RemoteOracleLeaseId,
319}
320
321impl RemoteSliceRequestV1 {
322 #[allow(clippy::too_many_arguments)]
323 pub fn new(
324 remote_slice_request_id: RemoteSliceRequestId,
325 requested_slice_definition: impl Into<String>,
326 required_artifact_refs: Vec<String>,
327 allowed_disclosure_policy: impl Into<String>,
328 exactness_target: RemoteExactnessClassV1,
329 trust_root_set_id: TrustRootSetId,
330 citation: V25ConstitutionCitation,
331 challenge_expectations: Vec<String>,
332 remote_oracle_lease_id: RemoteOracleLeaseId,
333 ) -> Result<Self, &'static str> {
334 let value = Self {
335 schema_version: REMOTE_SLICE_REQUEST_V1_SCHEMA.to_string(),
336 remote_slice_request_id,
337 requested_slice_definition: requested_slice_definition.into(),
338 required_artifact_refs,
339 allowed_disclosure_policy: allowed_disclosure_policy.into(),
340 exactness_target,
341 trust_root_set_id,
342 citation,
343 challenge_expectations,
344 remote_oracle_lease_id,
345 };
346 value.validate()?;
347 Ok(value)
348 }
349
350 pub fn validate(&self) -> Result<(), &'static str> {
351 require_schema_version(
352 &self.schema_version,
353 REMOTE_SLICE_REQUEST_V1_SCHEMA,
354 "schema_version",
355 )?;
356 require_non_empty(
357 self.remote_slice_request_id.as_str(),
358 "remote_slice_request_id",
359 )?;
360 require_non_empty(
361 &self.requested_slice_definition,
362 "requested_slice_definition",
363 )?;
364 require_non_empty_slice(&self.required_artifact_refs, "required_artifact_refs")?;
365 require_non_empty(&self.allowed_disclosure_policy, "allowed_disclosure_policy")?;
366 require_non_empty(self.trust_root_set_id.as_str(), "trust_root_set_id")?;
367 require_citation(&self.citation)?;
368 require_non_empty(
369 self.remote_oracle_lease_id.as_str(),
370 "remote_oracle_lease_id",
371 )?;
372 Ok(())
373 }
374}
375
376#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
377pub struct RemoteSliceResultV1 {
378 pub schema_version: String,
379 pub remote_slice_result_id: RemoteSliceResultId,
380 pub remote_slice_request_id: RemoteSliceRequestId,
381 #[serde(flatten)]
382 pub citation: V25ConstitutionCitation,
383 #[serde(default, skip_serializing_if = "Vec::is_empty")]
384 pub returned_artifact_refs: Vec<String>,
385 pub exactness_class: RemoteExactnessClassV1,
386 pub remote_execution_evidence: String,
387 #[serde(default, skip_serializing_if = "Vec::is_empty")]
388 pub disclosure_markers: Vec<String>,
389 pub replay_handle: String,
390 pub local_admission_recommendation: LocalAdmissionRecommendationV1,
391 pub attestation_envelope_id: AttestationEnvelopeId,
392}
393
394impl RemoteSliceResultV1 {
395 #[allow(clippy::too_many_arguments)]
396 pub fn new(
397 remote_slice_result_id: RemoteSliceResultId,
398 remote_slice_request_id: RemoteSliceRequestId,
399 citation: V25ConstitutionCitation,
400 returned_artifact_refs: Vec<String>,
401 exactness_class: RemoteExactnessClassV1,
402 remote_execution_evidence: impl Into<String>,
403 disclosure_markers: Vec<String>,
404 replay_handle: impl Into<String>,
405 local_admission_recommendation: LocalAdmissionRecommendationV1,
406 attestation_envelope_id: AttestationEnvelopeId,
407 ) -> Result<Self, &'static str> {
408 let value = Self {
409 schema_version: REMOTE_SLICE_RESULT_V1_SCHEMA.to_string(),
410 remote_slice_result_id,
411 remote_slice_request_id,
412 citation,
413 returned_artifact_refs,
414 exactness_class,
415 remote_execution_evidence: remote_execution_evidence.into(),
416 disclosure_markers,
417 replay_handle: replay_handle.into(),
418 local_admission_recommendation,
419 attestation_envelope_id,
420 };
421 value.validate()?;
422 Ok(value)
423 }
424
425 pub fn validate(&self) -> Result<(), &'static str> {
426 require_schema_version(
427 &self.schema_version,
428 REMOTE_SLICE_RESULT_V1_SCHEMA,
429 "schema_version",
430 )?;
431 require_non_empty(
432 self.remote_slice_result_id.as_str(),
433 "remote_slice_result_id",
434 )?;
435 require_non_empty(
436 self.remote_slice_request_id.as_str(),
437 "remote_slice_request_id",
438 )?;
439 require_citation(&self.citation)?;
440 require_non_empty_slice(&self.returned_artifact_refs, "returned_artifact_refs")?;
441 require_non_empty(&self.remote_execution_evidence, "remote_execution_evidence")?;
442 require_non_empty(&self.replay_handle, "replay_handle")?;
443 require_non_empty(
444 self.attestation_envelope_id.as_str(),
445 "attestation_envelope_id",
446 )?;
447 Ok(())
448 }
449}
450
451#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
452pub struct CrossRuntimeReplayTicketV1 {
453 pub schema_version: String,
454 pub cross_runtime_replay_ticket_id: CrossRuntimeReplayTicketId,
455 #[serde(flatten)]
456 pub citation: V25ConstitutionCitation,
457 #[serde(default, skip_serializing_if = "Vec::is_empty")]
458 pub artifact_refs: Vec<String>,
459 pub time_coordinates: String,
460 #[serde(default, skip_serializing_if = "Vec::is_empty")]
461 pub required_trust_roots: Vec<TrustRootSetId>,
462 pub allowed_disclosure: RemoteDisclosureClassV1,
463 pub lease_window: String,
464 #[serde(default, skip_serializing_if = "Vec::is_empty")]
465 pub replay_expectations: Vec<String>,
466 pub failure_behavior: ReplayFailureBehaviorV1,
467}
468
469impl CrossRuntimeReplayTicketV1 {
470 #[allow(clippy::too_many_arguments)]
471 pub fn new(
472 cross_runtime_replay_ticket_id: CrossRuntimeReplayTicketId,
473 citation: V25ConstitutionCitation,
474 artifact_refs: Vec<String>,
475 time_coordinates: impl Into<String>,
476 required_trust_roots: Vec<TrustRootSetId>,
477 allowed_disclosure: RemoteDisclosureClassV1,
478 lease_window: impl Into<String>,
479 replay_expectations: Vec<String>,
480 failure_behavior: ReplayFailureBehaviorV1,
481 ) -> Result<Self, &'static str> {
482 let value = Self {
483 schema_version: CROSS_RUNTIME_REPLAY_TICKET_V1_SCHEMA.to_string(),
484 cross_runtime_replay_ticket_id,
485 citation,
486 artifact_refs,
487 time_coordinates: time_coordinates.into(),
488 required_trust_roots,
489 allowed_disclosure,
490 lease_window: lease_window.into(),
491 replay_expectations,
492 failure_behavior,
493 };
494 value.validate()?;
495 Ok(value)
496 }
497
498 pub fn validate(&self) -> Result<(), &'static str> {
499 require_schema_version(
500 &self.schema_version,
501 CROSS_RUNTIME_REPLAY_TICKET_V1_SCHEMA,
502 "schema_version",
503 )?;
504 require_non_empty(
505 self.cross_runtime_replay_ticket_id.as_str(),
506 "cross_runtime_replay_ticket_id",
507 )?;
508 require_citation(&self.citation)?;
509 require_non_empty_slice(&self.artifact_refs, "artifact_refs")?;
510 require_non_empty(&self.time_coordinates, "time_coordinates")?;
511 require_non_empty_slice(&self.required_trust_roots, "required_trust_roots")?;
512 require_non_empty(&self.lease_window, "lease_window")?;
513 Ok(())
514 }
515}