gglib_core/paths/remote.rs
1//! Where the remote tunnel's stored endpoint key lives.
2//!
3//! `gglib remote enable` writes here every time, and the tunnel reuses what it
4//! finds: the endpoint key lasts, so a device pairs once instead of at every
5//! restart (ADR 0012, decision 4, reversed — see the amendment dated
6//! 2026-09-10). Deleting this file retires this machine's address and revokes
7//! no device: the tunnel mints a new key the next time it comes up, and still
8//! admits every device key kept beside this one that the roster lists.
9//! `gglib remote status` prints the path.
10
11use std::path::PathBuf;
12
13use super::error::PathError;
14use super::platform::data_root;
15use super::private::create_private_dir;
16
17/// Path to the stored iroh endpoint key for the remote tunnel.
18///
19/// Under `data/` rather than beside `pids/`, and that is load-bearing rather
20/// than tidy: a debug build resolves the data root to the repository checkout,
21/// and `.gitignore` ignores `/data` — so a private key written here cannot be
22/// committed by accident, where one written a level up would sit untracked in
23/// the working tree waiting for a `git add -A`.
24///
25/// The directory is created if it is not there; the file is not. modelpipe
26/// mints it `0600` on first use and refuses to read one that others can read.
27pub fn remote_identity_path() -> Result<PathBuf, PathError> {
28 let data_dir = data_root()?.join("data");
29
30 create_private_dir(&data_dir).map_err(|e| PathError::CreateFailed {
31 path: data_dir.clone(),
32 reason: e.to_string(),
33 })?;
34
35 Ok(data_dir.join("remote_identity"))
36}
37
38#[cfg(test)]
39mod tests {
40 use super::*;
41 use crate::paths::test_utils::ENV_LOCK;
42
43 #[test]
44 fn remote_identity_is_under_the_ignored_data_directory() {
45 let _guard = ENV_LOCK.lock().unwrap();
46 let identity = remote_identity_path().expect("remote_identity_path failed");
47 let data = data_root().expect("data_root failed");
48
49 assert!(identity.starts_with(&data));
50 assert!(identity.ends_with("remote_identity"));
51 // The parent must be `data/`, which is what `.gitignore` covers. A key
52 // that landed a level up would be untracked rather than ignored.
53 assert_eq!(
54 identity.parent().and_then(|p| p.file_name()),
55 Some(std::ffi::OsStr::new("data")),
56 "the identity file must sit inside the ignored data directory"
57 );
58 }
59}