Skip to content

About

A cross-platform CLI tool that automates the installation of toolsets - helpful after a fresh OS install.

Topics

Resources

Contributing

Security policy

Stars

3 stars

Watchers

0 watching

Forks

Repository files navigation

Blacksmith

Cross-platform CLI that installs curated development and cybersecurity
tool sets after a fresh OS install.

CI Python 3.8+ Version 0.7.0 Apache 2.0 Platform

Install  ·  Quick start  ·  Commands  ·  Configuration  ·  Package managers  ·  Trust  ·  Troubleshooting  ·  Contributing  ·  Security

Features

  • 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 --json for 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

Install

Requires Python 3.8+ and at least one supported package manager.

Recommended (isolated CLI via pipx):

pipx install jdi-blacksmith
blacksmith --version

Remove with blacksmith uninstall (detects pipx) or pipx uninstall jdi-blacksmith.

pip / virtualenv (fallback)

pip install jdi-blacksmith
blacksmith --version

Dedicated venv (Linux / macOS):

python3 -m venv ~/.blacksmith-venv
source ~/.blacksmith-venv/bin/activate
pip install jdi-blacksmith

Windows (PowerShell):

python -m venv $env:USERPROFILE\.blacksmith-venv
$env:USERPROFILE\.blacksmith-venv\Scripts\Activate.ps1
pip install jdi-blacksmith

From source: git clone the repo, then pip install -e ..

PATH or permission problems: Troubleshooting.

Quick start

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.

Commands

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.

Machine-readable output

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-run

Apply 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.

Audit

blacksmith audit
blacksmith audit --last 20

Mutating 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.

Configuration

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).

Package managers

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

Trust

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-trust

Version pins (inline)

Append |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|version excludes : 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.94 may not match installed 7.94-1
  • brew pins install versioned formulae (go@1.21), not specific Cellar patch versions
  • winget pins parse the Version column from winget list output; 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 --yes

Without --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 --yes

Troubleshooting

Command not found after install

  • Prefer pipx so blacksmith is on PATH without activating a venv.
  • Or use a virtualenv and activate it before running commands.
  • Linux/macOS user install: add $HOME/.local/bin to PATH.
  • Windows user install: add the user Scripts directory from python -m site --user-base to 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.

Contributing

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.

License

Apache 2.0 - see LICENSE.

Changelog

See CHANGELOG.md.

Author

jimididit

About

A cross-platform CLI tool that automates the installation of toolsets - helpful after a fresh OS install.

Topics

Resources

Contributing

Security policy

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages