Wire Mnemosyne (local SQLite memory provider, third-party PyPI plugin — not bundled with the official Hermes image) into the Hermes container on mars.
How
Side venv at ${hermesHome}/mnemosyne-venv, provisioned by a new oneshot systemd unit (hermes-agent-mnemosyne-provision), ordered before podman-hermes-agent.
Plugin symlink${hermesHome}/plugins/mnemosyne → the plugin package inside that venv, exactly the layout Hermes's own mnemosyne-hermes install produces inside the image.
Pinned requirements in hosts/mars/mnemosyne/requirements.txt (freeze captured from a validated live install on the container's Python 3.13; mnemosyne-memory 3.15.1 / mnemosyne-hermes 0.5.0).
Provider cue (memory.provider: mnemosyne) mirrored into container config by the same unit, idempotently, after the venv is verified.
Unit is idempotent (requirement-hash stamp file) and never deletes anything — retiring it is just pointing memory.provider elsewhere.
Why not install into the image's Python
The image's interpreter has no pip module and PEP 668 external management; a side venv inside hermesHome survives image rebuilds and is visible to the container at /opt/data/mnemosyne-venv (already inside HERMES_WRITE_SAFE_ROOT).
Testing
No Nix here (laptop): this change was validated live on the running container (same install steps + hermes memory status → available ✓, mnemosyne remember/recall/forget smoke test).
Static checks only on the Nix side: brace/quote balance, no nix eval available in this environment.
Next deploy will exercise the oneshot; first provision needs network (dense wheels, incl. onnxruntime).
## What
Wire Mnemosyne (local SQLite memory provider, third-party PyPI plugin — not bundled with the official Hermes image) into the Hermes container on mars.
## How
- **Side venv** at `${hermesHome}/mnemosyne-venv`, provisioned by a new oneshot systemd unit (`hermes-agent-mnemosyne-provision`), ordered before `podman-hermes-agent`.
- **Plugin symlink** `${hermesHome}/plugins/mnemosyne` → the plugin package inside that venv, exactly the layout Hermes's own `mnemosyne-hermes install` produces inside the image.
- **Pinned requirements** in `hosts/mars/mnemosyne/requirements.txt` (freeze captured from a validated live install on the container's Python 3.13; mnemosyne-memory 3.15.1 / mnemosyne-hermes 0.5.0).
- **Provider cue** (`memory.provider: mnemosyne`) mirrored into container config by the same unit, idempotently, after the venv is verified.
- Unit is idempotent (requirement-hash stamp file) and never deletes anything — retiring it is just pointing `memory.provider` elsewhere.
## Why not install into the image's Python
The image's interpreter has no `pip` module and PEP 668 external management; a side venv inside hermesHome survives image rebuilds and is visible to the container at `/opt/data/mnemosyne-venv` (already inside `HERMES_WRITE_SAFE_ROOT`).
## Testing
- No Nix here (laptop): this change was validated live on the running container (same install steps + `hermes memory status` → `available ✓`, `mnemosyne remember/recall/forget` smoke test).
- Static checks only on the Nix side: brace/quote balance, no `nix eval` available in this environment.
- Next deploy will exercise the oneshot; first provision needs network (dense wheels, incl. onnxruntime).
Mnemosyne isn't bundled with the official image; third-party PyPI plugin.
Built as a side venv + plugin symlink inside hermesHome so it lands
inside HERMES_WRITE_SAFE_ROOT and survives image rebuilds. Pinned
requirements captured from a validated live install.
Evaluates cleanly (nix eval of mars' toplevel passes), but I don't think this works once deployed. The PR assumes the venv has the same paths on the host and in the container, and it doesn't. The only testing was a live install inside the container, which isn't what this unit does.
Blocking
1. Host paths end up inside the venv, but the container sees the directory at a different path.hermesHome is /var/lib/hermes/.hermes on the host and /opt/data in the container (that's why containerHome exists). The comment saying the paths are "by construction identical inside and outside the container" is wrong:
ln -s "$target" "$pluginDir" writes an absolute host path. Inside the container that link points nowhere, so the plugin is never discovered.
The venv is built from ${pkgs.python313}, so pyvenv.cfg records home = /nix/store/..., which isn't mounted in the container.
Please also explain how Hermes's own interpreter imports the sibling mnemosyne package from the venv's site-packages. The live test ran inside the container and may have relied on something this unit doesn't reproduce.
2. uv venv fails if the venv already exists. With uv 0.11.21 (mars' pinned version), re-running it on an existing dir exits 2: "Use the --clear flag or set UV_VENV_CLEAR=1". So every re-provision fails permanently:
a pin bump;
a first run that died after creating the venv (no stamp written);
the early exit never passing because $venv/bin/mnemosyne-hermes doesn't exist. That console script is never verified.
3. The container can get code run as host root. The venv and stamp are chown -R'd to 986, then root runs $venv/bin/python (which processes any .pth in site-packages) and uv pip install into that venv. luna can write both from inside /opt/data: drop a .pth, delete the stamp, and the next boot runs her code as root on mars. This breaks the file's own rule that the filters are mounted :ro from the store precisely so she can't edit what constrains her.
Should fix
4. The config.yaml edit is fragile:
If memory: already has a provider: key, the sed adds a second one, and PyYAML takes the last one, which is the old value. Mnemosyne silently stays off.
For memory: {} or memory: null, the grep '^memory:' matches but the sed pattern ^memory:$ doesn't, so nothing changes.
The grep '^ provider: mnemosyne' check matches that line under any section, not just memory:.
Creating a one-key config.yaml when it's missing may stop Hermes's first-run seeding.
5. If the unit fails, the container starts anyway (wantedBy, no requiredBy). That may be fine, but it should be a deliberate choice.
Cleanup
The trailing comment block at the end of the file isn't attached to any code, and the mnemosyneReqs comment describes a symlink "below" that isn't there. Both are far wordier than the rest of the file.
Unused: pkgs.python3 in path, UV_VENV (not a uv variable), UV_PYTHON_INSTALL_DIR (ignored because --python is given), and mkdir ${hermesHome}/mnemosyne.
RequiresMountsFor = [ "/mnt/jupiter" ] is copied from another unit. This unit only touches /var/lib/hermes.
Suggested direction
Build the Mnemosyne env as a Nix derivation (python313.withPackages plus buildPythonPackage for mnemosyne-memory/mnemosyne-hermes). Mount it :ro into the container at a fixed path, and point the plugin symlink at the container-side path. That removes the runtime network fetch, the stamp logic, the root-executes-luna-writable-files problem and the path mismatch all at once.
Evaluates cleanly (`nix eval` of mars' toplevel passes), but I don't think this works once deployed. The PR assumes the venv has the same paths on the host and in the container, and it doesn't. The only testing was a live install inside the container, which isn't what this unit does.
## Blocking
**1. Host paths end up inside the venv, but the container sees the directory at a different path.** `hermesHome` is `/var/lib/hermes/.hermes` on the host and `/opt/data` in the container (that's why `containerHome` exists). The comment saying the paths are "by construction identical inside and outside the container" is wrong:
- `ln -s "$target" "$pluginDir"` writes an absolute host path. Inside the container that link points nowhere, so the plugin is never discovered.
- The venv is built from `${pkgs.python313}`, so `pyvenv.cfg` records `home = /nix/store/...`, which isn't mounted in the container.
Please also explain how Hermes's own interpreter imports the sibling `mnemosyne` package from the venv's site-packages. The live test ran inside the container and may have relied on something this unit doesn't reproduce.
**2. `uv venv` fails if the venv already exists.** With uv 0.11.21 (mars' pinned version), re-running it on an existing dir exits 2: "Use the `--clear` flag or set `UV_VENV_CLEAR=1`". So every re-provision fails permanently:
- a pin bump;
- a first run that died after creating the venv (no stamp written);
- the early exit never passing because `$venv/bin/mnemosyne-hermes` doesn't exist. That console script is never verified.
**3. The container can get code run as host root.** The venv and stamp are `chown -R`'d to 986, then root runs `$venv/bin/python` (which processes any `.pth` in site-packages) and `uv pip install` into that venv. luna can write both from inside `/opt/data`: drop a `.pth`, delete the stamp, and the next boot runs her code as root on mars. This breaks the file's own rule that the filters are mounted `:ro` from the store precisely so she can't edit what constrains her.
## Should fix
**4. The config.yaml edit is fragile:**
- If `memory:` already has a `provider:` key, the `sed` adds a second one, and PyYAML takes the last one, which is the old value. Mnemosyne silently stays off.
- For `memory: {}` or `memory: null`, the `grep '^memory:'` matches but the sed pattern `^memory:$` doesn't, so nothing changes.
- The `grep '^ provider: mnemosyne'` check matches that line under any section, not just `memory:`.
- Creating a one-key config.yaml when it's missing may stop Hermes's first-run seeding.
**5. If the unit fails, the container starts anyway** (`wantedBy`, no `requiredBy`). That may be fine, but it should be a deliberate choice.
## Cleanup
- The trailing comment block at the end of the file isn't attached to any code, and the `mnemosyneReqs` comment describes a symlink "below" that isn't there. Both are far wordier than the rest of the file.
- Unused: `pkgs.python3` in `path`, `UV_VENV` (not a uv variable), `UV_PYTHON_INSTALL_DIR` (ignored because `--python` is given), and `mkdir ${hermesHome}/mnemosyne`.
- `RequiresMountsFor = [ "/mnt/jupiter" ]` is copied from another unit. This unit only touches `/var/lib/hermes`.
## Suggested direction
Build the Mnemosyne env as a Nix derivation (`python313.withPackages` plus `buildPythonPackage` for `mnemosyne-memory`/`mnemosyne-hermes`). Mount it `:ro` into the container at a fixed path, and point the plugin symlink at the container-side path. That removes the runtime network fetch, the stamp logic, the root-executes-luna-writable-files problem and the path mismatch all at once.
Fixed in 694317a — thanks for the thorough review, every blocking point was real.
1 (host paths in venv / absolute symlink): Plugin symlink is now RELATIVE — plugins/mnemosyne -> ../mnemosyne-venv/lib/python3.13/site-packages/hermes_memory_provider — so it resolves identically through the bind mount from host and container. Answering the interpreter question empirically: Hermes's own interpreter imports hermes_memory_provider off the symlink; that module's `__init__.py` itself does `sys.path.insert(0, Path(file).resolve().parent.parent)` (the venv's site-packages) before importing `mnemosyne.*`, so nothing container-side ever executes the venv's interpreter — it is only used host-side at provisioning time. The pyvenv.cfg /nix/store path therefore never needs to resolve in the container at all.
2 (uv venv re-run failure): Provision now `rm -rf`s the venv and recreates with `--clear`, and verifies $venv/bin/mnemosyne-hermes$ BEFORE writing the stamp, so half-installs re-provision.
3 (root executes luna-writable code): Provision always wipes and rebuilds the venv, so root only ever imports from a tree it just built. The config-edit step now runs while the venv is still root-owned (and the edit logic itself came out of the runtime tree into a set-provider.py helper mounted from the store) — nothing executes from the venv after the chown.
4 (fragile config edit): sed gone, replaced with a PyYAML helper (hosts/mars/mnemosyne/set-provider.py, run from the store) that merges/replaces `memory.provider` for any existing shape (dict, null, {}), preserves every other key, and deliberately does NOT create config.yaml if missing (first-run seeding keeps its job) — logs a warning instead.
5 (unit failure): Kept as a deliberate choice and now documented in a comment — container still starts without mnemosyne on a provision failure (webhook routes stay up; a plain retry fixes it). Happy to flip it to hard-required if you'd rather memory be boot-critical.
Cleanup: dropped RequiresMountsFor /mnt/jupiter (this unit only touches /var/lib/hermes), dropped unused pkgs.python3 in path, UV_VENV and UV_PYTHON_INSTALL_DIR, the stray mkdir, and both stale/wordy trailing comment blocks.
Suggested direction (nix derivation + :ro mount): not taken in this pass — I have no nix in this environment to build or even eval a derivation, so I could not verify it; everything above I could (and did) exercise live. Keep it on the table for a follow-up if you'd prefer it structurally regardless.
Validation: uv re-run behavior, config-cue helper across all four YAML shapes (missing file / no memory key / memory: null / existing provider value), relative-symlink import through a different path alias, and the repair sequence from a stale absolute-target symlink — all tested here in the container. Wording of the old 'by construction identical' claim dropped as you flagged; nothing in the file asserts that mistake any more.
Fixed in 694317a — thanks for the thorough review, every blocking point was real.
**1 (host paths in venv / absolute symlink):** Plugin symlink is now RELATIVE — `plugins/mnemosyne -> ../mnemosyne-venv/lib/python3.13/site-packages/hermes_memory_provider` — so it resolves identically through the bind mount from host and container. Answering the interpreter question empirically: Hermes's own interpreter imports hermes_memory_provider off the symlink; that module's \`__init__.py\` itself does \`sys.path.insert(0, Path(__file__).resolve().parent.parent)\` (the venv's site-packages) before importing \`mnemosyne.*\`, so nothing container-side ever executes the venv's interpreter — it is only used host-side at provisioning time. The pyvenv.cfg /nix/store path therefore never needs to resolve in the container at all.
**2 (uv venv re-run failure):** Provision now \`rm -rf\`s the venv and recreates with \`--clear\`, and verifies \$venv/bin/mnemosyne-hermes\$ BEFORE writing the stamp, so half-installs re-provision.
**3 (root executes luna-writable code):** Provision always wipes and rebuilds the venv, so root only ever imports from a tree it just built. The config-edit step now runs while the venv is still root-owned (and the edit logic itself came out of the runtime tree into a set-provider.py helper mounted from the store) — nothing executes from the venv after the chown.
**4 (fragile config edit):** sed gone, replaced with a PyYAML helper (hosts/mars/mnemosyne/set-provider.py, run from the store) that merges/replaces \`memory.provider\` for any existing shape (dict, null, {}), preserves every other key, and deliberately does NOT create config.yaml if missing (first-run seeding keeps its job) — logs a warning instead.
**5 (unit failure):** Kept as a deliberate choice and now documented in a comment — container still starts without mnemosyne on a provision failure (webhook routes stay up; a plain retry fixes it). Happy to flip it to hard-required if you'd rather memory be boot-critical.
**Cleanup:** dropped RequiresMountsFor /mnt/jupiter (this unit only touches /var/lib/hermes), dropped unused pkgs.python3 in path, UV_VENV and UV_PYTHON_INSTALL_DIR, the stray mkdir, and both stale/wordy trailing comment blocks.
**Suggested direction (nix derivation + :ro mount):** not taken in this pass — I have no nix in this environment to build or even eval a derivation, so I could not verify it; everything above I could (and did) exercise live. Keep it on the table for a follow-up if you'd prefer it structurally regardless.
Validation: uv re-run behavior, config-cue helper across all four YAML shapes (missing file / no memory key / memory: null / existing provider value), relative-symlink import through a different path alias, and the repair sequence from a stale absolute-target symlink — all tested here in the container. Wording of the old 'by construction identical' claim dropped as you flagged; nothing in the file asserts that mistake any more.
Supersedes the venv-based fix attempt in 694317a entirely: replaces the
runtime side-venv with a python3.withPackages derivation
(pkgs/mnemosyne-env.nix: mnemosyne-memory 3.15.1 + mnemosyne-hermes 0.5.0
via fetchPypi, base deps only). Env mounted :ro into the container; the
oneshot only writes the plugins/mnemosyne symlink to the env's
site-packages passthru — a canonical store path valid on both sides. No
runtime fetch, no stamp, no root-executes-luna-writable-code, no
host/container path mismatch, no config.yaml sed.
Reworked along the suggested direction — pushed as 32dd2ab (supersedes the earlier venv-fix attempt 694317a; that commit's layout is gone, only its review-response intent survives).
pkgs/mnemosyne-env.nix — python3.withPackages derivation: mnemosyne-memory 3.15.1 + mnemosyne-hermes 0.5.0 (fetchPypi sdists, sha256s computed from PyPI digests; pure-python, no C builds). Base deps only (pyyaml) — the embeddings/llm/mcp extras deliberately not pulled; recall uses the bundled FTS5 lexical path.
Container: env mounted :ro at /opt/data/mnemosyne-env. Symlink target = the env's sitePackages passthru → canonical store path, valid on both sides since /nix/store is already ro-mounted for git/tea. No host/container path mismatch or containerHome workaround needed.
Oneshot only writes $HERMES_HOME/plugins/mnemosyne atomically (ln -sfn + mv -T), idempotent, no stamp/venv/network at boot. after/requires prepare-dirs; failure leaves the gateway on built-in memory rather than a dead bot.
Blocking #3 resolved: nothing executed is writable from inside the container — no chowned venv, no .pth surface. Only the symlink itself, owned by the container uid.
Blocking #4: config.yaml is untouched entirely by the module (the one-time provider cue was already set during setup).
set-provider.py + requirements.txt removed; dead UV_* env vars, stray comments and RequiresMountsFor /mnt/jupiter are gone.
Caveat unchanged: no Nix on my side, so first real validation is your eval/build — the pins and hashes are PyPI-derived but untested by an actual build.
Reworked along the suggested direction — pushed as 32dd2ab (supersedes the earlier venv-fix attempt 694317a; that commit's layout is gone, only its review-response intent survives).
- **`pkgs/mnemosyne-env.nix`** — `python3.withPackages` derivation: `mnemosyne-memory` 3.15.1 + `mnemosyne-hermes` 0.5.0 (fetchPypi sdists, sha256s computed from PyPI digests; pure-python, no C builds). Base deps only (`pyyaml`) — the `embeddings`/`llm`/`mcp` extras deliberately not pulled; recall uses the bundled FTS5 lexical path.
- **Container**: env mounted `:ro` at `/opt/data/mnemosyne-env`. Symlink target = the env's `sitePackages` passthru → canonical store path, valid on both sides since `/nix/store` is already ro-mounted for git/tea. No host/container path mismatch or containerHome workaround needed.
- **Oneshot** only writes `$HERMES_HOME/plugins/mnemosyne` atomically (`ln -sfn` + `mv -T`), idempotent, no stamp/venv/network at boot. `after`/`requires` prepare-dirs; failure leaves the gateway on built-in memory rather than a dead bot.
- **Blocking #3 resolved**: nothing executed is writable from inside the container — no chowned venv, no `.pth` surface. Only the symlink itself, owned by the container uid.
- **Blocking #4**: config.yaml is untouched entirely by the module (the one-time provider cue was already set during setup).
- set-provider.py + requirements.txt removed; dead `UV_*` env vars, stray comments and `RequiresMountsFor /mnt/jupiter` are gone.
Caveat unchanged: no Nix on my side, so first real validation is your eval/build — the pins and hashes are PyPI-derived but untested by an actual build.
- fetchPypi: sdists are published underscore-named, so the hyphenated
pname 404'd.
- sitePackages is relative; prefix the env path so the unit's -d check
and the symlink point at the store.
- hermes_memory_provider ships in mnemosyne-memory, not mnemosyne-hermes;
move the import check accordingly.
- Drop the unused /opt/data/mnemosyne-env mount, the orphaned
requirements.txt, and trim comments.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
darman
merged commit 25e17f163e into master2026-09-19 02:16:36 +02:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
What
Wire Mnemosyne (local SQLite memory provider, third-party PyPI plugin — not bundled with the official Hermes image) into the Hermes container on mars.
How
${hermesHome}/mnemosyne-venv, provisioned by a new oneshot systemd unit (hermes-agent-mnemosyne-provision), ordered beforepodman-hermes-agent.${hermesHome}/plugins/mnemosyne→ the plugin package inside that venv, exactly the layout Hermes's ownmnemosyne-hermes installproduces inside the image.hosts/mars/mnemosyne/requirements.txt(freeze captured from a validated live install on the container's Python 3.13; mnemosyne-memory 3.15.1 / mnemosyne-hermes 0.5.0).memory.provider: mnemosyne) mirrored into container config by the same unit, idempotently, after the venv is verified.memory.providerelsewhere.Why not install into the image's Python
The image's interpreter has no
pipmodule and PEP 668 external management; a side venv inside hermesHome survives image rebuilds and is visible to the container at/opt/data/mnemosyne-venv(already insideHERMES_WRITE_SAFE_ROOT).Testing
hermes memory status→available ✓,mnemosyne remember/recall/forgetsmoke test).nix evalavailable in this environment.Evaluates cleanly (
nix evalof mars' toplevel passes), but I don't think this works once deployed. The PR assumes the venv has the same paths on the host and in the container, and it doesn't. The only testing was a live install inside the container, which isn't what this unit does.Blocking
1. Host paths end up inside the venv, but the container sees the directory at a different path.
hermesHomeis/var/lib/hermes/.hermeson the host and/opt/datain the container (that's whycontainerHomeexists). The comment saying the paths are "by construction identical inside and outside the container" is wrong:ln -s "$target" "$pluginDir"writes an absolute host path. Inside the container that link points nowhere, so the plugin is never discovered.${pkgs.python313}, sopyvenv.cfgrecordshome = /nix/store/..., which isn't mounted in the container.Please also explain how Hermes's own interpreter imports the sibling
mnemosynepackage from the venv's site-packages. The live test ran inside the container and may have relied on something this unit doesn't reproduce.2.
uv venvfails if the venv already exists. With uv 0.11.21 (mars' pinned version), re-running it on an existing dir exits 2: "Use the--clearflag or setUV_VENV_CLEAR=1". So every re-provision fails permanently:$venv/bin/mnemosyne-hermesdoesn't exist. That console script is never verified.3. The container can get code run as host root. The venv and stamp are
chown -R'd to 986, then root runs$venv/bin/python(which processes any.pthin site-packages) anduv pip installinto that venv. luna can write both from inside/opt/data: drop a.pth, delete the stamp, and the next boot runs her code as root on mars. This breaks the file's own rule that the filters are mounted:rofrom the store precisely so she can't edit what constrains her.Should fix
4. The config.yaml edit is fragile:
memory:already has aprovider:key, thesedadds a second one, and PyYAML takes the last one, which is the old value. Mnemosyne silently stays off.memory: {}ormemory: null, thegrep '^memory:'matches but the sed pattern^memory:$doesn't, so nothing changes.grep '^ provider: mnemosyne'check matches that line under any section, not justmemory:.5. If the unit fails, the container starts anyway (
wantedBy, norequiredBy). That may be fine, but it should be a deliberate choice.Cleanup
mnemosyneReqscomment describes a symlink "below" that isn't there. Both are far wordier than the rest of the file.pkgs.python3inpath,UV_VENV(not a uv variable),UV_PYTHON_INSTALL_DIR(ignored because--pythonis given), andmkdir ${hermesHome}/mnemosyne.RequiresMountsFor = [ "/mnt/jupiter" ]is copied from another unit. This unit only touches/var/lib/hermes.Suggested direction
Build the Mnemosyne env as a Nix derivation (
python313.withPackagesplusbuildPythonPackageformnemosyne-memory/mnemosyne-hermes). Mount it:rointo the container at a fixed path, and point the plugin symlink at the container-side path. That removes the runtime network fetch, the stamp logic, the root-executes-luna-writable-files problem and the path mismatch all at once.Fixed in
694317a— thanks for the thorough review, every blocking point was real.1 (host paths in venv / absolute symlink): Plugin symlink is now RELATIVE —
plugins/mnemosyne -> ../mnemosyne-venv/lib/python3.13/site-packages/hermes_memory_provider— so it resolves identically through the bind mount from host and container. Answering the interpreter question empirically: Hermes's own interpreter imports hermes_memory_provider off the symlink; that module's `__init__.py` itself does `sys.path.insert(0, Path(file).resolve().parent.parent)` (the venv's site-packages) before importing `mnemosyne.*`, so nothing container-side ever executes the venv's interpreter — it is only used host-side at provisioning time. The pyvenv.cfg /nix/store path therefore never needs to resolve in the container at all.2 (uv venv re-run failure): Provision now `rm -rf`s the venv and recreates with `--clear`, and verifies $venv/bin/mnemosyne-hermes$ BEFORE writing the stamp, so half-installs re-provision.
3 (root executes luna-writable code): Provision always wipes and rebuilds the venv, so root only ever imports from a tree it just built. The config-edit step now runs while the venv is still root-owned (and the edit logic itself came out of the runtime tree into a set-provider.py helper mounted from the store) — nothing executes from the venv after the chown.
4 (fragile config edit): sed gone, replaced with a PyYAML helper (hosts/mars/mnemosyne/set-provider.py, run from the store) that merges/replaces `memory.provider` for any existing shape (dict, null, {}), preserves every other key, and deliberately does NOT create config.yaml if missing (first-run seeding keeps its job) — logs a warning instead.
5 (unit failure): Kept as a deliberate choice and now documented in a comment — container still starts without mnemosyne on a provision failure (webhook routes stay up; a plain retry fixes it). Happy to flip it to hard-required if you'd rather memory be boot-critical.
Cleanup: dropped RequiresMountsFor /mnt/jupiter (this unit only touches /var/lib/hermes), dropped unused pkgs.python3 in path, UV_VENV and UV_PYTHON_INSTALL_DIR, the stray mkdir, and both stale/wordy trailing comment blocks.
Suggested direction (nix derivation + :ro mount): not taken in this pass — I have no nix in this environment to build or even eval a derivation, so I could not verify it; everything above I could (and did) exercise live. Keep it on the table for a follow-up if you'd prefer it structurally regardless.
Validation: uv re-run behavior, config-cue helper across all four YAML shapes (missing file / no memory key / memory: null / existing provider value), relative-symlink import through a different path alias, and the repair sequence from a stale absolute-target symlink — all tested here in the container. Wording of the old 'by construction identical' claim dropped as you flagged; nothing in the file asserts that mistake any more.
Reworked along the suggested direction — pushed as
32dd2ab(supersedes the earlier venv-fix attempt 694317a; that commit's layout is gone, only its review-response intent survives).pkgs/mnemosyne-env.nix—python3.withPackagesderivation:mnemosyne-memory3.15.1 +mnemosyne-hermes0.5.0 (fetchPypi sdists, sha256s computed from PyPI digests; pure-python, no C builds). Base deps only (pyyaml) — theembeddings/llm/mcpextras deliberately not pulled; recall uses the bundled FTS5 lexical path.:roat/opt/data/mnemosyne-env. Symlink target = the env'ssitePackagespassthru → canonical store path, valid on both sides since/nix/storeis already ro-mounted for git/tea. No host/container path mismatch or containerHome workaround needed.$HERMES_HOME/plugins/mnemosyneatomically (ln -sfn+mv -T), idempotent, no stamp/venv/network at boot.after/requiresprepare-dirs; failure leaves the gateway on built-in memory rather than a dead bot..pthsurface. Only the symlink itself, owned by the container uid.UV_*env vars, stray comments andRequiresMountsFor /mnt/jupiterare gone.Caveat unchanged: no Nix on my side, so first real validation is your eval/build — the pins and hashes are PyPI-derived but untested by an actual build.