pub struct DataPaths {
pub home_dir: PathBuf,
pub instance_dir: PathBuf,
pub channel: String,
pub data_dir: PathBuf,
pub config_dir: PathBuf,
pub logs_dir: PathBuf,
pub cef_cache_dir: PathBuf,
pub agents_dir: PathBuf,
pub instance_runtime_dir: PathBuf,
pub shared_dir: PathBuf,
pub mode: RuntimeMode,
}Expand description
All paths a launcher / host / srv needs. Computed once by the launcher; downstream binaries read paths from env vars set by the launcher rather than recomputing (avoids the legacy desync risk where each binary made its own portable / dev-mode determination).
Fields§
§home_dir: PathBuf~/.agentmux/ itself — the resolved root. Account-wide config
that predates the unified layout (e.g. the launcher’s
config.toml) lives directly here. Honors
AGENTMUX_HOME_OVERRIDE for tests.
instance_dir: PathBufTop-level dir for this channel+mode. All per-channel paths
below are children. Either ~/.agentmux/channels/<channel>/
(installed / portable / AGENTMUX_CHANNEL override) or
~/.agentmux/dev/<branch>/ (dev mode without env override).
Note: the field name is instance_dir for backward compat with
downstream call sites; semantically it’s now the channel root,
not the version root.
channel: StringChannel identifier this resolution used (e.g. "stable",
"local-main", "dev-main", or a user-specified custom
channel from AGENTMUX_CHANNEL). Surfaced for diagnostics,
logging, and the launcher splash; downstream binaries usually
don’t need it (paths are passed via env vars).
data_dir: PathBufinstance_dir/data/ — srv DB (objects.db, sagas.db, …).
config_dir: PathBufinstance_dir/config/ — settings.json, repos.json, etc.
logs_dir: PathBufinstance_dir/logs/ — host + srv + launcher logs (rotated).
cef_cache_dir: PathBufinstance_dir/cef-cache/ — Chromium runtime cache (regenerable).
agents_dir: PathBufinstance_dir/agents/ — agent workspace state.
instance_runtime_dir: PathBufinstance_dir/runtime/ — single-instance lock + IPC (pid,
lockfile, ipc-port, named-pipe). One set per version+mode.
~/.agentmux/shared/ — version-independent, account-wide
state (cookies, OAuth tokens, API keys, dictionary downloads).
mode: RuntimeModeSnapshot of the RuntimeMode this resolution used. Helpful
for logging and feature gates.
Implementations§
Source§impl DataPaths
impl DataPaths
Sourcepub fn resolve(version: &str, mode: &RuntimeMode) -> Result<Self, String>
pub fn resolve(version: &str, mode: &RuntimeMode) -> Result<Self, String>
Resolve all paths for the given version + mode. Honors
AGENTMUX_HOME_OVERRIDE for tests (replaces ~/.agentmux root).
Returns Err if the input contains values that cannot be
represented as a safe single-segment subpath — e.g. .. in the
version string, or a Dev branch that sanitizes to empty. This
is belt-and-braces safety: parse-time sanitization in
crate::RuntimeMode should already have caught these, but a
RuntimeMode::Dev { branch } constructed directly (e.g. by a
test or future caller) is also rejected here.
Sourcepub fn resolve_path_only(
version: &str,
mode: &RuntimeMode,
) -> Result<Self, String>
pub fn resolve_path_only( version: &str, mode: &RuntimeMode, ) -> Result<Self, String>
Like Self::resolve, but ignores the AGENTMUX_CHANNEL env
override and uses only the mode-based default channel. Mirror
of RuntimeMode::current_path_only for path resolution.
Used by dev-build self-detection paths in agentmux-cef’s
main.rs and sidecar.rs. Those paths run when a dev host
has been launched from inside a parent AgentMux instance (e.g.
task dev invoked from inside an agent pane in a portable
build), where the child would otherwise inherit the parent’s
AGENTMUX_* env — including AGENTMUX_CHANNEL — and write
into the parent’s channel instead of dev/<branch>/. That
cross-contamination would also trip the channel’s single-
instance lock and route every “open” back to the parent
window. Path-based mode detection is authoritative for dev
builds; channel resolution here mirrors that discipline.
Codex P1 follow-up on PR #1027.
Sourcepub fn ensure_dirs(&self) -> Result<(), String>
pub fn ensure_dirs(&self) -> Result<(), String>
Create every directory that may be written to. Idempotent. Safe to call on every launch.
Makes the AgentMux root owner-only first (issue #3682): it holds
per-agent jekt HMAC keys and LAN private keys (in the DBs), provider
login state (identity dirs) and logs, and on Unix every directory
below it was otherwise created with the process umask — typically
0755, readable by other local users wherever the home dir is
traversable (macOS /Users/<name> is 0755 by default). Tightening
the root blocks traversal into everything beneath it, including dirs
srv creates later with plain create_dir_all (shared/, archives/).
Best-effort: a root this process cannot chmod (owned by another user,
read-only mount) is logged and the launch continues, as before.
Sourcepub fn to_env_vars(&self) -> Vec<(&'static str, OsString)>
pub fn to_env_vars(&self) -> Vec<(&'static str, OsString)>
Env vars to pass to host + srv subprocesses. The launcher
computes DataPaths once and exports these; downstream
binaries read them via Self::from_env instead of
recomputing.
Returns OsString (not String) so paths with non-UTF-8 bytes
— possible on Linux/macOS for users with exotic home dirs —
round-trip losslessly. Command::env(k, v) accepts any
AsRef<OsStr>, so the OsString flows through to children
unchanged. The mode value is the only String-typed entry
(it’s a fixed ASCII vocabulary).
Sourcepub fn from_env() -> Option<Self>
pub fn from_env() -> Option<Self>
Reconstruct from env vars set by the launcher. Returns
None if any required var is missing — fail-fast vs.
silently falling back to legacy paths the way the old
sidecar.rs did.
Uses var_os (not var) so non-UTF-8 path bytes survive.
Sourcepub fn identities_dir(&self) -> PathBuf
pub fn identities_dir(&self) -> PathBuf
~/.agentmux/shared/identities/ — root for per-bundle OAuth
credential directories. Lives under shared_dir so it’s
account-wide and version-independent: upgrading agentmux does
not move a user’s bundle credentials. Per
docs/specs/archive/SPEC_OAUTH_IDENTITY_BUNDLES_2026_05_22.md §4.1.
When isolated_auth_enabled is set, this resolves to
instance_dir/identities/ instead — a channel-scoped credential
tree, used only with AGENTMUX_ISOLATED_AUTH=1: auth is shared on
every channel by default
(docs/specs/SPEC_SHARED_AUTH_ACROSS_CHANNELS_2026_10_03.md).
Originally scoped to destructive
account testing (delete-account flows) that must never touch the
real global identity store other channels/instances use.
Isolating this directory isolates a provider’s conversation
transcripts too, since e.g. Claude Code’s projects/ lives
inside the same per-bundle directory tree as its credentials —
see [identity_history_dir] for the always-global path those
transcripts are kept reachable at regardless of this flag, and
docs/specs/SPEC_AGENT_IDENTITY_HISTORY_PERSISTENCE_PROTOCOL_2026_08_16.md
§4.1 for why that split exists.
See docs/specs/SPEC_ISOLATED_AUTH_DEV_TESTING_2026_07_27.md.
Sourcepub fn identity_history_dir(
&self,
bundle_id: &str,
provider_dir: &str,
history_subdir: &str,
) -> PathBuf
pub fn identity_history_dir( &self, bundle_id: &str, provider_dir: &str, history_subdir: &str, ) -> PathBuf
~/.agentmux/shared/identities/<bundle_id>/<provider_dir>/<history_subdir>/
— ALWAYS this location, regardless of isolated_auth_enabled.
Conversation transcripts must survive a channel/build change even
when the surrounding credential directory ([identities_dir]) is
isolated per-channel for account testing — history and credentials
are different data-persistence categories (CONVERSATION HISTORY
vs CREDENTIAL in
docs/specs/SPEC_AGENT_IDENTITY_HISTORY_PERSISTENCE_PROTOCOL_2026_08_16.md
P1) and must not share one isolation switch just because they
happen to live under the same directory as far as the provider
CLI is concerned. Callers that isolate credentials are expected
to redirect the isolated <bundle_id>/<provider_dir>/<history_subdir>
subpath to this location via ensure_history_link rather than
letting the provider CLI write real session data there directly.
history_subdir is caller-supplied rather than hardcoded — it is
NOT the same directory name for every provider (reagentx P1 on PR
#2605: this used to hardcode "projects", which is Claude-
specific; Codex uses "sessions", Gemini uses "history", per
docs/specs/SPEC_UNIFIED_AGENT_HISTORY_STORE_2026-06-10.md §2.1).
Callers should read this from
ProviderConfig::history_native_subdir, the single source of
truth for which providers have a linkable directory at all.
bundle_id/provider_dir are not path-validated here — callers
must use [sanitize_path_segment]-checked values, same
requirement as [identity_dir].
Sourcepub fn identity_dir(&self, bundle_id: &str) -> Option<PathBuf>
pub fn identity_dir(&self, bundle_id: &str) -> Option<PathBuf>
~/.agentmux/shared/identities/<bundle_id>/ — a specific
bundle’s credential root, when bundle_id is a safe path
segment. Returns None for empty / . / .. / any segment
containing /, \, drive-letter colons, or Windows-reserved
characters (same rules as the version/branch sanitizer in
resolve).
Defensive return type: bundle_id flows from auth.start
request bodies (PR C) into create_dir_all, so an
unvalidated PathBuf::join would let a crafted id escape the
identities root and write outside the bundle area. codex P1
follow-up on #981.
Per-provider subdirectories (e.g. claude/, codex/) hang
off this when the bundle gains an OAuth binding (PR C). The
directory is created lazily by the bundle / OAuth flow that
needs it — ensure_dirs() does not pre-create it.
Sourcepub fn provider_auth_dir(&self, auth_dir_name: &str) -> PathBuf
pub fn provider_auth_dir(&self, auth_dir_name: &str) -> PathBuf
~/.agentmux/shared/providers/<auth_dir_name>/ — the DEFAULT provider
config + auth dir. Lives under shared_dir, so it is account-wide,
version-independent, AND channel-independent: every instance / channel /
version logs in ONCE and shares it. This is the structural fix for the
per-channel “validate-spin” regression — there is no empty-per-instance
auth dir to spin on. The per-identity override (identity_dir) still
takes precedence for explicit multi-account bundles. auth_dir_name
comes from the static provider registry (e.g. “claude”), never user
input. Retro:
docs/retro/retro-provider-auth-isolation-regression-2026-06-05.md.
Sourcepub fn wan_identity_dir(&self) -> PathBuf
pub fn wan_identity_dir(&self) -> PathBuf
instance_dir/wan-identity/ — the channel-wide WAN identity store
(wan.db: the instance key, agent WAN keys, and WAN verification
state). Beside config/ and agents/ rather than under the
per-version data_dir, so an upgrade keeps the same instance and the
same agent keys. Deliberately not identity/, which would sit next to
the unrelated identities/. For dev builds this is the branch (or
clone) dir, so each dev checkout is its own instance.
SPEC_WAN_JEKT_VERIFICATION_2026_09_24.md §2.2.