Relevant area(s)
Linux, Windows
Brief description of your issue
With network.egress.default: "deny" and ingress.hostLoopback: "deny", any Python asyncio program hangs forever at startup, with no error and no output.
On Windows, asyncio creates its internal self-pipe with socket.socketpair(), which CPython implements as a TCP connection to 127.0.0.1 within the same process. Under MXC that connection never completes, so accept() blocks forever. This silently breaks every asyncio-based server under the most restrictive network policy, including MCP SDK / FastMCP servers, which never answer their first request.
A plain AppContainer with no network capabilities, created without MXC, allows this same-process loopback while still blocking the host and the internet. So the block appears to come from MXC's WFP filters, not from AppContainer itself.
Related: #1260 (found in the same investigation).
Steps to reproduce
Environment: MXC v0.8.0 release binaries (x64), schema 0.8.0-alpha, Windows 11 Pro 10.0.26200, unelevated user, wxc-exec --probe → base-container, Python 3.12.
- Save as
cfg.json:
{
"version": "0.8.0-alpha",
"containment": "processcontainer",
"process": {
"env": ["SystemRoot=C:\\Windows", "LOCALAPPDATA=C:\\Users\\<user>\\AppData\\Local"]
},
"filesystem": { "readonlyPaths": ["C:\\Program Files\\Python312"] },
"network": {
"egress": { "default": "deny" },
"ingress": { "default": "deny", "hostLoopback": "deny" }
},
"ui": { "disable": true }
}
(LOCALAPPDATA is included to avoid error 203, see #1130.)
- Run:
wxc-exec.exe cfg.json -- "C:\Program Files\Python312\python.exe" -c "import socket; a,b=socket.socketpair(); print('ok')"
-
Observe: ok is never printed and the process never exits. A faulthandler dump shows it blocked in socket._fallback_socketpair → accept.
-
Change only the network block and rerun. Results:
network config |
socketpair |
host 127.0.0.1 listener |
internet |
| egress deny, hostLoopback deny (step 1) |
hangs |
— |
— |
| egress deny, hostLoopback allow (± ingress allow) |
hangs |
— |
— |
| egress allow |
ok |
blocked |
open |
egress deny + "allow": [{"to": [{"cidr": "127.0.0.1/32"}]}], hostLoopback deny |
ok |
blocked |
blocked |
(no MXC) raw AppContainer via CreateProcessW + SECURITY_CAPABILITIES, no network capabilities |
ok |
blocked |
blocked |
Expected behavior
socket.socketpair() completes (intra-container loopback, same AppContainer SID to itself), while the host 127.0.0.1 listener and the internet stay blocked. A raw AppContainer with no network capabilities, created without MXC, behaves this way (last row of the table).
Actual behavior
socket.socketpair() hangs indefinitely in accept() with no error and no output, so any asyncio program never starts. This includes MCP SDK / FastMCP servers, which never answer initialize.
Workaround: an explicit egress allow rule for {"cidr":"127.0.0.1/32"}. It also adds internetClient to the token, so internet blocking then relies entirely on WFP.
Request
Allow intra-container loopback (same AppContainer SID to itself) by default under deny policies, or document the rule above as the supported recipe.
Relevant area(s)
Linux, Windows
Brief description of your issue
With
network.egress.default: "deny"andingress.hostLoopback: "deny", any Pythonasyncioprogram hangs forever at startup, with no error and no output.On Windows, asyncio creates its internal self-pipe with
socket.socketpair(), which CPython implements as a TCP connection to 127.0.0.1 within the same process. Under MXC that connection never completes, soaccept()blocks forever. This silently breaks every asyncio-based server under the most restrictive network policy, including MCP SDK / FastMCP servers, which never answer their first request.A plain AppContainer with no network capabilities, created without MXC, allows this same-process loopback while still blocking the host and the internet. So the block appears to come from MXC's WFP filters, not from AppContainer itself.
Related: #1260 (found in the same investigation).
Steps to reproduce
Environment: MXC v0.8.0 release binaries (x64), schema
0.8.0-alpha, Windows 11 Pro 10.0.26200, unelevated user,wxc-exec --probe→base-container, Python 3.12.cfg.json:{ "version": "0.8.0-alpha", "containment": "processcontainer", "process": { "env": ["SystemRoot=C:\\Windows", "LOCALAPPDATA=C:\\Users\\<user>\\AppData\\Local"] }, "filesystem": { "readonlyPaths": ["C:\\Program Files\\Python312"] }, "network": { "egress": { "default": "deny" }, "ingress": { "default": "deny", "hostLoopback": "deny" } }, "ui": { "disable": true } }(
LOCALAPPDATAis included to avoid error 203, see #1130.)Observe:
okis never printed and the process never exits. A faulthandler dump shows it blocked insocket._fallback_socketpair → accept.Change only the
networkblock and rerun. Results:networkconfig"allow": [{"to": [{"cidr": "127.0.0.1/32"}]}], hostLoopback denyCreateProcessW+SECURITY_CAPABILITIES, no network capabilitiesExpected behavior
socket.socketpair()completes (intra-container loopback, same AppContainer SID to itself), while the host 127.0.0.1 listener and the internet stay blocked. A raw AppContainer with no network capabilities, created without MXC, behaves this way (last row of the table).Actual behavior
socket.socketpair()hangs indefinitely inaccept()with no error and no output, so any asyncio program never starts. This includes MCP SDK / FastMCP servers, which never answerinitialize.Workaround: an explicit egress allow rule for
{"cidr":"127.0.0.1/32"}. It also addsinternetClientto the token, so internet blocking then relies entirely on WFP.Request
Allow intra-container loopback (same AppContainer SID to itself) by default under deny policies, or document the rule above as the supported recipe.