Skip to main content

Flash images onto target disks, locally or over PXE

Project description

bty mascot - a blue bat holding a PXE handshake card and a disk labelled .qcow2 / .img / .raw

bty - flash a fleet without leaving your chair

CI Docs Documentation PyPI Python Container

Reflash a homelab box, a CI runner, or a rack of bare-metal targets in the time it takes to make coffee. bty writes pre-built ("cooked") system images onto disks - locally over USB or remotely over PXE - then hands the freshly-booted system to cloud-init or a CIJOE task for first-boot configuration. No imperative configuration management, no idempotency mind games: rebuild the image, reflash the target.

# Local: USB stick into target, two arrows + Enter, done.
bty-tui

# Remote: bind a MAC to an image, the next PXE boot reflashes itself.
curl -X PUT http://bty-server:8080/machines/aa:bb:cc:dd:ee:ff \
  -d '{"image_sha256":"<sha>","boot_policy":"flash"}'

# Per-job CI: every job a clean OS, no drift, no snowflakes.

Three delivery shapes, one runtime

Shape What it is When it fits
USB live stick bty boots from a flash drive, runs bty-tui, flashes the box it's plugged into Single-machine local imaging
USB + server catalog Same stick, but the image list comes from bty-web --server URL A handful of boxes, shared image library
PXE-boot appliance bty-web on a Pi or x86 box runs DHCP/TFTP/HTTP; targets PXE-chain into a netboot live env that flashes them unattended CI fleets, racks, anything you don't want to walk to

All three share the same Python codebase, the same image catalog, the same SHA-keyed machine bindings.

Why bty

  • Reflash on every CI job. Per-job cadence: each job lands on a freshly-imaged target, runs, gets reflashed for the next job. No state leaks. No snowflakes. No "works on my machine" because the machine is bit-identical to the manifest every single boot.
  • Cooked images, not recipes. You build the image once (in your build system of choice), bty writes the bytes. Provisioning is cloud-init or a CIJOE task on first boot - small, declarative, inspectable. No agent, no daemon, no convergence loops.
  • OS-agnostic by design. Linux, FreeBSD, Windows - if it boots from a disk image, bty can flash it. macOS targets are out (Apple Silicon's boot story isn't friendly to imaging).
  • Trust model is explicit. PXE / live-env routes are open (clients have no token); operator routes (/machines, /catalog/*, /boot/releases) require a session cookie. bty-web is for trusted networks (homelab, CI segment), not the open internet.

Try it without flashing anything

A multi-arch container is published on every release:

docker run -d --name bty-web -p 8080:8080 -v bty-data:/var/lib/bty \
  ghcr.io/safl/bty-web:latest
# -> http://localhost:8080/ui   (login: bty / bty)

Image catalog only - no DHCP / TFTP / PXE proxy in the container (those need bare-metal LAN access; use the appliance for that). See docs/src/walkthrough-server-docker.md for bind-mount permissions, env vars, and password rotation.

Install

bty is one Python package - bty-lab on PyPI - with three console scripts:

pipx install bty-lab            # `bty` CLI, zero third-party deps
pipx install "bty-lab[tui]"     # adds `bty-tui` (Textual)
pipx install "bty-lab[web]"     # adds `bty-web` (FastAPI + Pydantic)
pipx install "bty-lab[all]"     # everything

bty list disks, bty inspect image, bty flash --dry-run need only Python 3.11+ and stdlib. bty flash --yes shells out to dd, qemu-img, zstd, lsblk, and friends - your distro provides those.

For an appliance you can boot directly (USB stick, server image, PXE-chain live env), grab the bake from GitHub Releases. The appliance builder lives under bty-media/.

Status

Pre-1.0 but actively shipping. Every tag publishes wheels (PyPI), appliance images, and the bty-web container. The end-to-end PXE flow (server + netboot live env + target flash + completion signal) runs in CI on every push. CLI flags and wire formats may still shift between minor versions until 1.0 - watch the schema_version field on --json output and the Machine wire type. The PLAN.md tracks the roadmap milestone by milestone.

Development

pipx install uv
uv sync --all-extras --group dev
uv run pytest                    # full suite
uv run ruff check                # lint
uv run mypy src                  # types

The docs tooling installs separately:

pipx install ./docs/tooling
cd docs
bty-docs-serve                   # live-rebuild dev server on :8000
bty-docs-build-html              # one-shot HTML build
bty-docs-build-pdf               # one-shot PDF (requires LaTeX)

More

Project details


Release history Release notifications | RSS feed

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

bty_lab-0.7.38.tar.gz (3.0 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

bty_lab-0.7.38-py3-none-any.whl (723.0 kB view details)

Uploaded Python 3

File details

Details for the file bty_lab-0.7.38.tar.gz.

File metadata

  • Download URL: bty_lab-0.7.38.tar.gz
  • Upload date:
  • Size: 3.0 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for bty_lab-0.7.38.tar.gz
Algorithm Hash digest
SHA256 ebcd44c8874812c61f0fce457a9ec3487a0dab3c47b8b7153ec852329fc97096
MD5 dd642e32de48e668ac1f08f4872bc06c
BLAKE2b-256 f74188130dcdf26946f2d1a89ab6e03cb686aad1bb30c93cdd0182d53b7579ed

See more details on using hashes here.

Provenance

The following attestation bundles were made for bty_lab-0.7.38.tar.gz:

Publisher: release.yml on safl/bty

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file bty_lab-0.7.38-py3-none-any.whl.

File metadata

  • Download URL: bty_lab-0.7.38-py3-none-any.whl
  • Upload date:
  • Size: 723.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for bty_lab-0.7.38-py3-none-any.whl
Algorithm Hash digest
SHA256 6cdc427b6331e820510867c357c46ca37b79dbdac3e7f2b17ec2152d912b2d88
MD5 3c648a232edc9a7ebc11a8797c8ec823
BLAKE2b-256 5cf9e015ecfbb00a510091e96281dcd26bf0a1afaaf89d8524b6caca686c09de

See more details on using hashes here.

Provenance

The following attestation bundles were made for bty_lab-0.7.38-py3-none-any.whl:

Publisher: release.yml on safl/bty

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page