Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

mordred-hermes

PyPI CI

Privacy-preserving plugin bundle for the Hermes agent: at-rest encryption for your secrets (.env, config, agent memory), hardware-backed key management (Secure Enclave / TPM 2.0), Tor / VPN network routing, and policy enforcement for local-only LLM operation.

Status: active alpha — current release 0.1.0a7 (PyPI has the release history and dates).

New here? The step-by-step QUICKSTART takes you from zero to a protected install.

⭐ Recommended: set up with an AI coding agent. The first-run setup (configure, network init, keyvault init) is a series of interactive ceremonies with several prompts. Running them inside Claude Code or Codex is the recommended path — the agent walks you through each prompt, explains the options, and picks sensible defaults for your platform.

The plugins

The Mordred entry-point plugins, exposed via the hermes_agent.plugins entry-point group:

Plugin What it does
mordred_privacy_check Skill-metadata policy enforcement and audit logging
mordred_wizard The CLI surface — configure, status, encryption, keyvault, network, audit, …
mordred_llm_guard Strict-mode enforcement of local-only LLM usage
mordred_network Privacy-path management: Tor / VPN / clearnet
mordred_keyvault Hardware-backed key management — Secure Enclave (macOS), TPM 2.0 (Linux), software fallback
mordred_e2e End-to-end encryption for gateway messaging platforms (Slack / Discord) — decrypts inbound, re-encrypts outbound replies

Requirements

  • Python ≥ 3.11 (CI tests 3.11–3.13)
  • hermes-agent ≥ 0.13.0 (its first PyPI release — older versions were never published and are not installable). The floor is exercised in CI on every PR (the hermes-floor job pins it exactly); behavior against the latest release was last verified on 0.19.0, 2026-07-21
  • macOS or Linux. No special hardware required — without a Secure Enclave / TPM, the keyvault degrades to a software-protected key automatically.

Install (users, from PyPI)

Install into the same environment that runs hermes-agent (usually ~/.hermes/hermes-agent/venv) so its plugin loader can discover the entry points. Hermes-managed venvs are often created by uv and ship no pip, so the robust form is uv pip install --python … (no uv on your machine? Install it first — brew install uv on macOS, or see the uv installation guide):

# macOS — includes the Secure Enclave keyvault stack
uv pip install --python ~/.hermes/hermes-agent/venv/bin/python3 "mordred-hermes[macos]==0.1.0a7"

# Linux — cross-platform crypto stack for `encryption` / `keyvault`
uv pip install --python ~/.hermes/hermes-agent/venv/bin/python3 "mordred-hermes[keyvault]==0.1.0a7"

(If your venv does have pip, ~/.hermes/hermes-agent/venv/bin/pip install … works the same.) The pinned form above is the recommended default — it's deterministic and reproducible. Prefer not to look up the current version? The unpinned --upgrade form resolves to the newest pre-release — every release is currently a pre-release, so the all-prereleases fallback applies (the same goes for plain pip, which also accepts an explicit --pre; see Upgrading for the same command used to update later):

# macOS — newest release without a version lookup; use [keyvault] on Linux
uv pip install --python ~/.hermes/hermes-agent/venv/bin/python3 --upgrade "mordred-hermes[macos]"

Optional extras, all opt-in:

Extra Adds Install when you need
keyvault cryptography / argon2-cffi / blake3 encryption / keyvault commands on any platform
macos keyvault + pyobjc Security / SystemConfiguration / Quartz bridges Secure Enclave key protection on macOS
ethereum eth-keys / eth-hash / eth-account / rlp HD-wallet commands (keyvault eth new / derive / address)
tor-control stem Deep Tor liveness probing for strict-mode operators
messaging qrcode Terminal QR rendering for extension pair
extension aiohttp / cryptography The browser-extension WebSocket gateway and pairing (since 0.1.0a2)

Enable the plugins

