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 installs the same logical role profiles on a desktop, one or more laptops, and optionally 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.

The original deployment examples were tested with Hermes Agent 0.21.0. Shared-bot routing is a separate capability to verify in your installed revision; do not assume an older release implements every configuration fragment below. Pin your tested revision and review the current multi-profile gateway documentation before migrating older services.

Changelog

DateChange
2026-10-14Matrix and Reliability Update: Added Main chats per device, safe room archival, and safer background synchronization.
2026-10-08Recovery Update: Added independent control and recovery access for hypervisor maintenance.
2026-10-06Gateway Update: Added host multiplexing, Main as the normal chat entry, preservation of existing notification routes, and explicit migration/verification boundaries.
2026-09-05Authentication Update: Documented independent and centrally managed provider-login models without placing credentials in Forgejo.
2026-09-01Hermes 0.21 Update: Added the fleet-relevant Hermes 0.21 features, clarified the boundary between Forgejo synchronization and runtime deployment, generalized the examples, and added bounded deterministic snapshots for faster scheduled briefings.
2026-08-31Fleet Update: Replaced the workstation/server model with role-based profiles, documented guarded automatic synchronization, gateway ownership, and one provider login per host.
2026-07-18Always-on Update: Added private-home deployment, sizing considerations, controlled host migration, and local mail/PIM mirror guidance.
2026-07-17Security Update: Corrected backup scope and clarified full-trust operation, approvals, Matrix recovery, and off-host protection.
2026-07-16Fleet Update: Added profile-scoped gateways, machine-aware Matrix identities, session boundaries, and the profile-versus-sandbox distinction.
2026-07-16Initial Version: Added the multi-instance Hermes design with Forgejo, profile distributions, QMD knowledge, handoffs, Matrix hardening, and backups.

1. Reference Architecture

The fleet standardizes roles and reviewed capabilities across endpoints. It does not replicate a live Hermes home.

                                Private Forgejo
                    ┌─────────────────────────────────┐
                    │ hermes-shared                   │
                    │ distributions/{roles}/         │
                    │ skills/{profiles,shared}/       │
                    │ knowledge/  handoffs/  scripts/ │
                    └───────────────┬─────────────────┘
                                    │ guarded Git sync
                  ┌─────────────────┼─────────────────┐
                  │                 │                 │
             Desktop          Laptop(s)        Private VM
          active/standby   active/standby      always-on
                  │                 │                 │
          main · personal · business · specialist · ops
          same roles and policy; independent local runtime state

The following five roles are one useful example. Rename, remove, or add roles to match your own trust boundaries:

ProfileSurfaceResponsibility
mainOne Matrix chat per device; CLICoordinates that endpoint’s work, correlates reviewed cross-domain signals, prepares briefings, and delegates mutations to the owning role.
personalInternal; direct CLI optionalPrivate mail, calendars, contacts, tasks, notes, and personal files.
businessInternal; direct CLI optionalBusiness work and customer context; receives tasks from Main and delegates customer-specific work where needed.
specialistInternal workerIsolated customer or domain-specific files, connectors, and memory.
opsInternal workerInfrastructure diagnosis and execution without standing mail, cloud-file, Paperless, or customer-data authority.

An always-on private VM can host its gateway and scheduled work. Keep the same role profiles on workstations and laptops; a laptop does not need to remain online. The user-facing design here is one Main chat per device, with specialist work delegated internally. The VM’s Main does not replace the desktop’s or laptop’s Main. An offline device cannot answer in its own chat. Prepared fallback nodes can remain stopped until deliberately activated; never start a copied identity or encryption store on a second host. Existing specialist rooms can become archives after their delivery routes have been replaced and checked, as described in Section 10.

📝 A PUBLIC SERVICE HOST IS NOT AUTOMATICALLY A FLEET NODE

A public mail or web server can run unrelated automation without joining the fleet. Install role profiles there only when they 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.md files,
  • 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, flock (usually provided by util-linux), and user-level systemd services on Linux endpoints,
  • a Matrix Synapse homeserver for remote chat,
  • Node.js >= 22 and QMD when local hybrid knowledge search is enabled.

Original example baseline (not a compatibility guarantee for the later routing section):

ComponentVersion
Hermes Agent0.21.0 (v2026.8.31)
Example profile distributions1.0.0
QMD2.5.3
Node.js for QMD>= 22

