Skip to content

Latest commit

 

History

History
410 lines (339 loc) · 19 KB

quickstart.md

File metadata and controls

410 lines (339 loc) · 19 KB

Hermes Helmet quickstart

Start with one bounded GitHub issue. This guide prepares the worker policy, starts the published Docker image, clones the worker checkout, and shows the first-officer commands that follow the pull request through review and acceptance.

You remain the Captain. Your coding agent is the first officer: it runs on your host with the Captain GitHub identity (captain_github_login in the policy). Hermes is the worker: it runs in Docker with a separate GitHub identity (worker_github_login). FAVA Trails, OpenViking, private company repositories, and external skill packs are optional. The bundled setup-helmet, helmet-issue, and helmet-epic skills are the first-officer workflow.

Prerequisites

  • Docker and Docker Compose v2
  • A GitHub token for the worker identity named in your policy (worker_github_login)
  • Python 3.11+ for the host CLI and tests
  • Model-provider access for the Hermes worker
  • For first-officer orchestration: the Captain GitHub identity and the portable helmet-issue / helmet-epic skills. The documented workflow has been exercised through Codex; Claude Code and Hermes have installation and static validation.

The published worker image is a separate installation from this CLI. Installing helmet does not pull GHCR or start Docker. The current stable digest, from the Hermes Helmet 1.0.0 release, is ghcr.io/machinewisdomai/hermes-helmet/runtime@sha256:04f772b48151813aec165ec81ee7323e316cababa686963fbc4064a40b283c2b, built from 24876c7583f5dd3219e94e27b72a8952ba9752aa. See runtime-image.md. Development builds still pin the official Hermes Agent base nousresearch/hermes-agent:v0.21.6@sha256:55e192fba0cd4fde61142abbff5adeacff40efdb482ccd0ff877924bd274f909 (Hermes Agent 0.21.6). Override only with another immutable tag+digest; do not use latest or main.

Install the CLI

Clone and install the current public source:

git clone https://github.com/MachineWisdomAI/hermes-helmet.git
cd hermes-helmet
python3 -m venv .venv
. .venv/bin/activate
python -m pip install .
helmet --help

The help output lists the host commands. Current source installs helmet and its compatible alias hermes-helmet; the Python package is named hermes-helmet. If you installed the September 23 preview wheel, use hermes-helmet in place of helmet in these examples.

1. Prepare the worker environment and policy

The published Compose stack reads deploy/.env and mounts a secret-free policy file. Start there. The host setup wizard is a separate later path; it does not write these files and does not mount them into the container.

cp deploy/.env.example deploy/.env
chmod 600 deploy/.env
# set HERMES_GITHUB_TOKEN= to the worker PAT
# set HERMES_HELMET_IMAGE to the published digest named in Prerequisites;
# do not copy the older digest from the deploy/.env.example comment

cp config/policy.example.json config/policy.json
# edit company, captain_github_login, worker_github_login, assignee,
# repositories[].slug, repositories[].worktree

Keep the token only in deploy/.env. Make that file owner-only before entering the token. Do not commit it. The policy is secret-free public configuration and must stay readable by container UID 10000.

Compose mounts ${HERMES_HELMET_POLICY_HOST_PATH:-../config/policy.json} read-only at /opt/hermes-helmet/config/policy.mounted.json and runs as UID/GID 10000. That host file must be readable by that user; the copied example is. At startup the entrypoint copies the mount to /opt/data/github-issue-poller/policy.json on the worker volume. Intake and the poller use the copy, not the mount.

The example worktree /opt/data/repos/demo-repo is a path inside the worker volume. It is created in the next container steps, not on the host. Host helmet doctor --home inspects the machine that invoked it and will not see that checkout.

render_crew_contract(policy) prints the crew contract from the policy. It does not install instructions. After you create the assignee profile, copy the reviewed contract into that profile's SOUL.md at $HERMES_HOME/profiles/<assignee>/SOUL.md. ExampleCo uses profile builder and Compose HERMES_HOME=/opt/data.

PYTHONPATH=src python3 - <<'PY'
from pathlib import Path
from hermes_helmet.authority import load_authority, render_crew_contract
policy = load_authority(Path("config/policy.json"))
print(render_crew_contract(policy))
PY

2. Start the published worker image

