capsem-doctor

Last updated: 2026-03-11

testingdiagnosticsvm

capsem-doctor is a pytest-based diagnostic suite that runs inside the guest VM. It validates sandbox isolation, network policy, kernel hardening, runtime environment, and AI CLI configuration. Tests are baked into the rootfs and run automatically during CI.

Running

# Boot VM, run all diagnostics, shut down (~10s)
just run "capsem-doctor"

# Inside a running VM
capsem-doctor              # all tests
capsem-doctor -k sandbox   # subset by keyword
capsem-doctor -x           # stop on first failure

Test suites

Sandbox security (test_sandbox.py)

Validates the VM’s isolation model.

TestWhat it verifies
test_clock_is_synchronizedGuest clock within 60s of host
test_squashfs_is_immutable/dev/vda is squashfs
test_overlay_configuredRoot is overlay with lowerdir + upperdir
test_overlay_writes_are_ephemeralWrites go to tmpfs, not squashfs
test_writable_mounts/root, /tmp, /run, /var/log, /var/tmp are writable
test_guest_binary_not_writablecapsem binaries are chmod 555
test_guest_binary_executablecapsem binaries are executable
test_no_setuid_binariesNo setuid files in rootfs
test_no_setgid_binariesNo setgid files in rootfs
test_no_kernel_modulesmodprobe fails (MODULES=n)
test_no_dev_mem/dev/mem absent
test_no_dev_port/dev/port absent
test_no_proc_kcore/proc/kcore not readable
test_proc_modules_empty/proc/modules empty or absent
test_no_debugfsdebugfs not mounted
test_no_ipv6IPv6 disabled
test_no_kallsyms/proc/kallsyms empty or absent
test_kernel_cmdline_has_roro in cmdline
test_init_on_allocinit_on_alloc=1 in cmdline
test_slab_nomergeslab_nomerge in cmdline
test_page_alloc_shufflepage_alloc.shuffle=1 in cmdline
test_seccomp_availableSeccomp line in /proc/self/status
test_swap_activeSwap on scratch disk
test_loopback_interface_uplo is UP

Network isolation:

TestWhat it verifies
test_dummy_interface_existsdummy0 NIC present
test_dns_resolves_to_localDNS resolves to 10.0.0.1
test_iptables_redirectPort 443 redirected to 10443
test_net_proxy_runningcapsem-net-proxy running
test_allowed_domainFull HTTPS handshake to allowed domain
test_denied_domainDenied domain returns 403 or refused
test_no_real_nicsOnly lo and dummy0 exist

Process integrity:

TestWhat it verifies
test_pty_agent_runningcapsem-pty-agent running
test_dnsmasq_runningdnsmasq running
test_no_systemdNo service manager
test_no_sshdNo remote access
test_no_cronNo scheduled tasks

Network & MITM proxy (test_network.py)

Tests ordered from low-level to high-level so failures pinpoint the exact broken layer.

TestWhat it verifies
test_dummy0_has_ipdummy0 has 10.0.0.1
test_dnsmasq_respondsDNS on 127.0.0.1:53 works
test_dns_all_resolve_to_localAll DNS queries -> 10.0.0.1
test_iptables_redirect_443_to_10443iptables REDIRECT rule
test_net_proxy_listeningTCP accepted on 127.0.0.1:10443
test_tcp_443_reaches_proxy443 redirected to net-proxy
test_vsock_bridge_delivers_bytesRaw bytes through proxy
test_tls_handshake_completesTLS handshake via MITM
test_tls_cert_from_capsem_caCert signed by Capsem CA
test_curl_https_with_skip_verifycurl -k gets HTTP response
test_mitm_ca_cert_file_existsCA cert file present
test_mitm_ca_in_system_bundleCA in system trust store
test_certifi_includes_capsem_caCA in Python certifi
test_curl_allowed_domain_ca_trustedcurl without -k succeeds
test_python_urllib_https_trustedPython urllib TLS works
test_ca_env_var_setSSL_CERT_FILE, REQUESTS_CA_BUNDLE, NODE_EXTRA_CA_CERTS set
test_denied_domain_rejectedDenied domain -> 403
test_post_to_random_domain_deniedPOST to non-allowed domain -> 403
test_ai_provider_domain_blockedAI provider domains blocked unless allowed
test_http_port_80_not_proxiedPort 80 not proxied
test_non_standard_port_failsNon-443 ports fail
test_direct_ip_no_routeDirect IP has no route
test_proxy_download_throughput100MB download above minimum speed