Use these as known-good reference versions, not as an instruction to ignore newer releases. Pin the Hermes release you have tested, record it in VERSIONS.md, and deploy the same revision to every endpoint. If you carry local patches, record their revision separately from the upstream version.

Section 10 additionally requires native host multiplexing, Matrix shared-bot profile routing, and role-scoped cron delivery. Its routing example was checked against the linked implementation, not certified on the original 0.21.0 baseline. No minimum release is inferred here: qualify your chosen revision and record its exact upstream and patch IDs separately. Check hermes gateway migrate --help for the migration flags and test routing and delivery before adopting that section.

Hermes 0.21 Features Relevant to a Fleet

Hermes 0.21 introduced several facilities worth considering in a multi-device design:

  • built-in Bot Mode for persistent autonomous operation,
  • hermes peer for direct agent-to-agent collaboration,
  • cron continuity through memory and prior-run context,
  • live steering of running subagents,
  • an MCP command center and a gateway control socket,
  • profile-aware gateway serving and control,
  • write approval for protected agent-instruction files,
  • additional model providers and security hardening.

These are optional building blocks, not a reason to blur endpoint boundaries. Enable Bot Mode, peer links, or multiplexing only after defining their identity, trust, lifecycle, and failure behavior.

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/
│   ├── specialist/
│   └── ops/
├── skills/
│   ├── profiles/
│   │   ├── main/
│   │   ├── personal/
│   │   ├── business/
│   │   ├── specialist/
│   │   └── ops/
│   └── shared/
├── knowledge/
├── projects/
├── handoffs/
├── scripts/
└── systemd/user/

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
*.swp

Clone the repository on each endpoint:

git clone git@git.example.com:user/hermes-shared.git ~/hermes-shared

5. 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.json

The 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.0.0
description: Global cross-domain coordinator and Chief of Staff
hermes_requires: ">=0.21.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. Use this mode only when that level of host access is intentional.

The declarative fleet capability policy is therefore important:

  • all profiles use approvals.mode: smart and approvals.cron_mode: deny,
  • main and business receive orchestration capabilities,
  • personal and specialist receive only their required domain integrations,
  • ops uses an explicit infrastructure-tool allowlist and no standing customer, mail, search, or creative-suite 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 the Role Profiles

Because the distributions are subdirectories of one repository, install them from their local paths on the first endpoint:

cd ~/hermes-shared
git pull --ff-only --prune

for profile in main personal business specialist ops; do
  hermes profile install "$PWD/distributions/$profile" --alias --yes
done

On later updates, pull the reviewed repository and update each installed profile by name:

cd ~/hermes-shared
git pull --ff-only --prune

for profile in main personal business specialist ops; do
  hermes profile update "$profile" --yes
done

hermes profile update reloads distribution-owned files from the source recorded during installation. It preserves memories, sessions, authentication, .env, and the existing config.yaml unless you explicitly pass --force-config.

⚠️ CONFIG.YAML IS LOCAL ON EXISTING PROFILES

hermes profile update <name> preserves an existing local config.yaml by default. Use --force-config only when you intentionally want the distribution’s config to replace endpoint-specific settings. If you use a fleet helper, have it apply only the policy you have explicitly declared and reviewed.

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. Provider Authentication Ownership

Hermes 0.21 lets a named profile fall back to the global provider store at ~/.hermes/auth.json when that profile has no credential for the provider. Several profiles on one host therefore do not need separate provider logins.

Authenticate once without --profile:

hermes auth add openai-codex --type oauth --no-browser

Then verify resolution from the roles:

for profile in main personal business specialist ops; do
  hermes --profile "$profile" auth status openai-codex
done

The per-host invariant is:

  • one authoritative openai-codex entry in the host-global auth store,
  • no duplicate openai-codex entry in profile-local auth.json files,
  • profile-local credentials only where a role truly requires a different identity.

A profile-local provider entry takes precedence over the global fallback. Keep it absent unless a role deliberately uses a different provider identity.

Choose the Cross-Host Model Deliberately

There are two valid operational models. In an independent-host fleet, every active host completes its own provider login. In a centrally managed fleet, one selected source host owns the interactive provider login and a reviewed local synchronizer distributes that chosen account to enrolled private hosts.

