hermes profile create copies memory the success line omits

John Burns

The success line was specific. After hermes profile create labclone --no-alias --clone-from labsource, Hermes printed that it had cloned config, .env, SOUL.md, and skills from labsource. It did not mention memory. memories/MEMORY.md and memories/USER.md were on the clone anyway, and their SHA-256 hashes matched the source byte for byte.

That gap is the check worth running before a second always-on agent is pointed at the new profile. The files that moved are the agent’s identity. The sentence that reported the copy is not the inventory.

The profiles used for the measurement

The binary was Hermes Agent v0.22.5+471.g5894e75, commit 5894e750c3db, on Linux x86_64. The install method reported by hermes --version was git. The default gateway was already running and serving the existing profiles. Nothing in the probe used hermes profile use, so the sticky default stayed the default profile. --no-alias was set on every create, and no wrapper appeared under the local bin directory afterward.

labsource was created with --no-skills and a one-line description that marked it as a disposable probe. While its memory files were still absent, hermes -p labsource config path printed the new profile’s config.yaml, and hermes -p labsource config env-path printed that profile’s .env. A loader pointed at the same home read zero characters of memory and zero characters of user profile. The loaded memory text did not hash-equal the default profile’s MEMORY.md. An empty profile on this install does not fall back to the default home’s notes.

The source then received synthetic markers, not a copy of a live agent. MEMORY.md and USER.md held short probe sentences. memories/EXTRA.md held a third sentence, because only the two named files are the built-in stores. sessions/note.txt and cron/jobs.json held markers that a history-preserving copy would have to move. A one-line synthetic assignment was written to .env. A 16-byte state.db replaced whatever the create step had just initialized. notes.txt and logs/probe.log sat outside the documented clone list. .no-bundled-skills was already present from the --no-skills create.

Two clones were then taken from that source, still with --no-alias. labclone used --clone-from labsource. labfull used --clone-from labsource --clone-all. Neither command was pointed at the default profile. That choice was deliberate: a clone of the live default home would have copied real keys and real notes, and the measurement would have been harder to publish.

What each flag actually moved

The config clone’s success line named four things. The file compare named more.

config.yaml, .env, SOUL.md, memories/MEMORY.md, and memories/USER.md were present on labclone with the same hashes as labsource. The probe skill file was present with the same hash. memories/EXTRA.md was absent. sessions/note.txt was absent. cron/jobs.json was absent. logs/probe.log and notes.txt were absent. .no-bundled-skills was absent, even though the source had been created with that opt-out and the file was sitting in the source root at clone time.

The full-copy success line said it was a full copy excluding session history, cron jobs, backups, and snapshots. The markers agree on the first two exclusions. sessions/note.txt and cron/jobs.json were not on labfull. memories/EXTRA.md, logs/probe.log, notes.txt, and .no-bundled-skills were on labfull, each with the source hash. So --clone-all is the mode that keeps an extra file you dropped beside the two memory stores, and it is also the mode that keeps the opt-out marker --clone drops.

The 16-byte state.db did not survive either mode. Both clones had a state.db of 327680 bytes, and the three database hashes were all different. --clone is not supposed to copy that file at all. --clone-all excludes state.db, its wal, and its shm, for any source, in the commit this binary was built from. What landed on the clones was a new database, not the marker. A same filename is not a copied session store. Hash it, or read the size, before you treat a clone as a restored agent.

The source also had a large state.db-wal by the time of the clone. Both clones had a wal of the same size, and neither hash matched the source. Size equality here was a coincidence of a fresh SQLite file, not evidence of a copy. The history exclusion is doing what the source comment says it is for: a clone that inherited cron/jobs.json and the session database would run the source’s jobs a second time as soon as a gateway served it.

The success line, the help text, and the current docs

hermes profile create --help on this binary describes --clone as a copy of config.yaml, .env, SOUL.md, and skills from the active profile. --clone-from implies that copy unless --clone-all is set. The success line repeated the same four names and then, on this host, added one more sentence: Honcho config cloned, with the new profile name as the peer. Memory was still unnamed.

The profiles page on the Hermes site, read the same night, describes the same flag differently. It says --clone copies config.yaml, .env, SOUL.md, skills, and the curated memory files memories/MEMORY.md and memories/USER.md, and that memory is treated as part of the agent’s identity. That page matches the files. The installed help text and the success line do not.

The installed source matches the files as well. In hermes_cli/profiles.py at commit 5894e750c3db, _CLONE_CONFIG_FILES is config.yaml, .env, and SOUL.md. _CLONE_SUBDIR_FILES is memories/MEMORY.md and memories/USER.md. Skills are copied as a tree when the source skills/ directory exists. The success line in hermes_cli/profile_cmd.py prints the config-file list and the word skills. It does not print the subdirectory list. A reader who trusts the sentence will think a clone has a fresh memory. A reader who hashes the two files will see the source notes.

--clone-all has the same kind of gap in the other direction, but the measurement did not catch the command in a lie about history. The history exclude set in that commit is state.db, the wal and shm sidecars, sessions, backups, state-snapshots, checkpoints, and cron. The probe’s session note and cron file stayed behind. A new backups/config/ file did appear on each clone, with a timestamp from the minute of the test, not the source backup names. The gateway wrote a fresh backup. The source backup was not what moved.

A running multiplexer adopts the new directory

Create was not a quiet disk operation on this host. The success text for every profile included the line that it was served now by the running multiplexed gateway, and that adding a bot token would connect it. hermes profile list then showed labsource, labclone, and labfull with Gateway running, in the same table as the default profile, before any gateway start was run for them.

Each new directory contained a gateway.lock by the time the inventory ran. The default gateway pid recorded before the first create was still the live process after all three deletes. The delete command did not agree with that observation in its wording. For each probe it printed that a gateway was running and would be stopped, then removed the profile directory. The default process was still alive, and hermes profile list returned to the same profiles that had been there before the probe. The three lab names were gone.

