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}