Skip to main content

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}