Treat “Gateway running” on a brand-new profile as a claim about the multiplexer, not as proof that you started a second daemon. The code path that builds the list marks a named profile running when its own gateway is up or when the running multiplexer is serving that name. On this host the second condition was true within the create command. If you needed a profile that no gateway had opened, this install did not give you one while the default gateway was up.

That also explains the fresh databases. The clones did not receive the 16-byte marker because the copy excluded it, and they did not stay empty because the multiplexer opened the new homes and initialized state. An inventory taken after create is an inventory of the copy plus whatever the live gateway wrote next. Compare hashes against the source. Do not assume a new file of a familiar size came from the clone.

What the memory loader will and will not read

The built-in memory tool reads MEMORY.md and USER.md from the profile home. It does not scan the rest of memories/. On labfull, EXTRA.md was present and carried the extra marker. The loader reported the memory marker and the user marker, and it did not report the extra marker. A note you hoped would ride along because it lives next to the real stores will not enter the system prompt unless it is in one of those two files.

The empty-source check is the other half of that boundary. With both files absent, the loader returned no entries and did not substitute the default profile’s file. The profiles page says the same thing: delete the two files if you want a blank memory, and the agent does not fall back to another profile’s notes when they are absent. This host matched that sentence. It does not match an older report that a missing memory file makes the agent read ~/.hermes/memories/ instead. That fallback was not what this loader did.

Path selection and filesystem permission are different questions. hermes -p labclone config path stayed inside ~/.hermes/profiles/labclone. The memory directory resolved to that profile’s memories/. The profiles page also says a profile does not sandbox the agent. On the local terminal backend the process still has the operating-system user’s filesystem access. A profile that cannot see another profile’s memory in its prompt can still read that file if a tool is pointed at the path. The probe did not start a chat and did not ask a model to do that. The path check only shows where Hermes itself looks for config and memory.

Host installs keep the real HOME for tool subprocesses unless terminal.home_mode is profile. The docs separate that from HERMES_HOME. This article did not change terminal.home_mode. A clone on this host would still see the same user-level CLI state the default profile sees, including whatever git, ssh, and gh already use. Isolation of Hermes notes is not isolation of the account.

Honcho is copied outside the profile tree

Both clone commands printed Honcho config cloned, with the new profile name as the peer. No honcho.json appeared inside labclone or labfull. The write went to the default home’s honcho.json, as a new host block. After hermes profile delete -y removed the directories, the keys hermes_labclone and hermes_labfull were still in that file. Profile delete did not unwind the Honcho clone.

Those two keys were removed by hand after the inventory. The plugin function that prints the success line also tries to create the peer in Honcho before the first message. This probe did not confirm that the remote peers were deleted. If you clone on a host where Honcho is configured, plan on a second cleanup: the profile directory and the host block are not the same object, and delete only promised the directory.

The plugin does not inherit the default host’s API key into the new block. The block can still be marked enabled when a key resolves another way. Do not read “Honcho config cloned” as “a second credential was minted.” Read it as “a host block now exists, and a peer create was attempted.” Then open the host list and look for the profile name.

Test the clone before you connect a bot

Run the create from a source you are willing to duplicate. --clone without --clone-from uses the active profile. On a machine where the active profile is the live default, that is the live notes and the live .env, minus the messaging channels the command strips unless you pass --clone-channels. This probe did not pass --clone-channels and did not clone the default profile. The channel strip is why the help text warns that two profiles holding one bot token collide. It is not a reason to skip the memory hash.

After create, run hermes -p labclone config path and require the printed file to live under that profile. Then hash memories/MEMORY.md, memories/USER.md, and .env against the source. If you wanted a blank memory, those two files should be absent, or you should delete them before the first session. The success line will not tell you which of those worlds you are in.

If you used --clone and the source had .no-bundled-skills, check whether the marker survived. On this binary it did not. The skill tree that was already in the source did survive, including the essential Hermes skill that --no-skills still left on disk. A later hermes update syncs bundled skills to profiles that lack the marker. The clone can opt back into that sync while the source stays opted out. --clone-all kept the marker here. If the opt-out is the point, check the file, and prefer the flag that preserved it, or write the marker again.

If a gateway is already up, expect the new name to show as running, and expect gateway.lock plus a fresh state.db. That is not the session history. Confirm cron/jobs.json is absent unless you intentionally copied jobs by some other path. The exclude exists so two gateways do not fire the same job ids.

Then delete the probe, list profiles, and confirm the default gateway process you recorded is still the one running. If Honcho printed a clone line, list host keys and remove the probe key. A clean profile list is not a clean Honcho config.

What the probe does not answer

No chat was started. No model was asked to recall the marker, and no model was asked to open another profile’s files. The loader result is the on-disk read Hermes uses for the built-in stores, not a transcript of a session. Permission to read the rest of the disk was not re-tested; the project documentation already says the local backend does not sandbox a profile, and this host was not used to prove a bypass.

--clone-channels was not used. OAuth refresh-token stripping was not exercised, because the source .env was a synthetic one-line file and the clones were not given a separate auth.json login. The website’s warning about single-use refresh tokens still stands: do not treat a copied OAuth file as a second login. This run did not produce a second login to inspect.

The remote Honcho peers, if the plugin created them, were not deleted through the API. The local host blocks were. A later session on this machine should not see labclone or labfull in hermes profile list. It might still see those names in an external Honcho workspace until someone removes the peers there.

The website and the installed help text can move independently of each other. The file compare is the check that does not depend on which sentence was updated last. On this binary, the night of 29 October 2026, --clone copied the two memory files and did not say so.

Sources