1fn default_instruction_cost() -> usize {
2 1
3}
4
5fn default_initial_cost_budget() -> usize {
6 usize::MAX
7}
8
9fn default_config_schema_version() -> u32 {
10 1
11}
12
13fn default_max_payload_bytes() -> usize {
14 64 * 1024
15}
16
17pub type ScopeId = usize;
19
20pub type Program = Box<[Instr]>;
22
23#[derive(Debug, Clone, Default, Serialize, Deserialize)]
25pub struct ProgramStore {
26 programs: Vec<Program>,
27 #[serde(default)]
28 cache: BTreeMap<Vec<u8>, usize>,
29}
30
31impl ProgramStore {
32 #[must_use]
34 pub fn new() -> Self {
35 Self::default()
36 }
37
38 fn ensure_cache_initialized(&mut self) {
39 if self.cache.len() == self.programs.len() {
40 return;
41 }
42 self.cache.clear();
43 for (idx, program) in self.programs.iter().enumerate() {
44 self.cache.insert(Self::cache_key(program), idx);
45 }
46 }
47
48 fn cache_key(program: &[Instr]) -> Vec<u8> {
49 bincode::serialize(program).expect("program serialization for cache key should succeed")
50 }
51
52 pub fn reserve(&mut self, additional: usize) {
54 self.programs.reserve(additional);
55 }
56
57 pub fn intern(&mut self, program: Vec<Instr>) -> usize {
59 self.ensure_cache_initialized();
60 let key = Self::cache_key(&program);
61 if let Some(existing) = self.cache.get(&key) {
62 return *existing;
63 }
64 let program_id = self.programs.len();
65 self.programs.push(program.into_boxed_slice());
66 self.cache.insert(key, program_id);
67 program_id
68 }
69
70 #[must_use]
72 pub fn get(&self, program_id: usize) -> Option<&Program> {
73 self.programs.get(program_id)
74 }
75
76 #[must_use]
78 pub fn len(&self) -> usize {
79 self.programs.len()
80 }
81
82 #[must_use]
84 pub fn is_empty(&self) -> bool {
85 self.programs.is_empty()
86 }
87
88 #[must_use]
90 pub fn instruction_count(&self) -> usize {
91 self.programs.iter().map(|program| program.len()).sum()
92 }
93
94 #[cfg(test)]
95 fn replace_for_test(&mut self, program_id: usize, program: Vec<Instr>) {
96 self.ensure_cache_initialized();
97 if let Some(existing) = self.programs.get(program_id) {
98 let key = Self::cache_key(existing);
99 self.cache.remove(&key);
100 }
101 self.programs[program_id] = program.into_boxed_slice();
102 let new_key = Self::cache_key(&self.programs[program_id]);
103 self.cache.insert(new_key, program_id);
104 }
105}
106
107type BranchList = Vec<(
109 telltale_types::Label,
110 Option<telltale_types::ValType>,
111 LocalTypeR,
112)>;
113
114pub(crate) fn runtime_value_val_type(value: &Value) -> ValType {
116 match value {
117 Value::Unit => ValType::Unit,
118 Value::Nat(_) => ValType::Nat,
119 Value::Bool(_) => ValType::Bool,
120 Value::Str(_) => ValType::String,
121 Value::Prod(left, right) => ValType::Prod(
122 Box::new(runtime_value_val_type(left)),
123 Box::new(runtime_value_val_type(right)),
124 ),
125 Value::Endpoint(endpoint) => ValType::Chan {
126 sid: endpoint.sid,
127 role: endpoint.role.clone(),
128 },
129 }
130}
131
132pub(crate) fn runtime_value_wire_size_bytes(value: &Value) -> usize {
134 match value {
135 Value::Unit => 1,
136 Value::Nat(_) => 8,
137 Value::Bool(_) => 1,
138 Value::Str(text) => 8_usize.saturating_add(text.len()),
139 Value::Prod(left, right) => 1_usize
140 .saturating_add(runtime_value_wire_size_bytes(left))
141 .saturating_add(runtime_value_wire_size_bytes(right)),
142 Value::Endpoint(endpoint) => 8_usize
143 .saturating_add(8_usize)
144 .saturating_add(endpoint.role.len()),
145 }
146}
147
148pub(crate) fn runtime_value_matches_val_type(value: &Value, expected: &ValType) -> bool {
150 match (value, expected) {
151 (Value::Unit, ValType::Unit) => true,
152 (Value::Nat(_), ValType::Nat) => true,
153 (Value::Bool(_), ValType::Bool) => true,
154 (Value::Str(_), ValType::String) => true,
155 (Value::Prod(left, right), ValType::Prod(expected_left, expected_right)) => {
156 runtime_value_matches_val_type(left, expected_left)
157 && runtime_value_matches_val_type(right, expected_right)
158 }
159 (Value::Endpoint(endpoint), ValType::Chan { sid, role }) => {
160 endpoint.sid == *sid && endpoint.role == *role
161 }
162 _ => false,
163 }
164}
165
166#[derive(Debug, Clone, Serialize, Deserialize, Default)]
168pub struct ResourceState {
169 commitments: BTreeSet<crate::verification::Commitment>,
170 nullifiers: BTreeSet<crate::verification::Nullifier>,
171}
172
173impl ResourceState {
174 #[must_use]
176 pub fn commit(&mut self, value: &Value) -> crate::verification::Commitment {
177 let commitment = crate::verification::DefaultVerificationModel::commitment(value);
178 self.commitments.insert(commitment);
179 commitment
180 }
181
182 pub fn consume(&mut self, value: &Value) -> Result<crate::verification::Nullifier, String> {
188 let nullifier = crate::verification::DefaultVerificationModel::nullifier(value);
189 if self.nullifiers.contains(&nullifier) {
190 return Err("resource already consumed".to_string());
191 }
192 self.nullifiers.insert(nullifier);
193 Ok(nullifier)
194 }
195
196 #[must_use]
198 pub fn verify_uncommitted(&self, value: &Value) -> bool {
199 let nullifier = crate::verification::DefaultVerificationModel::nullifier(value);
200 !self.nullifiers.contains(&nullifier)
201 }
202}
203
204#[derive(Debug, Clone, Serialize, Deserialize)]
206pub struct Arena {
207 slots: Vec<Option<Value>>,
208 next_free: usize,
209 capacity: usize,
210}
211
212impl Default for Arena {
213 fn default() -> Self {
214 Self::new(128)
215 }
216}
217
218impl Arena {
219 #[must_use]
221 pub fn new(capacity: usize) -> Self {
222 let cap = capacity.max(1);
223 Self {
224 slots: vec![None; cap],
225 next_free: 0,
226 capacity: cap,
227 }
228 }
229
230 pub fn alloc(&mut self, value: Value) -> Result<usize, String> {
236 for offset in 0..self.capacity {
237 let idx = (self.next_free + offset) % self.capacity;
238 if self.slots[idx].is_none() {
239 self.slots[idx] = Some(value);
240 self.next_free = (idx + 1) % self.capacity;
241 debug_assert!(self.check_invariants());
242 return Ok(idx);
243 }
244 }
245 Err("arena full".to_string())
246 }
247
248 pub fn free(&mut self, idx: usize) -> Result<Value, String> {
254 if idx >= self.capacity {
255 return Err("arena index out of bounds".to_string());
256 }
257 let value = self.slots[idx]
258 .take()
259 .ok_or_else(|| "arena slot already free".to_string())?;
260 if idx < self.next_free {
261 self.next_free = idx;
262 }
263 debug_assert!(self.check_invariants());
264 Ok(value)
265 }
266
267 #[must_use]
269 pub fn get(&self, idx: usize) -> Option<&Value> {
270 self.slots.get(idx).and_then(Option::as_ref)
271 }
272
273 pub fn get_mut(&mut self, idx: usize) -> Option<&mut Value> {
275 self.slots.get_mut(idx).and_then(Option::as_mut)
276 }
277
278 #[must_use]
280 pub fn check_invariants(&self) -> bool {
281 self.slots.len() == self.capacity && self.next_free < self.capacity
282 }
283}
284
285#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
287pub enum SessionKind {
288 Client,
290 Server,
292 Peer,
294}
295
296#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
298pub struct WellTypedInstr {
299 pub endpoint: Endpoint,
301 pub instr_tag: String,
303 pub tick: u64,
305}
306
307#[derive(Debug, Clone, Serialize, Deserialize, Default)]
309pub struct SessionMonitor {
310 session_kinds: BTreeMap<SessionId, SessionKind>,
311 last_judgment: Option<WellTypedInstr>,
312}
313
314impl SessionMonitor {
315 pub fn set_kind(&mut self, sid: SessionId, kind: SessionKind) {
317 self.session_kinds.insert(sid, kind);
318 }
319
320 pub fn remove_kind(&mut self, sid: SessionId) {
322 self.session_kinds.remove(&sid);
323 }
324
325 pub fn record(&mut self, endpoint: &Endpoint, instr_tag: &str, tick: u64) {
327 self.last_judgment = Some(WellTypedInstr {
328 endpoint: endpoint.clone(),
329 instr_tag: instr_tag.to_string(),
330 tick,
331 });
332 }
333}
334
335pub type SiteId = String;
337
338#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
340pub struct CorruptedEdge {
341 edge: Edge,
342 corruption: CorruptionType,
343}
344
345#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
347pub struct SiteTimeout {
348 site: SiteId,
349 until_tick: u64,
350}
351
352#[derive(Debug, Clone, Serialize, Deserialize)]
354pub struct GuardLayerConfig {
355 pub id: String,
357 pub active: bool,
359}
360
361#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
363pub enum MonitorMode {
364 Off,
366 #[default]
368 SessionTypePrecheck,
369}
370
371pub enum FlowPolicy {
373 AllowAll,
375 DenyAll,
377 AllowRoles(BTreeSet<String>),
379 DenyRoles(BTreeSet<String>),
381 Predicate(Box<dyn FlowPolicyFn>),
384 PredicateExpr(FlowPredicate),
386}
387
388pub trait FlowPolicyFn: Send + Sync {
390 fn eval(&self, knowledge: &KnowledgeFact, target_role: &str) -> bool;
392 fn clone_box(&self) -> Box<dyn FlowPolicyFn>;
394}
395
396impl<F> FlowPolicyFn for F
397where
398 F: Fn(&KnowledgeFact, &str) -> bool + Clone + Send + Sync + 'static,
399{
400 fn eval(&self, knowledge: &KnowledgeFact, target_role: &str) -> bool {
401 self(knowledge, target_role)
402 }
403
404 fn clone_box(&self) -> Box<dyn FlowPolicyFn> {
405 Box::new(self.clone())
406 }
407}
408
409impl Clone for Box<dyn FlowPolicyFn> {
410 fn clone(&self) -> Self {
411 self.clone_box()
412 }
413}
414
415#[allow(clippy::derivable_impls)]
416impl Default for FlowPolicy {
417 fn default() -> Self {
418 Self::AllowAll
419 }
420}
421
422impl Clone for FlowPolicy {
423 fn clone(&self) -> Self {
424 match self {
425 Self::AllowAll => Self::AllowAll,
426 Self::DenyAll => Self::DenyAll,
427 Self::AllowRoles(roles) => Self::AllowRoles(roles.clone()),
428 Self::DenyRoles(roles) => Self::DenyRoles(roles.clone()),
429 Self::Predicate(predicate) => Self::Predicate(predicate.clone()),
430 Self::PredicateExpr(predicate) => Self::PredicateExpr(predicate.clone()),
431 }
432 }
433}
434
435impl fmt::Debug for FlowPolicy {
436 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
437 match self {
438 Self::AllowAll => f.write_str("AllowAll"),
439 Self::DenyAll => f.write_str("DenyAll"),
440 Self::AllowRoles(roles) => f.debug_tuple("AllowRoles").field(roles).finish(),
441 Self::DenyRoles(roles) => f.debug_tuple("DenyRoles").field(roles).finish(),
442 Self::Predicate(_) => f.write_str("Predicate(<dynamic>)"),
443 Self::PredicateExpr(predicate) => {
444 f.debug_tuple("PredicateExpr").field(predicate).finish()
445 }
446 }
447 }
448}
449
450impl PartialEq for FlowPolicy {
451 fn eq(&self, other: &Self) -> bool {
452 match (self, other) {
453 (Self::AllowAll, Self::AllowAll) => true,
454 (Self::DenyAll, Self::DenyAll) => true,
455 (Self::AllowRoles(lhs), Self::AllowRoles(rhs)) => lhs == rhs,
456 (Self::DenyRoles(lhs), Self::DenyRoles(rhs)) => lhs == rhs,
457 (Self::Predicate(lhs), Self::Predicate(rhs)) => {
458 std::ptr::eq::<dyn FlowPolicyFn>(&**lhs, &**rhs)
462 }
463 (Self::PredicateExpr(lhs), Self::PredicateExpr(rhs)) => lhs == rhs,
464 _ => false,
465 }
466 }
467}
468
469impl Eq for FlowPolicy {}
470
471#[derive(Serialize, Deserialize)]
472enum FlowPolicyRepr {
473 AllowAll,
474 DenyAll,
475 AllowRoles(BTreeSet<String>),
476 DenyRoles(BTreeSet<String>),
477 PredicateExpr(FlowPredicate),
478}
479
480impl Serialize for FlowPolicy {
481 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
482 where
483 S: Serializer,
484 {
485 let repr = match self {
486 Self::AllowAll => FlowPolicyRepr::AllowAll,
487 Self::DenyAll => FlowPolicyRepr::DenyAll,
488 Self::AllowRoles(roles) => FlowPolicyRepr::AllowRoles(roles.clone()),
489 Self::DenyRoles(roles) => FlowPolicyRepr::DenyRoles(roles.clone()),
490 Self::PredicateExpr(predicate) => FlowPolicyRepr::PredicateExpr(predicate.clone()),
491 Self::Predicate(_) => {
492 return Err(serde::ser::Error::custom(
493 "runtime closure predicate is not serializable",
494 ))
495 }
496 };
497 repr.serialize(serializer)
498 }
499}
500
501impl<'de> Deserialize<'de> for FlowPolicy {
502 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
503 where
504 D: Deserializer<'de>,
505 {
506 let repr = FlowPolicyRepr::deserialize(deserializer)?;
507 let policy = match repr {
508 FlowPolicyRepr::AllowAll => Self::AllowAll,
509 FlowPolicyRepr::DenyAll => Self::DenyAll,
510 FlowPolicyRepr::AllowRoles(roles) => Self::AllowRoles(roles),
511 FlowPolicyRepr::DenyRoles(roles) => Self::DenyRoles(roles),
512 FlowPolicyRepr::PredicateExpr(predicate) => Self::PredicateExpr(predicate),
513 };
514 Ok(policy)
515 }
516}
517
518impl FlowPolicy {
519 #[must_use]
521 pub fn predicate<F>(predicate: F) -> Self
522 where
523 F: Fn(&KnowledgeFact, &str) -> bool + Clone + Send + Sync + 'static,
524 {
525 Self::Predicate(Box::new(predicate))
526 }
527}
528
529#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
531pub enum FlowPredicate {
532 TargetRolePrefix(String),
534 FactContains(String),
536 EndpointRoleMatchesTarget,
538 All(Vec<FlowPredicate>),
540 Any(Vec<FlowPredicate>),
542}
543
544impl FlowPolicy {
545 #[must_use]
547 pub fn allows(&self, target_role: &str) -> bool {
548 match self {
549 Self::AllowAll => true,
550 Self::DenyAll => false,
551 Self::AllowRoles(roles) => roles.contains(target_role),
552 Self::DenyRoles(roles) => !roles.contains(target_role),
553 Self::Predicate(_) | Self::PredicateExpr(_) => true,
554 }
555 }
556
557 #[must_use]
559 pub fn allows_knowledge(&self, knowledge: &KnowledgeFact, target_role: &str) -> bool {
560 match self {
561 Self::Predicate(predicate) => predicate.eval(knowledge, target_role),
562 Self::PredicateExpr(predicate) => predicate.eval(knowledge, target_role),
563 other => other.allows(target_role),
564 }
565 }
566}
567
568impl FlowPredicate {
569 #[must_use]
571 pub fn eval(&self, knowledge: &KnowledgeFact, target_role: &str) -> bool {
572 match self {
573 Self::TargetRolePrefix(prefix) => target_role.starts_with(prefix),
574 Self::FactContains(fragment) => knowledge.fact.contains(fragment),
575 Self::EndpointRoleMatchesTarget => knowledge.endpoint.role == target_role,
576 Self::All(predicates) => predicates
577 .iter()
578 .all(|predicate| predicate.eval(knowledge, target_role)),
579 Self::Any(predicates) => predicates
580 .iter()
581 .any(|predicate| predicate.eval(knowledge, target_role)),
582 }
583 }
584}
585
586#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
588#[serde(rename_all = "snake_case")]
589pub enum RuntimeTuningProfile {
590 #[default]
592 Standard,
593 M1StressReference,
595}
596
597#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
599#[serde(rename_all = "snake_case")]
600pub enum ThreadedRoundSemantics {
601 #[default]
603 CanonicalOneStep,
604 WaveParallelExtension,
606}
607
608#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
610#[serde(rename_all = "snake_case")]
611pub enum EffectTraceCaptureMode {
612 #[default]
614 Full,
615 TopologyOnly,
617 Disabled,
619}
620
621#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
623#[serde(rename_all = "snake_case")]
624pub enum ObservabilityRetentionMode {
625 #[default]
627 Full,
628 Capped,
630 Disabled,
632}
633
634#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
636pub struct ObservabilityRetentionConfig {
637 #[serde(default)]
639 pub mode: ObservabilityRetentionMode,
640 #[serde(default = "default_observability_retention_capacity")]
642 pub capacity: usize,
643}
644
645const fn default_observability_retention_capacity() -> usize {
646 4_096
647}
648
649impl Default for ObservabilityRetentionConfig {
650 fn default() -> Self {
651 Self {
652 mode: ObservabilityRetentionMode::Full,
653 capacity: default_observability_retention_capacity(),
654 }
655 }
656}
657
658#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
660#[serde(rename_all = "snake_case")]
661pub enum PayloadValidationMode {
662 Off,
664 #[default]
666 Structural,
667 StrictSchema,
669}