From the repository root, pull the published digest and start Compose without building. Put that digest in deploy/.env as HERMES_HELMET_IMAGE. Compose interpolates image: from that file. A leftover shell export of HERMES_HELMET_IMAGE overrides .env; docker pull "$HERMES_HELMET_IMAGE" does not read .env. If this shell previously exported the variable, unset it so .env wins, then pull and start through Compose:

unset HERMES_HELMET_IMAGE
docker compose --env-file deploy/.env -f deploy/compose.yaml pull hermes
docker compose --env-file deploy/.env -f deploy/compose.yaml up -d --no-build

The image was built from 24876c7583f5dd3219e94e27b72a8952ba9752aa. Confirm the running container recorded that commit. Publishing a release does not install or restart a worker, so this check is how you learn what your container runs:

docker compose --env-file deploy/.env -f deploy/compose.yaml exec hermes \
  cat /opt/hermes-helmet/SOURCE_COMMIT

The digest identifies the image built from that recorded source SHA. SOURCE_COMMIT inside the container is that SHA. The image is the 1.0.0 stable release image; it is distributed beside, and separate from, the release's wheel and other assets.

The image entrypoint writes the worker token to its owner-only runtime file, copies the mounted policy into /opt/data/github-issue-poller/policy.json, removes the raw token variables, and only then enters the upstream s6 init chain. Supervised dashboard and gateway processes therefore do not inherit the PAT, while /usr/local/bin/gh can read it for one authorized GitHub command.

To wrap deploy/Dockerfile around a local checkout instead, select hermes-helmet:local explicitly. Unsetting the shell variable is not enough when deploy/.env still pins the published digest:

export HERMES_HELMET_SOURCE_COMMIT="$(git rev-parse HEAD)"
export HERMES_HELMET_IMAGE=hermes-helmet:local
docker compose --env-file deploy/.env -f deploy/compose.yaml up -d --build

That development SOURCE_COMMIT should match git rev-parse HEAD. Keep company-specific Compose, policy, and volumes in a private overlay.

3. Clone the allowlisted checkout in the worker volume

The poller installer requires every policy repositories[].worktree to exist as a Git checkout the worker can see. Compose persists that tree on the hermes_state volume under /opt/data. The ExampleCo fixture uses slug example-org/demo-repo and worktree /opt/data/repos/demo-repo. Replace those placeholders with your allowlisted repository before enabling intake.

Do this in the worker container, with the worker GitHub identity. Do not clone into a first-officer checkout or reuse Captain credentials.

Set the wrapper-backed GitHub credential helper globally before cloning so HTTPS clone can authenticate. After the checkout exists, set worker identity and the same helper in that repository's local Git config. Linked worktrees inherit those local values, including Kanban checkouts that use a different home than Compose HOME. Do not run gh auth setup-git; it can record the underlying gh binary and bypass /usr/local/bin/gh.

docker compose --env-file deploy/.env -f deploy/compose.yaml exec hermes \
  mkdir -p /opt/data/repos
docker compose --env-file deploy/.env -f deploy/compose.yaml exec hermes \
  git config --global --replace-all credential.https://github.com.helper \
  '!gh auth git-credential'
docker compose --env-file deploy/.env -f deploy/compose.yaml exec hermes \
  git clone https://github.com/example-org/demo-repo.git \
  /opt/data/repos/demo-repo
docker compose --env-file deploy/.env -f deploy/compose.yaml exec hermes \
  git -C /opt/data/repos/demo-repo config --local user.name "example-agent"
docker compose --env-file deploy/.env -f deploy/compose.yaml exec hermes \
  git -C /opt/data/repos/demo-repo config --local user.email \
  "example-agent@users.noreply.github.com"
docker compose --env-file deploy/.env -f deploy/compose.yaml exec hermes \
  git -C /opt/data/repos/demo-repo config --local --replace-all \
  credential.https://github.com.helper '!gh auth git-credential'

The helper asks /usr/local/bin/gh for GitHub HTTPS credentials. That wrapper reads the owner-only runtime token file. Do not put a token in the clone command, in Git config, in policy, or in source files. The worker Git identity is your worker_github_login (example-agent in the fixture), never example-captain.

Confirm the path the installer will check, after it exists, inside the container that holds it:

docker compose --env-file deploy/.env -f deploy/compose.yaml exec hermes \
  git -C /opt/data/repos/demo-repo rev-parse --is-inside-work-tree

4. Create the worker profile and complete provider login

