Skip to main content

DataPaths

Struct DataPaths 

Source
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: PathBuf

Top-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: String

Channel 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: PathBuf

instance_dir/data/ — srv DB (objects.db, sagas.db, …).

§config_dir: PathBuf

instance_dir/config/ — settings.json, repos.json, etc.

§logs_dir: PathBuf

instance_dir/logs/ — host + srv + launcher logs (rotated).

§cef_cache_dir: PathBuf

instance_dir/cef-cache/ — Chromium runtime cache (regenerable).

§agents_dir: PathBuf

instance_dir/agents/ — agent workspace state.

§instance_runtime_dir: PathBuf

instance_dir/runtime/ — single-instance lock + IPC (pid, lockfile, ipc-port, named-pipe). One set per version+mode.

§shared_dir: PathBuf

~/.agentmux/shared/ — version-independent, account-wide state (cookies, OAuth tokens, API keys, dictionary downloads).

§mode: RuntimeMode

Snapshot of the RuntimeMode this resolution used. Helpful for logging and feature gates.

Implementations§

Source§

impl DataPaths

Source

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.

Source

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.

Source

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.

Source

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).

Source

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.

Source

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.

Source

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].

Source

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.

Source

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.

Source

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.

Trait Implementations§

Source§

impl Clone for DataPaths

Source§

fn clone(&self) -> DataPaths

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for DataPaths

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more