Hermes Bootstrap is a NixOS deployment kit for running Hermes Agent on hardware you control.
It turns a small server, mini PC, or spare x86_64 machine into a reproducible, self-owned AI agent node:
- NixOS system definition in
system/nixos/flake.nix - Hermes Agent managed by systemd
- isolated
/var/lib/hermesstate directory for memory, logs, skills, wiki, and workspace - provider credentials loaded from a root-owned env file, not committed config
- native first-boot runtime by default, with optional container mode after cache/network provisioning
- local Phase 1 observability harness: systemd timers, deterministic sensors, JSONL events, and daily Markdown reports
- USB/NixOS installer workflows plus hardening and rollback notes
This repository is intentionally infrastructure-focused. It does not include private memories, private wiki content, real API keys, or personal workspace data.
Public prototype / operator toolkit.
The repo has been used as a real deployment workbench, so some scripts are still opinionated. The public goal is to make those opinions explicit, safe, and configurable rather than hidden in local state.
Current defaults are centralized in system/nixos/deployment-options.nix:
| Area | Default |
|---|---|
| Target architecture | x86_64-linux |
| OS | NixOS 24.05 |
| Agent | NousResearch/hermes-agent flake input |
| Service user | hermes |
| Interactive admin user | hermes-admin |
| Hostname | hermes-node |
| Gateway bind address | 127.0.0.1 |
| Provider example | MiniMax via /var/lib/hermes/secrets/hermes.env |
| Local harness | hermes-harness system user, systemd timers, /var/lib/hermes/{harness,events,reports} |
Edit system/nixos/deployment-options.nix before deployment to change host identity, admin account, provider/model defaults, gateway binding, locale, or the secrets env-file path.
Linux build/admin machine
|
| prepares USB / copies repo / bundles hermes-agent source
v
USB installer media
|
| boots target machine into NixOS installer or bootstrap environment
v
Target machine internal disk
|
| NixOS + systemd + Hermes Agent + local harness timers
v
/var/lib/hermes
|- .hermes/ runtime config, logs, sessions, memory
|- workspace/ agent working directory
|- secrets/ provider credentials, mode 0600, never committed
|- wiki/ optional private knowledge base seed
|- skills/ optional skill seed
|- harness/ latest deterministic local sensor snapshot
|- events/ bounded harness event JSONL
|- reports/ daily local Markdown reports
`- backups/ local backup target
Runtime posture:
- SSH is enabled, root login disabled.
- Hermes gateway binds to localhost by default.
- Public internet exposure is out of scope unless you add a reviewed reverse proxy/TLS layer.
- The agent has broad local power by design; use this on machines you are willing to dedicate to agent work.
hermes-bootstrap/
├── README.md
├── SPEC.md
├── .github/workflows/ci.yml
├── boot-image/
│ ├── make-boot-image.sh
│ └── overlay/
│ ├── auto-deploy.sh
│ └── usr/local/bin/{hw-detect,wifi-setup}
├── boot/ventoy/ventoy.json
├── data/
│ ├── memory/.gitkeep
│ ├── secrets/hermes.env.template
│ ├── skills/.gitkeep
│ ├── tools/.gitkeep
│ └── wiki/.gitkeep
├── docs/
│ ├── deployment-skill.md
│ ├── hardening-runbook.md
│ ├── install-manual-nixos.md
│ ├── local-artifact-policy.md
│ ├── node-harness-phase1.md
│ ├── phase1-live-validation.md
│ ├── phase2-boundaries.md
│ ├── symbiosis-assimilation.md
│ └── public-audit.md
├── scripts/
│ ├── backup-memories.py
│ ├── create-nixos-findiso-usb.sh
│ ├── deploy-hermes.sh
│ ├── harness/
│ ├── setup-hermes-agent.sh
│ ├── update-nixos-usb-autodeploy.sh
│ └── verify-bootstrap.sh
├── system/nixos/
│ ├── agent-extra-packages.nix
│ ├── deployment-options.nix
│ ├── flake.nix
│ ├── harness.nix
│ ├── hardware-configuration.nix
│ └── README.md
└── tests/
├── deployment-readiness.sh
├── findiso-autodeploy-static.sh
├── harness_phase1_static.sh
├── harness_phase1_fixtures.py
└── shell-syntax.sh
On the build/admin machine:
- Linux shell environment
- Git
- Nix, if you want to validate the flake locally
- ShellCheck, if you want to run shell lint locally
- Python + pytest, if you want to run the Phase 1 harness behavior tests
- Docker, only if building the experimental Alpine boot image
- a NixOS minimal ISO for manual installer flows
- a target x86_64 machine whose disk can be erased
Danger zone: deployment scripts can partition and format disks. Always identify devices live with:
lsblk -o NAME,SIZE,MODEL,SERIAL,TRAN,FSTYPE,MOUNTPOINTS
sudo blkidNever rely on remembered device names.
git clone https://github.com/steezkelly/hermes-bootstrap.git
cd hermes-bootstrap
# Optional but recommended: validate scripts and flake/module evaluation.
tests/shell-syntax.sh
tests/findiso-autodeploy-static.sh
tests/deployment-readiness.sh
tests/harness_phase1_static.sh
python3 -m pytest -q
shellcheck --severity=error scripts/*.sh boot-image/*.sh boot-image/overlay/auto-deploy.sh boot-image/overlay/usr/local/bin/hw-detect boot-image/overlay/usr/local/bin/wifi-setup
nix flake metadata ./system/nixos --accept-flake-config
nix eval ./system/nixos#nixosConfigurations.hermes.config.networking.hostName --accept-flake-config
nix eval --expr 'let flake = builtins.getFlake (toString ./system/nixos); pkgs = import flake.inputs.nixpkgs { system = "x86_64-linux"; }; in builtins.length (import ./system/nixos/agent-extra-packages.nix { inherit pkgs; })' --accept-flake-config --impure
nix eval ./system/nixos#nixosConfigurations.hermes.config.system.build.toplevel.drvPath --accept-flake-config
# Bundle a local hermes-agent checkout if you want an offline/local-source install.
./scripts/setup-hermes-agent.sh --copy /path/to/hermes-agent
# Review hardening, Phase 2 delivery, Symbiosis assimilation, and Foundry wrapper notes before touching hardware.
less docs/hardening-runbook.md
less docs/phase2-boundaries.md
less docs/symbiosis-assimilation.md
less docs/foundry-dry-run-wrapper.mdThere are two practical paths in this repo.
This is the most understandable path for humans to audit. The detailed walkthrough is docs/install-manual-nixos.md.
- Put the NixOS minimal ISO and this repository on USB media.
- Boot the target machine into the NixOS installer.
- Run the deploy script from the installer environment.
Example commands inside the installer, after the USB is mounted and this repo is available:
cd /path/to/hermes-bootstrap
# Destroys the selected target disk after confirmation.
sudo ./scripts/deploy-hermes.sh --partition /dev/nvme0n1
# Installs NixOS + Hermes Agent onto the mounted target.
sudo ./scripts/deploy-hermes.sh --bootstrap /dev/nvme0n1 /path/to/hermes-bootstrapThis is the current live-hardware path for a mostly plug-and-play install. It keeps the NixOS minimal ISO on a FAT32 USB as nixos-minimal.iso, boots it with GRUB findiso=, mounts the same USB at /run/hermes-usb, and runs scripts/deploy-hermes.sh --auto-live from systemd.run.
# Create a new FAT32 NIXOS-BOOT USB. Destructive: formats the selected disk.
sudo ./scripts/create-nixos-findiso-usb.sh /dev/sdX /path/to/nixos-minimal-24.05-x86_64-linux.iso
# Refresh an existing NIXOS-BOOT USB without repartitioning/reformatting.
sudo ./scripts/update-nixos-usb-autodeploy.sh /dev/sdXLive-deployment guardrails:
- Intel N100/N150-class machines use
module_blacklist=i915on the installer kernel line to avoid early i915 boot hangs. systemd.runentries are separate commands: create/run/hermes-usb, mountLABEL=NIXOS-BOOT, then invokebash deploy-hermes.sh --auto-livewith an explicit PATH.- The GRUB
linuxline must use unwrappedsystemd.run="/run/... args"tokens. Do not surround those tokens with single quotes and do not emit literal\"; both forms can make systemd treat the whole command string as the executable. - Do not set
HERMES_LIVE_TTY=1in GRUB. The live script sets it only after attaching stdin/stdout/stderr to/dev/tty1, so SSID/password prompts can be typed without relying onopenvt. - The USB update/create scripts self-check the generated command line with
systemd-run-generatorwhen available.
boot-image/make-boot-image.sh builds an Alpine-based bootstrap image intended to automate more of the process.
cd boot-image
sudo ./make-boot-image.sh --size 256M --output hermes-boot.img --force-rootfs
cd ..
tests/boot-image-smoke.sh boot-image/hermes-boot.imgThe smoke test is non-destructive: it reads the image with mtools, validates MBR/GPT/FAT contents, and checks /EFI/BOOT/BOOTX64.EFI plus grub.cfg. It does not write a USB device. See docs/boot-image-smoke-tests.md.
This path is more hardware-sensitive. See SPEC.md and docs/deployment-skill.md for known USB, UEFI, Ventoy, and N100-class hardware notes.
The installed service defaults to native first boot (containerMode = false in system/nixos/deployment-options.nix). This avoids a post-install dependency on Docker Hub, Ubuntu apt, NodeSource, Astral uv, and uv python install before hermes-agent.service can start. Enable container mode only after provisioning network/cache for the upstream writable OCI tool layer. Optional image archives placed in data/container-images/ are staged and loaded before hermes-agent.service on explicit container-mode systems. See docs/first-boot-network.md.
Do not paste real API keys into shell commands or commit them to this repo.
The target system expects credentials at:
/var/lib/hermes/secrets/hermes.env
Create/edit it safely after install:
sudo install -d -m 0700 -o hermes -g hermes /var/lib/hermes/secrets
sudo install -m 0600 -o hermes -g hermes /dev/null /var/lib/hermes/secrets/hermes.env
sudoedit /var/lib/hermes/secrets/hermes.env
sudo systemctl restart hermes-agentExample content:
MINIMAX_API_KEY=replace-with-real-keyThe tracked template is data/secrets/hermes.env.template; the real data/secrets/hermes.env is ignored by Git.
systemctl is-active hermes-agent
journalctl -u hermes-agent -n 100 --no-pager
hermes status
hermes tools list
sudo ss -tlnp | grep -E ':22|:8080'
sudo stat -c '%U:%G %a %n' /var/lib/hermes/secrets /var/lib/hermes/secrets/hermes.env
systemctl list-timers 'hermes-*' --no-pager
sudo systemctl start hermes-node-health-watchdog.service
sudo systemctl start hermes-daily-local-brief.serviceExpected network posture:
- SSH is reachable only where intended.
- Hermes gateway is bound to
127.0.0.1unless you deliberately changed it. - Phase 1 harness outputs are readable to the
hermesgroup under/var/lib/hermes/{harness,events,reports}.
For the full hardware checklist, use docs/phase1-live-validation.md.
For the next push-delivery design boundary, use docs/phase2-boundaries.md.
CI currently runs:
- Bash syntax checks via
tests/shell-syntax.sh - ShellCheck with
--severity=error - Phase 1 harness static checks via
tests/harness_phase1_static.sh - Python harness behavior tests via
pytest nix flake metadata ./system/nixos --accept-flake-config- targeted NixOS module/config evaluation for deployment defaults and package references
Local equivalent:
tests/shell-syntax.sh
tests/deployment-readiness.sh
tests/findiso-autodeploy-static.sh
tests/boot-image-static.sh
tests/container-mode-static.sh
tests/container-mode-preflight-unit.sh
tests/harness_phase1_static.sh
python3 -m pytest -q
shellcheck --severity=error scripts/*.sh boot-image/*.sh boot-image/overlay/auto-deploy.sh boot-image/overlay/usr/local/bin/hw-detect boot-image/overlay/usr/local/bin/wifi-setup
nix flake metadata ./system/nixos --accept-flake-config
nix eval ./system/nixos#nixosConfigurations.hermes.config.networking.hostName --accept-flake-config
nix eval --expr 'let flake = builtins.getFlake (toString ./system/nixos); pkgs = import flake.inputs.nixpkgs { system = "x86_64-linux"; }; in builtins.length (import ./system/nixos/agent-extra-packages.nix { inherit pkgs; })' --accept-flake-config --impure
nix eval ./system/nixos#nixosConfigurations.hermes.config.system.build.toplevel.drvPath --accept-flake-configRead docs/hardening-runbook.md before deploying to real hardware. It covers:
- credential handling
- destructive disk checks
- backups before rebuild/reinstall
- NixOS rollback
- live verification checks
- Phase 1 harness live validation
- gateway/SSH exposure assumptions
Near-term cleanup that would make this easier for others to reuse:
- add a VM-based smoke test for the NixOS configuration and Phase 1 timers
- replace historical hardware-specific notes with a cleaner compatibility matrix
- document a fully automated boot-image path separately from the manual installer path
- add screenshots or terminal transcripts of a successful install
- implement Phase 2 push delivery only after Phase 1 live validation remains stable and
docs/phase2-boundaries.mdis resolved
Apache-2.0. See LICENSE.
This project is for self-hosted agent infrastructure. It intentionally gives the agent a powerful execution environment. Treat deployments as privileged systems: isolate them, back them up, review secrets handling, and do not expose services publicly without a separate security review.