Kanban work runs as the policy assignee Hermes profile (builder in the ExampleCo fixture). Create that profile in the worker container, set the policy provider and model, then complete that profile's own provider login. Keep Captain and worker identities distinct: do not copy first-officer host credentials, sessions, or profiles into the worker.

These hermes invocations are the in-container Hermes Agent CLI. They are not the host Hermes Helmet command. Current source installs that host CLI as helmet, with compatible alias hermes-helmet; the dated preview wheel exposes only hermes-helmet.

docker compose --env-file deploy/.env -f deploy/compose.yaml exec hermes \
  /opt/hermes/.venv/bin/hermes profile create builder
docker compose --env-file deploy/.env -f deploy/compose.yaml exec hermes \
  /opt/hermes/.venv/bin/hermes -p builder config set model.provider openai
docker compose --env-file deploy/.env -f deploy/compose.yaml exec hermes \
  /opt/hermes/.venv/bin/hermes -p builder config set model.default gpt-4.1
docker compose --env-file deploy/.env -f deploy/compose.yaml exec hermes \
  /opt/hermes/.venv/bin/hermes -p builder config set agent.max_turns 2000
docker compose --env-file deploy/.env -f deploy/compose.yaml exec hermes \
  /opt/hermes/.venv/bin/hermes -p builder auth add openai

auth add prompts locally for the credential. Do not pass --api-key or put provider secrets in commands or files. Match model.provider / auth add to your policy inference_provider and inference_model. Then make one bounded smoke call:

docker compose --env-file deploy/.env -f deploy/compose.yaml exec hermes \
  /opt/hermes/.venv/bin/hermes -p builder chat --oneshot --max-turns 1 \
  -q "Reply with the single word ready."

To run the worker on a Claude Pro or Max subscription instead of an API key, use the token and profile commands in Claude subscription workers, then make the same smoke call.

OpenViking, FAVA Trails, Signal, and company skill packs stay optional and remain disabled in the ExampleCo fixture. Do not enable them on this path.

5. Install the poller cron (once)

docker compose --env-file deploy/.env -f deploy/compose.yaml exec hermes \
  /opt/hermes/.venv/bin/python -m hermes_helmet.install_poller \
  --config /opt/data/github-issue-poller/policy.json \
  --skip-model-config

Use --skip-model-config so install keeps the assignee profile you configured and smoke-tested in the previous step instead of rewriting provider, model, and max turns. The installer does not perform provider login, and this flag does not prove credentials work; the bounded smoke call does. The installer still validates allowlisted worktrees and reconciles exactly one no-agent cron job (cron_deliver defaults to local).

6. Trigger once

Label an allowlisted open issue with the policy dispatch_label (default hermes-kanban-go), then:

docker compose --env-file deploy/.env -f deploy/compose.yaml exec hermes \
  /opt/hermes/.venv/bin/python -m hermes_helmet \
  --config /opt/data/github-issue-poller/policy.json

An eligible issue should now have one linked Kanban root task. Repeating intake for that issue reuses the existing task. Follow the worker's linked pull request when implementation finishes; task completion alone does not accept or merge it.

7. First-officer helmet-issue / helmet-epic

On the Captain host (not the worker container identity), install the bundled skills with helmet install-skills as below, or use the first-officer plugin for Claude Code or Codex. Plugin installation copies instructions only; it does not provision Docker, tokens, or worker access. After setup, reuse policy and worker settings across sessions with a user-owned env file; see Starting a first-officer session.

PYTHONPATH=src python3 -m hermes_helmet.cli install-skills --target codex
PYTHONPATH=src python3 -m hermes_helmet.cli issue \
  https://github.com/example-org/demo-repo/issues/123 \
  --config config/policy.json \
  --host-continuation none --one-pass-only
PYTHONPATH=src python3 -m hermes_helmet.cli status \
  https://github.com/example-org/demo-repo/issues/123 \
  --config config/policy.json
PYTHONPATH=src python3 -m hermes_helmet.cli wait \
  https://github.com/example-org/demo-repo/issues/123 \
  --timeout-seconds 1800
PYTHONPATH=src python3 -m hermes_helmet.cli epic \
  https://github.com/example-org/demo-repo/issues/42 \
  --config config/policy.json \
  --accept-graph --host-continuation none --one-pass-only
PYTHONPATH=src python3 -m hermes_helmet.cli epic-status \
  https://github.com/example-org/demo-repo/issues/42 \
  --config config/policy.json

