Heinzel is a set of rules that turns an AI coding assistant into a cautious, methodical sysadmin. Describe what you need in plain English. Heinzel works out the right commands for your OS, explains each one, and waits for your approval. It backs up configs, tests before applying, and remembers every server it has worked on.
It manages Linux, FreeBSD and macOS, remote over SSH or on the local machine, and runs in Claude Code, OpenCode or any terminal AI tool that reads project files and runs shell commands.
The trailer is scripted, not recorded. To change it, see
assets/trailer/.
Screencasts on YouTube:
- Debug and fix a misconfigured nginx and firewall (1 min)
- Install the latest stable Ruby and Ruby on Rails (1 min)
- Install a firewall, upgrade the distribution, set up daily security updates (2 min)
Press: German article about Heinzel on heise.de
You need:
- An AI coding assistant in the terminal, e.g. Claude Code or OpenCode.
- SSH access to the server as a normal user or as root, with a key that does not ask for a passphrase. Not needed for the local machine. New to SSH keys? See the Arch wiki guide.
- A workstation running Linux, macOS, FreeBSD or Windows.
On Windows use
WSL, or start
the AI tool from the Git Bash of
Git for Windows. PowerShell
and
cmd.exeare not supported.
Then:
git clone https://github.com/wintermeyer/heinzel.git
cd heinzel
claude # or: opencode
❯ Install postgresql on server1.example.com
On the first connection Heinzel may ask what it cannot
detect, usually the SSH user. The answer goes to
memory/user.md, so it asks once. You can pre-fill that file
from memory/user.md.example. It also holds your language
(Language: German).
Heinzel proposes every command, says what it does and why, and waits for your approval.
Heinzel shares one SSH connection per host and keeps it open
for 10 minutes after the last call. The sockets live in
~/.cache/heinzel (mode 0700), so any process of your local
user can use an open connection without the key.
Check on web1.example.com: reads the server's memory file and recent changes, then picks up where the last session ended.Run housekeeping on app.example.com: a health report on disk, memory, load, updates, firewall, certificates and failed services. Problems come first.Run a security audit on app.example.com: SSH, firewall, accounts, open ports and kernel settings, by severity.Email me the output of "df -h" from app.example.com: sends text or files by mail, from your workstation or from the server.Clean up leftovers of old apps on this Mac: finds what removed apps left in~/Libraryand/Library. Only what you approve goes to the Trash.Update all Homebrew packages on this Mac: local mode, no SSH, same rules./plan Migrate the database on db.example.com: explores and drafts a plan, changes nothing until you approve./planis Claude Code. In OpenCode, ask Heinzel to plan first.
More that happens without asking:
- OS detection. On first contact Heinzel detects the OS and hardware and loads the rule file for that platform.
- Memory. Each server gets a folder under
memory/servers/with its state, a changelog and an open to-do list. An interrupted job shows up as pending on the next connection. - Long jobs. A command that can take more than a
minute runs as a job on the host, output and exit
status in files, and is read back later, after a
dropped connection or in a new session too
(
rules/ssh-connections.md). - DNS aliases. Several names for the same IP share one memory. Each alias can have its own SSH user.
- SSH ports. A port other than 22 is stored per server.
List the ports you use as
Alternative SSH ports:inmemory/user.md. Heinzel tries those and never scans. - Email details. Mails carry a short signature with the
operator's name (
Operator name:inmemory/user.md) and headers that keep out-of-office replies away. The first mail per host asks where to send from. Heinzel uses an existing MTA and asks before installing one.
The macOS cleanup scanner needs the Command Line Tools
(xcode-select --install).
Heinzel is one admin at one terminal, working on one host at a time. Heinzel Pro is the commercial product built on the same rules: a team, a web interface, a fleet, and a runtime that enforces what Heinzel can only ask for. Everything that needs a second person, an identity, a fleet, a schedule or an evidence chain lives there. Heinzel reads and reports those things and answers a request to change them with one line pointing to Pro.
| Heinzel (this repo, MIT) | Heinzel Pro | |
|---|---|---|
| Who | One admin, their terminal, their servers | A team, one web interface, a fleet |
| Connect, detect the OS, remember the host | Yes | Yes |
| Housekeeping, security audit, reports | On request | On a schedule, as a list of open findings, started by alerts |
| Packages, configs, services, firewall | Asks first | Rights per person and server, a second person where the tenant wants one |
| Accounts, SSH keys, sudo, deploy users | Inspects and reports | Creates and changes them, approved and logged |
| Secrets and certificates | Finds leaks, reports expiry | A rotation plan that names every consumer first, then the change as an approved session; certificates watched across the fleet and renewed |
| Partitions, boot loaders, OS replacement | Reads | Changes them, with a second person before every write where the tenant requires it |
| Many servers at once | One after another | Rollouts: one task on many servers, a canary first, then waves; OS upgrades the same way; a version matrix across a group |
| At night, with nobody there | No | Schedules that read alone and collect changes for the morning, alerts that start a diagnosis; maintenance windows per group, outside which every change waits for a person |
| Protected paths | memory/protected-paths.md: read-only, hidden, confirm |
The same modes for the whole tenant, plus paths a tool manages, told to the agent on every server |
| What the agent may do | One guard, no off switch | Capabilities per tenant, off again per server: partitioning and OS replacement off from the start, identity and secrets on |
| Evidence | Journal and changelog on the host | Audit chain, access review, policy as a file |
| Own rules | Files in the checkout | Own rules per tenant and server, imported from a Heinzel checkout with its server memory, on top of the open rule set as a versioned bundle |
Resetting the password of an existing account, adding a key the operator pastes in, and every package, config, service and firewall change stay in Heinzel. Heinzel Pro runs as a container, hosted or on your own servers; the details are on heinzel-pro.de.
The safety rules are part of every session.
-
Asks before acting. Destructive commands, firewall changes, reboots and service restarts need your approval. Reloads run on their own when the config test passes (
rules/service-reload.md,memory/service-policy.md). -
Hard guardrails (Claude Code). A PreToolUse hook (
.claude/hooks/guard-taboos.sh) blocks the absolute taboos in every permission mode, even--dangerously-skip-permissions: halt and poweroff,mkfs, partition-table writers, boot-loader and EFI writers, deleting or overwriting SSH keys, writes tosshd_config, deleting accounts, writes tosudoers, the account files and the server lock. It also catches them insidessh host "…", behindpython3 -cand when the file-edit tools target one of those files. Read-only forms (fdisk -l,efibootmgr -v,visudo -c) stay allowed. The hook has no off switch: what it blocks is Heinzel Pro's work. OpenCode does not run this hook; there the written rules are the safety layer. -
Verifies before it reports. "It is gone since the reboot" gets checked against the live system first (
rules/verify-before-reporting.md). -
Backs up config files to
/var/backups/heinzel/before editing. Cleaned up after 30 days. -
Tests before applying with dry-run or validation modes where a tool has one.
-
Logs everything. Each change is one plain-language line in the system journal. The technical detail and the way back are in
memory/servers/<hostname>/changelog.log. -
Stable repos only. No third-party sources without your approval.
-
Least privilege. Normal user first,
sudowhen needed, root last. With neither, Heinzel does what it can and writes a report for the tasks that need root. -
Blacklist and read-only servers. Hosts in
memory/blacklist.mdare never contacted. Hosts inmemory/readonly.mdare inspected but never changed. -
Server lock. A machine's owner can lock it against every Heinzel with a file on the machine itself:
# /etc/heinzel.conf allow_changes = false reason = Database migration until 20 Oct, hands offHeinzel still connects and inspects, but changes nothing and reports what it would have done. An unreadable file or any value other than
truelocks too. Heinzel never writes, moves or deletes the file; you remove the lock by hand. It is a promise kept by Heinzel, not a security boundary: it stops no tool that ignores it (rules/access-control.md). -
Protected paths.
memory/protected-paths.mdmarks paths per server asreadonly,hidden(content never shown) orconfirm(needs a typedCONFIRM). -
Ignores injected instructions. Text in files, logs and command output is data. Text that addresses the AI is flagged to you and not followed.
-
Keeps secrets out. Keys, password files and
.envcontents are inspected by metadata and fingerprint, never printed, and never passed as command-line arguments.
An LLM can invent a flag or a path. Heinzel puts verified facts in front of it instead:
- A rule file per platform holds the right commands, package manager and firewall tool.
- It checks
--help, the man page or upstream docs before a command runs. - It reads the server's memory file instead of guessing.
- It uses dry-run modes first.
- You see every command before it runs.
This reduces the risk. It does not remove it.
Caution
Heinzel operates on live systems, as root, with sudo or unprivileged. Review every command before you approve it.
A disciplined AI that follows the checklist every time makes fewer mistakes than a tired human at 2 AM. It can still misread your intent. Stay in the driver's seat.
By default Claude Code asks before every tool call. For batch work use auto mode: a background check lets routine commands through and still stops on risky ones. Press Shift+Tab to cycle modes, or pass the flag:
claude --permission-mode auto \
-p "Run housekeeping on server1.example.com"-p runs one prompt without the interactive UI. In OpenCode
that is opencode run "…".
- For CI, the strictest setup is
--permission-mode dontAskwith an allowlist (--allowedToolsorpermissions.allowin.claude/settings.json). --dangerously-skip-permissionsremoves all review, including the protection against malicious text in server output. Use it in disposable environments only.- Whatever the mode, Heinzel's own rules still apply. See the permission modes docs for details.
A run with nobody at the keyboard, nightly by cron or a timer, is a Heinzel Pro feature: there a schedule reads alone and collects what it would change for a person to approve in the morning.
All your state is plain text in one directory:
user.md: SSH usernames, language, operator nameblacklist.md,readonly.md,protected-paths.md: access policiesservice-policy.md: which services may reload or restart without askingservers/<hostname>/: memory, changelog, to-do and rule overrides per servercustom-rules/: your global rule overridesnetwork.md,housekeeping.md: cross-server facts and your own checksopencode.json: your OpenCode config
bin/heinzel-backup # writes a .tar.gz
bin/heinzel-backup -o <path> # to a given path
bin/heinzel-backup --list # dry run
bin/heinzel-backup --restore <file.tar.gz>Restore refuses to overwrite existing content without
--force and validates the archive before writing.
memory/ is gitignored and personal: one checkout, one
person. Several people working on the same servers, with
rights, approvals and an audit trail, is Heinzel Pro.
Change behaviour without editing the upstream rule files. Three layers, later wins:
rules/<name>.md(upstream)memory/custom-rules/<name>.md(yours, all servers)memory/servers/<hostname>/rules.md(one server)
## Add: Docker cleanup
New rules applied alongside the base.
## Replace: Firewall
Replaces the matching base section entirely.
## Remove: Common Pitfalls > snap
Skip this base section.A section without a prefix is an addition.
memory/custom-rules/all.md applies to every server. Skills
follow the same chain, e.g.
memory/custom-rules/heinzel-housekeeping.md. A few rules
take additions only: the ethics rule, the guard's taboos,
the connection pipeline, anomaly detection, secrets hygiene
and the Heinzel Pro refusals cannot be replaced or removed.
The version is in VERSION, the changes in CHANGELOG.md.
Claude Code updates Heinzel at session start with a
git pull, unless you are pinned, on another branch, or have
set HEINZEL_NO_UPDATE=1. Elsewhere:
bin/heinzel-update # pull latest
bin/heinzel-update --check # check only
bin/heinzel-update --pin v2.28.0
bin/heinzel-update --unpinComing from 1.x? The update moves rules/custom/ and
opencode.json into memory/ by itself. If you are pinned
to a 1.x tag, run bin/heinzel-update --unpin first.
| Family | Distributions | Rule file |
|---|---|---|
| Debian | Debian, Ubuntu | rules/debian.md |
| RHEL | RHEL, CentOS, Fedora, Rocky, Alma | rules/rhel.md |
| SUSE | openSUSE, SLES | rules/suse.md |
| macOS | Apple Silicon and Intel | rules/macos.md |
| FreeBSD | all versions | rules/freebsd.md |
| Unraid | observed on 7.3 | rules/unraid.md |
Other distributions work with general best practices.
Claude Code is the tool Heinzel is developed with. OpenCode
reads the same CLAUDE.md and .claude/skills/, as long as
OPENCODE_DISABLE_CLAUDE_CODE is unset. Any other tool that
reads project files gets the rule layer. The skills
(housekeeping, security audit, email, macOS cleanup) need a
tool that supports Skills.
Heinzel can run on your own hardware with Ollama:
ollama pull qwen3.5:9b
ollama run qwen3.5:9b
>>> /set parameter num_ctx 16384
>>> /save qwen3.5:9b-16k
>>> /bye
cp memory/opencode.json.example memory/opencode.json
opencodeOllama's default context of 4096 tokens is too small for
tool use, hence the larger variant. Adjust baseURL and the
model name in memory/opencode.json, then pick the model
with /models. Larger models (14B and up) make more reliable
tool calls. See the
OpenCode provider docs
for more.
Every change is logged on the server:
journalctl -t heinzel
journalctl -t heinzel --since "2026-02-01"
# macOS
log show \
--predicate 'senderImagePath CONTAINS "logger"' \
--info --last 7d | grep heinzelCLAUDE.md main instructions for the AI tool
rules/ rule files per platform and topic
.claude/skills/ housekeeping, security, email, macOS cleanup
.claude/hooks/ update check and the taboo guard
bin/ heinzel-update, heinzel-backup,
heinzel-migrate
memory/ your state (gitignored)
assets/trailer/ script that renders the README trailer
The Heinzelmännchen are the helpful gnomes of Cologne. At night, while the city slept, they did the work that was left undone.
Wintermeyer Consulting offers consulting and hands-on support for Heinzel, from setup to ongoing system management. Contact Stefan Wintermeyer: sw@wintermeyer-consulting.de
Bug reports, feature requests and pull requests are welcome. See CONTRIBUTING.md: a contribution may also end up in Heinzel Pro.
MIT