The central model removes repeated browser logins, but it is credential distribution and must be designed as such. Keep the following boundary:

  • Forgejo carries the reviewed synchronizer, policy, and status format—never auth.json, tokens, or backups.
  • Transfer the credential only over a dedicated encrypted host-to-host channel with a key restricted to the exact read operation it needs.
  • Store an atomic local backup before replacing a host credential, record only a short one-way account fingerprint in the status file, and restart affected gateways in a controlled way.
  • Stop the adoption when the source is unavailable or the received data is malformed; it must never fall back to a token in Git, a log, or a chat message.

The provider’s account terms, workforce policy, and token-rotation behavior determine whether this model is appropriate. Test the chosen model with a small inference and a gateway restart before enabling it for scheduled work.

⚠️ NEVER PUT PROVIDER CREDENTIALS IN FORGEJO

Whether the fleet uses independent or centrally managed logins, Git remains a transport for reviewed code and knowledge only. Do not commit or publish auth.json, OAuth records, API keys, refresh-token backups, or account identifiers.

8. Guarded Forgejo Synchronization

A guarded fleet should not run a blind git pull --rebase loop. The following minimal pull-only baseline refuses dirty worktrees and divergent history, fast-forwards clean clones, and never executes downloaded files. It is a conservative starting point, not the only possible publication policy; section 8.4 describes an explicitly authorized automatic alternative.

8.1. Create the Synchronizer

Create the local runtime directory:

install -d -m 0700 ~/.local/libexec

Create ~/.local/libexec/hermes-fleet-sync:

cat > ~/.local/libexec/hermes-fleet-sync <<'EOF'
#!/usr/bin/env bash
set -euo pipefail

repo="${HERMES_FLEET_REPO:-$HOME/hermes-shared}"
lock_root="${XDG_RUNTIME_DIR:-/tmp}"

exec 9>"$lock_root/hermes-fleet-sync-${UID}.lock"
flock -n 9 || exit 0

if [[ ! -d "$repo/.git" ]]; then
  echo "Not a Git repository: $repo" >&2
  exit 1
fi

if [[ -n "$(git -C "$repo" status --porcelain)" ]]; then
  echo "Refusing to sync a dirty worktree: $repo" >&2
  exit 1
fi

export GIT_TERMINAL_PROMPT=0
git -C "$repo" fetch --prune origin main

local_rev="$(git -C "$repo" rev-parse HEAD)"
remote_rev="$(git -C "$repo" rev-parse origin/main)"
base_rev="$(git -C "$repo" merge-base HEAD origin/main)"

if [[ "$local_rev" == "$remote_rev" ]]; then
  echo "Already current: $local_rev"
elif [[ "$local_rev" == "$base_rev" ]]; then
  git -C "$repo" merge --ff-only origin/main

  if command -v qmd >/dev/null 2>&1 \
    && qmd collection list 2>/dev/null | grep '^hermes-shared ' >/dev/null;
  then
    qmd update
    qmd embed -c hermes-shared --max-docs-per-batch 32 --max-batch-mb 16
  fi
elif [[ "$remote_rev" == "$base_rev" ]]; then
  echo "Local commits exist; refusing to push automatically." >&2
  exit 1
else
  echo "Local and remote history diverged; manual review required." >&2
  exit 1
fi
EOF

chmod 0700 ~/.local/libexec/hermes-fleet-sync

This script deliberately does not push. Publication is a separate, explicit workflow, so a forgotten local commit cannot silently propagate to every device.

8.2. Create the systemd User Units

Create the user-unit directory:

install -d -m 0700 ~/.config/systemd/user

Create ~/.config/systemd/user/hermes-fleet-sync.service:

[Unit]
Description=Fast-forward the reviewed Hermes fleet repository
After=network-online.target
Wants=network-online.target

[Service]
Type=oneshot
Environment=HERMES_FLEET_REPO=%h/hermes-shared
ExecStart=%h/.local/libexec/hermes-fleet-sync

Create ~/.config/systemd/user/hermes-fleet-sync.timer:

[Unit]
Description=Synchronize the Hermes fleet repository periodically

[Timer]
OnBootSec=2min
OnUnitActiveSec=10min
RandomizedDelaySec=2min

[Install]
WantedBy=timers.target

