Unified cyberful-os runtime¶
Cyberful uses one tooling image and exactly one tooling container per engagement. The image combines cyberful-os, Kali tools, headless OWASP ZAP, Firefox ESR/Xvfb, native Ghidra/PyGhidra, and all three MCP bridges.
Release builds embed an immutable multi-architecture reference:
ghcr.io/cyberful/cyberful-os@sha256:<OCI-index-digest>
The index has native linux/amd64 and linux/arm64 executables. Source runs
default to the locally built cyberful-os:latest. CYBERFUL_OS_IMAGE overrides
the complete image reference in either mode.
Engagement lifecycle¶
The host creates the runtime before the first phase and removes it after Report, an error, or interruption. It carries these ownership labels:
org.cyberful.managed=engagement
org.cyberful.runtime=cyberful-os
org.cyberful.session=<session-id>
The workarea is mounted read/write. Ghidra receives a separate, owner-only
0700 project store; ZAP keeps its session in the container writable layer, so
it disappears with the engagement. Browser and EVM runtimes remain separate.
Pentest and Bug Bounty use normal container networking and publish only ZAP's
port 8080 as a random host-loopback port. Code Audit creates the same image
with --network none, never starts ZAP, and keeps Ghidra and loopback working.
Network policy is fixed when Docker creates the container and never changes at
phase handoff.
Every phase gets a fresh private gateway, but it reconnects to the same tooling container. Phase policy decides which MCP tools are visible; service colocation is not used as an internal security boundary.
Supervisor¶
The image entrypoint is:
tini -- /opt/cyberful/runtime-supervisor
The Python supervisor keeps the container alive, starts enabled ZAP with a
private Xvfb display, and starts Ghidra through setpriv with the host UID/GID
(or 1000:1000 fallback). It stores state under /run/cyberful. The
cyberful-os shell remains available
with the container's NET_ADMIN and SYS_PTRACE capabilities.
TERM is forwarded through bounded shutdown. A dead optional service is not
restarted: /run/cyberful/status.json becomes degraded, preserving the
failure instead of silently replacing its CA, API key, project, or state. The
container remains alive so surviving tools and diagnostics are still usable.
MCP connections¶
The cyberful-os launcher executes tools in the received container. ZAP and
Ghidra bridges are fresh stdio processes created with docker exec, not
containers:
docker exec -i <container> node /opt/cyberful/zap/zap_bridge.mjs
docker exec -i <container> \
/opt/cyberful-os-venv/bin/python /opt/cyberful/ghidra/ghidra_bridge.py
When CYBERFUL_OS_REQUIRE_ENGAGEMENT_CONTAINER=1, the cyberful-os launcher
verifies the supplied running container, ownership labels, and workspace mount.
It cannot create, replace, or restart it. Standalone MCP use retains lazy local
container creation.
Tool discovery is derived from the live capability preflight. Required tools
remain part of the image contract, while optional tools fail closed until the
selected image proves they are installed. JEB therefore appears only for a
private image built with its licensed installer; running
capability_attestation refreshes the snapshot used by discovery, while
tool_inventory independently reports the current live availability.
Configuration¶
| Variable | Default | Meaning |
|---|---|---|
CYBERFUL_OS_IMAGE |
embedded digest in releases; cyberful-os:latest in source |
Complete unified image override |
CYBERFUL_OS_MCP_ENABLED |
1 |
Expose cyberful-os in eligible phases |
CYBER_ZAP_ENABLED |
1 |
Enable ZAP for live-target engagements |
CYBER_BROWSER_THROUGH_ZAP |
1 |
Chain the isolated browser through ready ZAP |
CYBER_ZAP_PROXY_PORT |
random loopback port | Optional fixed host-loopback port |
CYBER_ZAP_STARTUP_TIMEOUT_SECONDS |
120 |
ZAP readiness deadline |
CYBER_GHIDRA_ENABLED |
1 |
Enable the persistent Ghidra service |
CYBER_GHIDRA_STARTUP_TIMEOUT_SECONDS |
300 |
Ghidra readiness deadline |
Separate ZAP/Ghidra image or container variables and
CYBERFUL_OS_AUTOSTART are rejected by preflight. Use CYBERFUL_OS_IMAGE
instead of any former component-image override.
Build and verification¶
The build context is mcps/; the Dockerfile is
mcps/cyberful-os/Dockerfile. Kali, ZAP, Bun, Ghidra, add-ons, and downloaded
tool archives are versioned or digest-pinned. The build fails unless Node 24,
Firefox/Xvfb, every required ZAP add-on, PyGhidra, and the architecture-native
decompiler are loadable.
make runtime-build # build cyberful-os:latest for this host architecture
make test-runtime # full cyberful-os, ZAP, bridge, Ghidra, and persistence contract
make test-zap # focused ZAP tests against the existing unified image
make test-ghidra # focused Ghidra tests against the existing unified image
Use CYBERFUL_OS_IMAGE=<reference> with the test targets to verify another
local image. Building needs at least 100 GB free; running and pulling should
start with at least 40 GB free.