rootle_trace/event.rs
1//! Schema vocabulary shared by every producer; payload `fields` keep their
2//! domain's types and stay under the per-record cap.
3//!
4//! Schema 1 is rootle's initial closed vocabulary plus the always-present
5//! terminal `TraceEnd` marker that distinguishes a complete capture from a
6//! capped, failed, panicked or abandoned one.
7
8use serde::{Deserialize, Serialize};
9
10/// Envelope schema version for every rootle session trace.
11pub const SCHEMA_VERSION: u32 = 1;
12/// Hard upper bounds a capture may use. They exist so a runaway producer
13/// cannot fill the disk; `start` refuses anything outside them.
14pub const MAX_CAPTURE_BYTES: usize = 64 * 1024 * 1024;
15pub const MAX_CAPTURE_EVENTS: u64 = 100_000;
16pub const MAX_RECORD_BYTES: usize = 256 * 1024;
17/// The writer always reserves this much of the byte budget for the
18/// terminal marker, so a capture that hits its cap still ends legibly.
19pub const TERMINAL_RESERVE: usize = 1024;
20
21/// The closed event vocabulary. Payloads are producer-defined `fields`;
22/// the kind only fixes the category so files stay joinable across crates.
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
24#[serde(rename_all = "snake_case")]
25pub enum EventKind {
26 SessionStart,
27 SessionEnd,
28 TraceEnd,
29 Input,
30 Action,
31 State,
32 Render,
33 Resize,
34 JobStarted,
35 JobFinished,
36 JobRejected,
37 RpcMessage,
38 ProviderLifecycle,
39 ProviderStderr,
40 HttpRequest,
41 HttpResponse,
42 Cache,
43 Config,
44 ExternalCommand,
45 Error,
46 Panic,
47}
48
49/// What producer payloads may carry.
50///
51/// `Metadata` (the default) records kinds, lengths, identities and timing —
52/// diagnostic, not anonymous: paths, repository identities, revisions and
53/// operation names can still be sensitive. `Full` is an explicit opt into
54/// sensitive capture (typed text, UI field text, provider stderr); it adds
55/// surfaces, it is not a redaction boundary. Authorization headers,
56/// environment values, command arguments and raw RPC/HTTP bodies are never
57/// recorded automatically under either policy.
58#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
59pub enum ContentPolicy {
60 /// Structure, lengths and identities only.
61 #[default]
62 Metadata,
63 /// Explicit sensitive capture: exact input/UI text and provider stderr.
64 Full,
65}
66
67/// Bounded capture: total bytes, total events, per-record bytes.
68#[derive(Debug, Clone, Copy, PartialEq, Eq)]
69pub struct Limits {
70 pub max_bytes: usize,
71 pub max_events: u64,
72 pub max_record_bytes: usize,
73}
74
75impl Default for Limits {
76 fn default() -> Self {
77 Self {
78 max_bytes: MAX_CAPTURE_BYTES,
79 max_events: MAX_CAPTURE_EVENTS,
80 max_record_bytes: MAX_RECORD_BYTES,
81 }
82 }
83}
84
85impl Limits {
86 /// Hard bounds only — nothing, including callers, may lift a capture
87 /// past the crate maxima, and the writer needs room for its terminal
88 /// marker.
89 pub fn valid(&self) -> bool {
90 self.max_bytes >= TERMINAL_RESERVE * 2
91 && self.max_bytes <= MAX_CAPTURE_BYTES
92 && self.max_events > 0
93 && self.max_events <= MAX_CAPTURE_EVENTS
94 && self.max_record_bytes > 0
95 && self.max_record_bytes <= MAX_RECORD_BYTES
96 }
97}
98
99/// Startup controls for [`crate::start`].
100#[derive(Debug, Default, Clone, Copy)]
101pub struct TraceOptions {
102 pub content: ContentPolicy,
103 pub limits: Limits,
104}