Enable the timer and run the first synchronization:

systemctl --user daemon-reload
systemctl --user enable --now hermes-fleet-sync.timer
systemctl --user start hermes-fleet-sync.service

For a headless endpoint whose user services must survive logout and start without an interactive login, an administrator can enable lingering for that user:

sudo loginctl enable-linger "$USER"

This timer uses monotonic boot/activity intervals; it does not replay missed calendar runs. Persistent=true is relevant to calendar timers, not these intervals. See the systemd timer manual and loginctl documentation.

The synchronizer now:

  1. takes a per-host flock,
  2. refuses to operate on a dirty worktree,
  3. fetches without interactive credential prompts,
  4. fast-forwards only when the host is behind,
  5. stops when local commits or divergent history require review,
  6. refreshes QMD after a successful fast-forward.
⚠️ SYNCHRONIZATION IS NOT RUNTIME DEPLOYMENT

The timer fast-forwards the reviewed repository but does not run newly downloaded installers, profile updates, or scripts. This prevents a pushed Forgejo commit from becoming immediate code execution on every fleet host. Apply Hermes and profile upgrades explicitly, or design and audit a separately installed, version-pinned updater before granting that authority.

Inspect it with:

systemctl --user status hermes-fleet-sync.timer
journalctl --user -u hermes-fleet-sync.service -n 100

8.3. Publish Reviewed Changes Explicitly

Inspect and commit only the intended file. Do not use a blanket git add . in a repository that may contain generated or sensitive data:

cd ~/hermes-shared
git pull --ff-only --prune
git status --short
git diff -- handoffs/current/example.md
git add -- handoffs/current/example.md
git diff --cached --check
git commit -m "Update project handoff"
git push

Add a secret scanner and protected branches on Forgejo when several people or agents can publish. If the remote advances before the push, stop and review the histories instead of rebasing an unattended automation branch.

8.4. Optional Authorized Automatic Publication

For an owner-operated private fleet, repeated manual commit requests can become an operational burden. An explicitly installed publication policy can instead snapshot quiescent source changes, verify permitted paths, reject secrets and generated runtime files, run appropriate checks, create a commit and synchronize nonconflicting histories. This is an additional reviewed automation design, not behavior supplied by the minimal script above.

Keep private recoverable snapshots before merging, preserve foreign staged work, and retain real conflicting content for resolution. Never use force-push, destructive reset or blanket source inclusion to make synchronization appear successful. Offline endpoints retry after returning online. Record what revision actually reached each host; Git convergence does not prove gateway migration or runtime health.

Such standing authorization covers the permitted private source workflow only. It does not authorize mail delivery, advertising changes, public website publication or unrestricted software upgrades. Ordinary unfinished source edits should not block an unrelated task that can safely use its existing installed tools.

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 16

The generated index stays under ~/.cache/qmd/; it is reproducible and never synchronized. The sync timer can update and embed it after the Git revision changes.

To continue work on another device, maintain a compact topic handoff such as:

handoffs/current/hermes-architecture.md

Record 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

Profiles retain independent messaging configuration, session namespaces and Matrix crypto state. A host multiplex gateway can supervise them in one process; the current Hermes gateway guide describes the native migration and profile lifecycle.

One host gateway, Main as the normal entry point

Desktop / laptop / always-on VM
└── one host gateway process
    ├── main        ← normal user chat; delegates work
    ├── business    → business and customer tasks
    ├── personal    → private tasks
    ├── specialist  → customer/domain context
    └── ops         → infrastructure work

This tree describes roles on one host, not a replicated execution hierarchy across the fleet. Forgejo shares reviewed definitions and handoffs; running processes, local task databases and live conversations do not move automatically to another computer.

The technical default home at ~/.hermes can own the host service and its single Matrix transport while the named main profile handles the conversation. Keep their runtime homes distinct. For a new installation, configure the device’s Main bot under the transport owner; for an existing installation, plan a same-host transport handover that preserves its device identity and current crypto store. Stop the old owner before enabling its replacement. Do not enable the same bot in both homes or distribute its credentials among workers. The shared-bot routing guide describes the native routing mechanism.

Process consolidation and chat consolidation are separate steps. First obtain a healthy host gateway; then replace specialist ingress, notifications, scheduled reports and failure alerts before disabling their old adapters. Main owns coordination and user-facing completion, while the assigned role executes domain work with its existing application credentials and approvals.

