Last verified 2026-08-02
- Python 3.12 or 3.13
- uv — this project uses uv exclusively; there's no
requirements.txt, no Poetry, no barepip install - A Fire TV or NVIDIA Shield with ADB debugging enabled, if you want to point it at a real device
- An SMB share, if you want artifacts to live off your machine — optional; local storage under
~/.fleetctl/artifactsworks with no config
git clone https://github.com/salvuswarez/fleetctl.git
cd fleetctl
uv sync --all-extrasuv sync --all-extras creates .venv/ and installs the package plus the dev and mcp dependency groups defined in pyproject.toml.
uv run fleetctl --version
uv run fleetctl --help--help lists the ten commands: scan, run, workflow, devices, config, steps, packs, artifacts, audit, mcp. Full reference: cli-reference.md.
Nothing runs against a device until there is somewhere to record what a scan finds, and — optionally — somewhere to put artifacts other than your local disk.
cp config/fleet.yml.example config/fleet.yml
cp .env.example .envEdit .env if you have an SMB share (fills !ref env:SMB_USER / !ref env:SMB_PASS in fleet.yml); leave fleet.yml's artifacts.smb block out entirely to store captures and builds under ~/.fleetctl/artifacts instead. config/inventory/devices.yml does not need creating by hand — the first scan writes it. See configuration.md for every field in both files.
Both .env and config/inventory/devices.yml are gitignored: real credentials and real device MAC/IP data never get committed. The .example files are the tracked reference.
uv run fleetctl scan 192.168.1.0/24Sweeps the subnet, identifies anything an installed pack (firetv, shield) recognizes, and merges it into config/inventory/devices.yml. Add --dry-run to see what would be found without writing anything. A device that's reachable but refuses the ADB key is recorded too, flagged unauthorized rather than dropped — approve the on-device debugging prompt and scan again.
Scan never touches tags or vars on a device it already knows — those are yours to hand-edit. Tag a device kodi to bring it into the shipped Kodi workflows; tag your reference device gold as well, since kodi-capture-gold targets that tag.
# config/inventory/devices.yml
devices:
- id: living-room-stick
type: firetv
address: 192.168.1.50
tags: [kodi, gold]
vars: {}Every mutating workflow needs a plan reviewed before it runs — the plan's digest has to match what you pass to --confirm, so a workflow can't be run against a fleet that changed since you looked at it.
uv run fleetctl workflow plan kodi-check
uv run fleetctl workflow run kodi-check --confirm <digest-from-plan>kodi-check is read-only and needs no approval under any policy — a safe first run. To see everything shipped: uv run fleetctl workflow list. A single step, outside a workflow, runs directly:
uv run fleetctl run firetv.check --device living-room-stickuv run pytest500 tests, weighted toward the failure modes that bit the predecessor project on real hardware. For coverage:
uv run pytest --cov=src --cov-report=term-missingThe gate requires 90% and currently sits around 90.6%.
Every pull request runs the same four checks CI runs, on Python 3.12 and 3.13:
uv run black src tests --check
uv run isort src tests --check-only
uv run mypy
uv run pytest --cov=src --cov-report=term-missingDrop --check/--check-only to have black/isort fix formatting in place rather than just report on it. mypy runs in strict mode against both src and tests and must stay clean.
uv build # wheel + sdist to dist/, if you want to check packagingfleetctl/
├── src/fleetctl/
│ ├── core/ # device-agnostic kernel — the only ring apps/packs may not skip
│ │ ├── transport/ # Transport protocol, AdbTransport, netcat push
│ │ ├── artifacts/ # ArtifactStore protocol: SMB + local backends
│ │ ├── config/ # fleet.yml loading, !ref secrets, .env, layered resolution
│ │ ├── discovery/ # subnet sweep, pack claiming, the fleet.scan step
│ │ ├── inventory/ # devices.yml read/write/reconcile
│ │ ├── operations/ # operation tracking, one-per-device guard, background dispatch
│ │ ├── observability/ # diagnostics / timeline / audit — three streams
│ │ ├── workflow/ # Workflow, engine, plan/dry-run, the step runner
│ │ └── policy.py # per-actor allow/confirm/deny, protected devices
│ ├── packs/ # what a device *is*: firetv, shield (both compose packs/android)
│ ├── apps/kodi/ # software *on* a device: capture/build/deploy, recipes as YAML
│ ├── agent/toolkit.py # the policy-aware surface MCP and a future HA integration call
│ ├── mcp/ # MCP server exposing the toolkit over stdio
│ └── cli/ # Click commands — a thin wrapper over the same step bodies
├── tests/ # 500 tests, mirroring the src/ layout
├── config/
│ ├── fleet.yml.example # SMB, observability, policy — copy to fleet.yml
│ └── inventory/devices.yml.example
├── docs/
│ ├── architecture.md # design source of truth — 15 sections, 21 diagrams
│ ├── cli-reference.md # every command and flag
│ ├── configuration.md # fleet.yml, .env, devices.yml, every field
│ ├── pack-authoring.md # the extension contract for a new device type
│ ├── safety.md # policy, protected devices, plan-then-run, --approve
│ ├── observability.md # diagnostics / timeline / audit
│ └── ha-parity.md # every Home Assistant panel command mapped to its fleetctl equivalent
├── .claude/ # agents, skills, commands, per-ring rules
├── pyproject.toml
├── CLAUDE.md
├── README.md
├── SECURITY.md
└── CONTRIBUTING.md
- Every command and flag:
cli-reference.md - Every config field,
fleet.ymlthroughdevices.yml:configuration.md - The full design:
architecture.md - How the Home Assistant integration maps onto fleetctl:
ha-parity.md - Writing a new device pack:
pack-authoring.md - The house rules for contributing:
../CONTRIBUTING.md - The non-negotiables baked into the project's own
CLAUDE.md:../CLAUDE.md
|
fleetctl Getting Started |
|
|