Skip to main content

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}