Route one room without merging role permissions

Use a general route from the device’s Main room to main. When specialist notifications need a reply path, give each role a persistent thread in that same room and a more specific room-plus-thread route. A reply in such a thread reaches that role; ordinary room messages stay with Main. This is an optional interaction pattern inside one chat, not an instruction to open another specialist room.

For a revision accepting top-level profile_routes, this is an illustrative fragment in the transport owner’s config.yaml. Merge it with the existing configuration and replace the example room and event IDs:

gateway:
  multiplex_profiles: true
platforms:
  matrix:
    enabled: true
profile_routes:
  - name: device-main
    platform: matrix
    chat_id: "!mainroom:matrix.example.org"
    profile: main
  - name: personal-notifications
    platform: matrix
    chat_id: "!mainroom:matrix.example.org"
    thread_id: "$personal-thread-root"
    profile: personal

Add a distinct thread route for every other role that sends reports. In the named Main and worker profiles, disable only their own Matrix adapters; keep the profiles served. Do not park a worker merely to remove its bot: parking also excludes its cron jobs. Keep the thread-root events and IDs as active routing references. The upstream routing implementation selects the more specific match; schema placement must agree with the installed configuration loader.

These routes use the default home’s bot unless bot_profile explicitly names another transport owner. A route whose target is not served is rejected rather than silently reassigned to Main; verify the served set as well as the profile directories.

Update each role’s scheduled delivery and failure target to its exact Main room and thread, and retain the completion route of delegated tasks. A worker’s shared-bot delivery needs a location route owned by that worker; a Main-only route or sender-only rule does not establish that destination. Keep the bot’s authenticated-user and room allowlists: route selection alone does not authorize a sender. Check these conditions against the native shared-bot delivery rules.

Migrate an existing installation

  1. Finish or drain active work. Record the installed revision, active services, scheduled deliveries and current profile configuration. Back up configuration and service definitions privately.
  2. Review existing gateway.standalone: true opt-outs. The native migration leaves them alone; remove only the opt-outs you deliberately intend to fold into the host gateway.
  3. Run the native dry-run from the technical default home. Verify profiles, supervisor scope, token uniqueness, listener ownership and expected changes.
  4. Apply the reviewed migration. It replaces the old profile services with one host service while preserving profile-local tokens and encryption stores in place.
  5. Verify the actual served_profiles, every required platform connection, removed old service ownership, memory limits and restart behavior. A running unit alone is insufficient.

For an existing user-service installation:

# Run in a separate terminal, not as a slash command inside Hermes.
HERMES_HOME="$HOME/.hermes" hermes gateway migrate --multiplex --dry-run
HERMES_HOME="$HOME/.hermes" hermes gateway migrate --multiplex --yes
systemctl --user status hermes-gateway.service
HERMES_HOME="$HOME/.hermes" hermes gateway status
hermes gateway list

System services require administrative service management while preserving the intended unprivileged runtime user. Do not blindly apply user-service commands to a system-service host.

The native lifecycle distinguishes stopping the host from parking one served profile. The former affects the entire gateway; the latter leaves other profiles available. A host dashboard reports that host’s runtime; a fleet-wide live tree requires additional collection and verified cross-host task links.

Archive former specialist rooms after verifying delivery

Before changing memberships, verify each replacement path with an encrypted delivery under the intended Main identity. Check the destination thread, a thread reply, an ordinary message to Main, delegated completion, scheduled reports and failure alerts. Distinguish a transport-only check from a complete model/task execution. A healthy service or successful enqueue does not prove receipt.

Archive the old rooms using the human account’s own client session, with explicit authorization for that limited room set; a bot leaving is not the same as the user’s chat being archived. Stop old delivery first, then leave the former specialist rooms while retaining the Main rooms. Preserve room IDs, local encryption keys and recovery material. Do not call forget, purge history, delete rooms or revoke old devices as part of this migration. Matrix distinguishes leaving a room from forgetting it, which removes the user’s ability to retrieve its history.

Client archive views and server retention policies vary. Verify that historical messages remain accessible with the retained keys; archive is not a substitute for backup. Returning to an invite-only room may require a new invitation.

