blob: d3d9fac982bd5b8f01ddfb38bfd2a1c4f6a343f0 [file]
//! The `wslc` backend: the harness in the harness image, under WSL containers.
//!
//! Two containers in the session VM. The egress container runs this same binary as
//! `amem inside`, with the record mounted in and the only route outside: it opens the
//! memory, serves the egress on a socket in a shared volume, and does everything the
//! launcher does on Linux, including task mode. The harness container has no network at
//! all, the working tree and its private home mounted in, and reaches the egress through
//! the shared volume. This host side is deliberately dumb: it starts the egress container,
//! then turns each run request the inside writes into one `wslc run`, and reports the
//! exit code the inside leaves behind. Nothing of the record is touched from here.
//!
//! The container is the boundary; the runtime's seccomp filter refuses our own
//! namespaces inside it, and does not need to allow them.
use crate::requests::{RunRequest, DONE, REQUEST_DIR_INSIDE};
use dm_sandbox::{AuthMode, Harness, INSIDE_HOME};
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::time::Duration;
type Fallible<T> = Result<T, Box<dyn std::error::Error>>;
/// The default image tag; `MEMORY_IMAGE` or `--image` overrides it.
pub const DEFAULT_IMAGE: &str = "durable-memory/harness:dev";
/// The record inside the egress container.
pub(crate) const INSIDE_ROOT: &str = "/memory";
/// The host's login directories, read-only, inside the egress container.
pub(crate) const INSIDE_HOST_HOME: &str = "/host-home";
/// The working tree inside the harness container.
pub(crate) const INSIDE_WORK: &str = "/work";
/// Variables the inside may need, passed to the egress container when set here.
const PASS_ENV: &[&str] = &[
"ANTHROPIC_API_KEY",
"OPENAI_API_KEY",
"GEMINI_API_KEY",
"XAI_API_KEY",
"LLMAO_KEY",
"LLMAO_URL",
"GH_TOKEN",
"COPILOT_GITHUB_TOKEN",
"GOOGLE_CLOUD_PROJECT",
// the web launcher's id for the session, for the status it reads; an id, not a path
"AMEM_WEB_SESSION",
];
/// Login directories copied from, never written.
const LOGIN_DIRS: &[&str] = &[".claude", ".codex", ".gemini"];
/// The gateway inside the session VM, for `--auth gateway`: the same LiteLLM over
/// Postgres as `make local-up` runs on the host, which the VM cannot reach. Both live on a
/// network of their own; the egress container joins it. They are left running between
/// launches, like the host's.
const GATEWAY_NETWORK: &str = "memory-gateway";
const GATEWAY_LITELLM: &str = "memory-litellm";
const GATEWAY_POSTGRES: &str = "memory-postgres";
const LITELLM_IMAGE: &str = "ghcr.io/berriai/litellm:main-stable";
const POSTGRES_IMAGE: &str = "docker.io/library/postgres:16-alpine";
/// The gateway's configuration, the same file the host stack uses.
const LITELLM_CONFIG: &str = include_str!("../../../local/litellm.yaml");
/// What to run.
pub struct Launch {
/// This run's projection directory name under `state/projection/<harness>/`, which
/// the container mounts and the inside renders under: [`projection_name`]'s.
pub projection: String,
/// The record on this host.
pub root: PathBuf,
/// Which harness.
pub harness: Harness,
/// How it authenticates.
pub auth: AuthMode,
/// Another destination, where the harness allows it.
pub provider: Option<String>,
/// A credential of their own for the memory's calls, when they have one.
pub server_auth: Option<AuthMode>,
/// Where those calls go under a key.
pub server_provider: Option<String>,
/// The working tree on this host.
pub workdir: PathBuf,
/// The harness image.
pub image: String,
/// The flags for `amem inside` that say what to run: `--prompt`, `--work`, and so
/// on, followed by `--` and the harness's own arguments.
pub inside_args: Vec<String>,
/// Attached to the terminal: no prompt and no task.
pub interactive: bool,
/// How the host drives WSL containers.
pub via: Via,
/// Where the C API's session disks live, when not under the root.
#[cfg_attr(not(windows), allow(dead_code))] // the C API is Windows only
pub storage: Option<PathBuf>,
}
/// How the host drives WSL containers.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, clap::ValueEnum)]
pub enum Via {
/// The C API on Windows where the launch allows it, the command line otherwise.
#[default]
Auto,
/// The `wslc` command line.
Cli,
/// The C API in `wslcsdk.dll`: Windows, and neither interactive nor the gateway.
Api,
}
/// Which way a launch goes. The API gives a process no terminal and makes no networks,
/// so an interactive launch and the gateway inside the VM need the command line;
/// `auto` picks the API wherever it can serve.
///
/// # Errors
///
/// When the API is asked for and cannot serve this launch.
pub(crate) fn via_for(l: &Launch, windows: bool) -> Result<Via, String> {
let blocker = if !windows {
Some(dm_wslc_api::WINDOWS_ONLY)
} else if l.interactive {
Some("the C API gives a process no terminal, so an interactive launch goes through the command line")
} else if wants_gateway(l) {
Some("the gateway inside the VM needs a network of its own, which the C API cannot make, so --auth gateway or --server-auth gateway goes through the command line")
} else {
None
};
match (l.via, blocker) {
(Via::Cli, _) | (Via::Auto, Some(_)) => Ok(Via::Cli),
(Via::Api | Via::Auto, None) => Ok(Via::Api),
(Via::Api, Some(why)) => Err(format!(
"--wslc-via api: {why}; pass --wslc-via cli or auto"
)),
}
}
/// The `wslc` command: `wslc.exe` through interop from a WSL distribution, `wslc` on
/// Windows itself.
pub(crate) fn wslc() -> &'static str {
if which("wslc.exe") {
"wslc.exe"
} else {
"wslc"
}
}
fn which(name: &str) -> bool {
std::env::var_os("PATH")
.is_some_and(|p| std::env::split_paths(&p).any(|d| d.join(name).is_file()))
}
/// A host path as `wslc` must see it: from inside a WSL distribution that is the UNC
/// name `wslpath -w` gives; on Windows it is the path itself.
pub(crate) fn mount_path(p: &Path) -> Fallible<String> {
if std::env::var_os("WSL_DISTRO_NAME").is_none() {
return Ok(p.display().to_string());
}
let out = Command::new("wslpath").arg("-w").arg(p).output()?;
if !out.status.success() {
return Err(format!(
"wslpath -w {}: {}",
p.display(),
String::from_utf8_lossy(&out.stderr)
)
.into());
}
Ok(String::from_utf8_lossy(&out.stdout).trim().to_string())
}
pub(crate) fn run_ok(args: &[String]) -> Fallible<String> {
let out = Command::new(wslc())
.args(args)
.stdin(Stdio::null())
.output()?;
if !out.status.success() {
return Err(format!(
"{} {}: {}",
wslc(),
args.first().cloned().unwrap_or_default(),
String::from_utf8_lossy(&out.stderr).trim()
)
.into());
}
Ok(String::from_utf8_lossy(&out.stdout).trim().to_string())
}
fn s(v: &[&str]) -> Vec<String> {
v.iter().map(|x| (*x).to_string()).collect()
}
/// The arguments of the egress container's `wslc run`. `extra` is environment the
/// launch adds, such as the gateway's address inside the VM; `network` joins a network.
fn egress_run_args(
l: &Launch,
name: &str,
volume: &str,
root_m: &str,
host_home: Option<(&str, &[String])>,
extra: &[(String, String)],
network: Option<&str>,
) -> Vec<String> {
let mut a = s(&["run", "-d", "--name", name]);
if let Some(n) = network {
a.push("--network".into());
a.push(n.to_string());
}
a.push("-v".into());
a.push(format!("{root_m}:{INSIDE_ROOT}"));
a.push("-v".into());
a.push(format!("{volume}:{REQUEST_DIR_INSIDE}"));
if let Some((home_m, dirs)) = host_home {
for d in dirs {
a.push("-v".into());
a.push(format!(
"{home_m}{}{d}:{INSIDE_HOST_HOME}/{d}:ro",
std::path::MAIN_SEPARATOR
));
}
}
for (k, v) in egress_env(&tap_credentials(&l.root), extra) {
a.push("-e".into());
a.push(format!("{k}={v}"));
}
a.push(l.image.clone());
a.extend(inside_command(l));
a
}
/// The variables the root's tap grants name as credentials. The egress container runs
/// the taps and calls the hosted tool servers, so these go there with `PASS_ENV`. A name
/// that would change how the container runs, rather than hold a credential, is left out.
pub(crate) fn tap_credentials(root: &Path) -> Vec<String> {
let Ok(g) = dm_policy::Grants::load(&root.join(dm_server::GRANTS_FILE)) else {
return Vec::new();
};
let mut names: Vec<String> = g
.taps
.values()
.filter_map(|t| t.credential.clone())
.filter(|n| {
!n.is_empty()
&& n.chars()
.all(|c| c.is_ascii_uppercase() || c.is_ascii_digit() || c == '_')
&& !matches!(n.as_str(), "PATH" | "HOME" | "USER" | "SHELL" | "TMPDIR")
&& !n.starts_with("LD_")
&& !n.starts_with("MEMORY_")
})
.collect();
names.sort();
names.dedup();
names
}
/// The egress container's environment: what `PASS_ENV` and the taps' credentials name
/// and is set here, then what the launch adds, which wins.
pub(crate) fn egress_env(taps: &[String], extra: &[(String, String)]) -> Vec<(String, String)> {
let mut env = Vec::new();
let names = PASS_ENV.iter().map(|k| (*k).to_string()).chain(
taps.iter()
.filter(|t| !PASS_ENV.contains(&t.as_str()))
.cloned(),
);
for k in names {
let k = k.as_str();
if extra.iter().any(|(ek, _)| ek == k) {
continue;
}
// a GitHub login lives in gh's keyring, where the container cannot ask for it
let v = crate::credentials::present(std::env::var(k).ok());
let v = if k == "GH_TOKEN" {
v.or_else(crate::credentials::gh_token)
} else {
v
};
if let Some(v) = v {
env.push((k.to_string(), v));
}
}
env.extend(extra.iter().cloned());
env
}
/// The egress container's command: this binary as `amem inside`, told what to run.
pub(crate) fn inside_command(l: &Launch) -> Vec<String> {
let mut a = s(&[
"/opt/memory/memory",
"--root",
INSIDE_ROOT,
"inside",
"--requests",
REQUEST_DIR_INSIDE,
"--host-home",
INSIDE_HOST_HOME,
"--workdir-inside",
INSIDE_WORK,
"--projection",
&l.projection,
"--harness",
l.harness.binary(),
"--auth",
]);
a.push(auth_name(l.auth).into());
if let Some(p) = &l.provider {
a.push("--provider".into());
a.push(p.clone());
}
if let Some(s) = l.server_auth {
a.push("--server-auth".into());
a.push(auth_name(s).into());
}
if let Some(p) = &l.server_provider {
a.push("--server-provider".into());
a.push(p.clone());
}
a.extend(l.inside_args.iter().cloned());
a
}
/// Whether this launch needs the gateway inside the VM: the harness's route or the
/// memory's own asks for it.
fn wants_gateway(l: &Launch) -> bool {
l.auth == AuthMode::Gateway || l.server_auth == Some(AuthMode::Gateway)
}
pub(crate) fn auth_name(a: AuthMode) -> &'static str {
match a {
AuthMode::Subscription => "subscription",
AuthMode::Key => "key",
AuthMode::Gateway => "gateway",
}
}
/// The arguments of a harness container's `wslc run`, from one request: no network, the
/// harness user, the shared volume, the private home and the working tree, the plan's
/// environment, and a shell wrapper that leaves the exit code (and, when captured, the
/// output) in the shared volume for the inside to read.
fn harness_run_args(
req: &RunRequest,
n: usize,
image: &str,
volume: &str,
projection_m: Option<&str>,
home_m: &str,
work_m: &str,
) -> Vec<String> {
let mut a = s(&["run", "--rm"]);
a.push(if req.interactive {
"-it".into()
} else {
"-i".into()
});
a.extend(s(&["--network", "none", "-u", "1000:1000", "-v"]));
a.push(format!("{volume}:{REQUEST_DIR_INSIDE}"));
a.push("-v".into());
a.push(format!("{home_m}:{INSIDE_HOME}"));
a.push("-v".into());
a.push(format!("{work_m}:{INSIDE_WORK}"));
// the projection's path is the host's own, from the launch, never the request's:
// the request file sits in a volume the harness container can write
if let Some(projection_m) = projection_m {
a.push("-v".into());
a.push(format!(
"{projection_m}:{}:ro",
dm_sandbox::INSIDE_PROJECTION
));
}
a.push("-w".into());
a.push(req.cwd.clone());
for (k, v) in &req.env {
a.push("-e".into());
a.push(format!("{k}={v}"));
}
a.push(image.to_string());
a.extend(wrapped(req, n));
a
}
/// A request's command in the shell wrapper that leaves its exit code (and, when
/// captured, its output) in the shared volume for the inside to read.
pub(crate) fn wrapped(req: &RunRequest, n: usize) -> Vec<String> {
let exit = RunRequest::exit_file(n);
let script = if req.interactive {
format!("\"$0\" \"$@\"; echo $? > {exit}")
} else {
format!(
"\"$0\" \"$@\" > {}; echo $? > {exit}",
RunRequest::out_file(n)
)
};
let mut a = vec!["/bin/sh".to_string(), "-c".to_string(), script];
a.extend(req.argv.iter().cloned());
a
}
/// The host home's mount path and the login directories under it that a subscription
/// mode copies from, when there are any; nothing else of the host home is mounted.
pub(crate) fn host_logins() -> Fallible<(Option<String>, Vec<String>)> {
let host_home = std::env::var_os("HOME")
.or_else(|| std::env::var_os("USERPROFILE"))
.map(PathBuf::from);
let login_dirs: Vec<String> = host_home
.as_ref()
.map(|h| {
LOGIN_DIRS
.iter()
.filter(|d| h.join(d).is_dir())
.map(|d| (*d).to_string())
.collect()
})
.unwrap_or_default();
let host_home_m = match &host_home {
Some(h) if !login_dirs.is_empty() => Some(mount_path(h)?),
_ => None,
};
Ok((host_home_m, login_dirs))
}
/// This run's projection directory on the host, `state/projection/<harness>/<name>`,
/// made here so the container mounts it before the inside renders into it, and its mount
/// path. One per run, since two sessions of a harness at different ceilings would
/// otherwise each read what the other rendered; [`run`] removes it when the run ends.
pub(crate) fn projection_mount(l: &Launch) -> Fallible<Option<String>> {
let dir = projection_dir(l);
std::fs::create_dir_all(&dir)?;
Ok(Some(mount_path(&dir)?))
}
/// A fresh name for a run's projection: the pid alone could come round again on a root a
/// killed run left a render in.
#[must_use]
pub fn projection_name() -> String {
let nanos = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.subsec_nanos())
.unwrap_or_default();
format!("run-{}-{nanos:08x}", std::process::id())
}
pub(crate) fn projection_dir(l: &Launch) -> PathBuf {
l.root
.join(dm_sandbox::PROJECTION)
.join(l.harness.binary())
.join(&l.projection)
}
/// Run a launch through WSL containers, over the C API or the command line as
/// [`via_for`] decides. Returns the exit code the inside reports.
///
/// # Errors
///
/// When the way asked for cannot serve the launch, `wslc` or the DLL is not there, the
/// image cannot be had, or a container cannot be started.
pub fn run(l: &Launch) -> Fallible<i32> {
let code = match via_for(l, cfg!(windows))? {
Via::Api => run_api(l),
_ => run_cli(l),
};
std::fs::remove_dir_all(projection_dir(l)).ok();
code
}
/// Over the C API. Under `auto`, what keeps the API from serving, a missing DLL or a
/// root with nowhere for a session disk, sends the launch to the command line instead.
#[cfg(windows)]
fn run_api(l: &Launch) -> Fallible<i32> {
let ready = crate::wslc_api::storage_for(&l.root, l.storage.as_deref())
.map_err(|e| e.to_string())
.and_then(|storage| {
dm_wslc_api::Api::load()
.map(|api| (api, storage))
.map_err(|e| e.to_string())
});
match ready {
Ok((api, storage)) => crate::wslc_api::run(l, &api, &storage),
Err(why) if l.via == Via::Auto => {
eprintln!("amem: not over the C API ({why}); driving the wslc command line");
run_cli(l)
}
Err(why) => Err(why.into()),
}
}
#[cfg(not(windows))]
fn run_api(_: &Launch) -> Fallible<i32> {
Err(dm_wslc_api::WINDOWS_ONLY.into())
}
/// Run a launch through the `wslc` command line.
fn run_cli(l: &Launch) -> Fallible<i32> {
let pid = std::process::id();
let volume = format!("memory-run-{pid}");
let egress = format!("memory-egress-{pid}");
let root_m = mount_path(&l.root)?;
let work_m = mount_path(&l.workdir)?;
let home_dir = l.root.join("state/sandbox").join(l.harness.binary());
std::fs::create_dir_all(&home_dir)?;
let home_m = mount_path(&home_dir)?;
let projection_m = projection_mount(l)?;
let (host_home_m, login_dirs) = host_logins()?;
let (extra, network) = if wants_gateway(l) {
let key = gateway_up(&l.root)?;
(
vec![
(
"LLMAO_URL".to_string(),
format!("http://{GATEWAY_LITELLM}:4000"),
),
("LLMAO_KEY".to_string(), key),
],
Some(GATEWAY_NETWORK),
)
} else {
(Vec::new(), None)
};
run_ok(&s(&["volume", "create", &volume]))?;
let result = (|| -> Fallible<i32> {
let args = egress_run_args(
l,
&egress,
&volume,
&root_m,
host_home_m.as_deref().map(|m| (m, login_dirs.as_slice())),
&extra,
network,
);
run_ok(&args)?;
let mut logs = Command::new(wslc())
.args(["logs", "-f", &egress])
.stdin(Stdio::null())
.spawn()?;
let code = drive(
&egress,
&volume,
&l.image,
projection_m.as_deref(),
&home_m,
&work_m,
);
logs.kill().ok();
logs.wait().ok();
code
})();
Command::new(wslc())
.args(["remove", "-f", &egress])
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
.ok();
Command::new(wslc())
.args(["volume", "remove", &volume])
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
.ok();
result
}
fn container_state(name: &str) -> Option<String> {
run_ok(&s(&["inspect", "--format", "{{.State.Status}}", name]))
.ok()
.map(|t| t.trim().to_string())
}
/// Bring the gateway up inside the VM if it is not already, and return the key the egress
/// uses against it. Locally that is the master key, as `make local-key` would mint a
/// virtual key from; a per-session virtual key is for the Foundation's front desk.
fn gateway_up(root: &Path) -> Fallible<String> {
let master = std::env::var("LITELLM_MASTER_KEY")
.ok()
.filter(|k| !k.is_empty())
.ok_or("the gateway inside the VM (--auth or --server-auth gateway) needs LITELLM_MASTER_KEY, in the environment or the root's credentials.env (make reads local/.env)")?;
let pg_password = std::env::var("POSTGRES_PASSWORD").unwrap_or_else(|_| "litellm".into());
if run_ok(&s(&["network", "inspect", GATEWAY_NETWORK])).is_err() {
run_ok(&s(&["network", "create", GATEWAY_NETWORK]))?;
}
ensure_running(GATEWAY_POSTGRES, || {
let mut a = s(&[
"run",
"-d",
"--name",
GATEWAY_POSTGRES,
"--network",
GATEWAY_NETWORK,
]);
a.extend(s(&[
"-e",
"POSTGRES_DB=litellm",
"-e",
"POSTGRES_USER=litellm",
]));
a.push("-e".into());
a.push(format!("POSTGRES_PASSWORD={pg_password}"));
a.push(POSTGRES_IMAGE.into());
a
})?;
let config_dir = root.join("state/gateway");
std::fs::create_dir_all(&config_dir)?;
std::fs::write(config_dir.join("litellm.yaml"), LITELLM_CONFIG)?;
let config_m = mount_path(&config_dir)?;
ensure_running(GATEWAY_LITELLM, || {
let mut a = s(&[
"run",
"-d",
"--name",
GATEWAY_LITELLM,
"--network",
GATEWAY_NETWORK,
]);
a.push("-v".into());
a.push(format!("{config_m}:/etc/litellm"));
a.push("-e".into());
a.push(format!("LITELLM_MASTER_KEY={master}"));
a.push("-e".into());
a.push(format!(
"DATABASE_URL=postgresql://litellm:{pg_password}@{GATEWAY_POSTGRES}:5432/litellm"
));
for k in [
"ANTHROPIC_API_KEY",
"OPENAI_API_KEY",
"GEMINI_API_KEY",
"XAI_API_KEY",
] {
if let Ok(v) = std::env::var(k) {
if !v.is_empty() {
a.push("-e".into());
a.push(format!("{k}={v}"));
}
}
}
a.push(LITELLM_IMAGE.into());
a.extend(s(&[
"--config",
"/etc/litellm/litellm.yaml",
"--port",
"4000",
]));
a
})?;
// LiteLLM takes a while to come up; wait for its liveliness probe from inside
let probe = "import urllib.request,sys; urllib.request.urlopen('http://127.0.0.1:4000/health/liveliness', timeout=3)";
for _ in 0..60 {
if run_ok(&s(&["exec", GATEWAY_LITELLM, "python3", "-c", probe])).is_ok() {
eprintln!("amem: gateway up inside the VM at http://{GATEWAY_LITELLM}:4000");
return Ok(master);
}
std::thread::sleep(Duration::from_secs(2));
}
Err("the gateway inside the VM did not come up in two minutes; `wslc logs memory-litellm` says why".into())
}
/// Start a named container if it is not running: create it if absent, start it if it
/// exited.
fn ensure_running(name: &str, create: impl FnOnce() -> Vec<String>) -> Fallible<()> {
match container_state(name).as_deref() {
Some("running") => Ok(()),
Some(_) => run_ok(&s(&["start", name])).map(|_| ()),
None => run_ok(&create()).map(|_| ()),
}
}
/// Serve the inside's requests until it writes `done`.
fn drive(
egress: &str,
volume: &str,
image: &str,
projection_m: Option<&str>,
home_m: &str,
work_m: &str,
) -> Fallible<i32> {
let mut n = 0;
loop {
let listing = match run_ok(&s(&["exec", egress, "ls", REQUEST_DIR_INSIDE])) {
Ok(l) => l,
Err(e) => {
// the egress container has exited; its `done` is still in the volume if it
// finished properly, readable from a throwaway container
let done = format!("{REQUEST_DIR_INSIDE}/{DONE}");
let vol = format!("{volume}:{REQUEST_DIR_INSIDE}");
if let Ok(text) = run_ok(&s(&["run", "--rm", "-v", &vol, image, "cat", &done])) {
if let Ok(code) = text.trim().parse::<i32>() {
return Ok(code);
}
}
let logs = run_ok(&s(&["logs", egress])).unwrap_or_default();
return Err(format!("the egress container stopped early: {e}\n{logs}").into());
}
};
let names: Vec<&str> = listing.lines().map(str::trim).collect();
if names.contains(&DONE) {
let text = run_ok(&s(&[
"exec",
egress,
"cat",
&format!("{REQUEST_DIR_INSIDE}/{DONE}"),
]))?;
return Ok(text.trim().parse().unwrap_or(1));
}
let req_name = RunRequest::file(n);
if names.contains(&req_name.as_str()) {
let text = run_ok(&s(&[
"exec",
egress,
"cat",
&format!("{REQUEST_DIR_INSIDE}/{req_name}"),
]))?;
let req: RunRequest = serde_json::from_str(&text)?;
let args = harness_run_args(&req, n, image, volume, projection_m, home_m, work_m);
// the harness's terminal is ours; its exit code goes to the inside via the volume
Command::new(wslc()).args(&args).status()?;
n += 1;
continue;
}
std::thread::sleep(Duration::from_millis(400));
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_taps_credentials_go_to_the_egress_container_and_nothing_that_runs_it() {
let dir = tempfile::tempdir().unwrap();
dm_server::Memory::init(dir.path(), "human:test:me", "dom-me").unwrap();
let path = dir.path().join(dm_server::GRANTS_FILE);
let mut g = dm_policy::Grants::load(&path).unwrap();
for (tap, var) in [
("search", "DM_SEARCH_KEY"),
("issues", "GH_TOKEN"),
("bad", "LD_PRELOAD"),
("worse", "PATH"),
("odd", "not-a-name"),
] {
g.taps.insert(
tap.into(),
dm_policy::TapGrant {
domain: "dom-me".into(),
ceiling: dm_core::Label::Public,
credential: Some(var.into()),
..dm_policy::TapGrant::default()
},
);
}
std::fs::write(&path, g.render().unwrap()).unwrap();
assert_eq!(
tap_credentials(dir.path()),
vec!["DM_SEARCH_KEY".to_string(), "GH_TOKEN".to_string()]
);
std::env::set_var("DM_SEARCH_KEY", "from-host");
let env = egress_env(&tap_credentials(dir.path()), &[]);
std::env::remove_var("DM_SEARCH_KEY");
assert!(
env.iter()
.any(|(k, v)| k == "DM_SEARCH_KEY" && v == "from-host"),
"{env:?}"
);
assert!(
env.iter().filter(|(k, _)| k == "GH_TOKEN").count() <= 1,
"a name in PASS_ENV and a grant is passed once: {env:?}"
);
}
use std::collections::BTreeMap;
fn request(interactive: bool) -> RunRequest {
RunRequest {
argv: vec![
"/opt/memory/memory".into(),
"relay".into(),
"--".into(),
"/usr/local/bin/claude".into(),
"-p".into(),
"hi".into(),
],
env: BTreeMap::from([
("HOME".to_string(), "/home/user".to_string()),
("PATH".to_string(), "/usr/local/bin".to_string()),
]),
cwd: "/work".into(),
home: "state/sandbox/claude".into(),
interactive,
}
}
#[test]
fn a_harness_container_has_no_network_and_leaves_its_exit_code_in_the_volume() {
let a = harness_run_args(
&request(false),
3,
"img:dev",
"vol",
Some(r"\\wsl.localhost\U\home\me\memory\state\projection\claude"),
r"\\wsl.localhost\U\home\me\memory\state\sandbox\claude",
r"\\wsl.localhost\U\home\me\proj",
);
let joined = a.join(" ");
assert!(joined.contains("--network none"), "{joined}");
assert!(
joined.contains(r"\projection\claude:/memory:ro"),
"the projection is mounted read-only, from the host's own path: {joined}"
);
assert!(joined.contains("-u 1000:1000"), "{joined}");
assert!(joined.contains("vol:/run/memory"), "{joined}");
assert!(joined.contains(r"\proj:/work"), "{joined}");
assert!(joined.contains("-e HOME=/home/user"), "{joined}");
assert!(
joined.contains("> /run/memory/req-3.out; echo $? > /run/memory/req-3.exit"),
"captured: {joined}"
);
assert!(a.contains(&"-i".to_string()) && !a.contains(&"-it".to_string()));
assert_eq!(&a[a.len() - 6..], &request(false).argv[..]);
let a = harness_run_args(&request(true), 0, "img:dev", "vol", None, "h", "w");
assert!(
!a.join(" ").contains(":/memory"),
"nothing rendered, nothing mounted"
);
let joined = a.join(" ");
assert!(a.contains(&"-it".to_string()));
assert!(
!joined.contains("req-0.out"),
"interactive output stays on the terminal: {joined}"
);
assert!(joined.contains("echo $? > /run/memory/req-0.exit"));
}
#[test]
fn the_egress_container_gets_the_record_the_volume_and_the_logins_read_only() {
let l = Launch {
projection: "run-7".into(),
root: "/home/me/memory".into(),
harness: Harness::Codex,
auth: AuthMode::Key,
provider: Some("xai".into()),
server_auth: Some(AuthMode::Gateway),
server_provider: Some("llmao".into()),
workdir: "/home/me/proj".into(),
image: "img:dev".into(),
inside_args: vec!["--prompt".into(), "hello".into()],
interactive: false,
via: Via::Auto,
storage: None,
};
let dirs = vec![".codex".to_string()];
let a = egress_run_args(
&l,
"eg",
"vol",
"/home/me/memory",
Some(("/home/me", &dirs)),
&[(
"LLMAO_URL".to_string(),
"http://memory-litellm:4000".to_string(),
)],
Some("memory-gateway"),
);
let joined = a.join(" ");
assert!(
joined.starts_with(
"run -d --name eg --network memory-gateway -v /home/me/memory:/memory -v vol:/run/memory"
),
"{joined}"
);
assert!(
joined.contains("-e LLMAO_URL=http://memory-litellm:4000"),
"{joined}"
);
assert!(
joined.contains(&format!(
"/home/me{}.codex:/host-home/.codex:ro",
std::path::MAIN_SEPARATOR
)),
"{joined}"
);
assert!(
joined.contains(
"img:dev /opt/memory/memory --root /memory inside --requests /run/memory"
),
"{joined}"
);
assert!(
joined.contains("--projection run-7 --harness codex"),
"the inside renders under the name the outside mounts: {joined}"
);
assert!(
joined.ends_with(
"--harness codex --auth key --provider xai --server-auth gateway \
--server-provider llmao --prompt hello"
),
"{joined}"
);
// the server's route alone wanting the gateway is enough to bring it up
assert!(wants_gateway(&l));
assert!(!wants_gateway(&Launch {
server_auth: None,
server_provider: None,
..l
}));
}
#[test]
fn the_api_serves_where_it_can_and_says_why_where_it_cannot() {
let l = l_default();
assert_eq!(via_for(&l, true), Ok(Via::Api));
assert_eq!(
via_for(&l, false),
Ok(Via::Cli),
"off Windows, the command line"
);
let interactive = Launch {
interactive: true,
..l
};
assert_eq!(via_for(&interactive, true), Ok(Via::Cli));
let asked = Launch {
via: Via::Api,
..interactive
};
assert!(via_for(&asked, true).unwrap_err().contains("no terminal"));
let gateway = Launch {
interactive: false,
server_auth: Some(AuthMode::Gateway),
..asked
};
assert!(via_for(&gateway, true)
.unwrap_err()
.contains("network of its own"));
assert!(via_for(
&Launch {
server_auth: None,
..gateway
},
false
)
.unwrap_err()
.contains("Windows only"));
let cli = Launch {
via: Via::Cli,
..l_default()
};
assert_eq!(
via_for(&cli, true),
Ok(Via::Cli),
"asked for, always served"
);
}
fn l_default() -> Launch {
Launch {
projection: "run-7".into(),
root: "/m".into(),
harness: Harness::Claude,
auth: AuthMode::Key,
provider: None,
server_auth: None,
server_provider: None,
workdir: "/w".into(),
image: "img".into(),
inside_args: Vec::new(),
interactive: false,
via: Via::Auto,
storage: None,
}
}
#[test]
fn outside_wsl_a_path_is_itself() {
if std::env::var_os("WSL_DISTRO_NAME").is_some() {
return;
}
assert_eq!(mount_path(Path::new("/x/y")).unwrap(), "/x/y");
}
}