A MemWal memory-provider plugin for the Hermes agent.
MemWal gives Hermes portable, owner-controlled long-term memory: memories are embedded, SEAL-encrypted, and stored on Walrus, with semantic vector search in the MemWal relayer. The agent only ever sends and receives plaintext over an Ed25519-signed HTTP connection — all encryption, embedding, Walrus, and Sui work happens server-side in the relayer.
Why a standalone repo? Per Hermes policy (AGENTS.md, "No new in-tree memory providers", May 2026), the in-tree
plugins/memory/set is closed. New backends ship as standalone plugins installed into~/.hermes/plugins/. This repo follows that rule: it implements the sameMemoryProviderABC and registers through the same discovery path, with zero changes to Hermes core.
Unofficial / unaffiliated. Maintained by Unconfirmed Labs, Inc. This is an independent integration. It is not affiliated with, endorsed by, or supported by Mysten Labs (MemWal/Walrus/Sui) or Nous Research (Hermes). "MemWal", "Walrus", "Sui", and "Hermes" are the marks of their respective owners and are used here only to describe interoperability.
| Area | State |
|---|---|
| Python MemWal client (Ed25519-signed HTTP) | ✅ implemented + unit-tested |
MemoryProvider implementation |
✅ implemented + unit-tested |
| Hermes discovery / load / ABC compliance | ✅ verified against real Hermes |
| Dual wire-protocol support (fork and upstream) | ✅ auto-detecting |
Async remember jobs (submit + poll to done) |
✅ implemented |
| Live end-to-end test against the hosted relayer | ✅ verified (relayer.memory.walrus.xyz) |
| Verified inside a real Hermes runtime | ✅ CLI + MemoryManager (uv sync'd Hermes) |
34 unit tests pass (pytest). Verified live against the hosted MemWal relayer
AND inside a real Hermes install (uv sync):
- Discovery + load via Hermes' own plugin loader;
hermes memwal statusCLI. MemoryManager.add_provider→initialize_all→ tool routing, system-prompt block,memwal_search/memwal_rememberdispatch, andqueue_prefetch_all/prefetch_allcontext injection — all round-trip against the live relayer.- Two integration bugs that only a real Hermes run surfaces were found & fixed:
(1) user-plugin
cli.pymust import config from a submodule, not a package attribute (the parent package is a synthetic shell at CLI-scan time); (2)get_tool_schemas()must advertise unconditionally because Hermes builds its tool routing map inadd_provider()beforeinitialize().
Hermes (this plugin) MemWal relayer (TEE, Rust)
───────────────────── ──────────────────────────
remember/recall/analyze ──signed──▶ verify Ed25519 sig
(plaintext only) HTTP resolve owner via Sui (on-chain)
embed → SEAL encrypt → Walrus upload
pgvector search ← Walrus download ← SEAL decrypt
◀────────── plaintext results
The plugin performs no SUI / WAL / SEAL / Walrus operations. The only local
cryptography is standard Ed25519 request signing (RFC 8032), used to authenticate
to the relayer. This is a faithful port of the default
@mysten-incubation/memwal SDK client (MemWal.create() mode).
These steps need SUI and WAL and are not done by this plugin:
- Run a MemWal relayer. Either point at a hosted MemWal server or run your
own from
MystenLabs/MemWal(services/server). The relayer needs Sui RPC access, SEAL key servers, and a funded Walrus account. - Create a
MemWalAccounton Sui and add a delegate key. The MemWal SDK exposescreateAccount(),generateDelegateKey(), andaddDelegateKey()(packages/sdk/src/account.ts). This yields:- a delegate private key (Ed25519, hex) →
MEMWAL_PRIVATE_KEY - a MemWalAccount object id (
0x…) →MEMWAL_ACCOUNT_ID
- a delegate private key (Ed25519, hex) →
- Fund Walrus storage (WAL) so the relayer can upload blobs.
Once you have the relayer URL, the delegate key, and the account id, this plugin needs nothing else.
Memory plugins are discovered by directory scan of $HERMES_HOME/plugins/.
Clone this repo, then symlink (or copy) the memwal/ package into your Hermes home:
git clone https://github.com/unconfirmedlabs/hermes-memwal.git
cd hermes-memwal
./install.sh # symlinks memwal/ -> $HERMES_HOME/plugins/memwal
# or manually:
ln -s "$PWD/memwal" "${HERMES_HOME:-~/.hermes}/plugins/memwal"The plugin declares cryptography in plugin.yaml; hermes memory setup
installs it automatically. (It is also a normal PyPI dependency if you prefer
pip install cryptography.)
Either run the wizard:
hermes memory setup # pick "memwal", then enter the values…or set environment variables / $HERMES_HOME/memwal.json:
| Config key | Env var | Default | Notes |
|---|---|---|---|
server_url |
MEMWAL_SERVER_URL |
http://localhost:8000 |
Relayer base URL |
delegate_key |
MEMWAL_PRIVATE_KEY |
— (required) | Ed25519 delegate key (hex) |
account_id |
MEMWAL_ACCOUNT_ID |
— (required) | MemWalAccount object id |
namespace |
MEMWAL_NAMESPACE |
hermes |
Memory isolation namespace |
capture |
MEMWAL_CAPTURE |
analyze |
analyze (LLM facts) | remember (raw) | off |
auth_scheme |
MEMWAL_AUTH_SCHEME |
auto |
Leave as auto (see below) |
Then activate it (one external memory provider at a time):
# $HERMES_HOME/config.yaml
memory:
provider: memwalCheck it:
hermes memwal status # shows config + relayer health + your public key
hermes memwal restore # rebuild the vector index for the namespace| MemWal | Hermes hook |
|---|---|
recall() |
queue_prefetch() / prefetch() (background context injection) + memwal_search tool |
analyze() |
sync_turn() when capture=analyze (server-side LLM fact extraction) |
remember() |
sync_turn() when capture=remember, and the memwal_remember tool |
restore() |
hermes memwal restore |
Trivial turns (greetings, "thanks") are skipped. Cron/flush contexts never write memory. A circuit breaker pauses calls if the relayer is repeatedly unreachable.
Two MemWal relayer versions exist with different request-signing schemes:
- Legacy / fork (
MystenLabs/MemWalpinned fork, published SDK): 4-field signature{ts}.{method}.{path}.{sha256(body)}. - Upstream
main: 6-field signature adding a UUIDx-nonceand the account id; it rejects the legacy scheme with HTTP 426.
With auth_scheme=auto (default) the client starts with the legacy scheme and
transparently upgrades to the nonce scheme on a 426, caching the choice. So this
works against both your local fork and current upstream without
configuration. (The no-SEAL x-delegate-key path is still accepted by upstream
during its migration window.)
The current upstream relayer processes remember asynchronously: it returns
{job_id, status} and the write lands on Walrus + Sui a few seconds later
(~20s observed). The client handles this with remember_and_wait() (submit +
poll GET /api/remember/{job_id} to a terminal state). The memwal_remember
tool waits up to remember_wait_timeout (default 30s) and reports "stored" only
once the job is done, otherwise "queued". Background turn capture
(sync_turn) is fire-and-forget. Fork relayers that respond synchronously are
also supported (no job_id → returned immediately).
Heads-up (not blocking): the relayer marks x-delegate-key as deprecated
(removal at relayer apiVersion 2.0.0) in favor of x-seal-session. That path
requires client-side SEAL SessionKey signing — i.e. SUI/SEAL crypto — so it's
intentionally not implemented here yet. The plugin works today; when the
relayer drops x-delegate-key, add an x-seal-session builder (needs your Sui
keypair) to client.py.
uv venv --python 3.13 .venv
uv pip install --python .venv/bin/python pytest cryptography
.venv/bin/python -m pytest -q # 34 hermetic tests, no network/SUITests are fully offline: they stub Hermes core modules and never touch SUI,
Walrus, or a live relayer. CI runs them on Python 3.11–3.13
(.github/workflows/ci.yml).
Read this before pointing the plugin at a relayer you don't control.
- Your delegate key is sent to the relayer. In the default (
MemWal.create()) mode the client sends the Ed25519 delegate private key to the relayer in thex-delegate-keyheader, because the relayer performs SEAL decrypt on your behalf during recall/restore. This matches the upstream SDK. It is safe only to the extent you trust the relayer (the hosted MemWal relayer runs in a TEE). Mitigations baked into the design: the delegate key is not your Sui wallet key — it is a scoped key you add to aMemWalAccounton-chain and can revoke at any time (removeDelegateKey). Use a relayer you run or trust, prefer HTTPS, and rotate/revoke keys you expose. - Forward path: the relayer marks
x-delegate-keydeprecated (removal at itsapiVersion2.0.0) in favor ofx-seal-session, where the client signs a SEAL SessionKey locally and never ships the raw key. That requires SUI/SEAL signing and is not implemented here yet (see "Async writes" above). Until then thex-delegate-keypath is accepted by the relayer. - Secrets live in
$HERMES_HOME/memwal.json(written0600) or env vars;memwal.jsonand.envare git-ignored. Never commit your delegate key. - Report security issues via GitHub private vulnerability reporting on this repo.
Honest scope as of v0.1.0:
- Live-tested against the hosted upstream relayer only. The legacy/fork auth scheme is unit-tested but not exercised against a live fork relayer.
restore(CLI) and theanalyze-via-sync_turnturn-capture path are unit-tested; the underlying/api/analyzeendpoint is live-tested, but those two specific wrappers have not been run end-to-end against a live relayer.- No
x-seal-sessionsupport yet (see Security). Works until relayerapiVersion2.0.0. - Single-account / single external memory provider at a time (a Hermes constraint, not specific to this plugin).
MIT — Copyright (c) 2026 Unconfirmed Labs, Inc. See LICENSE.