Running hermes-mordred configure (next section) does this for you — every run back-fills all five mordred_* entries into plugins.enabled in ~/.hermes/config.yaml if they're missing, so there's no manual step. Afterwards ~/.hermes/config.yaml should contain:

plugins:
  enabled:
    - mordred_privacy_check
    - mordred_wizard
    - mordred_llm_guard
    - mordred_network
    - mordred_keyvault

Only edit this by hand if you want the plugins enabled before the first configure run — otherwise there's nothing to do here.

Use it

The CLI is the standalone hermes-mordred console script (installed next to hermes in the same venv):

M=~/.hermes/hermes-agent/venv/bin/hermes-mordred

# First run — set up, in order:
$M configure                       # interactive Mordred setup (policy / LLM / harness)
$M network init                    # optional — pick a privacy route (Tor / VPN / clearnet)
$M keyvault enable-se --unattended # macOS, recommended — SE helper as a no-Touch-ID key (see note below)
$M keyvault init                   # create the hardware-backed key (interactive ceremony)
$M encryption enable env           # encrypt your .env at rest
$M status                          # verify — the `env` row reads [on] enrolled

# Everyday commands:
$M status                          # protection at a glance
$M encryption status               # what's encrypted (env / config / memory)
$M encryption enable <target>      # turn on at-rest encryption for a target
$M network use <tor|vpn|clearnet>  # switch the active privacy route
$M network status                  # show the active route and liveness
$M encryption change-passphrase    # rotate the vault recovery passphrase
$M configure                       # re-run interactive setup anytime
$M configure --with-hermes-setup   # re-run and include the upstream `hermes setup` wizard

Why enable-se --unattended is recommended (macOS). The default attended device key asks for Touch ID on every vault unwrap — a background process (a launchd-started gateway, extension serve) can never answer that prompt and silently starts without the vault-managed secrets. Run it before keyvault init: the attended/unattended choice is fixed at key creation. Already hit this, or want the full trade-off? See Troubleshooting below.

Network troubleshooting. If network communication drops out now and then, check the active privacy path first — network status tells you whether Tor / VPN is actually up:

$M network status
# active_path = tor  state = ready      last_health = ok       ← path is up
# active_path = tor  state = not ready  last_health = FAILED   ← path is down

state is ready / not ready, last_health is ok / FAILED. A trailing [warning] path was flagged as DROPPED line means the liveness worker saw consecutive failures; in strict mode tool calls refuse until you re-establish the path with network use <tor|vpn|clearnet>.

Step-by-step guide with expected output: QUICKSTART. Full command reference and interactive-command walkthroughs: USAGE.

Why not hermes mordred …? Hermes does not yet wire entry-point CLI commands into its argparse (still true on 0.18.2) — hermes-mordred is the same subcommand tree and works today. Once upstream wires it, both forms will coexist without code changes.

Verify discovery

Use Mordred's own command — it lists the Mordred plugins on any supported Hermes version:

~/.hermes/hermes-agent/venv/bin/hermes-mordred plugins list
# → mordred_e2e / mordred_keyvault / mordred_llm_guard / mordred_network / mordred_privacy_check / mordred_wizard

The host hermes plugins list scans plugin directories only (bundled / user / project), so it does not list entry-point plugins like these. To confirm the loader itself discovers them, query it directly:

~/.hermes/hermes-agent/venv/bin/python3 -c "
from hermes_cli.plugins import PluginManager
mgr = PluginManager(); mgr.discover_and_load(force=True)
print(sorted(k for k, p in mgr._plugins.items() if p.manifest.source == 'entrypoint'))
"
# → ['mordred_keyvault', 'mordred_llm_guard', 'mordred_network', 'mordred_privacy_check', 'mordred_wizard']

Browser-extension WebSocket gateway (preview)

mordred_hermes.extension ships the WebSocket server the Mordred browser extension talks to — ws://127.0.0.1:7788/ext, localhost-only, no TLS. It was ported from the full-Hermes gateway layer in #30 and ships on PyPI since 0.1.0a2 (install the extension extra).

