Skip to main content

agentmux_srv/server/
state.rs

1// Copyright 2025-2026, AgentMux Corp.
2// SPDX-License-Identifier: Apache-2.0
3
4//! Shared server state: AppState and HostIpc.
5//! Split out of server/mod.rs unchanged (SPEC_LARGE_FILE_MODULE_ANALYSIS_2026_09_30.md §4.1).
6
7use super::*;
8
9/// The paired CEF host's CDP-automation credentials, pushed once via
10/// `host_ipc.Register` — see `AppState::host_ipc`.
11#[derive(Clone, Debug)]
12pub struct HostIpc {
13    pub port: u16,
14    pub token: String,
15}
16
17#[derive(Clone)]
18pub struct AppState {
19    pub auth_key: String,
20    /// LAN peer-discovery credential — see `Config::lan_key`'s doc comment.
21    /// Accepted only by `lan_or_full_auth_middleware`'s three routes, never
22    /// the general `auth_middleware` gating everything else.
23    pub lan_key: String,
24    /// Random identifier generated once per process boot — NOT the
25    /// `--instance` channel name (`config.instance_id`), which is
26    /// stable across restarts and shared by every process on the same
27    /// channel. Used as the owner id for cross-process session leases
28    /// (`registry::LeaseStore`) so two live processes can be told
29    /// apart even when they're the same channel/version.
30    /// See `docs/retro/RETRO_DEV_BUILD_SHARED_AGENT_SESSION_COLLISION_2026_07_29.md`.
31    pub boot_id: Arc<str>,
32    pub version: String,
33    /// OS hostname of the machine this instance runs on (e.g. "narko").
34    /// Already computed at boot for LAN discovery's mDNS TXT records; kept
35    /// here so `/agentmux/discovery` can name its own host too — `local_url`
36    /// is always loopback, so without this a client has no way to say which
37    /// machine it is talking to.
38    pub hostname: String,
39    pub app_path: String,
40    pub mstore: Arc<Store>,
41    /// GLOBAL shared store (`~/.agentmux/shared/store.db`). Holds durable
42    /// user content that must survive version upgrades: accounts,
43    /// bundles, drone definitions, and MuxBus credentials.
44    /// `None` when the shared root can't be resolved (CI / unusual envs).
45    /// See `docs/specs/SPEC_GLOBAL_IDENTITY_MEMORY_DRONE_2026_06_24.md`.
46    pub shared_store: Option<Arc<Store>>,
47    /// Effective identity/memory/drone/muxbus store — `shared_store` when
48    /// available, otherwise `mstore`. Handlers capture this instead of
49    /// `mstore` for any operation that must survive across version upgrades.
50    ///
51    /// Deprecated for everything except `db_accounts` reads/writes as of
52    /// `docs/specs/SPEC_IDENTITY_STORE_SPLIT_2026_08_17.md` — new call sites
53    /// for agent→account links, bundles, drone definitions, muxbus
54    /// creds, native memory, or cron jobs should use `identity_store`
55    /// instead, which (unlike this field) is never redirected by
56    /// `isolated_auth_enabled()`. `id_store` itself stays because
57    /// `db_accounts` still legitimately needs an isolatable path (account
58    /// testing) — see the spec's §3.2 for the account-specific split, not
59    /// yet implemented.
60    pub id_store: Arc<Store>,
61    /// Permanently-global identity store — see `identity_store`'s own doc
62    /// comment on `bootstrap::Stores` for the full explanation. Never
63    /// `None`: falls back to `mstore` on the same best-effort terms as
64    /// `id_store` if the shared root can't be resolved.
65    pub identity_store: Arc<Store>,
66    pub filestore: Arc<FileStore>,
67    /// GLOBAL, channel-independent transcript store backing the
68    /// `agent:<defId>:current` zone. `None` when the shared root can't be
69    /// resolved (global transcripts disabled — falls back to per-channel
70    /// `filestore`). Lets an agent's conversation load when opened from any
71    /// build/channel, finishing the cross-channel arc started by #1387–#1396.
72    /// See `docs/analysis/ANALYSIS_CROSS_CHANNEL_CONVERSATION_HISTORY_2026_06_14.md`.
73    pub global_transcript_store: Option<Arc<FileStore>>,
74    pub event_bus: Arc<EventBus>,
75    pub broker: Arc<Broker>,
76    pub reactive_handler: &'static ReactiveHandler,
77    pub poller: Arc<Poller>,
78    pub config_watcher: Arc<wconfig::ConfigState>,
79    pub messagebus: Arc<MessageBus>,
80    pub subagent_watcher: Arc<SubagentWatcher>,
81    pub history_service: Arc<HistoryService>,
82    /// Tracks every OS-level process each agent CLI has spawned, via
83    /// platform-specific mechanisms (Windows Job Objects, Linux cgroups; a
84    /// stub on macOS). Surfaces the tree to the agent pane and
85    /// provides kill-tree on pane close / host exit.
86    /// See `backend::process_tracker`.
87    pub process_tracker: Arc<crate::backend::process_tracker::registry::AgentProcessRegistry>,
88    /// Process Broker (Phase A) — unified `ProcessStatus` per block, read
89    /// through instead of composing `blockcontroller`/`process_tracker`
90    /// directly at each call site. See `crate::broker::process` and
91    /// `docs/specs/REPORT_PROCESS_ARCHITECTURE_STATE_AND_RETHINK_2026_07_22.md`.
92    pub process_broker: Arc<crate::broker::ProcessBroker>,
93    /// In-memory, latest-per-block cache of Activity Dock `ToolNode` status
94    /// deltas pushed by the frontend. Backs `muxspect dock`/`muxspect dock
95    /// clear`. Never persisted — see
96    /// `docs/specs/SPEC_MUXSPECT_DOCK_DIAGNOSIS_AND_REMEDIATION_2026_08_06.md`.
97    pub dock_snapshots: Arc<crate::backend::dock_snapshot::DockSnapshotCache>,
98    /// Holding pen for a declared-background task's OS pid when it arrives
99    /// (from bashwrap, over MPS) before its `db_background_tasks` row
100    /// exists yet — closes the race `background_task_set_pid`'s silent
101    /// no-op on a missing row would otherwise lose permanently. See
102    /// `docs/specs/SPEC_BACKGROUND_TASK_PID_CAPTURE_2026_08_20.md` and the
103    /// Codex/reagentx findings on PR #2681.
104    pub pending_background_pids: Arc<crate::backend::pending_background_pids::PendingBackgroundPids>,
105    /// Which events have already produced an ambient narration. Process-wide
106    /// and bounded — a per-connection set is reset by every reconnect, which is
107    /// precisely when the frontend re-emits nodes and would narrate them twice.
108    /// See `backend::narrated_events`.
109    pub narrated_events: Arc<crate::backend::narrated_events::NarratedEvents>,
110    /// Live controller for mDNS-based LAN/host peer discovery. The controller
111    /// owns a swappable daemon slot so the `network:lan_discovery` setting can
112    /// be toggled at runtime without restarting the process.
113    /// See `docs/specs/lan-discovery-toggle.md`.
114    pub lan_discovery: Arc<LanDiscoveryController>,
115    /// Owns the LAN-facing HTTP listeners. Paired with `lan_discovery`: that
116    /// one controls whether we ADVERTISE on the LAN, this one controls whether
117    /// we are actually REACHABLE there. Both must be driven from the same
118    /// place — advertising without listening is the "discovered but
119    /// unreachable" state described in `backend::lan_listeners`.
120    pub lan_listeners: Arc<crate::backend::lan_listeners::LanListenerSupervisor>,
121    /// The names-only fleet feed behind `/agentmux/fleet` and
122    /// `/agentmux/fleet/events` (`backend::fleet_feed`). Its change-detection
123    /// task is started by `main.rs` once the shutdown token exists.
124    pub fleet_feed: Arc<crate::backend::fleet_feed::FleetFeed>,
125    /// Paired devices' read-only view: pairing codes, the certificate, the
126    /// feed hub and the feed caps (`backend::viewer`). Its routes are served
127    /// only by the viewer listener (`server::build_viewer_router`).
128    pub viewer: Arc<crate::backend::viewer::ViewerService>,
129    /// Language Server Protocol supervisor — owns the lifecycle of LSP
130    /// server child processes (one per workspace/language) and proxies
131    /// LSP messages between the editor pane and the server.
132    /// Spec: `docs/specs/SPEC_EDITOR_LSP_AND_THEMES_2026-05-26.md`.
133    pub lsp_supervisor: Arc<LspSupervisor>,
134    /// Local HTTP URL of this instance (e.g. "http://127.0.0.1:PORT").
135    /// Used for cross-instance inject forwarding and file registry entries.
136    pub local_web_url: String,
137    /// Shared HTTP client for cross-instance inject forwarding.
138    pub http_client: reqwest::Client,
139    /// This instance's paired CEF host's `ipc_port` + `ipc_token`
140    /// (`/agentmux/browser/*` on the host's own IPC server — CDP-based
141    /// screenshot/click/query automation). `None` until the host calls
142    /// `host_ipc.Register` once at its own startup (there is no way to
143    /// know these values before then — the host generates `ipc_token`
144    /// for itself and is the sole source of truth, see
145    /// `crates/cef/src/client/helpers.rs::register_ipc_with_backend`).
146    /// Backs the `/api/v1/ui/{screenshot,click,query}` proxy routes.
147    /// See `docs/specs/SPEC_AGENT_UI_AUTOMATION_CLICK_SCREENSHOT_2026_08_18.md`.
148    pub host_ipc: Arc<tokio::sync::Mutex<Option<HostIpc>>>,
149    /// Shared secret only the paired host (never an agent) is given —
150    /// gates `host_ipc.Register` so an agent process can't spoof the
151    /// host's own credential push (both share the general `auth_key`
152    /// already, so that alone can't distinguish them). `None` if nobody
153    /// configured one, in which case `handle_register` rejects every
154    /// registration. See `Config::host_reg_secret`'s doc comment.
155    pub host_reg_secret: Option<String>,
156    /// Phase E.2c.2 — srv reducer's canonical state. Workspace HTTP/WS
157    /// RPC handlers route through the reducer (dispatch
158    /// `Command::Create/Delete/...Workspace` and read out of
159    /// `state.workspaces`); the persist subscriber mirrors emitted
160    /// events back to SQLite. Tab/Block RPC migrations land in
161    /// E.2c.3 / E.2c.4.
162    pub srv_state: std::sync::Arc<tokio::sync::Mutex<crate::state::State>>,
163    /// Phase E.2c.2 — broadcast bus for srv reducer events. RPC
164    /// handlers publish reducer-emitted events here so the persist
165    /// subscriber writes them back to SQLite. Pipe IPC server (when
166    /// bound) shares the same bus.
167    pub srv_events_tx: tokio::sync::broadcast::Sender<agentmux_common::ipc::Event>,
168    /// Phase E.5.5 — monotonic saga-id allocator. Each saga
169    /// (TearOffTab, TearOffBlock, RestoreTornOffTab, etc.) calls
170    /// `fetch_add` to claim a unique id; the id is stamped onto
171    /// `Event::SagaStarted/Completed/Failed` so subscribers can
172    /// correlate. Per-instance scope (no cross-process sharing — see
173    /// `docs/retro/saga-coordinator-location-analysis-2026-04-30.md`).
174    pub saga_id_alloc: std::sync::Arc<std::sync::atomic::AtomicU64>,
175    /// Saga durability — durable on-disk log of saga lifecycle.
176    /// Written by `SagaCtx::dispatch` / `compensate` (per-step) and
177    /// `emit_terminal` (per-saga) so a srv crash mid-saga leaves a
178    /// recoverable trail. PR 1 ships the log + instrumentation; PR 2
179    /// adds resume-on-startup + `--diag sagas`.
180    /// See `docs/specs/SPEC_SAGA_DURABILITY_2026-05-01.md`.
181    pub saga_log: std::sync::Arc<crate::sagas::log::SagaLog>,
182    /// Pre-launch OAuth session state — one entry per in-flight
183    /// "Connect with OAuth" attempt from the launch modal. See
184    /// `docs/specs/SPEC_PRE_LAUNCH_OAUTH_FLOW_2026_05_14.md`.
185    pub auth_session_manager: std::sync::Arc<crate::identity::auth_session::AuthSessionManager>,
186
187    /// In-flight `install.start` sessions. Frontend subscribes to
188    /// `install_chunk` MPS events scoped by session id; the registry
189    /// holds per-session cancel handles so `install.cancel` can abort
190    /// an install mid-flight.
191    /// See `SPEC_AGENT_INSTALL_STAGE_2026_05_17.md` §9.
192    pub install_sessions: std::sync::Arc<crate::server::install_handlers::InstallSessionRegistry>,
193    /// Docker container runtime handle for container-type agent panes
194    /// (Phase 2). Self-healing — `.get()`/`.is_available()` retry the
195    /// Docker connection on demand rather than being fixed at process
196    /// boot, so a daemon that starts after AgentMux launched is picked up
197    /// without an app restart. See `ContainerRuntimeHandle` and
198    /// docs/retro/RETRO_DOCKER_DETECTION_DIVERGENCE_2026_07_04.md.
199    pub container_manager: std::sync::Arc<crate::backend::container::ContainerRuntimeHandle>,
200    /// Native dev-proxy routing table (`"<project>-<agent_id>"` →
201    /// container-internal `ip:port`) — backs the `RegisterDevServer` MCP
202    /// tool and the standalone dev-proxy HTTP server spawned in `main.rs`.
203    /// Cheap to clone (one `Arc` inside); `container_manager` also holds a
204    /// clone (wired via `ContainerRuntimeHandle::set_dev_proxy_registry`
205    /// in `bootstrap::build_app_state`) so `stop`/`remove` can clear a
206    /// dead container's routes. See
207    /// docs/specs/SPEC_NATIVE_CONTAINER_DEV_PROXY_2026_09_19.md.
208    pub dev_proxy: crate::backend::dev_proxy::DevProxyRegistry,
209    /// Phase 3 — per-shell stop handles so `ShellStop` (MCP tool) and the UI
210    /// stop button can tree-kill a running persistent shell node. See
211    /// `docs/specs/SPEC_PERSISTENT_SHELL_PHASE3_STOP_2026_06_14.md`.
212    pub shell_sessions: std::sync::Arc<crate::backend::shell_node::ShellSessionRegistry>,
213    /// Persistent cron scheduler — loaded from `db_cron_jobs` at startup.
214    /// Creates/cancels tokio tasks that fire on the specified UTC schedule and
215    /// POST to `/agentmux/reactive/inject`. See
216    /// `docs/specs/SPEC_CRON_LOOP_ROBUSTNESS_2026_06_25.md §3.2`.
217    pub cron_scheduler: std::sync::Arc<crate::backend::cron::CronScheduler>,
218    /// Filesystem watcher for files open in editor/preview panes — publishes
219    /// `EVENT_EDITOR_FILE_CHANGED` (scoped per-block) when a watched path
220    /// changes on disk, so panes can refresh instead of silently going
221    /// stale. Migrated onto the shared `fs_watch_pool` below
222    /// (SPEC_SHARED_FS_WATCHER_FRAMEWORK_2026_08_07.md) — always constructs
223    /// successfully now (a construction-time failure degrades into
224    /// `fs_watch_pool.health()`'s `degraded_paths` instead of `None`), so
225    /// unlike before this field is no longer optional.
226    /// See docs/specs/SPEC_EDITOR_LIVE_FILE_RELOAD_2026_07_18.md.
227    pub editor_file_watcher: std::sync::Arc<crate::backend::editor_file_watcher::EditorFileWatcher>,
228    /// Filesystem watcher for directories a Media pane is pointed at —
229    /// publishes `EVENT_MEDIA_FILE_CHANGED` (scoped per-block) when a
230    /// matching-extension file is created/modified. Same migration and
231    /// no-longer-optional note as `editor_file_watcher` above.
232    /// See docs/specs/SPEC_MEDIA_PANE_2026_07_26.md.
233    pub media_file_watcher: std::sync::Arc<crate::backend::media_file_watcher::MediaFileWatcher>,
234    /// Directory watcher behind `fs.watch` for Files panes — publishes
235    /// `files:changed` (scoped per-block) when a watched folder's listing
236    /// changes. Process-wide rather than per connection, so a watch outlives
237    /// the WebSocket that made it, like the two watchers above.
238    /// See docs/specs/SPEC_FILE_BROWSER_PANE_2026_10_01.md §6.3.
239    pub files_watcher: std::sync::Arc<crate::backend::files_watcher::FilesWatcher>,
240    /// Shared filesystem-watcher framework (retry/fallback/self-healing on
241    /// top of `notify`) that `editor_file_watcher`/`media_file_watcher`
242    /// above are built on. See
243    /// docs/specs/SPEC_SHARED_FS_WATCHER_FRAMEWORK_2026_08_07.md.
244    pub fs_watch_pool: std::sync::Arc<crate::backend::fs_watch::FsWatchPool>,
245}