Before leaving, verify that a remaining room member can issue that invitation or that an authorized server-administration recovery path exists. Retaining a bot account alone does not prove it has invitation rights.

Identity, isolation and completion checks

Use one stable Main Matrix identity/device per endpoint, with allowlists and encryption configured on its transport owner. Replace these example values:

MATRIX_ALLOWED_USERS=@you:matrix.example.org
MATRIX_ALLOWED_ROOMS=!roomid:matrix.example.org
MATRIX_DEVICE_ID=NODE1_MAIN
MATRIX_E2EE_MODE=required
MATRIX_APPROVAL_REQUIRE_SENDER=true

Never run the same device/E2EE store concurrently on two hosts. Multiplexing on one host does not require transferring those stores. After the new gateway exchanges encrypted events, preserve its current stores during recovery; blindly restoring an older crypto snapshot can discard newly advanced state.

Multiplexing provides profile-aware application behavior, not a new OS security boundary. Profiles sharing a UNIX user still share that user’s filesystem authority, and profiles in one process share its failure and memory domain. Retain separate users or containers wherever a real security boundary is required. Connector ACLs, tool policy and requester-bound approvals remain distinct from process supervision.

Check that a model/configuration installer does not reintroduce standalone opt-outs. Update-maintenance scripts must recognize the host service, drain it, restore exactly the previously active services and check profile-prefixed platform statuses. Preserve existing memory and privacy settings when replacing units.

Bound optional MCP background initialization so an unavailable connector cannot hold the whole gateway startup behind a long browser-authentication wait. Check the installed connector’s timeout behavior; an interactive login can need a different callback window. A shorter background timeout does not resolve missing OAuth authorization.

After transport checks, perform a real user-initiated delegation and observe a naturally due scheduled delivery. Only then call those paths verified end to end. Keep the source chat, task identity and completion route intact; profile selection in a dashboard does not by itself guarantee automatic cross-channel completion.

CLI and Matrix retain separate conversations. Read the authoritative task record and current handoff before reporting an existing task’s status; an earlier chat answer is not a fresh execution-state check.

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,
  • mbsync and vdirsyncer own remote synchronization credentials.

For unattended collection, use a dedicated configuration whose channel downloads new messages without propagating deletions or flags. For example, after defining the existing remote and local mirror stores privately in ~/.mbsyncrc-assistant, add this channel fragment:

Channel assistant-readonly
Far :remote:
Near :mirror:
Patterns *
Sync Pull New
Create Near
Remove None
Expunge None
ExpungeSolo None
MaxMessages 0

Then run the dedicated channel:

mbsync --config ~/.mbsyncrc-assistant --pull-new assistant-readonly
notmuch new

Pull direction alone does not disable an inherited expunge policy. The explicit channel settings preserve local messages that have disappeared remotely; this is a collection view, not an exact deletion mirror. Review the mbsync channel and override rules before reusing an interactive two-way configuration.

Keep 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.

11.1. Use Bounded Domain Snapshots for Scheduled Briefings

A scheduled agent that repeatedly discovers commands, searches every source, reads large tool results, and decides what to fetch next spends time and tokens on data collection before it starts the actual analysis. A better pattern for stable recurring work is:

mail ───────┐
calendar ───┤
tasks ──────┤  deterministic, read-only collectors
projects ───┤              │
documents ──┤              ▼
weather ────┘      one bounded JSON snapshot
                            │
                            ▼
                  one cross-domain analysis

Run independent read-only collectors concurrently, but keep the reasoning in one model call when relationships between domains matter. Splitting mail, calendar, and project analysis among isolated agents can hide a conflict that only becomes visible when the evidence is combined.

Every source envelope should include at least:

  • generation time and source freshness,
  • collector duration and success or error state,
  • total records found, included, and omitted,
  • the limit or reason responsible for an omission,
  • a complete flag and a targeted read-only retrieval hint,
  • an explicit warning that source fields are untrusted data.

Enforce a hard serialized-output budget, not merely a record count. A practical starting point is a compact 25–40 KB snapshot, adjusted after measuring real coverage. If the budget removes an item, record that removal in the envelope; the model must never turn “omitted” into “does not exist.” Keep high-value evidence first—for example, recent overdue and upcoming tasks before an old backlog—and trim low-priority tails deterministically.