How it works

An Origin check admits only chrome-extension:// / moz-extension:// clients, header-less local processes, and the server's own localhost page. On connect the server sends an auth_challenge; the extension authenticates with its paired ext_token (plus a WebAuthn assertion once a credential is registered), the localhost page with a per-process page token. After auth:

Message What it does
pair_init Consume a MORT-… pairing code, establish the shared key (pre-auth)
chat Stream one conversation turn as chat_chunk* + chat_end; replies-in-kind E2E with K_extchat (encrypted in → encrypted out)
encrypt / decrypt Slack-message crypto with the paired key
accounts_request Wallet address + chain id from the keyvault
sign_requestsign_prompt, then sign_approvesign_result Deterministic risk analysis, user approval, then keyvault signing (personal_sign, eth_signTypedData_v4, eth_sendTransaction incl. RPC fill + broadcast)
history_get / history_clear Encrypted-at-rest conversation history

Wire protocol: the extension repo's SPEC.ja.md §6 / src/lib/protocol.ts; server side in src/mordred_hermes/extension/api.py.

Run it (standalone)

Nothing starts the server automatically yet: Hermes exposes no gateway-boot hook a plugin could use, so register(ctx) cannot launch a long-running server (see docs/dev/ROADMAP.md § browser-extension gateway counterpart). Until that lands, start it in the foreground with one command — it needs the extension extra (see the extras table above).

PyPI install — make sure the extension extra is included (swap macos for keyvault on Linux):

uv pip install --python ~/.hermes/hermes-agent/venv/bin/python3 "mordred-hermes[macos,extension]==0.1.0a7"

Then use the $M alias from Use it:

$M extension serve      # ws://127.0.0.1:7788/ext — Ctrl+C to stop
# equivalent module form, same --host/--port flags:
~/.hermes/hermes-agent/venv/bin/python3 -m mordred_hermes.extension

From a dev checkout instead:

uv sync --extra extension     # or: uv pip install -e ".[extension]"

.venv/bin/hermes-mordred extension serve      # ws://127.0.0.1:7788/ext — Ctrl+C to stop
.venv/bin/python -m mordred_hermes.extension

Bind failures (port already in use, bad host, privileged port) exit with a one-line error instead of a traceback — see Troubleshooting if you hit a bound-port error. Both Ctrl+C and SIGTERM (systemd, docker stop) shut down cleanly. One divergence between the two forms: with the extension extra missing, only hermes-mordred extension serve prints the install hint — the module form fails on the package import itself with a plain ImportError.

Pairing, auth (incl. WebAuthn), encrypt/decrypt, history, and the keyvault-backed accounts_request / sign_request flows are fully functional standalone — they only touch ~/.hermes and the keyvault.

Standalone behavior notes

  • Chat runs the real agent. serve probes for the Hermes runtime (gateway / run_agent — shipped by the PyPI hermes-agent package, a base dependency) and wires extension/chat.py:make_gateway_chat_handler automatically; E2E-encrypted messages are decrypted, answered by the real AIAgent, and re-encrypted reply-in-kind. A stub reply appears only when that runtime is missing — the startup log names which handler was wired.
  • Pairing works end-to-end: run hermes-mordred extension pair in a second terminal while extension serve is running — both sides share ~/.hermes/extension/pending.json, so codes are also consumable by a full Hermes gateway hosting the WS server.
  • GET http://127.0.0.1:7788/ serves the bundled localhost web app — a browser page over the same WS API (the startup log prints both URLs; the extension's WS endpoint is /ext). A 503 "web app not built" response appears only if the bundled page is missing — the PyPI wheel ships it.

Install (development)

Canonical dev flow: editable install into the Hermes-managed venv (see setup.md for the full environment build):

