zuka
zuka/src/brand.rs

worklyn / zukapublic

Agent-first git hosting. One Rust binary: git over HTTP and SSH, a REST API, MCP, CI, and multi-tenant isolation.

Get a copy: git clone https://zuka.worklyn.com/worklyn/zuka.git
zuka/src/brand.rs
RSbrand.rs7.9 KBDownload
1// Product identity, in one place.
2//
3// Everything that carries the product's name — the environment-variable prefix, the
4// default data directory, the problem-type URIs, the auth realm, the git config
5// namespace, the CI filename, the hook shim, user-facing message prefixes — derives
6// from `NAME` rather than repeating it as a literal.
7//
8// This module has already earned itself: the product was renamed once, and the edit
9// was one line in `Cargo.toml` plus a directory move. Before it existed the name was
10// fifty string literals across the tree — the kind of change that is technically
11// easy and practically never done.
12
13use std::sync::OnceLock;
14
15/// The product name. Taken from the crate so the two cannot disagree.
16pub const NAME: &str = env!("CARGO_PKG_NAME");
17
18/// Semantic version. Bumped by CI on release; this is the number for humans.
19pub const VERSION: &str = env!("CARGO_PKG_VERSION");
20
21/// Commit this binary was built from, from `build.rs`.
22pub const COMMIT: &str = env!("ZUKA_BUILD_COMMIT");
23
24/// The identity upgrade detection compares: version plus commit.
25///
26/// Not the version alone. A forgotten bump would make an old tenant look current,
27/// and the failure mode of that is a security patch silently not landing — which is
28/// exactly the case the automatic upgrade exists for.
29pub fn build() -> &'static str {
30 static BUILD: OnceLock<String> = OnceLock::new();
31 BUILD.get_or_init(|| format!("{VERSION}+{COMMIT}"))
32}
33
34/// Uppercase name, cached. Used to build the environment-variable prefix.
35fn upper() -> &'static str {
36 static UPPER: OnceLock<String> = OnceLock::new();
37 UPPER.get_or_init(|| NAME.to_ascii_uppercase())
38}
39
40/// Prefix on every environment variable this service reads, e.g. `ZUKA_`.
41pub fn env_prefix() -> &'static str {
42 static PREFIX: OnceLock<String> = OnceLock::new();
43 PREFIX.get_or_init(|| format!("{}_", upper()))
44}
45
46/// Full name of a namespaced environment variable.
47pub fn env_name(leaf: &str) -> String {
48 format!("{}{leaf}", env_prefix())
49}
50
51/// Read a namespaced environment variable.
52///
53/// Call sites name the suffix (`"BIND"`), never the whole variable, so the prefix is
54/// defined once.
55pub fn env(leaf: &str) -> Option<String> {
56 std::env::var(env_name(leaf)).ok()
57}
58
59/// Default data directory, `/var/lib/<name>`.
60pub fn default_data_dir() -> &'static str {
61 static DIR: OnceLock<String> = OnceLock::new();
62 DIR.get_or_init(|| format!("/var/lib/{NAME}"))
63}
64
65/// Base for RFC 9457 `type` URIs.
66///
67/// Configurable because a self-hoster should be able to point clients at their own
68/// documentation rather than at ours. The slugs after the base are the stable part of
69/// the contract; the origin is not.
70pub fn problem_base() -> &'static str {
71 static BASE: OnceLock<String> = OnceLock::new();
72 BASE.get_or_init(|| {
73 env("PROBLEM_BASE")
74 .map(|v| v.trim_end_matches('/').to_string())
75 .unwrap_or_else(|| format!("https://{NAME}.dev/problems"))
76 })
77}
78
79/// Full problem `type` URI for a slug.
80pub fn problem_uri(slug: &str) -> String {
81 format!("{}/{slug}", problem_base())
82}
83
84/// HTTP Basic authentication realm.
85pub fn realm() -> &'static str {
86 NAME
87}
88
89/// Prefix for messages printed at a user's terminal by git or ssh.
90pub fn say(message: impl std::fmt::Display) -> String {
91 format!("{NAME}: {message}")
92}
93
94/// Filename of the in-repository CI spec, `.<name>.toml`.
95pub fn ci_filename() -> &'static str {
96 static FILE: OnceLock<String> = OnceLock::new();
97 FILE.get_or_init(|| format!(".{NAME}.toml"))
98}
99
100/// An HTTP header in this product's namespace, e.g. `x-zuka-identity`.
101pub fn header_name(leaf: &str) -> &'static str {
102 // Leaked deliberately: header names are `&'static str` throughout hyper, and
103 // there are a handful of them for the life of the process.
104 Box::leak(format!("x-{NAME}-{leaf}").into_boxed_str())
105}
106
107/// A git config key in this product's namespace, e.g. `zuka.protected`.
108///
109/// Settings live in the repository's own git config rather than in our metadata so
110/// the hooks can read them with no data directory, no environment and no lookup —
111/// and so they travel with the repository if it is copied.
112pub fn git_config_key(leaf: &str) -> String {
113 format!("{NAME}.{leaf}")
114}
115
116#[cfg(test)]
117mod tests {
118 use super::*;
119
120 #[test]
121 fn the_env_prefix_is_derived_from_the_crate_name() {
122 assert_eq!(env_prefix(), format!("{}_", NAME.to_ascii_uppercase()));
123 assert_eq!(env_name("BIND"), format!("{}BIND", env_prefix()));
124 }
125
126 #[test]
127 fn derived_identifiers_all_track_the_name() {
128 assert!(default_data_dir().ends_with(NAME));
129 assert_eq!(ci_filename(), format!(".{NAME}.toml"));
130 assert_eq!(header_name("identity"), format!("x-{NAME}-identity"));
131 assert_eq!(git_config_key("protected"), format!("{NAME}.protected"));
132 assert_eq!(realm(), NAME);
133 assert_eq!(say("hello"), format!("{NAME}: hello"));
134 }
135
136 #[test]
137 fn the_build_identity_carries_both_the_version_and_the_commit() {
138 // Version alone is not enough: CI bumps it, and a forgotten bump would make
139 // an old tenant look current.
140 let build = build();
141 assert!(build.starts_with(VERSION), "{build}");
142 assert!(build.contains('+'), "{build}");
143 assert!(
144 build.len() > VERSION.len() + 1,
145 "the commit must be present: {build}"
146 );
147 }
148
149 #[test]
150 fn a_problem_uri_is_the_base_plus_the_slug() {
151 let uri = problem_uri("not-found");
152 assert!(uri.starts_with(problem_base()));
153 assert!(uri.ends_with("/not-found"));
154 }
155
156 /// No Rust source outside this module may contain the product name as a
157 /// literal.
158 ///
159 /// This is the guard that makes the rename claim true rather than aspirational.
160 /// It has already caught one leak — an auth realm hardcoded in a header — that
161 /// a manual rename test surfaced only by inspecting the wire.
162 #[test]
163 fn the_name_appears_in_no_other_source_file() {
164 let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("src");
165 let mut offenders = Vec::new();
166
167 let mut stack = vec![root];
168 while let Some(dir) = stack.pop() {
169 for entry in std::fs::read_dir(&dir).expect("read src") {
170 let path = entry.expect("dir entry").path();
171 if path.is_dir() {
172 stack.push(path);
173 continue;
174 }
175 if path.extension().and_then(|e| e.to_str()) != Some("rs") {
176 continue;
177 }
178 if path.file_name().and_then(|f| f.to_str()) == Some("brand.rs") {
179 continue;
180 }
181 let body = std::fs::read_to_string(&path).expect("read source");
182 for (n, line) in body.lines().enumerate() {
183 if line.to_ascii_lowercase().contains(NAME) {
184 offenders.push(format!(
185 "{}:{}: {}",
186 path.file_name().unwrap().to_string_lossy(),
187 n + 1,
188 line.trim()
189 ));
190 }
191 }
192 }
193 }
194
195 assert!(
196 offenders.is_empty(),
197 "the product name is hardcoded outside brand.rs; derive it instead:\n{}",
198 offenders.join("\n")
199 );
200 }
201
202 #[test]
203 fn nothing_here_hardcodes_the_current_name() {
204 // If the crate is renamed, every derived value must follow. This fails if
205 // someone reintroduces a literal.
206 for derived in [
207 default_data_dir().to_string(),
208 ci_filename().to_string(),
209 git_config_key("x"),
210 realm().to_string(),
211 say("x"),
212 ] {
213 assert!(
214 derived.contains(NAME),
215 "{derived:?} does not track the crate name"
216 );
217 }
218 }
219}