The snapshot collector should not silently take ownership of synchronization. Let dedicated timers update Maildir, Notmuch, and CalDAV/CardDAV mirrors. The briefing collector reads their current state without changing seen state, flags, folders, drafts, attachments, or remote objects. This keeps the fast path reproducible and makes failures source-specific rather than losing the whole report.

An illustrative deployment reduced the collection phase to roughly five or six seconds while producing a 30–39 KB combined snapshot. Its older end-to-end briefings took several minutes, but those runs also included model inference, graphics, speech synthesis, cloud verification, and message delivery, so the numbers are not a like-for-like model benchmark. Record collection time, inference usage, rendering time, and delivery time separately.

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/null

Passwordless 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.zip

The 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.gz

It 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 the intended role profiles at the approved distribution versions.
  • Obsolete or experimental distributions are not active.
  • All active endpoints run the same reviewed Hermes upstream and patch revisions.
  • Each role retains its own sessions, memories, SQLite state and application credentials; each endpoint has one active Main Matrix transport with its local crypto store.
  • General Main-room traffic and specialist thread replies select the intended profiles; shared-bot routing preserves ingress allowlists and role-owned delivery targets.
  • Scheduled reports, failure alerts and delegated completions reach Main before former specialist rooms are archived; history and encryption keys remain available.
  • 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.
  • The selected cross-host authentication model is documented, tested after gateway restart, and keeps credentials out of Forgejo.
  • The intended gateway owners are documented; fallback units remain disabled until deliberately started, and no Matrix identity has two active owners.
  • Matrix allowlists, requester-bound approvals, stable device IDs, and MATRIX_E2EE_MODE=required are set.
  • The chosen sync policy is documented: pull-only refusal or explicitly authorized snapshot/validation/automatic publication; genuine conflicts and secret violations preserve existing work.
  • QMD returns current results after a synchronized commit.
  • The coordinator only coordinates cross-domain writes; the owning Personal, Business, Specialist, or Ops role performs them.
  • approvals.mode: smart and approvals.cron_mode: deny are 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
git -C ~/hermes-shared status --short --branch
systemctl --user status hermes-fleet-sync.timer
journalctl --user -u hermes-fleet-sync.service -n 20
qmd --version

15. Updates and Maintenance

If the always-on endpoint is a VM, run hypervisor maintenance and its post-reboot checks from a separate physical machine. Keep the runbook, SSH access, and recovery material there. Verify a console path that survives loss of the host network, such as local keyboard/display access or an independent management controller. An SSH session, a terminal multiplexer, or a second VM on the same host cannot provide that recovery path.

Check storage dependencies before stopping guests. A backup repository served by a guest on the host goes offline with it; retain a recovery copy outside that failure domain. Start storage providers and verify their exports before dependent backup services and agent gateways. Treat the configured guest startup delay as a delay, not a storage readiness test. Consult the Proxmox administration guide for boot recovery, guest startup/shutdown ordering, and backup limitations.

Roll out changes in this order:

  1. update and test one endpoint,
  2. record the exact upstream, patch, distribution, QMD, and Node versions,
  3. fast-forward the clean repository clone on the other endpoints,
  4. run hermes profile update <name> --yes for each installed role,
  5. review and reinstall the local synchronizer if its implementation changed,
  6. compare hermes --version, hermes profile list, and the Git revision across endpoints,
  7. test provider auth, Matrix E2EE, role routing, and a QMD query,
  8. 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 a consistent set of logical roles on every endpoint, with an optional always-on home, explicitly selected active gateways, and independent on-demand fallbacks. Forgejo synchronizes only reviewed, device-independent material; the selected publication policy handles source changes explicitly and preserves real conflicts; runtime state and credentials stay local.

The key distinctions are simple: share roles, knowledge, and handoffs; do not share live databases, Matrix crypto state, or credentials through Git. A provider login can be independent per host or centrally managed through a separately reviewed private channel. Consolidate user-facing coordination while preserving role ownership and endpoint-level recovery.

📚HERMES DOCUMENTATION 🤖PROFILES & GATEWAYS 🛰️MULTI-PROFILE GATEWAYS 📦PROFILE DISTRIBUTIONS 💾SESSIONS & EXPORT 💬MATRIX INTEGRATION

Sources