Cross-platform CLI that installs curated development and cybersecurity
tool sets after a fresh OS install.
Install · Quick start · Commands · Configuration · Package managers · Trust · Troubleshooting · Contributing · Security
- Pre-made sets:
development,cybersecurity,minimal- see Quick start - Custom YAML sets and a create wizard - see Configuration
- Multiple package managers on Linux, Windows, and macOS - see Package managers
- Smart manager selection with OS preferences and fallback
- Automation flags:
--yes,--dry-run,--fail-fast- see Commands - Machine-readable output: global
--jsonfor scripting - see Machine-readable output - Package ID allowlist for argv safety (not upstream existence or content trust) - see Trust
- Export to native manager formats - see Commands
Requires Python 3.8+ and at least one supported package manager.
Recommended (isolated CLI via pipx):
pipx install jdi-blacksmith
blacksmith --versionRemove with blacksmith uninstall (detects pipx) or pipx uninstall jdi-blacksmith.
pip install jdi-blacksmith
blacksmith --versionDedicated venv (Linux / macOS):
python3 -m venv ~/.blacksmith-venv
source ~/.blacksmith-venv/bin/activate
pip install jdi-blacksmithWindows (PowerShell):
python -m venv $env:USERPROFILE\.blacksmith-venv
$env:USERPROFILE\.blacksmith-venv\Scripts\Activate.ps1
pip install jdi-blacksmithFrom source: git clone the repo, then pip install -e ..
PATH or permission problems: Troubleshooting.
blacksmith list
blacksmith install minimal --dry-run
blacksmith apply minimal --yes| Set | Focus |
|---|---|
development |
Git, Docker, editors, language toolchains |
cybersecurity |
Security and pentest tooling |
minimal |
Essentials (includes brew: IDs for macOS) |
Custom YAML and create wizard: Configuration. Flags and policy: Commands. Shared files: Trust.
| Command | Purpose |
|---|---|
blacksmith |
Interactive menu |
blacksmith list |
List sets |
blacksmith info <set> |
Set details (full package list; --limit N to truncate; pager for long lists) |
blacksmith install <set> |
Install a set |
blacksmith install <set> --dry-run |
Preview plan only |
blacksmith install <set> --yes |
Non-interactive (required without a TTY) |
blacksmith install <set> --fail-fast |
Stop on first package failure |
blacksmith install --file path.yaml --yes |
Install custom YAML (untrusted) |
blacksmith install --file path.yaml --require-signature |
Require minisign verify before install |
blacksmith install --file path.yaml --strict-trust |
Fail closed on trust-scan findings (local --file warns by default) |
blacksmith install --url https://… --yes |
Install remote HTTPS set YAML (requires signature unless --allow-unsigned; untrusted; REMOTE) |
blacksmith apply <set> --yes |
Ensure set state (idempotent; skip installed) |
blacksmith apply <set> --dry-run |
Preview apply plan only |
blacksmith apply --file path.yaml --yes |
Apply custom YAML (untrusted) |
blacksmith apply --file path.yaml --require-signature |
Require minisign verify before apply |
blacksmith apply --url https://… --yes |
Apply remote HTTPS set YAML (requires signature unless --allow-unsigned; untrusted; REMOTE) |
blacksmith create / create --advanced |
Create a set |
blacksmith sign <path.yaml> |
Create detached minisign signature (<path>.minisig) |
blacksmith search <query> [--manager name] |
Search managers |
blacksmith export <set> --format <fmt> |
Export (winget, chocolatey, apt, pacman, scoop) |
blacksmith validate <path> |
Schema + ID allowlist check |
blacksmith validate --url https://… |
Same checks for a remote HTTPS set YAML |
blacksmith gallery list [--refresh] |
Browse curated remote sets |
blacksmith gallery install <id> --yes |
Install a gallery set through the remote URL trust path |
blacksmith audit [--last N] |
Show recent local audit events (default 50) |
blacksmith uninstall [--yes] |
Remove Blacksmith (uses pipx when detected) |
Other install flags: --skip-installed, --prefer <mgr>, --force (ignore target_os mismatch).
Apply also supports --prefer, --force, and --fail-fast.
Signature flags (with --file or --url): --require-signature, --signature PATH|URL, --pubkey PATH.
Remote --url also supports --allow-unsigned (accepts unsigned install/apply risk; mutually exclusive with --require-signature). Validate and --dry-run do not require a signature.
Authors: blacksmith sign path.yaml [--secret-key PATH] [-x out.minisig] (requires minisign on PATH; optional - uses minisign's default secret key when --secret-key is omitted).
Trust-scan flag: --strict-trust on install / apply / validate (fail closed on findings; remote --url always escalates).
--url is mutually exclusive with --file and with a set name (install/apply) or local path (validate). HTTPS only.
Gallery entries are curated pointers to remote set YAML and use the same trust and unsigned-set policy as --url. The bundled index is used by default; pass --refresh to fetch the latest index from GitHub and cache it in your user configuration directory.
Apply exit codes: 0 already compliant, 2 changed with no failures, 1 failures. Install stays 0/1.
Policy: default is best-effort continue after failures (no rollback). Each package installs individually.
Privileges: on Linux, apt / pacman / yum|dnf / snap may prompt for sudo. Flatpak and Homebrew do not use that path.
Search note: Snap and Flatpak support install, but search does not query them yet.
Pass --json on supported commands to emit a single JSON object on stdout. Human banners, tables, and prompts are suppressed; diagnostics may appear on stderr.
Supported commands: list, info, search, install, apply. All other commands (including the bare interactive menu) emit a failure envelope with error.code json_unsupported and exit 2.
Every envelope includes schema_version (currently 1). Consumers should ignore unknown fields.
Success envelope:
{
"schema_version": 1,
"command": "list",
"ok": true,
"exit": 0,
"data": { }
}Failure envelope:
{
"schema_version": 1,
"command": "install",
"ok": false,
"exit": 1,
"error": { "code": "not_found", "message": "Set 'nope' not found." }
}Install and apply failure envelopes may also include a data block with summary and outcomes so scripts can act on partial runs. Treat this as additive; other failure shapes omit data.
Examples:
blacksmith --json list | jq '.data.sets[].name'
blacksmith --json info minimal | jq '.data.packages | length'
blacksmith --json search git --limit 5 | jq '.data.results[].manager'
blacksmith --json install minimal --yes --dry-run | jq '.data.outcomes[] | select(.action=="install")'
blacksmith --json apply minimal --yes | jq '{ok, exit, changed: .data.summary.changed}'Mutating commands under --json require --yes or --dry-run (prompts are disabled). A set name, --file, or --url is also required; the interactive set menu is unavailable.
blacksmith --json install --file path/to/set.yaml --yes
blacksmith --json apply --file path/to/set.yaml --dry-runApply exit codes in JSON mode match human mode: 0 already compliant, 2 changed with no failures, 1 failures. On exit 2, the envelope has ok: true (the run succeeded; state changed).
Common error.code values: json_unsupported, needs_args, not_found, invalid_query, no_managers, invalid_config, signature_failed, cancelled, install_failed.
blacksmith audit
blacksmith audit --last 20Mutating install / apply (and self-uninstall) append events to a local JSONL file (audit.jsonl) under the platform config directory unless --no-audit or BLACKSMITH_NO_AUDIT (truthy: 1, true, yes) is set. Dry-run and no-op runs are not logged.
Default path: %APPDATA%\blacksmith\audit.jsonl (Windows) or ~/.config/blacksmith/audit.jsonl (Linux/macOS; honors XDG_CONFIG_HOME).
Use --no-audit on install, apply, and uninstall. The bare interactive menu (no subcommand) has no --no-audit flag; set BLACKSMITH_NO_AUDIT=1 to disable audit there.
Privacy: the log may include OS username, set name, config path/hash, and package ids. It is local only; delete the file to clear history. Integrity and threat model: SECURITY.md.
name: "My Custom Set"
description: "My favorite tools"
target_os: ["windows", "linux", "macos"]
preferred_managers:
windows: ["winget", "chocolatey"]
linux: ["apt", "flatpak"]
macos: ["brew"]
packages:
- name: git
managers:
apt: git
brew: git
winget: Git.Git
chocolatey: git| Field | Required | Notes |
|---|---|---|
name |
yes | Set name |
description |
no | Short summary |
target_os |
no | windows, linux, macos / darwin |
preferred_managers |
no | Per-OS manager order |
managers_supported |
no | Limit which managers are considered |
packages |
yes | Manager IDs must pass the argv allowlist |
Validate: blacksmith validate path/to/config.yaml or blacksmith validate --url https://example.com/set.yaml (structure and allowlist only - not upstream existence).
| OS | Managers |
|---|---|
| Linux | apt, yum/dnf, pacman, snap, flatpak |
| Windows | winget, chocolatey, scoop |
| macOS | brew (Homebrew formulas and casks) |
Selection uses set preferred_managers when present, otherwise OS defaults (winget then chocolatey then scoop on Windows; brew on macOS), then any other available managers.
macOS: Darwin is detected; Homebrew is registered when brew is on PATH. The minimal set includes brew: IDs. Broader brew coverage across other sets, MacPorts, and brew export are later work. Install Homebrew: https://brew.sh
Treat set YAML like code you are willing to run. Package ID allowlists block shell metacharacters; they do not prove packages are safe or exist upstream. Third-party --file or --url YAML is untrusted - review it, prefer --dry-run, then --yes. Remote --url sets print a REMOTE banner with the final HTTPS URL and content SHA-256. Mutating install / apply --url (not --dry-run) requires a verified minisign signature, or explicit --allow-unsigned (you accept the risk). Local --file signing stays opt-in via --require-signature.
After a successful load of custom --file or remote --url YAML, Blacksmith runs an offline trust scan (size, optional/empty bundled denylist reserved for known-bad IDs, manager mix, junk IDs, conflicting duplicates). Findings print as warnings. Local --file continues by default; pass --strict-trust to fail closed. Remote --url always fails closed when findings are present. Built-in set names are not scanned. The scan never claims a set is "safe" or "trusted" - it is not a malware scanner.
blacksmith install --file path/to/set.yaml --strict-trust --yes
blacksmith validate path/to/set.yaml --strict-trustAppend |version to a manager package ID (the version segment must start with a digit):
packages:
- name: nmap
managers:
apt: "nmap|7.94"
- name: git
managers:
chocolatey: "git|2.40.0"Supported for pins: chocolatey, apt, yum/dnf, winget, brew. A pin on snap, flatpak, scoop, or pacman fails closed with pin_unsupported (no install is attempted).
Version pin notes:
- Pin syntax
name|versionexcludes:and~characters (epochs and pre-release markers; L1.2 follow-up) - apt and yum/dnf pins must match the manager's native version format exactly (e.g., full dpkg Version or rpm VERSION-RELEASE); short pins like
nmap|7.94may not match installed7.94-1 - brew pins install versioned formulae (
go@1.21), not specific Cellar patch versions - winget pins parse the Version column from
winget listoutput; ensure pin matches the reported version
On apply, an installed package must match the pin exactly or Blacksmith fails (version_mismatch or version_unknown); it does not auto-upgrade to satisfy a pin. Unpinned IDs still install the latest available from the manager. Companion lockfiles are not in this release.
Optional authenticity (minisign): authors sign with minisign -Sm set.yaml and distribute set.yaml + set.yaml.minisig + their .pub. Operators add the pubkey under ~/.config/blacksmith/trusted_keys/ (Linux/macOS), %APPDATA%\blacksmith\trusted_keys\ (Windows), or pass --pubkey, then:
blacksmith install --file path/to/set.yaml --require-signature --yes
blacksmith install --url https://example.com/set.yaml --yes
blacksmith install --url https://example.com/set.yaml --allow-unsigned --yesWithout --require-signature, unsigned --file behavior is unchanged. Remote --url installs/applies fail closed when unsigned unless you pass --allow-unsigned. Requires the minisign CLI on PATH for the signed path.
Threat model, reporting vulnerabilities, and a safe review workflow: SECURITY.md.
blacksmith validate path/to/set.yaml
blacksmith install --file path/to/set.yaml --dry-run
blacksmith install --file path/to/set.yaml --strict-trust --yesCommand not found after install
- Prefer pipx so
blacksmithis on PATH without activating a venv. - Or use a virtualenv and activate it before running commands.
- Linux/macOS user install: add
$HOME/.local/bintoPATH. - Windows user install: add the user
Scriptsdirectory frompython -m site --user-baseto PATH. - Fallback:
python -m blacksmith --version
Permission errors
Prefer pipx install jdi-blacksmith, or pip install --user jdi-blacksmith, instead of sudo pip / admin installs.
Do not push directly to main. Use a feature branch and a PR; merge only when the required CI check is green.
See CONTRIBUTING.md for the full workflow.
Apache 2.0 - see LICENSE.
See CHANGELOG.md.
jimididit
- GitHub: @jimididit
- Website: www.jimididit.com
- Discord: Nokturnal Community