# from this repo's root; add ".[macos]" on macOS
uv pip install --python ~/.hermes/hermes-agent/venv/bin/python3 -e .

Refresh a non-editable live venv. If the live venv holds a wheel instead of the editable install above — the PyPI wheel, or a prior repo build — repo edits do not reach the hermes-mordred binary until you rebuild and reinstall it from the repo root:

uv pip install --python ~/.hermes/hermes-agent/venv/bin/python3 --reinstall --no-deps .

--reinstall is required whenever the version string is unchanged (two builds both reporting the same version, say): without it uv treats the requirement as already satisfied and no-ops, leaving the binary on stale code — the symptom is a newly added flag such as configure --with-hermes-setup failing with unrecognized arguments. --no-deps keeps the live editable hermes-agent checkout untouched. Re-run the same command if Hermes rebuilds its venv and drops the wheel.

Local checks run from the repo's own uv-managed venv and mirror the CI test job (HERMES_HOME keeps the tests away from your real ~/.hermes):

uv sync --all-extras                # one-time: builds .venv from uv.lock

uv run ruff check src tests
uv run ruff format --check src tests
uv run mypy
HERMES_HOME=/tmp/hermes-mordred-test-home uv run pytest

The default suite is hermetic — integration tests (-m integration) are excluded and opt back in per suite:

  • Tor: hermetic Docker-based integration-tor job in CI (ci.yml).
  • Keyvault live hardware: MORDRED_KEYVAULT_LIVE=1 pytest -m integration tests/integration/test_keyvault_macos.py
  • Live VPN: MORDRED_LIVE_VPN_TEST=1 MORDRED_MULLVAD_ACCOUNT=… pytest -m integration tests/integration/test_vpn.py (also available as the workflow_dispatch-only integration-vpn.yml job)

Both live suites were last validated on real devices on 2026-05-25.

Releases are cut via the release.yml workflow (PyPI Trusted Publishing); bump versions in lockstep with tools/bump_version.py. Runbook: CI.md.

Troubleshooting

