Skip to main content

hermes-pubky

Communityv0.2.3

Setup helper for managed Pubky agents. The launcher syncs agent state, including conversation database snapshots and portable configuration, to your remote Pubky homeserver.

Open in Hermes Desktop
hermes plugins install hermes-pubky

README

From the reviewed commit 3603b82; it updates when the author re-pins.

hermes-pubky

Run a Hermes agent whose saved state lives on your own Pubky homeserver. The laptop becomes a working copy, but the agent belongs to you.

Why

Hermes keeps everything it knows in one directory on one machine: SOUL.md, your memories, its learned skills, your conversations. New laptop, fresh container, throwaway VM, and the agent starts from nothing. The usual alternative is a memory SaaS, which makes your agent portable by moving it into someone else's account.

This is a third option. The homeserver holds the authoritative copy of the agent, a local Hermes reconstructs a working copy, runs it, and saves changes back. What travels:

  • instructions (SOUL.md), user and agent memories
  • learned skills and their assets
  • allowlisted settings
  • the conversation database, including tool calls, compaction and rewind history
  • a designated workspace: notes, references, outputs

What stays on the machine: API keys, OAuth sessions, the Pubky grant itself, and anything else machine-specific.

Install

uv pip install hermes-pubky==0.2.3
hermes-pubky agent init default
hermes-pubky run default

Run these commands in your activated Hermes environment. Needs Python 3.11–3.13, Hermes 0.21.3 with conversation schema 30, and a Pubky homeserver on v0.11 or later. This release was verified against Hermes commit a51143fbbe6ddbc0c7f403d0579c4d75504c6793; use that checkout as described under Development below. Hermes is distributed from source, not through PyPI. agent init asks Pubky Ring to authorize one scoped capability, then publishes the agent's first checkpoint.

On a second computer:

uv pip install hermes-pubky==0.2.3
hermes-pubky agent attach pubky://<owner>/priv/hermes.pubky.app/v2/agents/default/head.json
hermes-pubky run default

Nothing is copied between machines. attach downloads the instructions, memories, skills, settings and conversation database; workspace documents are fetched when the agent asks for them. Every run saves a checkpoint when it ends, and the next run on any machine starts from the newest one.

Hermes plugin packaging

plugin/ is the directory-plugin wrapper for catalog distribution. It declares the pinned hermes-pubky package dependency, which includes the native Pubky extension, and adds /pubky setup help when enabled. Managed agents still start through hermes-pubky run; enabling the companion does not sync an ordinary Hermes profile or select its memory provider.

Once this directory is published in the repository, install it with:

hermes plugins install MCarlomagno/hermes-pubky/plugin
hermes plugins enable hermes-pubky

Current Hermes installers install the pinned dependency from plugin/pyproject.toml into the Hermes environment. If dependency installation is disabled or fails, install it explicitly with the pip command above. The installer also displays setup instructions.

The catalog entry is a setup helper. Running a managed agent syncs its instructions, memories, skills, conversation database snapshots, portable configuration, and workspace to your remote Pubky homeserver. The homeserver operator can read that data; it is not end-to-end encrypted.

For a catalog submission, set subdir: plugin and requires_hermes: ">=0.21.3". The setup helper remains available on newer Hermes versions. The managed launcher still requires the verified Hermes 0.21.3 / schema-30 runtime and rejects unverified runtimes before touching agent state. Keep the wrapper's manifest version, project version, and exact package dependency aligned with the published release. The wrapper never downloads or updates code at import time.

Upgrading an existing agent

Stop the agent and sync its latest work with the old runtime before upgrading. Install hermes-pubky 0.2.3 in the verified Hermes 0.21.3 environment on every computer that runs the agent. Schema-22 conversations from Hermes 0.19.0 are upgraded using Hermes' own migrations on a staged copy during capture or restore. The next checkpoint saves schema 30; old checkpoints remain in history. An upgrade failure leaves the working database unchanged. Other unverified schemas are refused. After the new checkpoint is saved, use the newer runtime on every device; hermes-pubky 0.2.1 cannot read schema 30.

Commands

hermes-pubky run <id> [--resume SESSION] [--offline] [--query TEXT]

hermes-pubky agent init <id> [--from-hermes-home PATH] [--workspace PATH]
hermes-pubky agent attach <pubky-uri>
hermes-pubky agent list
hermes-pubky agent status <id> [--json] [--offline]
hermes-pubky agent login <id>
hermes-pubky agent logout <id>
hermes-pubky agent sync <id> [--prefer local|remote]
hermes-pubky agent files <id> list|fetch|import|remove
hermes-pubky agent history <id>
hermes-pubky agent restore <id> <snapshot-id>

hermes-pubky template init|inspect|publish <directory>
hermes-pubky template adopt <agent-id> <pubky-uri>
hermes-pubky template update <agent-id>

agent sync seals whatever changed locally, including files added by hand or with agent files import, then publishes everything pending. agent logout revokes the grant at the homeserver and says so only when that succeeded.

Exit codes: 0 done, 1 usage, 2 saved locally but not yet on the homeserver, 3 conflict, 4 authorization needed, 5 quota, 6 integrity or unsupported runtime.

What it can reach

agent init requests one capability:

/priv/hermes.pubky.app/v2/agents/<id>/:rw

Read and write inside that agent's own directory. No root capability, no access to your other apps. The launcher also refuses paths outside that directory locally, before a request leaves the process, and keeps the grant out of the Hermes child's environment entirely.