Environment (test_environment.py)

TestWhat it verifies
test_term_is_xterm_256colorTERM set correctly
test_home_is_rootHOME is /root
test_path_includes_standard_dirsPATH has /usr/local/bin, /usr/bin
test_python_venv_activePython venv activated
test_shell_is_bashBash installed
test_kernel_is_linux_6Linux 6.x kernel
test_architecture_is_aarch64ARM64 architecture
test_proc_mounted/proc mounted
test_sys_mounted/sys mounted
test_dev_mounted/dev mounted
test_dev_pts_mounted/dev/pts mounted
test_root_is_ext4_scratch_disk/root on ext4 scratch disk
test_root_scratch_disk_sizeScratch disk >= 4GB
test_tmp_is_writable/tmp writable
test_rootfs_is_overlayRoot is overlay mount

Runtimes (test_runtimes.py)

TestWhat it verifies
test_runtime_versionpython3, node, npm, pip3, git respond to —version
test_pip_install_workspip install succeeds
test_uv_pip_install_worksuv pip install succeeds
test_npm_install_global_worksnpm -g install works
test_npm_install_local_worksnpm local install works
test_python_executionPython stdlib + file I/O
test_node_executionNode.js file I/O
test_git_workflowgit init, commit, log

Utilities (test_utilities.py)

Verifies ~36 unix utilities are available: coreutils, text processing, network tools, system inspection.

File I/O workflows (test_workflows.py)

TestWhat it verifies
test_file_write_readText write/read
test_python_json_roundtripPython JSON roundtrip
test_node_file_roundtripNode writes, Python reads
test_pipe_workflowShell pipe chains
test_large_file_write10MB file to tmpfs

AI CLI (test_ai_cli.py)

TestWhat it verifies
test_ai_cli_installedclaude/gemini/codex in PATH
test_ai_cli_help—help runs without crash
test_gemini_api_key_no_duplicateNo GOOGLE_API_KEY alongside GEMINI_API_KEY
test_gemini_settings_existGemini settings.json seeded
test_gemini_projects_exist/root registered as project
test_gemini_trusted_folders_exist/root is trusted
test_google_ai_domain_allowedGoogle AI domain reachable

Boot injection (test_injection.py)

Verifies that env vars, boot files, and git credentials injected by the host arrived correctly inside the guest.

MCP gateway (test_mcp.py)

TestWhat it verifies
test_mcp_server_binary_existscapsem-mcp-server installed
test_mcp_initializeJSON-RPC initialize handshake
test_mcp_tools_listThree built-in HTTP tools
test_mcp_fetch_http_allowed_domainfetch_http on allowed domain
test_mcp_fetch_http_blocked_domainfetch_http on blocked domain returns error
test_mcp_grep_http_finds_matchesgrep_http finds content
test_mcp_http_headers_allowed_domainhttp_headers returns status
test_fastmcp_availablefastmcp Python package importable

Adding new tests

  1. Add test functions to the appropriate images/diagnostics/test_*.py file, or create a new test_<category>.py
  2. Use from conftest import run for shell commands, output_dir fixture for temp files
  3. Tests auto-skip outside the capsem VM (conftest.py checks for root + writable /root)
  4. Rebuild rootfs with just build-assets to pick up new test files
  5. Verify with just run "capsem-doctor"