magehand
The developer CLI for building apps on davidlarrimore's homelab, the same way on the owner's MacBook (Claude Code) and in OpenClaw's VM (its coding workers). It reads platform state and runs apps locally. It never deploys: changes go through pull requests.
It is public because it holds nothing sensitive. Every command needs access
that only the owner has: the private homelab repos on GitHub, the private
*.lab network, and an authentik passkey for
OpenBao.
Install
brew install uv # once
uv tool install magehand
magehand setup # once per machine
magehand setup checks GitHub (gh auth login first) and which container
runtime this machine has (only needed for database blocks; see magehand runtime below), makes sure the homelab's *.lab
names resolve (if they don't, it asks for the homelab's DNS address, your home
gateway, and points only *.lab lookups at it; magehand setup --undo
reverses that), then signs you in and installs the homelab-app skill for Claude Code (and Codex). Every other command assumes this is done.
Upgrade with magehand upgrade (any command says, at most once a day, when a new version is out). After upgrading, run magehand update-agents in each app repo to bring in the template's current AGENTS.md rules.
Commands
magehand --help lists them; magehand COMMAND --help (or magehand help COMMAND) explains one, with examples.
Start an app
| Command | What it does |
|---|---|
magehand new REPO [--app-name APP] [--no-clone] [--dir DIR] [--json] |
Creates davidlarrimore/REPO from homelab-app-template (private) with gh, as you, and invites OpenClaw's bot davidlarrimore-bot with write; OpenClaw accepts by itself. The app is named after the repo unless --app-name APP says otherwise (new lab-website --app-name lab: hostnames, namespace and image use lab): it then commits deploy/app.yaml with app: APP to the new repo's main (before the ruleset exists; an existing deploy/app.yaml is never rewritten, only reported). It then clones the repo into ./REPO (--dir DIR, or --no-clone; an existing non-empty directory is left alone) and prints two lists: what is left for you, and what the platform does by itself. Registration needs no step: the platform registers the app by itself when deploy/ (with base/) is merged to main. It also adds the main: owner approves ruleset (a PR needs one approval of its latest push; the owner can bypass it as admin; a refusal only warns). Safe to re-run: on an existing repo it only adds the bot and the ruleset if missing, and warns if the repo is public. It first waits (about a minute) for GitHub's asynchronous copy of the template to land (AGENTS.md, .github/workflows/build.yaml), because a commit or the ruleset on main before then makes the copy fail: if the files never appear it stops with nothing else changed (exit 1; re-run), and on an existing repo that lacks them it says how to recover (gh repo delete and re-run when nothing is in it, else restore them through a PR); if GitHub can't be asked (rate limit, authorization, network) it says so and changes nothing, rather than calling the repo broken. Its one write, and to GitHub, never the homelab |
Learn the platform and check an app
| Command | What it does |
|---|---|
magehand guide [TOPIC ["SECTION"]] |
The app platform guide, live from homelab-apps docs/platform/: magehand guide (overview), guide recipes, guide blocks "Web search" (one section), guide catalog (blocks and models) |
magehand guide search WORDS |
The guide sections (docs/platform/ and homelab-apps AGENTS.md) that best answer a question, e.g. magehand guide search web search |
magehand update-agents [--check] [--code DIR] [--json] |
Updates this repo's AGENTS.md from homelab-app-template: everything above the ## This app heading is replaced with the template's current text; the heading and the app's own notes below it are kept. Writes only that file (commit it yourself). --check writes nothing and exits 1 when it is out of date. Refuses a file with no ## This app heading |
magehand app [APP] [--json] |
The app's blocks, every env var its pod gets and where it comes from, its URLs. In an app repo with deploy/app.yaml they come from the checkout's deploy/ (the app's deployment config, which merging copies into homelab-apps); otherwise from homelab-apps apps/<app>/. Says when the app isn't registered on the platform yet, or an llm block isn't deployed yet: until then it has no LLM key |
magehand check [APP] [--code DIR] [--apps-dir DIR] [--manifests-only] [--json] |
The platform's rules for the app's code and its deployment (deploy/ in the checkout when it has one, else homelab-apps): block keys and options, optional: true keys, Secrets nothing makes, the image, files in deploy/ a merge won't deploy (it holds app.yaml, base/, and the overlays dev/ and prod/, whose namespaces must be <app>-dev and <app>), env the code reads but the pod doesn't set, models the code names that its llm block doesn't request (warnings), secrets in the committed .magehand.env, direct AI/search provider calls, own login, database files. An app: in deploy/app.yaml must be a valid app name that homelab-apps hasn't registered to another repo, and a repo registered under another name must keep saying so (checked against --apps-dir in CI). Also warns when the repo's AGENTS.md platform rules are behind homelab-app-template (magehand update-agents fixes it; skipped offline). Each finding names the guide section; exit 1 on errors. --manifests-only skips the code rules. Run before every push; CI runs it too (--apps-dir) |
Run an app locally
| Command | What it does |
|---|---|
magehand run [-a APP] [--no-proxy] -- CMD |
Runs CMD with the env the app's pod gets: values from its Deployment, the LLM key from OpenBao, databases from dev up. Precedence: manifest < the repo's .magehand.env (committed, non-secret overrides such as WEB_DIR=./web) < your shell < secrets. A proxy on 127.0.0.1:8080 adds the X-authentik-* headers Traefik would, as you. With no key in OpenBao it says why (app not registered, block not deployed yet, key being created). In an app's repo, -a can't name another app: that would run this code on the other app's keys and budget |
magehand run --container [--no-build] [-- CMD] |
Builds the repo's Dockerfile and runs the image as its pod runs: read-only, /tmp tmpfs, the pod's user, no capabilities, exactly the pod's env. Secrets go in as -e NAME only, never in the image, argv or on disk |
magehand dev [up|down|status] [APP] |
Containers for the app's postgres/redis blocks (the blocks' own pinned images) on a per-app network, and local values for secret blocks. down deletes the local data |
magehand runtime [docker|podman|none|auto] [--json] |
Shows or sets the container runtime for dev and run --container. Detected without sudo (<cli> info): the docker CLI of Docker Desktop, OrbStack, Colima or Rancher Desktop (dockerd), or Podman. auto (the default) uses the first running one; none means no containers (magehand run -- CMD still works for apps whose blocks are only llm and secret) |
magehand login |
Signs in to OpenBao through authentik (passkey) with role dev: read-only access to agent apps' LiteLLM keys, 1h (8h max). The token is kept in the macOS Keychain |
This machine
| Command | What it does |
|---|---|
magehand setup [--dns ADDRESS] [--undo] |
Once per machine: GitHub, the container runtime, *.lab name resolution (the only step that may ask for your password; without a terminal, pass --dns), sign-in, the skill |
magehand doctor [--json] |
Checks GitHub access, that openbao.lab is reachable and the sign-in; in an app's repo, also that OpenClaw's bot can write to it and that it has the approval ruleset, active, covering the default branch and requiring an approval (magehand new APP adds a missing one but never changes an existing, weaker one: fix that in Settings > Rules; the ruleset is only readable by an admin, and anything unreadable is a note, not a failure); shows the container runtime (missing is fine unless the app has database blocks) |
magehand upgrade |
Upgrades magehand the way it was installed (uv tool upgrade, pipx upgrade or pip in its venv) and refreshes the skill if you installed it. Never automatic: you run it when a command tells you (stderr, at most once a day, never in CI or scripts) that a newer version is out. In OpenClaw's VM it refuses: that copy is pinned in homelab platform/openclaw/vm/versions.env |
magehand skill [--install] |
The homelab-app skill, which tells Claude Code and Codex to use app, guide search and check while working on app code. setup installs it ($CLAUDE_CONFIG_DIR or ~/.claude, and $CODEX_HOME or ~/.codex if Codex is installed) |
magehand version, magehand -V |
The installed version, and whether a newer one exists |
Everywhere: -a/--app APP names the app (default: the app repo you're
in); --json prints JSON for scripts and agents; exit status 0 is success,
1 a failure (or check errors), 2 a usage error (with a "did you mean"),
130 Ctrl-C. Messages go to stderr, results to stdout.
Settings, highest first: flags, then environment variables, then
~/.config/magehand/config.json (written by magehand runtime).
| Variable | Effect |
|---|---|
MAGEHAND_RUNTIME |
docker, podman or none (overridden by --runtime) |
MAGEHAND_NO_UPDATE_CHECK |
Set: no new-version notice |
MAGEHAND_APPS_DIR |
Read this homelab-apps checkout instead of GitHub (check --apps-dir sets it) |
MAGEHAND_PROXY_PORT |
The identity proxy's port (default 8080) |
GITHUB_TOKEN, GH_TOKEN |
GitHub access (else gh auth token) |
Development
uv venv && uv pip install -e .
.venv/bin/python -m unittest discover -s tests
Python 3.9+ (the Mac's system Python works). Stdlib plus PyYAML. Releases:
bump version in pyproject.toml, merge, then tag vX.Y.Z. The release
workflow builds and publishes to PyPI through trusted publishing (no stored
token). OpenClaw's VM installs a pinned version from homelab
platform/openclaw/vm/versions.env.
Never commit secrets, tokens, IP addresses or email addresses: this repo is public.
Metadata
Release files for magehand 0.15.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| magehand-0.15.2.tar.gz | 61.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| magehand-0.15.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 106.1 kB
Release files / magehand-0.15.2.tar.gz
| Download URL | magehand-0.15.2.tar.gz |
|---|---|
| Size | 61.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
37df8636da08dd0b8875720bbafceb95d21f5ae0fd95cd98c4cef202ad923d4d
|
|
BLAKE2b-256 checksum How to use checksums |
7535868e0302ac8b975f6d3a3262c8d9f45adad5e449ed836431401e07dd54c8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 11, 2026.
Transparency logRelease files / magehand-0.15.2-py3-none-any.whl
| Download URL | magehand-0.15.2-py3-none-any.whl |
|---|---|
| Size | 44.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
1db320e7e1e88f162a8746a596d9527245b76e676e17a9af76c8a93d793d6703
|
|
BLAKE2b-256 checksum How to use checksums |
0c52bf66df7bd4104113b89012ce8709ec23f763d770746b4f1e9f3f6e71ea8f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 11, 2026.
Transparency log