Publishing a template needs its own separate capability under /pub/. An agent's grant can never publish.

A grant holds one session at a time. Authorize each computer with its own agent login rather than copying a grant between machines, or each will keep signing the other out.

Read this before you store anything

Your homeserver operator can read your agent. /priv is access-controlled, not encrypted. If you self-host, that operator is you, otherwise assume whoever runs your homeserver can read your instructions, memories and conversations. Credentials are never uploaded, but a conversation can contain anything you or a tool put in it.

One writer at a time. Read from as many machines as you like, but stop and sync on one before writing from another. If the homeserver's checkpoint moved while this machine has unsaved work, run stops before starting the agent, installs nothing over your work, and asks you to choose with agent sync --prefer local|remote; whichever side you drop is preserved under recovery/ first.

Offline works. Startup seals anything a previous run left unsaved, then refreshes from the homeserver with a five-second budget. Offline runs chain their checkpoints, survive restarts, and publish in order at the next sync, retrying with backoff capped at 60 seconds. A run that could not reach the homeserver exits 2 and says saved locally; not yet saved to homeserver. A run whose conversation database cannot be captured exits 6 and does not claim anything was saved.

A cold attach downloads the conversation database in full. It is streamed and reassembled on disk, never held in memory, but a large history takes time. Workspace documents stay remote until requested.

History is kept. Older checkpoints remain recoverable and count against your homeserver quota. Removing a file drops it from the current inventory, not from history; this is not secure erasure.

Not in 0.2: multi-device merge, client-side encryption, portable credentials, semantic retrieval, Windows wheels, and harnesses other than the verified Hermes 0.21.3 runtime.

How it fits together

hermes-pubky run <id>
  -> read and verify the remote checkpoint
  -> materialize a dedicated HERMES_HOME and workspace
  -> start Hermes as a child, with HERMES_HOME set before it imports
       -> the in-process plugin reports changes and serves two workspace tools
  -> the supervisor owns the lock, the journal and every network call
  -> final capture and sync after the child exits

Storage is content-addressed. A checkpoint is an immutable snapshot document naming immutable objects; only head.json is mutable, and it moves last, after every object it names has been verified as recorded. The homeserver offers no conditional write, so this is ordering, not a lock: concurrent writers remain unsupported and are detected rather than merged.

Locally, the journal tracks one base: the checkpoint the working copy corresponds to. Sealing a checkpoint advances it, installing one advances it, publishing never does. That is what lets a second run continue the first and offline runs form a chain.

/priv/hermes.pubky.app/v2/agents/<id>/{head,snapshots/*,objects/*}.json
/pub/hermes.pubky.app/v2/templates/<id>/{head,snapshots/*,objects/*}.json

Markdown is stored as Markdown and JSON as JSON, so what is on your homeserver is readable there. Files over 1 MiB, and the database always, are split into 1 MiB chunks.

Development

uv venv --python 3.11 .venv
git clone https://github.com/NousResearch/hermes-agent.git /tmp/hermes-pubky-runtime
git -C /tmp/hermes-pubky-runtime checkout a51143fbbe6ddbc0c7f403d0579c4d75504c6793
uv pip install --python .venv/bin/python maturin pytest -e /tmp/hermes-pubky-runtime
PYO3_PYTHON="$PWD/.venv/bin/python" .venv/bin/maturin develop

.venv/bin/pytest        # Python tests; the Hermes ones skip if it is absent
cargo test --lib        # Rust unit tests

tests/test_supervisor.py runs the whole launcher against a fake homeserver and a stand-in child: two runs in a row, an attach on a second machine, offline chains, a moved head over local edits, a corrupt database, a failed restore. Those are the scenarios that matter; the rest of the suite guards the wire format and the policy boundaries.

The end-to-end and acceptance suites need PostgreSQL for the homeserver and the well-known testnet ports free:

docker run -d --name hermes-pubky-pg \
  -e POSTGRES_USER=test_user -e POSTGRES_PASSWORD=test_pass -e POSTGRES_DB=postgres \
  -p 5432:5432 postgres:18-alpine

export TEST_PUBKY_CONNECTION_STRING="postgres://test_user:test_pass@localhost:5432/postgres?pubky-test=true"
cargo test --test v2 -- --ignored --test-threads=1

A real managed Hermes turn, against a local fake model so no credentials are needed:

python scripts/check_managed_hermes_integration.py

Verify the catalog wrapper in a fresh environment using the candidate wheel:

maturin build --release --out dist
python scripts/check_plugin_install.py --hermes-source /tmp/hermes-pubky-runtime --wheel-dir dist

The full recovery scenario through the shipped command line, one agent across two machines, ending with the grant revoked (so each run needs a fresh fixture):

cargo run --example testnet_fixture > /tmp/fixture.json &
HERMES_PUBKY_TESTNET=1 python scripts/check_managed_handoff.py --fixture /tmp/fixture.json

Pinned upstream versions, changed only as a deliberate compatibility decision with tests to match:

Hermes Hermes 0.21.3 at the verified commit above, conversation schema 30
Pubky SDK v0.11.0 commit 6a14bdb8fa2e30ef4e4b241fcdd3992c453d2378

Cargo.lock is committed. tests/test_hermes_contract.py holds the upstream contract as executable tests and fails if the installed package drifts from it.

License

MIT

← Back to the catalog · catalog built Sep 21, 2026