wait holds one bounded, read-only worker-side process. Exit 0 means run one fresh issue or epic pass. Exit 2 is a clean timeout. Pass the returned cursor to the next wait after every outcome. Exit 1 is a transport or contract failure.

See helmet-issue.md and helmet-epic.md. To have a worker write a DOCX or PPTX instead, follow the runnable walkthrough in Fresh writing worker and Helmet Write.

Host setup wizard (optional)

helmet setup configures the host that runs first-officer tools. It writes ~/.hermes-helmet/policy.json, owner-only secrets, and bundled skills. It does not start Docker, does not write deploy/.env, and does not mount its output into Compose.

cp config/setup.answers.example.json /private/path/setup.answers.json
# replace every ExampleCo identity, repository, checkout, and provider value
helmet setup --answers /private/path/setup.answers.json --json

Setup prompts locally without echo for the worker PAT and provider credential. It writes both only under ~/.hermes-helmet/secrets with owner-only modes. Keep the answers file secret-free. Put OpenViking and FAVA Trails selected/confirmed (or a decline) in that file before setup; both stay disabled unless those values are true when the policy is written. Matt Pocock skills and gstack remain pinned recommendations and are never installed by setup.

The default live model probe supports OpenAI. Another provider must expose an OpenAI-compatible endpoint configured as model_lanes.hermes_executor.base_url; unsupported providers fail before setup creates labels or writes state. Hermes Helmet state and secrets directories must be owned by the current user with mode 0700, credential files must be regular owner-owned 0600 files, and symlinks are rejected. Before prompting for credentials, probing providers, creating labels, or writing state, setup preflights every bundled-skill destination. Any foreign skill conflict stops setup without changing local or GitHub state.

The host wizard requires those Git checkout roots, with matching origins, to exist on this host before doctor. The Docker bootstrap uses container paths and its container checks; host doctor does not see them.

After those host paths exist, diagnose on this host:

helmet doctor --config ~/.hermes-helmet/policy.json --home ~ --live --json

Setup-state doctor (--home) is a pre-dispatch gate for this filesystem. It always verifies the live worker identity, configured work/action allowlist, labels, and provider/model even if --live is omitted. It also checks every existing component of the supplied home, Hermes Helmet state/generated/secret paths, and Codex/Claude/Hermes skill roots. It checks every configured origin fetch and push URL against the repository slug. Missing checkouts fail; doctor does not skip them. Worktree paths that exist only inside the worker container, such as /opt/data/repos/demo-repo, belong to the Docker route.

A shared policy is usable only when those exact worktree paths are valid in each environment that consumes them. Otherwise keep the host wizard policy and the Compose mount as separate files. Setup writes the generated policy mode 0600 for the host user; the container user is 10000 and often cannot read that file. A Compose copy must be readable by UID 10000. Restart Compose after changing the mounted file so the entrypoint recopy runs. First-officer host commands can keep HERMES_HELMET_CONFIG pointed at the generated policy; that does not change the worker mount.

If an unexpected skill apply failure occurs after preflight, the atomic skill transaction rolls back new destinations and setup records an incomplete, resumable skills stage. After removing a conflict, rerunning the same setup reuses the PAT and any labels already created.

See setup-helmet.md.

What this runtime does

  • Creates one Kanban root task per eligible issue (idempotent by issue URL)
  • Ignores closed, unlabeled, malformed, and non-allowlisted issues (the Captain-side helmet prepare-repo named-request enrollment path can admit an exact requested repository for first-officer workflows; the poller itself stays on the static allowlist)
  • After a PR exists, trusted review activity creates one dependent same-PR repair
  • Coalesces activity behind an outstanding repair; later activity can succeed it
  • Never merges from the worker and never force-pushes
  • Verifies the live GitHub login is the configured worker (Captain/unknown/mismatch fail closed)
  • First-officer helmet-issue adopts work, reviews heads, and applies merge gates without a second poller
  • First-officer helmet-epic validates parent/child graphs and invokes helmet-issue with bounded parallelism

Optional services and external skill import are intentionally out of this quickstart. See fava-trails.md when enabling the optional governed company brain after the minimum runtime is healthy. OpenViking working context is optional and documented in openviking.md. When you add a company pack later, see company-skills.md. Optional local or hosted model lanes are documented in model-lanes.md; omit them for the minimum runtime.