- Makefile 62.8%
- Shell 25%
- Dockerfile 9.1%
- HTML 3.1%
|
Daniel Snider
eeb495d83c
feat(hermes): run our own fork image; retire boot patches; add hermes-update
Switch the daemon image from upstream nousresearch/hermes-agent:latest to our own ghcr.io/dephekt/hermes-agent:latest, built from the dephekt/hermes-agent fork (public GHCR, amd64). All three deploy-local boot patches (opencode-go Kimi reasoning, MCP refresh-token retention, camofox VNC URL) are now baked into that image, so delete them; the patches/ dir keeps a README documenting the mechanism for future experiments. Add a hermes-update target: sync-secrets then `up -d --pull always --force-recreate --no-deps hermes` — the steady-state path after the fork publishes a new image. The cont-init patch loop is retained and no-ops on the now-empty patches/. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| hermes | feat(hermes): run our own fork image; retire boot patches; add hermes-update | |
| openwebui | feat(openwebui): add upsun-pc and personal-pc agents to the model list | |
| .gitignore | chore(hermes): track deployment-local code patches in git | |
| config.env | feat: initial hermes + openwebui deployment | |
| Makefile | feat(hermes): run our own fork image; retire boot patches; add hermes-update | |
| Makefile.include | feat: initial hermes + openwebui deployment | |
| README.md | chore(hermes): add hermes-recreate make target for config/patch/env changes | |
hermes-deployment
Home-server deployment of Hermes Agent + OpenWebUI. Two Docker Compose stacks
on containers.home.arpa, sharing the proxy external network alongside the
media stack at ~/docker/.
Why a separate repo from ~/docker/
The agent stack has its own lifecycle, secret set, change cadence, and
reviewer pool. Putting it next to media/, immich/, etc. would conflate
two unrelated infrastructures. Cost of separation is near-zero because both
repos share the same Makefile pattern and the same Docker host.
Topology
containers.home.arpa (Docker host)
│
├── ~/docker/ ← media stack (Jellyfin, Keycloak, Newt, etc.)
│ core/newt ← scans Docker labels on this host, registers with Pangolin
│
└── ~/hermes-deployment/ ← this repo (synced here by `make sync-secrets`)
hermes/ ← Hermes Agent daemon + runner containers
openwebui/ ← OpenWebUI, public at ai.dephekt.net
Pangolin edge VPS (~/docker/pangolin/ on a separate VPS)
← Newt registers labels from ALL containers on containers.home.arpa
← TLS termination for ai.dephekt.net → openwebui:8080
Discord cloud ←─ outbound WebSockets ─── hermes container
(two bots: upsun + personal profiles, s6-supervised)
Browser ─HTTPS─→ ai.dephekt.net (Pangolin) ─WireGuard→ openwebui:8080
openwebui ─────→ http://hermes:8642/v1 (upsun profile's API server)
hermes ─────────→ docker.sock → ONE hermes-runner container per profile
(shared by that profile's sessions; isolated sandboxes)
Newt in ~/docker/core/ picks up Pangolin labels from any container on the
host regardless of which compose project owns it. The openwebui service has
labels for ai.dephekt.net; the hermes service has none (API stays internal).
Stacks
| Stack | Service(s) | Exposed |
|---|---|---|
hermes |
hermes daemon |
Internal only (proxy network) |
openwebui |
openwebui |
ai.dephekt.net via Pangolin |
Quick bring-up
# 1. Populate secrets from 1Password (writes per-profile .env files)
make inject-secrets
# 2. Rsync everything to containers.home.arpa
make sync-secrets
# 3. Start stacks
make hermes-up
make openwebui-up
# 4. Install the agent as the 'upsun' profile from the distribution repo
# (in-container; uses /opt/data/.netrc for lab.plat.farm auth)
docker exec -it hermes hermes profile install \
https://lab.plat.farm/dephekt/hermes-upsun.git --name upsun --yes
# 5. One-time OAuth for the profile (Anthropic + Linear + Guru) — paste the
# redirect URL back at the prompt; see hermes/README.md
docker exec -it hermes hermes -p upsun setup
docker exec -it hermes hermes -p upsun mcp add linear --url https://mcp.linear.app/mcp --auth oauth
docker exec -it hermes hermes -p upsun mcp add guru --url https://mcp.api.getguru.com/mcp --auth oauth
# 6. Start the profile gateway (s6-supervised; survives docker restart)
docker exec hermes hermes -p upsun gateway start
# 7. OpenWebUI first login: visit https://ai.dephekt.net → Sign in with Keycloak
See hermes/README.md for the full step-by-step including Discord bot setup,
secret rotation cadence, backup notes, and the Docker socket risk callout.
Shipping agent updates (bundle → distribution → profiles)
SOUL.md, skills, and config defaults live in the hermes-upsun profile distribution (lab.plat.farm/dephekt/hermes-upsun), rendered from the upsun-development-agent bundle. To ship a change:
# In ~/upsun/dephekt/hermes-upsun/ after a bundle git pull:
make render # re-render SOUL.md + skills/ from the bundle
git commit -am "..." && git tag vX.Y.Z && git push --tags
# On any machine running the agent (incl. this deployment):
docker exec hermes hermes profile update upsun --yes
docker exec hermes hermes -p upsun gateway restart
Secrets/env changes still flow through this repo:
make inject-secrets && make sync-secrets && make hermes-recreate
Related repos
- Profile distribution (SOUL, skills, config):
lab.plat.farm/dephekt/hermes-upsun - Bundle / conventions / skills source:
gitlab.com/dephekt/upsun-development-agent - Runner image (terminal backend):
codeberg.org/stackdrift-images/hermes-runner - Pangolin config (edge VPS):
~/docker/in this machine's sibling checkout
Directory layout
.
├── Makefile STACKS, inject-secrets, hermes-render-profiles, sync-secrets
├── Makefile.include Copied verbatim from ~/docker/; provides up/down/restart/logs targets
├── config.env Shared DOMAIN, TZ
├── .gitignore **/secrets/, hermes/config/, openwebui/openwebui-data/
├── hermes/
│ ├── docker-compose.yml
│ ├── config.env Non-secret daemon env (author, PyPI groups, etc.)
│ ├── cont-init-secrets.sh s6 cont-init hook: signing key + GITLAB_TOKEN
│ │ into /run/s6/container_environment
│ ├── secrets/ gitignored; hermes.env + profile-*.env from inject-secrets
│ ├── config/ gitignored; bind-mounted to /opt/data in the container
│ │ (profiles/<name>/.env placed by hermes-render-profiles;
│ │ SOUL.md/skills/config.yaml come from the distribution)
│ └── README.md
└── openwebui/
├── docker-compose.yml
├── config.env
├── secrets/ gitignored; OPENAI_API_KEY.env, OAUTH_CLIENT_SECRET.env
├── openwebui-data/ gitignored; runtime bind mount
└── README.md