CLI & TUI · CLI reference

CLI reference

secvf-cli is a Swift-Argument-Parser binary that wraps every operation the GUI does — VM lifecycle, switch state, packet capture, USB, TUI launch. Built for headless work, scripting, and CI integrations.

Install / build

The CLI ships in the SecVF repo as a Swift package at SecVF/cli/. Build it with:

cd SecVF/cli
swift build                       # debug build
swift build -c release            # release build (use this for daily use)

# resulting binary
.build/debug/secvf-cli            # or .build/release/secvf-cli

Make it available on PATH:

# symlink (portable)
ln -s "$(pwd)/.build/release/secvf-cli" ~/.local/bin/secvf

# or copy
cp .build/release/secvf-cli /usr/local/bin/secvf

Verify:

$ secvf --version
1.0.0
$ secvf --help
OVERVIEW: SecVF - Security Virtualization Framework CLI
USAGE: secvf <subcommand>
SUBCOMMANDS:
  vm                      Virtual machine management commands
  usb                     USB device management commands
  switch                  Virtual network switch commands
  capture                 Packet capture commands
  tui                     Launch the Terminal User Interface
GUI app must be running for state-changing operations. The CLI is a thin client — it discovers state on disk and pushes mutations to the running GUI app via DistributedNotificationCenter. list, status, and read-only commands work standalone; start, stop, and similar require SecVF.app running. The CLI tells you so explicitly when it can't reach the app.

Global options

Every subcommand accepts:

FlagWhat it does
--jsonOutput as JSON. Use for scripting. Failure path emits {"success": false, "message": "..."}.
-v, --verboseExtra diagnostic detail on stderr.
--helpPrint help for the current subcommand.

secvf vm

Eleven subcommands cover the VM lifecycle.

vm list

secvf vm list                          # all VMs
secvf vm list --status running         # only running
secvf vm list --os-type linux          # only Linux guests
secvf vm list --json                   # for parsing

vm create

secvf vm create <name> \
    --os linux \
    --distro kali \
    --cpu 2 \
    --ram 4096 \
    --disk 64 \
    --network nat \
    [--router]                         # mark as security router VM
    [--start]                          # start immediately after creation

Defaults: --os linux --distro kali --cpu 2 --ram 4096 --disk 64 --network nat. Distros: kali, ubuntu, debian, fedora, arch, manjaro. Network modes: nat, virtual, isolated.

vm start / vm stop

secvf vm start <name>
secvf vm stop  <name> [--force]        # --force = pull the plug
                                       # (no --force = ACPI graceful)

vm status

secvf vm status <name>
# Prints: state, uptime, CPU/RAM allocated, network mode, IP if known

vm delete

secvf vm delete <name> [--force]
# Without --force, prompts for confirmation.
# Bundle moves to Trash (recoverable until emptied).

vm ssh

Convenience wrapper that finds the VM's IP and opens SSH.

secvf vm ssh <name> [--user agent] [--key ~/.ssh/secvf.pem]

vm exec

Run a one-shot command in the guest. For AI sandbox guests, uses vsock — much faster than SSH.

secvf vm exec <name> -- 'uname -a'
secvf vm exec ai-session-xyz -- 'clang --version'
secvf vm exec my-kali --timeout 30 -- 'nmap -sn 10.0.100.0/24'

vm copy-to / vm copy-from

Push or pull files between host and guest. Uses VirtioFS when available, scp fallback otherwise.

secvf vm copy-to   <name> ./payload.bin /tmp/payload.bin
secvf vm copy-from <name> /var/log/syslog ./syslog.txt

vm snapshot

Bundle-level clone (uses APFS CoW under the hood). Stopped VMs only.

secvf vm snapshot <name> --as <new-name>
# Result: ~/.avf/Linux/<new-name>.bundle/ — fresh MachineIdentifier, same data

secvf switch

switch status

$ secvf switch status
VIRTUAL NETWORK SWITCH
--------------------------------------------------
Status:        ● RUNNING
Ports:         3/8 connected
MACs Learned:  5
Uptime:        1h 42m 7s

switch stats / switch stats --watch

$ secvf switch stats
SWITCH STATISTICS
--------------------------------------------------
Packets Forwarded: 14,732
Packets Broadcast: 89
Packets Dropped:   0
Bytes Transferred: 12.42 MB

RX: 6.21 MB  TX: 6.21 MB

$ secvf switch stats --watch --interval 0.5
# Live, clear-screen update every 500 ms

switch ports

$ secvf switch ports
PORT  VM NAME            MAC ADDRESS         RX          TX
-----------------------------------------------------------------
0     kali-router        52:54:00:a1:b2:c3   3.2 MB      3.2 MB
1     ubuntu-malware     52:54:00:d4:e5:f6   1.8 MB      1.8 MB
2     ai-session-7b3a    52:54:00:99:88:77   234 KB      217 KB

