Skip to content

Commit 4bd787c

Browse files
author
Ivan
committed
feat: make AppContainer sandboxing opt-in for Windows for now
1 parent cf5bbb3 commit 4bd787c

8 files changed

Lines changed: 43 additions & 9 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file.
88

99
- **Windows desktop**: Frozen backends start again instead of failing when aiohttp imports the standard library email package.
1010
- **Desktop packaging**: Only the correct LXST filterlib binary ships per platform so integrity checks match the installer. CI runs a frozen import smoke test before release.
11+
- **Windows desktop**: AppContainer sandboxing is opt-in (set MESHCHAT_APPCONTAINER=1) instead of on by default, to avoid extra launcher processes and heavy startup until the path is stable.
1112

1213
## [4.8.0] - 2026-07-25
1314

docs/agents/skills/electron-frozen-packaging/SKILL.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ Package and recover the desktop shell correctly: frozen subprocess re-entry, loa
1313
- In frozen builds, `sys.executable` **is** MeshChatX.
1414
- Never spawn `python -m …` or `python -c …` for bots, rnsh, LXMFy, or self-check probes from the packaged app.
1515
- Use `--meshchatx-run-module <module>` so helpers re-enter the same binary without launching a second full app (storage lock collision).
16-
- On Windows, Electron starts the long-lived backend through `--meshchatx-run-module meshchatx.src.backend.appcontainer_launcher` unless `MESHCHAT_APPCONTAINER=0`. The launcher CreateProcess-es the real backend into an LPAC AppContainer. Orphan kill must use process-tree termination (`taskkill /T`) so both launcher and child exit.
16+
- On Windows, Electron can start the backend through `--meshchatx-run-module meshchatx.src.backend.appcontainer_launcher` when `MESHCHAT_APPCONTAINER=1`. The launcher CreateProcess-es the real backend into an LPAC AppContainer. Orphan kill must use process-tree termination (`taskkill /T`) so both launcher and child exit. Default is off (direct backend spawn).
1717

1818
## Loading and navigation
1919

docs/agents/skills/landlock-sqlite/SKILL.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ Landlock / Windows AppContainer + SQLite conversation-load failures (temp_store,
2929

3030
- Module: `meshchatx/src/backend/appcontainer_sandbox.py`
3131
- Launcher: `meshchatx/src/backend/appcontainer_launcher.py` via `--meshchatx-run-module`
32-
- Electron win32 spawn uses the launcher unless `MESHCHAT_APPCONTAINER=0`
32+
- Electron win32 spawn uses the launcher only when `MESHCHAT_APPCONTAINER=1`
3333
- Docs: `meshchatx-docs/en/platform-guides/windows-sandbox.md`
3434

3535
## Verification

electron/backendProcess.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,8 +208,8 @@ function createBackendProcessManager(deps) {
208208
return true;
209209
}
210210
}
211-
// Default on for Windows desktop shells (Landlock equivalent).
212-
return true;
211+
// Opt-in only: set MESHCHAT_APPCONTAINER=1 to use the AppContainer launcher.
212+
return false;
213213
}
214214

215215
function buildSpawnArgs(extraArgs = []) {

meshchatx.rsm

0 Bytes
Binary file not shown.

meshchatx/src/backend/appcontainer_sandbox.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ def appcontainer_requested() -> bool:
248248
return False
249249
if override is True:
250250
return True
251-
return appcontainer_supported()
251+
return False
252252

253253

254254
def appcontainer_auto_enabled() -> bool:

tests/backend/test_appcontainer_sandbox.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,15 @@ def test_appcontainer_forced_env(monkeypatch):
4444
assert ac.appcontainer_forced() is True
4545

4646

47-
def test_appcontainer_auto_when_supported(monkeypatch):
47+
def test_appcontainer_off_by_default_when_env_unset(monkeypatch):
4848
monkeypatch.delenv("MESHCHAT_APPCONTAINER", raising=False)
4949
with (
5050
patch.object(ac, "sys") as mock_sys,
5151
patch.object(ac, "appcontainer_supported", return_value=True),
5252
):
5353
mock_sys.platform = "win32"
54-
assert ac.appcontainer_requested() is True
55-
assert ac.appcontainer_auto_enabled() is True
54+
assert ac.appcontainer_requested() is False
55+
assert ac.appcontainer_auto_enabled() is False
5656

5757

5858
def test_is_appcontainer_child(monkeypatch):

tests/electron/backendProcess.test.js

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,10 +99,38 @@ describe("electron/backendProcess", () => {
9999
expect(showCrashPage).toHaveBeenCalledWith(expect.objectContaining({ code: 1 }));
100100
});
101101

102-
it("wraps win32 spawn through the AppContainer launcher by default", async () => {
102+
it("does not use AppContainer launcher on win32 by default", async () => {
103103
const previousPlatform = process.platform;
104104
Object.defineProperty(process, "platform", { value: "win32" });
105105
delete process.env.MESHCHAT_APPCONTAINER;
106+
try {
107+
const manager = createBackendProcessManager({
108+
log: vi.fn(),
109+
getDefaultStorageDir: () => "C:\\Users\\test\\.reticulum-meshchatx",
110+
getDefaultReticulumConfigDir: () => "C:\\Users\\test\\.reticulum",
111+
getMainWindowPageKind: () => "loading",
112+
isQuiting: () => false,
113+
notifyRenderer: vi.fn(),
114+
showCrashPage: vi.fn(),
115+
spawn: spawnMock,
116+
});
117+
manager.setUserProvidedArguments([]);
118+
await manager.spawnBackend("C:\\App\\ReticulumMeshChatX.exe", {
119+
backend: { ok: true, issues: [] },
120+
});
121+
const args = spawnMock.mock.calls[0][1];
122+
expect(args).not.toContain("--meshchatx-run-module");
123+
expect(args[0]).toBe("--headless");
124+
} finally {
125+
Object.defineProperty(process, "platform", { value: previousPlatform });
126+
}
127+
});
128+
129+
it("wraps win32 spawn through the AppContainer launcher when MESHCHAT_APPCONTAINER=1", async () => {
130+
const previousPlatform = process.platform;
131+
const previousEnv = process.env.MESHCHAT_APPCONTAINER;
132+
Object.defineProperty(process, "platform", { value: "win32" });
133+
process.env.MESHCHAT_APPCONTAINER = "1";
106134
try {
107135
const manager = createBackendProcessManager({
108136
log: vi.fn(),
@@ -131,6 +159,11 @@ describe("electron/backendProcess", () => {
131159
);
132160
} finally {
133161
Object.defineProperty(process, "platform", { value: previousPlatform });
162+
if (previousEnv === undefined) {
163+
delete process.env.MESHCHAT_APPCONTAINER;
164+
} else {
165+
process.env.MESHCHAT_APPCONTAINER = previousEnv;
166+
}
134167
}
135168
});
136169

0 commit comments

Comments
 (0)