Running Hermes Agent Across Multiple Devices with Forgejo and Matrix

Table of Contents ๐
- Changelog
- 1. Current Architecture
- 2. Shared and Local Boundaries
- 3. Prerequisites and Version Discipline
- 4. Build the Shared Repository
- 5. Profile Distributions and Capability Policy
- 6. Install and Update All Five Roles
- 7. One Provider Login per Host
- 8. Guarded Forgejo Synchronization
- 9. Knowledge Search and Handoffs
- 10. Matrix Gateways and E2EE
- 11. Mail, Calendar, and File Mirrors
- 12. Security Model
- 13. Backups and Recovery
- 14. Verification Checklist
- 15. Updates and Maintenance
- Conclusion
Hermes Agent by Nous Research is a self-hosted AI agent that runs shell, file, and development tools on the machine where its profile is active. That makes a multi-device installation a fleet of independent runtimes, not one database replicated between several computers.
The reference setup in this guide runs the same five logical profiles on a desktop, laptops, and an always-on private VM. A private Forgejo repository distributes reviewed role definitions, skills, prompts, knowledge, and handoffs. Every endpoint keeps its own sessions, memories, credentials, Matrix encryption store, service state, and search index.
This article reflects the production layout reviewed on August 31, 2026: Hermes Agent 0.20.6, Fleet distributions 1.8.0, and QMD 2.5.3.
Changelog
| Date | Change |
|---|---|
| 2026-08-31 | Fleet Update: Replaced the retired workstation/server model with the five-role Fleet, documented guarded automatic sync, current gateway placement, and one provider login per host. |
| 2026-07-18 | Always-on Update: Added private-home deployment, measured sizing, controlled host migration, and local mail/PIM mirror guidance. |
| 2026-07-17 | Security Update: Corrected backup scope and clarified full-trust operation, approvals, Matrix recovery, and off-host protection. |
| 2026-07-16 | Fleet Update: Added profile-scoped gateways, machine-aware Matrix identities, session boundaries, and the profile-versus-sandbox distinction. |
| 2026-07-16 | Initial Version: Multi-instance Hermes design with Forgejo, local profile distributions, QMD knowledge, handoffs, Matrix hardening, and backups. |
1. Current Architecture
The Fleet standardizes roles and reviewed capabilities across endpoints. It does not replicate a live Hermes home.
Private Forgejo
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ hermes-shared โ
โ distributions/{five roles}/ โ
โ skills/{profiles,shared}/ โ
โ knowledge/ handoffs/ scripts/ โ
โโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโ
โ guarded Git sync
โโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโ
โ โ โ
Desktop Laptop(s) HermesVM
warm/on-demand warm/on-demand always-on
โ โ โ
main ยท personal ยท business ยท solavolta ยท ops
same roles and policy; independent local runtime stateThe five logical roles are installed on every endpoint:
| Profile | Surface | Responsibility |
|---|---|---|
main | Interactive | Global Chief of Staff. Correlates reviewed cross-domain signals, prepares briefings, and delegates mutations to the owning role. |
personal | Interactive | Private mail, calendars, contacts, tasks, notes, and personal files. |
business | Interactive | Single conversational entry point for business work and customers; delegates specialist tasks where needed. |
solavolta | Internal worker | Isolated SOLAVOLTA files, campaigns, advertising connectors, and customer-specific memory. |
ops | Internal worker | Infrastructure diagnosis and execution without standing mail, cloud-file, Paperless, or customer-data authority. |
HermesVM is the normal always-on endpoint. Its main, personal, and business gateways are the permanent interactive surface. solavolta and ops are workers; a transitional worker gateway may remain active until every scheduled delivery path has been migrated and verified. Desktop and laptop retain all five roles as independently usable fallbacks.
| ๐ A PUBLIC SERVICE HOST IS NOT AUTOMATICALLY A FLEET NODE |
A public mail or web server can run unrelated automation without joining this Fleet. Install the five Fleet profiles there only when the roles genuinely need to act on that host. Keeping an old, stopped Hermes installation on such a server does not make it an active Fleet endpoint. |
Matrix gateways establish outbound connections to the homeserver. A Matrix-only endpoint therefore needs no public inbound port. A private LAN VM administered over VPN is a good default for personal mail, calendars, contacts, and documents. A public VPS remains useful when the agent must directly administer services on that VPS or remain available during a home outage.
2. Shared and Local Boundaries
Share through Forgejo:
- reviewed profile distributions and role-specific
SOUL.mdfiles, - reviewed shared and profile-specific skills,
- device-independent Markdown knowledge,
- compact project handoffs,
- reviewed cron prompts, policy files, deployment scripts, and version metadata.
Keep endpoint-local:
state.db, WAL files, sessions, and memories,.env,auth.json, API keys, OAuth tokens, and password-store data,- Matrix access tokens, device IDs, recovery material, and E2EE stores,
- Maildir, CalDAV/CardDAV mirrors, and synchronization journals,
- logs, caches, QMD indexes, gateway state, and backups.
| โ ๏ธ NEVER REPLICATE LIVE RUNTIME STATE |
SQLite WAL mode supports concurrent access on one machine; it is not a conflict-free multi-master database. Do not synchronize Hermes databases, Matrix crypto stores, Maildir state, or cloud-sync journals through Git, Syncthing, or Nextcloud. Each active instance owns its local state exclusively. |
3. Prerequisites and Version Discipline
You need:
- a private Forgejo repository reachable from every endpoint,
- Hermes Agent installed at the same reviewed version on every Fleet node,
- Git and user-level systemd services on Linux endpoints,
- a Matrix Synapse homeserver for remote chat,
- Node.js
>= 22and QMD when local hybrid knowledge search is enabled.
The audited reference versions are:
| Component | Version |
|---|---|
| Hermes Agent | 0.20.6 (2026.8.27) plus the same reviewed local patch set on active nodes |
| Fleet distributions | main, personal, business, solavolta, and ops at 1.8.0 |
| QMD | 2.5.3 |
| Node.js for QMD | >= 22 |
Pinning only a version label is not enough when you carry local patches. Record the upstream revision and reviewed patch revision in VERSIONS.md, then verify the installed revision on every endpoint before calling a rollout complete.
4. Build the Shared Repository
The active part of hermes-shared has this shape:
hermes-shared/
โโโ README.md
โโโ VERSIONS.md
โโโ SOUL.md
โโโ USER.shared.md
โโโ cron/
โ โโโ prompts/
โโโ distributions/
โ โโโ main/
โ โโโ personal/
โ โโโ business/
โ โโโ solavolta/
โ โโโ ops/
โโโ skills/
โ โโโ profiles/
โ โ โโโ main/
โ โ โโโ personal/
โ โ โโโ business/
โ โ โโโ solavolta/
โ โ โโโ ops/
โ โโโ shared/
โโโ knowledge/
โโโ projects/
โโโ handoffs/
โโโ scripts/
โโโ systemd/user/Old workstation and server distribution directories may remain for history or rollback, but they are retired and must not be installed on new endpoints.
The repository .gitignore and server-side protection must exclude at least:
.env
auth.json
*.key
*.pem
state.db*
memories/
sessions/
logs/
cache/
pending/
platforms/matrix/store/
matrix/store/
*.tmp
*.swpClone the repository on each endpoint:
git clone git@git.example.com:user/hermes-shared.git ~/hermes-shared5. Profile Distributions and Capability Policy
Hermes profile distributions package reviewed agent-owned files behind a distribution.yaml manifest. They deliberately exclude credentials, memories, sessions, and live state.
Each active distribution contains only the stable role payload:
distributions/main/
โโโ distribution.yaml
โโโ SOUL.md
โโโ config.yaml
โโโ mcp.jsonThe role-specific skills live outside the distribution root under skills/profiles/<profile>/ and are loaded through skills.external_dirs. Runtime cron state is also endpoint-owned. This prevents a distribution update from deleting or replacing local skill and scheduler state.
A manifest starts with the role name and version:
name: main
version: 1.8.0
description: Global cross-domain coordinator and Chief of Staff
hermes_requires: ">=0.19.0"Profiles separate Hermes configuration and state, but they are not an operating-system sandbox. With terminal.backend: local and terminal.home_mode: real, commands run with the permissions and home directory of the service user. The audited Fleet deliberately keeps this local-compatible mode while role-specific SSH identities and mounts are migrated.
The declarative Fleet capability policy is therefore important:
- all profiles use
approvals.mode: smartandapprovals.cron_mode: deny, mainandbusinessreceive orchestration capabilities,personalandsolavoltareceive only their required domain integrations,opsuses an explicit infrastructure-tool allowlist and no standing customer, mail, QMD, or Canva connector,- each role loads only its reviewed external skill directories.
Hard isolation still requires separate Unix users, filesystem ACLs, restricted SSH credentials, or a correctly configured sandbox/container.
6. Install and Update All Five Roles
Because the distributions are subdirectories of one repository, install them from their local paths:
cd ~/hermes-shared
git pull --ff-only --prune
for profile in main personal business solavolta ops; do
hermes profile install "$PWD/distributions/$profile" --alias --yes
doneThe production repository wraps installation, updates, patch validation, and capability policy in a reviewed helper:
cd ~/hermes-shared
./scripts/fleet-status.sh
./scripts/apply-fleet-distributions.sh
./scripts/fleet-status.shapply-fleet-distributions.sh updates existing profiles and installs missing ones. It preserves runtime data and does not use a forced reinstall. Review the script before execution; a pulled Git commit is data until you deliberately run it.
| โ ๏ธ CONFIG.YAML IS LOCAL ON EXISTING PROFILES |
|
USER.shared.md is curated source material, not an automatically loaded Hermes memory file. Stable cross-device rules belong in SOUL.md. If a role needs a local user-memory seed, copy it deliberately into that profile once; do not symlink it into a writable Hermes memory path.
7. One Provider Login per Host
Hermes 0.20.6 lets a named profile fall back to the global provider store at ~/.hermes/auth.json when that profile has no credential for the provider. This means five profiles on one host do not need five OpenAI Codex device-code logins.
Authenticate once without --profile:
hermes auth add openai-codex --type oauth --no-browserThen verify resolution from the roles:
for profile in main personal business solavolta ops; do
hermes --profile "$profile" auth status openai-codex
doneThe safe invariant is:
- one authoritative
openai-codexentry in the host-global auth store, - no duplicate
openai-codexentry in profile-localauth.jsonfiles, - profile-local credentials only where a role truly requires a different identity.
A profile-local provider entry takes precedence over the global fallback. Copying the same rotating OAuth record into five profile stores defeats the shared fallback: each copy has a different file lock and can independently rotate or invalidate the same refresh token.
| โ ๏ธ ONE HOST LOGIN IS NOT A CONCURRENT CROSS-HOST TOKEN CLONE |
The global auth lock coordinates processes on one host only. Do not run the same copied rotating OAuth credential concurrently on several machines. Use a separate grant per concurrently active host, or transfer the credential only during a controlled failover after the old owner and its gateways are stopped. Never transport auth through Forgejo. |
8. Guarded Forgejo Synchronization
The Fleet does not run a blind git pull --rebase loop. A user-level timer starts after boot and roughly every ten minutes:
[Timer]
OnBootSec=2min
OnUnitActiveSec=10min
RandomizedDelaySec=2minInstall the reviewed runtime once on every endpoint:
cd ~/hermes-shared
./scripts/install-fleet-sync.shThe installed synchronizer:
- takes a per-host
flock, - refuses to operate on a dirty worktree,
- fetches without interactive credential prompts,
- fast-forwards only when the host is behind,
- pushes an ahead-only branch only after curated-path and secret checks,
- stops on divergence instead of rebasing automatically,
- refreshes the QMD collection and embeddings after a commit change,
- records status under
~/.local/state/hermes-shared/statusand in the user journal.
Inspect it with:
systemctl --user status hermes-shared-sync.timer
journalctl --user -u hermes-shared-sync.service
cat ~/.local/state/hermes-shared/statusRoutine publication is explicit and path-bounded. Write a reviewed file into the private outbox, then publish only that exact path:
~/hermes-shared/scripts/publish.sh \
--message "Update project handoff" \
-- handoffs/current/example.mdThe publisher accepts curated knowledge, project, handoff, shared-skill, and version paths. It clones a clean temporary publisher, runs the secret guard, checks that Forgejo did not advance, commits only the named files, and pushes. If the histories diverge, a human resolves them.
9. Knowledge Search and Handoffs
Hermes memories are small, profile-local working context. Durable knowledge belongs in Markdown under knowledge/, projects/, and handoffs/.
QMD provides local keyword, vector, and reranked search:
npm install -g @tobilu/qmd
qmd collection add ~/hermes-shared --name hermes-shared
qmd update
qmd embed -c hermes-shared --max-docs-per-batch 32 --max-batch-mb 16The generated index stays under ~/.cache/qmd/; it is reproducible and never synchronized. The Fleet timer updates and embeds it after the Git revision changes.
To continue work on another device, maintain a compact topic handoff such as:
handoffs/current/hermes-architecture.mdRecord the goal, verified current state, decisions, changed files, open risks, and the next concrete action. On the new endpoint, read the handoff and re-read live external state before any mutation. A handoff transfers intent and evidence; it does not pretend to resume the other endpointโs live process.
Hermes can also export sessions for deliberate archival or review, but exports must be redacted and inspected before publication. They are not a substitute for a concise handoff.
10. Matrix Gateways and E2EE
Each profile has independent messaging configuration, session namespace, and Matrix crypto state. The production design uses separate profile gateways because process-level isolation makes failures and restarts easy to reason about.
Install and manage a gateway with the profile explicitly selected:
hermes --profile main gateway install
hermes --profile main gateway start
hermes --profile main gateway status
hermes gateway listUse a host- and profile-specific Matrix device ID, for example:
MATRIX_ALLOWED_USERS=@you:matrix.example.org
MATRIX_ALLOWED_ROOMS=!roomid:matrix.example.org
MATRIX_DEVICE_ID=HERMESVM_MAIN
MATRIX_E2EE_MODE=required
MATRIX_APPROVAL_REQUIRE_SENDER=trueNever reuse one Matrix device/E2EE store on two hosts. A controlled gateway migration is:
- stop and disable the old profile gateway,
- verify that no old process is running,
- copy the profileโs Matrix credentials and crypto store through an encrypted channel,
- start the new gateway and test encrypted send/receive,
- leave the old copy disabled as a rollback snapshot.
Provider auth and Matrix identity are separate concerns. A model credential may use the host-global fallback described above; every Matrix adapter still needs its own correctly scoped account/token and exclusive crypto store.
Hermes also supports an opt-in multiplexing gateway through gateway.multiplex_profiles. It is useful for many low-traffic profiles, but it changes lifecycle, listener, route, and failure behavior. The audited Fleet intentionally keeps independent gateway processes. Do not enable multiplexing merely to avoid configuring profile identities.
CLI and Matrix sessions share the selected profileโs home and database, but not a live response channel. Matrix-started work replies to Matrix; a CLI background task does not automatically report there. Use an explicit handoff or delivery workflow when the result must appear in Matrix.
11. Mail, Calendar, and File Mirrors
Hermes can read existing local mirrors without receiving the remote account password itself:
- NeoMutt reads Maildir,
- Notmuch indexes mail,
- Khal, Khard, and Todoman read local vdirs,
mbsyncandvdirsyncerown remote synchronization credentials.
For unattended mail collection, prefer a pull-oriented command:
mbsync --pull --create-near --expunge-near account@example.org
notmuch newKeep actual sending behind a task-specific approval. A read/triage workflow does not require SMTP. Mirror only the cloud subtrees and collections that a role needs, and keep each endpointโs synchronization journal local.
12. Security Model
Choose and document the trust model for each endpoint:
- Restricted automation identity: separate Unix user or sandbox, narrowly scoped paths and SSH identities.
- Full-trust personal assistant: the agent intentionally has the same files and administration capabilities as its owner.
A Hermes profile alone implements neither boundary at OS level. SOUL.md, model instructions, and approvals.mode: smart reduce mistakes but are not containment against prompt injection.
Treat mail, websites, documents, issues, repositories, and tool output as untrusted data. Require concrete authorization for the destination, payload, and purpose before sending mail, uploading files, pushing Git commits, publishing, buying, inviting users, changing cloud resources, or mutating another host.
Audit the effective service identity:
id
sudo -n -l
stat -c '%A %U:%G %n' /var/run/docker.sock 2>/dev/nullPasswordless sudo and access to a rootful Docker socket are both root-equivalent. If that authority is intentional, protect Matrix identities, model/tool supply chains, outbound-action policy, and recoverable backups accordingly.
13. Backups and Recovery
hermes backup creates a machine-level archive of the Hermes root, including named profiles. Run it once per endpoint, not once per profile:
hermes backup --output /secure/staging/hermes-backup.zipThe archive contains secrets and is not encrypted by Hermes. Encrypt it before it leaves the machine and store it off-host with deletion-resistant retention.
A profile export has a different purpose:
hermes profile export main --output ./main-profile.tar.gzIt strips .env and auth.json, making it useful for a controlled profile transfer, but it is not complete disaster recovery. Quick snapshots are local rollback aids, not off-host backups.
Back up separately:
- the Forgejo repository and server configuration,
- encrypted Matrix crypto stores, device IDs, and matching recovery material,
- external project and synchronization configuration,
- credentials stored outside the Hermes root.
Test an actual restore with the original runtime stopped. Never start the same restored database or E2EE store on two hosts.
14. Verification Checklist
Before relying on the Fleet, verify:
- Every endpoint has
main,personal,business,solavolta, andopsat the approved distribution version. - Retired
workstationandserverdistributions are not active. - All active endpoints run the same reviewed Hermes upstream and patch revisions.
- Each profile has its own sessions, memories, SQLite state, Matrix state, and endpoint credentials.
- No runtime database, secret, OAuth record, E2EE store, Maildir, or sync journal is in Forgejo.
- One global model login serves the profiles on a host; duplicate profile-local entries do not shadow it.
- A rotating OAuth credential is not active concurrently on multiple hosts.
- HermesVM owns the intended always-on gateways; fallback endpoints do not create duplicate Matrix responders.
- Matrix allowlists, requester-bound approvals, stable device IDs, and
MATRIX_E2EE_MODE=requiredare set. - The guarded sync timer is active and stops on a dirty tree or divergent history.
- QMD returns current results after a synchronized commit.
- Main only coordinates cross-domain writes; the owning Personal, Business, SOLAVOLTA, or Ops role performs them.
-
approvals.mode: smartandapprovals.cron_mode: denyare explicit. - Root-equivalent permissions and outbound-action paths were reviewed consciously.
- One encrypted off-host backup exists per endpoint and a restore was tested.
Useful read-only checks:
hermes --version
hermes profile list
hermes gateway list
~/hermes-shared/scripts/fleet-status.sh
systemctl --user status hermes-shared-sync.timer
qmd --version15. Updates and Maintenance
Roll out changes in this order:
- update and test one endpoint,
- record the exact upstream, patch, distribution, QMD, and Node versions,
- fast-forward the clean repository clone on the other endpoints,
- run
scripts/apply-fleet-distributions.sh, - reinstall the reviewed sync runtime when
FLEET_SYNC_VERSIONchanged, - run
scripts/fleet-status.sh, - test provider auth, Matrix E2EE, role routing, and a QMD query,
- only then restore normal always-on gateway and cron operation.
Do not treat a successful git pull as deployment: downloaded scripts and distributions take effect only after the reviewed apply/install step.
Conclusion
The durable design is five consistent logical roles on every endpoint, with HermesVM as the always-on home and desktop/laptop endpoints as independent fallbacks. Forgejo synchronizes only reviewed, device-independent material; guarded automation refuses dirty or divergent histories; runtime state and credentials stay local.
The key distinctions are simple: share roles, knowledge, and handoffs; do not share live databases or crypto state. Reuse one model login safely across profiles on a single host, but do not clone rotating OAuth credentials across concurrent hosts. Consolidate user-facing coordination while preserving role ownership and endpoint-level recovery.
๐ ๐ค ๐ฐ๏ธ ๐ฆ ๐พ ๐ฌ





