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