Symptom Cause Fix
Slack integration silently off after a Touch ID prompt at login Attended Secure Enclave device key + a launchd-started background process (gateway, extension serve) racing for Touch ID — a background process can never answer the prompt, so it blocks until the helper's 120 s timeout and starts without the vault-managed secrets (a sealed Slack bot token silently drops the platform, with only a Failed to load plugin 'mordred_keyvault': auth_failed warning in the logs). Run $M keyvault enable-se --unattended before $M keyvault init — the attended/unattended choice is fixed at key creation. Already affected? See USAGE.md §4.3 for re-keying onto an unattended key without destroying your sealed secrets — do not use keyvault reset.
~/.hermes/mordred/audit.log is plaintext NDJSON whose entries say mordred.degraded.audit_encryption_unavailable A Mordred process started somewhere the Keychain / Secure Enclave helper is unreachable (a sandboxed shell, a pre-unlock launchd start, a container). The audit-writer factory probes the audit-log wrapping key once per process; on failure it rotates the encrypted log aside and that process writes plaintext for its whole lifetime — fail-open by design, so auditing never stops (each downgrade leaves one warn marker in the trail). A healthy log starts with {"fmt":"MRAL" — check with head -c 20 ~/.hermes/mordred/audit.log. Recovery is automatic: the next audit write from a healthy context (a normal login shell) rotates the plaintext file aside and starts a fresh encrypted log; long-lived services pick that up on restart. Rotated plaintext siblings remain as dated audit.log.<date> files — remove them with $M audit purge --yes if they must not persist.
extension serve fails to start, citing port 7788 in use Something else already owns 127.0.0.1:7788 — usually a full Hermes gateway already hosting the extension API (nothing to start), occasionally a stale extension serve process from an earlier run. Check what's listening: lsof -i :7788. A full Hermes gateway there means there's nothing to do — the API is already up. A stale extension serve should be stopped, or bind a different port instead with $M extension serve --port 7799 (see the extension command reference).
extension serve logs show the extension reconnecting roughly every ~30s Expected: Chrome kills an idle Manifest V3 service-worker after about 30s, so the extension's background context reconnects — this is Chrome's extension lifecycle, not a server bug. The server already sends an app-level keepalive under that window to help keep the connection alive. Nothing to fix. If chats or signing requests are actually being dropped (not just reconnect log lines), that's a different problem — file an issue.
Network communication drops out now and then The active privacy path (Tor / VPN) is down or flagged unhealthy. See the network-troubleshooting note under Use it above — run $M network status to check state / last_health, then $M network use <tor|vpn|clearnet> to re-establish the path.
Lost the vault recovery passphrase The passphrase is the cold-path key and is never stored anywhere by design. Still on the same device, with its device key intact? Run $M encryption change-passphrase — it tries this device's key first, so you can set a new passphrase without knowing the old one (see USAGE.mdencryption). If the device key is also gone (new machine, keyvault reset, hardware failure), there is no documented recovery path — data sealed only behind that passphrase is permanently lost.

Upgrading

Two different things go by "upgrade": the installed package, and your existing config (Mordred / OpenClaw settings).

Upgrade the installed package

Same command as Install, with the version bumped — a different pinned version is never "already satisfied", so this reinstalls in place without needing --upgrade:

# macOS
uv pip install --python ~/.hermes/hermes-agent/venv/bin/python3 "mordred-hermes[macos]==<new-version>"

# Linux
uv pip install --python ~/.hermes/hermes-agent/venv/bin/python3 "mordred-hermes[keyvault]==<new-version>"

Check PyPI (or the badge at the top of this file) for the latest version. Prefer not to pin?

uv pip install --python ~/.hermes/hermes-agent/venv/bin/python3 --upgrade "mordred-hermes[macos]"

This resolves to the newest pre-release without extra flags — every release is currently a pre-release, so the all-prereleases fallback applies (see Install above; the same goes for plain pip, if your venv has it — the reference setup's venv ships without one).

Migrate config with hermes-mordred upgrade

Different command, different job: $M upgrade is an idempotent migration of an existing Hermes / OpenClaw setup onto Mordred's config.yaml conventions — it back-fills the plugins.mordred_privacy_check section if it's missing, no-ops if it already matches Mordred's defaults, and auto-detects and migrates a legacy ~/.openclaw install. Safe to re-run.

$M upgrade
$M upgrade --non-interactive --policy-conflict keep-existing

See USAGE.mdupgrade for the full flag reference (--reset, --audit-merge, --policy-conflict). Setting up fresh instead of migrating an existing install? Use configure (above), not upgrade.

Uninstall

Reverse the setup in the order below.

⚠️ Decrypt before you remove anything. If you turned on at-rest encryption, uninstalling the package or deleting the key first leaves your .env, config, and memory permanently unreadable. Run step 1 while the CLI and key are still present.

M=~/.hermes/hermes-agent/venv/bin/hermes-mordred

1. Decrypt everything back to plaintext.

$M encryption disable all        # env / config / memory / workspace → plaintext
$M vault disable-config-decrypt  # stop transparent config decrypt, restore plaintext config.yaml
$M encryption status             # verify — every row reads [off]

2. Destroy the key material — optional and irreversible. Skip it if you plan to reinstall and keep the same vault.

$M keyvault reset --yes          # DESTROY the hardware-backed key + remove the keyvault dir

3. Disable the plugins. Remove the five mordred_* entries from the plugins.enabled list in ~/.hermes/config.yaml (undo Enable the plugins).

4. Uninstall the package from the Hermes venv. This also removes the config-decrypt .pth bootstrap from site-packages.

uv pip uninstall --python ~/.hermes/hermes-agent/venv/bin/python3 mordred-hermes
# or, if the venv ships pip:
~/.hermes/hermes-agent/venv/bin/pip uninstall mordred-hermes

5. Remove leftover state — optional, and only after step 1 (the vault lives here):

rm -rf ~/.hermes/mordred/        # audit log, policy, credentials, tor-data, keyvault

Also delete any MORDRED_* entries you added to ~/.hermes/.env. If you ran keyvault enable-se / enable-tpm, the built helper is not removed by the steps above — delete it by hand (default location, unless you set MORDRED_SEKEY_INSTALL_DIR / MORDRED_TPMKEY_INSTALL_DIR):

rm -f ~/.local/bin/mordred-hermes-sekey    # macOS Secure Enclave helper
rm -f ~/.local/bin/mordred-hermes-tpmkey   # Linux TPM 2.0 helper

Repository layout

src/mordred_hermes/    the Mordred entry-point plugins + shared internals
native/                hardware-key helper sources, shipped in the wheel and built
                       on demand by `keyvault enable-se` / `enable-tpm`:
                         sekey-helper/  — Swift, Secure Enclave (macOS)
                         tpmkey-helper/ — Rust, TPM 2.0 (Linux)
skills/mordred-status/ read-only conversational status skill for the agent
packaging/             config-decrypt .pth bootstrap; PyPI name-reservation stub
scripts/               offline verification-digest tool for `keyvault init`
tools/                 dev tooling (version bump, hook-payload drift check)
tests/                 hermetic unit suite + opt-in tests/integration/
docs/                  user and developer documentation (see below)

Documentation

Audience Doc Contents
Users QUICKSTART.md Zero → protected install, step by step
Users USAGE.md Full command reference, interactive walkthroughs, storage model
Developers setup.md Development environment from scratch
Developers SPEC.md, POLICY.md Design spec and policy model
Developers SECRETS_ENV_ENCRYPTION.md, KEYVAULT_BACKENDS.md At-rest encryption and key-backend design
Developers CI.md, UPSTREAM.md CI strategy, release runbook, upstream tracking

More under docs/dev/: PLAN, TODO, ROADMAP, PATHS, MIGRATION, HARNESS_PRIVACY, HOOK_PAYLOADS.

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mordred_hermes-0.1.0a7.tar.gz (960.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mordred_hermes-0.1.0a7-py3-none-any.whl (634.7 kB view details)

Uploaded Python 3

File details

Details for the file mordred_hermes-0.1.0a7.tar.gz.

File metadata

  • Download URL: mordred_hermes-0.1.0a7.tar.gz
  • Upload date:
  • Size: 960.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for mordred_hermes-0.1.0a7.tar.gz
Algorithm Hash digest
SHA256 59344f58b5afd515e3ca37d4f0cfa565b6b6629e16f0f8ce4adbe5811ed54aa8
MD5 0ab4e93c0267243531a0ae9e1364df36
BLAKE2b-256 d26bc492aec2c790ab65aaab5d60fc3573d269bf96771d6ab542bdf4e8d2a877

See more details on using hashes here.

Provenance

The following attestation bundles were made for mordred_hermes-0.1.0a7.tar.gz:

Publisher: release.yml on InternetMaximalism/mordred-hermes

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file mordred_hermes-0.1.0a7-py3-none-any.whl.

File metadata

File hashes

Hashes for mordred_hermes-0.1.0a7-py3-none-any.whl
Algorithm Hash digest
SHA256 8c10584b7af235ccd061abc7c244c41c86b2be9d65dc438a7014a9bd89ed1eb0
MD5 a19dffc31c6d7059d9bc40a2306a33c3
BLAKE2b-256 41738c88a90a4645e769ac36a3271528cc8984b7adc3d0e70ab1e57e16e3227c

See more details on using hashes here.

Provenance

The following attestation bundles were made for mordred_hermes-0.1.0a7-py3-none-any.whl:

Publisher: release.yml on InternetMaximalism/mordred-hermes

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page