switch macs

$ secvf switch macs
MAC ADDRESS         PORT  VM NAME            AGE
-------------------------------------------------------
52:54:00:a1:b2:c3   0     kali-router        1m 2s
52:54:00:d4:e5:f6   1     ubuntu-malware     42s
52:54:00:99:88:77   2     ai-session-7b3a    11s
01:80:c2:00:00:0e   1     ubuntu-malware     8s   (multicast)

secvf capture

capture start

secvf capture start \
    [-i <iface>]              # default: any  (whole switch)
    [-f "<BPF filter>"]       # e.g. -f "tcp port 443"
    [-o output.pcap]          # write to disk as we capture
    [--count N]               # stop after N packets (0 = unlimited)

capture stop / capture status

$ secvf capture stop
✓ Packet capture stopped
  Packets captured: 14,732
  Bytes captured: 12.42 MB

$ secvf capture status
CAPTURE STATUS
----------------------------------------
Status: ● CAPTURING
Interface: any
Filter: tcp port 443
Packets: 7,431
Duration: 04:18

capture live

Streams packets to stdout, table-formatted.

secvf capture live                     # show everything
secvf capture live -f "dns"            # display filter (Wireshark grammar)
secvf capture live -c 100 --details    # 100 packets, full layer decode

capture export

secvf capture export out.pcap                       # pcap
secvf capture export out.json -f json               # JSON-EK
secvf capture export out.csv  -f csv                # for spreadsheet
secvf capture export out.pcap --filter "tls"        # filtered export
secvf capture export out.pcap --limit 1000

secvf usb

usb list

$ secvf usb list
DEVICE                  VENDOR        TYPE       MOUNTED TO      STATUS
---------------------------------------------------------------------------
○ Kingston DT 32GB      Kingston      Physical   --              Available
○ SanDisk Extreme       SanDisk       Physical   --              Available

$ secvf usb list --include-virtual
# Adds virtual disks (e.g. Scripts USB images SecVF created)

usb mount

secvf usb mount "Kingston DT 32GB" --to kali-router

usb eject

secvf usb eject "Kingston DT 32GB"

usb create-virtual

Builds a virtual USB disk image (handy for shipping scripts into a guest).

secvf usb create-virtual \
    --name secvf-scripts \
    --size 256 \                       # MB
    --format dmg \                     # or iso
    --source ./scripts                  # contents to include

secvf tui

Launches the Textual-based terminal UI. See the TUI guide for the full UX. CLI usage:

secvf tui                              # launch interactively
secvf tui --show-path                  # print Python + TUI module paths
                                       # (useful for debugging launch issues)

Requires Python 3.10+ and the secvf-tui package installed. The launcher hunts for Python at /opt/homebrew/bin, /usr/local/bin, /usr/bin, then PATH; rejects anything below 3.10.

JSON mode

Append --json to any subcommand. Schema is consistent:

{
  "success": true,
  "message": "Optional human-readable message",
  "data": <subcommand-specific payload>
}

On failure:

{
  "success": false,
  "message": "VM 'foo' not found"
}

JSON output goes to stdout; verbose diagnostics still go to stderr.

Scripting recipes

Stop every running VM

secvf vm list --status running --json \
  | jq -r '.data[].name' \
  | xargs -I{} secvf vm stop {}

Snapshot before detonation, restore after

SAMPLE="$1"
VM="malware-sandbox"
SNAP="${VM}-clean"

# Snapshot once (if it doesn't already exist)
secvf vm list --json | jq -e ".data[]|select(.name==\"$SNAP\")" \
  || secvf vm snapshot "$VM" --as "$SNAP"

# Detonate
secvf vm start "$VM"
secvf capture start -o "/tmp/${SAMPLE}.pcap"
secvf vm exec "$VM" -- "/bin/bash /tmp/run.sh"
sleep 60
secvf capture stop
secvf vm stop "$VM" --force

# Restore
secvf vm delete "$VM" --force
secvf vm snapshot "$SNAP" --as "$VM"

Watch the switch and alert if drops climb

while true; do
  drops=$(secvf switch stats --json | jq '.data.packetsDropped')
  if [ "$drops" -gt 0 ]; then
    osascript -e "display notification \"Switch dropping packets!\" with title \"SecVF\""
  fi
  sleep 10
done

Build the Scripts USB on demand

secvf usb create-virtual --name secvf-scripts \
                         --size 64 \
                         --format dmg \
                         --source ./scripts/router \
  && secvf usb mount secvf-scripts --to kali-router

Exit codes

CodeMeaning
0Success.
1Generic failure (bad arguments, command not understood by GUI app).
2Argument parsing error (Swift-Argument-Parser default).
3SecVF.app not running but the operation requires it.
4Resource not found (VM name, USB device, etc.).
5Permission or entitlement failure.