-
Notifications
You must be signed in to change notification settings - Fork 10
Architecture
A tour of how Medusa is laid out and why.
Medusa/
├── medusa.sh Entry point: loads lib/ in order, dispatches
│ `menu` (default) or a CLI subcommand.
├── lib/
│ ├── core.sh Colors, globals, tool registry, generic Docker
│ helpers, install primitives, prompt helpers.
│ ├── modules.sh Menu rendering, status dashboard, start/stop all,
│ environment selection at startup.
│ ├── run_cli.sh Guided sub-menus for CLI tools (run_<tool>).
│ ├── deploy_soc.sh 14 SOC deployers.
│ ├── deploy_grc.sh 5 GRC deployers.
│ ├── deploy_integration.sh 11 Integration deployers.
│ └── deploy_ot.sh 5 OT deployers.
└── medusa_deployments/ Generated at runtime (git-ignored).
└── <env_name>/
└── <tool>/
├── docker-compose.yml
├── .env
└── credentials.txt (chmod 600)
-
medusa.shresolves its own absolute path intoMEDUSA_HOME, the anchor forBASE_DIR. Every path helper (tool_dir, the deployers) therefore builds absolute paths, so acddeep inside the menu loop never breaks subsequent relative paths. - The original argv is captured into
MEDUSA_ARGVso a privilege-escalation re-exec (sudo) resumes the same command instead of dropping to the menu. - The seven
lib/files are sourced in dependency order:core→deploy_soc→deploy_grc→deploy_integration→deploy_ot→run_cli→modules. Each has a_LOADEDguard, so double-sourcing is a no-op. -
main "$@"reads the first argument to choose interactive menu mode or a CLI subcommand.
The script runs under set -uo pipefail. set -e is deliberately omitted, an interactive menu must survive a failing read or a grep with no match without the whole loop dying.
Every tool is a single register_tool call in core.sh:
register_tool "wazuh" "soc" "docker" "SIEM/XDR - Detection, response, compliance"
# name cat type descriptionThree parallel associative arrays, TOOL_DESC, TOOL_CAT, TOOL_TYPE, are populated. Everything that iterates tools (menu, dashboard, start-all, list) reads from them.
type is one of:
-
docker— the deployer writes adocker-compose.yml(or clones an upstream repo containing one) and runsdocker compose up -d. -
cli— the deployer installs a binary (apt,pip/pipx,curl | sh) and marks it withmark_cli_installed. -
vm— instructions only, no automation (Security Onion ISO, GRFICSv2 lab, GRASSMARLIN jar).
dispatch_deploy() { local f="deploy_${1//-/_}"; declare -f "$f" >/dev/null && "$f" || log_message error "no deployer for $1"; }
dispatch_run() { local f="run_${1//-/_}"; declare -f "$f" >/dev/null && "$f" || log_message error "no run menu for $1"; }This is what makes the "add a tool in 5 lines" pattern work: define deploy_<tool> (and optionally run_<tool>), add a register_tool line, nothing else needs wiring. See Adding-a-Tool.
get_tool_status returns one of:
| State | Meaning |
|---|---|
not_installed |
No marker file, no binary on PATH |
cli_installed |
.installed marker exists, or command -v <tool> succeeds |
installed |
Docker tool with a compose file, no running container |
running |
Docker tool with at least one container reporting Up
|
stopped |
Docker tool with a compose file, every container down |
The state determines which actions the per-tool menu offers.
-
No database, no state file. The
medusa_deployments/filesystem is the source of truth. - No background daemon. Medusa is invoked, acts, exits.
-
No abstraction over Docker engines beyond detecting
docker composevsdocker-compose.
Next: Environments · Adding-a-Tool
Repository · Issues · Security policy · MIT License
Get running
Concepts
Tool catalog
Reference
Develop