Repository navigation
Architecture
Bull is a modular Bash application. bull.sh is the entry point; the real work lives in sourced library files under lib/.
bull.sh Entry point: TUI + CLI dispatch
install.sh Unattended host setup (Vagrant + hypervisor)
lib/
core.sh Colors, logging, provider/WSL detection, deps, GPG
inventory.sh VM inventory CRUD (JSON via jq)
vagrant.sh VM lifecycle (create, start, stop, destroy, snapshots)
vpn.sh VPN configuration + iptables kill switch
toolkits.sh Toolkit installation + persistent registry
configs/
Vagrantfile.template Provider-specific Vagrant template
kali-provision.sh Kali provisioning (runs inside the VM as root)
parrot-provision.sh Parrot provisioning
ansible-playbook.yml Kali Ansible playbook
parrot-playbook.yml Parrot Ansible playbook
docs/ Architecture, contributing, security, tooling guides
bull.sh
├── require root
├── source lib/core.sh (colors, logging, detection, GPG)
├── source lib/inventory.sh (JSON inventory)
├── source lib/vagrant.sh (VM lifecycle)
├── source lib/vpn.sh (VPN + kill switch)
├── source lib/toolkits.sh (toolkit registry)
├── parse_arguments "$@"
└── no command → interactive_loop()
else → execute_command()
Each library guards against double-sourcing with a _BULL_<MOD>_LOADED sentinel, and core.sh must load first because the others use its logging and helpers.
Bull checks for /dev/kvm and sets BULL_PROVIDER to libvirt or virtualbox (overridable via the environment). Provider-specific logic is confined to lib/vagrant.sh and configs/Vagrantfile.template; the rest of the code is provider-agnostic. VAGRANT_DEFAULT_PROVIDER is exported so Vagrant never iterates over all providers — this avoids a Hyper-V detection crash in Vagrant 2.4.x on WSL2.
All Vagrant calls go through ${VAGRANT_CMD}, which resolves to vagrant or, on WSL2 with VirtualBox, vagrant.exe. This keeps a single code path working across native Linux and both WSL2 configurations.
Passwords are encrypted with GPG (AES256 cipher, SHA512 digest, 65M S2K iterations) and stored in .credentials.gpg per VM. The plaintext exists only in memory during provisioning and is wiped from environment variables immediately after use. The Vagrantfile, which briefly holds the password for provisioning, is chmod 600 and sanitized afterward. See the Security Model.
VM metadata lives in a JSON inventory under BULL_HOME. Every mutation goes through inventory_* functions that write atomically (mktemp + mv) to avoid corruption, and bull sync reconciles it with the hypervisor's actual state.
Saved toolkits live in ${BULL_HOME}/toolkits.json. Tools are installed inside VMs via git clone over vagrant ssh -c, with URL and name validation to prevent shell injection. See the Toolkit Manager.
core.sh detects WSL2 and adjusts VAGRANT_CMD, VAGRANT_HOME, BULL_HOME, and the PATH, and installs cmd.exe / powershell.exe shims so Vagrant's Windows checks pass on the libvirt path. See Installation.
| Dependency | Purpose |
|---|---|
| Vagrant 2.3+ | VM provisioning |
| libvirt/KVM or VirtualBox | Hypervisor |
jq |
JSON inventory manipulation |
gpg |
Credential encryption |
ssh / sshpass
|
VM connections |
curl or wget
|
Box and key downloads |
openssl |
Password generation |
The in-repo ARCHITECTURE.md tracks the same design at source level.
Bull · MIT License · Technical hardening only — see the Security Model.
Getting started
Features
Internals
Help