Skip to main content

agentmux_srv/backend/
account_login_guard.rs

1// Copyright 2026, AgentMux Corp.
2// SPDX-License-Identifier: Apache-2.0
3
4//! An agent process must not hold the account's cloud login.
5//!
6//! The srv used to put the user's muxbus access token in every agent's env
7//! (`MUXBUS_TOKEN`, with `MUXBUS_COGNITO_DOMAIN`). Nothing in AgentMux reads
8//! it there: the srv makes every cloud call with its own stored login, and
9//! agents reach the cloud through the srv. What the variable did give an
10//! agent is the power to act as the account: publish WAN keys for an install
11//! it made up and sign jekts as any agent name, which then verify
12//! (`SPEC_WAN_JEKT_VERIFICATION_2026_09_24.md` §6.4). That is why a verified
13//! WAN sender couldn't be trusted without a human approving its install.
14//!
15//! [`strip_account_login`] is called on every path that spawns an agent
16//! process, next to `gh_guard::apply_gh_guard`, and removes the variables
17//! wherever they came from — a persisted `cmd:env` from before this change,
18//! or a caller's override. Like `GH_CONFIG_DIR`, they are reserved.
19//!
20//! Limit: agents run as the same OS user as the srv, so one that goes
21//! looking can still read the srv's stored login from disk (§6.5). This
22//! takes the login out of the agent's hands by default; it is not an OS
23//! boundary.
24
25use std::collections::HashMap;
26
27/// The account-login variables no agent env may carry.
28pub const ACCOUNT_LOGIN_VARS: &[&str] = &["MUXBUS_TOKEN", "MUXBUS_COGNITO_DOMAIN"];
29
30/// Remove every [`ACCOUNT_LOGIN_VARS`] entry from `env_vars`.
31pub fn strip_account_login(env_vars: &mut HashMap<String, String>) {
32    for var in ACCOUNT_LOGIN_VARS {
33        env_vars.remove(*var);
34    }
35}
36
37#[cfg(test)]
38mod tests {
39    use super::*;
40
41    #[test]
42    fn the_account_login_is_removed_and_nothing_else() {
43        let mut env: HashMap<String, String> = [
44            ("MUXBUS_TOKEN", "eyJ.access"),
45            ("MUXBUS_COGNITO_DOMAIN", "https://auth.example"),
46            ("MUXBUS_AGENT_ID", "agenty"),
47            ("AGENTMUX_AGENT_ID", "agenty"),
48        ]
49        .into_iter()
50        .map(|(k, v)| (k.to_string(), v.to_string()))
51        .collect();
52        strip_account_login(&mut env);
53        assert!(!env.contains_key("MUXBUS_TOKEN"));
54        assert!(!env.contains_key("MUXBUS_COGNITO_DOMAIN"));
55        assert_eq!(env.get("MUXBUS_AGENT_ID").map(String::as_str), Some("agenty"), "routing id stays");
56        assert_eq!(env.len(), 2);
57    }
58
59    #[test]
60    fn an_env_without_it_is_unchanged() {
61        let mut env: HashMap<String, String> = [("PATH".to_string(), "/bin".to_string())].into_iter().collect();
62        strip_account_login(&mut env);
63        assert_eq!(env.len(), 1);
64    }
65}