Skip to main content

kernel/
consts_external.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4// Copyright (c) 2026 2youg1 and the sprawling contributors
5
6//! External-fact constants: values that follow the
7//! outside world. Changing one requires evidence that the world changed,
8//! never our own preference.
9//! The part `crates/kernel/spec/ConstsExternal.lean` specifies this module.
10//!
11//! Data, plus the classifications that read that data and nothing else:
12//! [`readable_log_v`] answers a question only [`EVENT_LOG_V`] can answer
13//! and every home it could have had instead would have been a second
14//! place to decide which ledgers this build opens.
15
16/// Provider-side explicit cache breakpoint ceiling.
17pub const CACHE_BREAKPOINTS_MAX: u32 = 4;
18
19/// Prompt-cache lifetime treated as the async-discipline horizon (6.4).
20pub const PROMPT_CACHE_TTL_SECS: u64 = 300;
21
22/// How long before the end of a prompt-cache lifetime a keep-warm
23/// renewal must already be due, beyond the lead the caller measured.
24///
25/// Not an external fact: it is subtracted from [`PROMPT_CACHE_TTL_SECS`]
26/// and nowhere else, so it lives beside it. It covers the delays the lead
27/// cannot measure - a timer that wakes a little late, a renewal thread
28/// that starts a little late - and costs that many seconds of a warm
29/// window that lasts 300 (`crates/kernel/spec/KeepWarm.lean`, kernel D62).
30pub const PROMPT_CACHE_RENEWAL_MARGIN_SECS: u64 = 5;
31
32/// EventRecord `v`: one monotonic integer, no major/minor split (3.1).
33/// 2: the four lines a turn waits for record their own moment
34/// (kernel D10).
35pub const EVENT_LOG_V: u32 = 2;
36
37/// Where a ledger line's `v` stands against the version this build
38/// writes. The one authority for "which ledgers open"; every reader that
39/// compares a `v` asks [`readable_log_v`] rather than comparing again.
40///
41/// Exhaustive on purpose: a reader that handles three of these and
42/// defaults the fourth is how an unversioned line came to be reported as
43/// a broken chain.
44#[derive(Debug, Clone, Copy, PartialEq, Eq)]
45pub enum LogVersion {
46    /// Exactly what this build writes. Readable, and appendable.
47    Current,
48    /// Written by an older build, at or above the first version.
49    /// Readable: the ledger is append-only and its history stays on
50    /// disk, so every version from 1 upwards still parses.
51    Older,
52    /// Written by a newer build. Refused whole rather than read in part:
53    /// a line this build does not fully understand must not become the
54    /// prefix of a chain it then extends.
55    Ahead,
56    /// Below the first version anybody ever wrote, `0` included. No
57    /// build produced this, so it is a damaged or foreign line rather
58    /// than an old one.
59    NotAVersion,
60}
61
62/// The first `v` any build of this ledger format wrote. A line below it
63/// was never written by us.
64const EVENT_LOG_V_FIRST: u64 = 1;
65
66/// Classifies one ledger line's `v`.
67///
68/// Takes `u64` because that is what JSON hands a reader before anything
69/// has been validated; a `v` too large for [`EVENT_LOG_V`]'s type is
70/// [`LogVersion::Ahead`] like any other future version.
71#[must_use]
72pub fn readable_log_v(v: u64) -> LogVersion {
73    let current = u64::from(EVENT_LOG_V);
74    if v < EVENT_LOG_V_FIRST {
75        LogVersion::NotAVersion
76    } else if v > current {
77        LogVersion::Ahead
78    } else if v == current {
79        LogVersion::Current
80    } else {
81        LogVersion::Older
82    }
83}
84
85/// The L0 tool set: always present, never discovered (5.1).
86pub const L0_TOOLS: [&str; 3] = [crate::ToolName::EXEC, "edit", "status"];
87
88/// One secret shape: prefix + charset + length window. The table is
89/// data, not code; it grows with providers, and `kernel::secret::scan`
90/// plus `xtask secret` are its only consumers.
91#[derive(Debug, Clone, Copy, PartialEq, Eq)]
92pub struct SecretShape {
93    pub provider: &'static str,
94    pub prefix: &'static str,
95    pub charset: SecretCharset,
96    /// Closed interval over the length of the whole token, prefix
97    /// included. `scan` measures from the first byte of the prefix to
98    /// the last byte the charset admits, so a window written against the
99    /// body alone is short by `prefix.len()`.
100    pub len: (u16, u16),
101}
102
103/// Character sets that public token formats draw from.
104#[derive(Debug, Clone, Copy, PartialEq, Eq)]
105pub enum SecretCharset {
106    /// `[A-Za-z0-9]`
107    Base62,
108    /// `[A-Za-z0-9_-]`
109    Base64Url,
110    /// `[0-9a-f]`
111    HexLower,
112    /// `[A-Z0-9]`
113    UpperBase36,
114}
115
116/// Public provider token shapes, per each provider's published format.
117pub const SECRET_SHAPES: [SecretShape; 11] = [
118    SecretShape {
119        provider: "anthropic",
120        prefix: "sk-ant-",
121        charset: SecretCharset::Base64Url,
122        len: (24, 120),
123    },
124    SecretShape {
125        provider: "openai",
126        prefix: "sk-proj-",
127        charset: SecretCharset::Base64Url,
128        len: (24, 200),
129    },
130    SecretShape {
131        provider: "github",
132        prefix: "ghp_",
133        charset: SecretCharset::Base62,
134        len: (36, 40),
135    },
136    SecretShape {
137        provider: "github-oauth",
138        prefix: "gho_",
139        charset: SecretCharset::Base62,
140        len: (36, 40),
141    },
142    SecretShape {
143        provider: "aws",
144        prefix: "AKIA",
145        charset: SecretCharset::UpperBase36,
146        len: (16, 16),
147    },
148    SecretShape {
149        provider: "gitlab",
150        prefix: "glpat-",
151        charset: SecretCharset::Base64Url,
152        len: (20, 22),
153    },
154    SecretShape {
155        provider: "slack",
156        prefix: "xoxb-",
157        charset: SecretCharset::Base62,
158        len: (24, 80),
159    },
160    SecretShape {
161        provider: "google",
162        prefix: "AIza",
163        charset: SecretCharset::Base64Url,
164        len: (35, 35),
165    },
166    // The three below are aggregators, and they share one property that
167    // decides they must be listed: their bodies are all-lowercase hex or
168    // single-case base62, so `secret::scan`'s entropy detector - which
169    // fires only on mixed upper/lower/digit runs, so that the city's own
170    // blake3 hashes stay quiet - cannot reach them. For these, the shape
171    // table is not the primary net; it is the only one.
172    SecretShape {
173        provider: "openrouter",
174        prefix: "sk-or-v1-",
175        charset: SecretCharset::HexLower,
176        len: (40, 80),
177    },
178    SecretShape {
179        provider: "zenmux",
180        prefix: "sk-ai-v1-",
181        charset: SecretCharset::HexLower,
182        len: (40, 80),
183    },
184    SecretShape {
185        provider: "groq",
186        prefix: "gsk_",
187        charset: SecretCharset::Base62,
188        len: (32, 96),
189    },
190];
191
192#[cfg(test)]
193#[allow(
194    clippy::unwrap_used,
195    clippy::expect_used,
196    clippy::panic,
197    clippy::indexing_slicing,
198    reason = "test code"
199)]
200mod tests {
201    use super::*;
202    use std::collections::BTreeSet;
203
204    #[test]
205    fn the_five_facts_hold_their_documented_values() {
206        assert_eq!(CACHE_BREAKPOINTS_MAX, 4);
207        assert_eq!(PROMPT_CACHE_TTL_SECS, 300);
208        assert_eq!(EVENT_LOG_V, 2);
209        assert_eq!(L0_TOOLS, ["exec", "edit", "status"]);
210        assert_eq!(SECRET_SHAPES.len(), 11);
211    }
212
213    #[test]
214    fn every_version_a_line_can_carry_lands_in_exactly_one_class() {
215        assert_eq!(readable_log_v(0), LogVersion::NotAVersion);
216        assert_eq!(readable_log_v(u64::from(EVENT_LOG_V)), LogVersion::Current);
217        assert_eq!(
218            readable_log_v(u64::from(EVENT_LOG_V).saturating_add(1)),
219            LogVersion::Ahead
220        );
221        assert_eq!(readable_log_v(u64::MAX), LogVersion::Ahead);
222        for v in EVENT_LOG_V_FIRST..u64::from(EVENT_LOG_V) {
223            assert_eq!(readable_log_v(v), LogVersion::Older, "v{v}");
224        }
225    }
226
227    #[test]
228    fn secret_shapes_are_wellformed_data() {
229        let prefixes: BTreeSet<&str> = SECRET_SHAPES.iter().map(|s| s.prefix).collect();
230        assert_eq!(
231            prefixes.len(),
232            SECRET_SHAPES.len(),
233            "prefixes must be unique"
234        );
235        for shape in &SECRET_SHAPES {
236            assert!(!shape.prefix.is_empty());
237            assert!(!shape.provider.is_empty());
238            let (lo, hi) = shape.len;
239            assert!(
240                lo > 0 && lo <= hi,
241                "{}: empty or inverted window",
242                shape.provider
243            );
244        }
245    }
246}