hermes-pubky
Portable Hermes agent context over Pubky.
Your agent's context stops living on one machine. hermes-pubky adds a Hermes
memory provider named pubky that loads:
- a public base context you explicitly approved — shareable agent
instructions addressed by a
pubky://URL, and - a private portable overlay — your own user facts and agent memory, stored in a private document on your homeserver and updated as Hermes writes memory.
Your local SOUL.md, USER.md and MEMORY.md are untouched and still take
precedence. The plugin only ever reads them.
Status: v0.1, alpha. Homeserver
/privstorage is itself alpha. See What the homeserver operator can see before storing anything sensitive.
Install
cd ~/.hermes/hermes-agent
uv pip install hermes-pubky==0.1.0
hermes-pubky install
hermes memory setup pubky
hermes-pubky install writes a three-file shim into
$HERMES_HOME/plugins/pubky/. Hermes discovers memory providers by scanning
that directory rather than through Python entry points, so a pip install alone
does not make the provider visible. The shim just imports the pip-installed
package, so upgrades are a plain uv pip install -U hermes-pubky.
Requires Python 3.11–3.13, Hermes 0.19+, and a Pubky homeserver running v0.11 or later.
Setup walks you through
- A profile id (default:
default). - Pubky Auth — an authorization URL you approve in Pubky
Ring. The grant secret is stored in your
profile-scoped
.envat mode0600and never leaves your machine. - Loading your existing remote profile, or creating one.
- For a new profile: optionally pinning a public base context, and optionally
importing your existing
USER.md/MEMORY.md. Filenames, entry counts and sizes are shown before anything is copied, and the local files are never modified. - Activating
memory.provider: pubky.
Commands
hermes pubky status # grant, cache, revision, pending writes
hermes pubky status --offline # skip the homeserver check
hermes pubky login # authorize this machine
hermes pubky logout # revoke the grant, delete the local secret
hermes pubky sync # reconcile pending writes
hermes pubky sync --prefer remote # resolve a conflict, keeping the remote
hermes pubky sync --prefer local # resolve a conflict, keeping local writes
hermes pubky base set <pubky-url> # pin a public base context
hermes pubky base refresh # re-approve it after its content changed
hermes pubky base clear # unpin it
Requested capabilities
Setup requests exactly one capability:
/priv/hermes.pubky.app/v1/profiles/:rw
Read and write, confined to this plugin's own private profile directory. It cannot read your other apps' data, cannot write anywhere else, and does not hold the root capability. The plugin also refuses locally — before any request is sent — to touch a path outside that directory.
hermes pubky logout revokes the grant through the session's own
DELETE /auth/grant/session, which a scoped grant is permitted to call.
What the homeserver operator can see
/priv is access-controlled, not encrypted. Your homeserver operator can
read the contents of your private profile document. If you self-host, that is
you. If you do not, treat the portable overlay as visible to whoever runs your
homeserver, and keep genuinely sensitive facts in your local USER.md
instead — the plugin never uploads local files unless you explicitly import
them during setup.
Never stored in either document: credentials, conversations, or tool results.
Offline behavior
The plugin is designed to be used on a laptop that is frequently offline.
- Startup reads the profile-scoped cache first, then refreshes from the homeserver with a five-second budget. If the network is slow or down, Hermes starts on the cache and the refresh finishes in the background.
- Memory writes are appended to a local JSONL outbox and mirrored to the homeserver asynchronously. They never block a turn.
- Pending writes survive restarts and retry with exponential backoff capped at 60 seconds.
- The injected prompt block says when it is working from a stale cache.
Conflict recovery
v0.1 supports one active writer per profile. Reading the same profile from several machines is fine; writing from two at once is not supported.
If the remote profile's revision changes while local writes are still pending, automatic syncing stops and the plugin asks you to choose:
hermes pubky sync --prefer remote # keep the remote profile, drop local writes
hermes pubky sync --prefer local # keep this machine's profile, drop the remote
The two are symmetric: whichever side you drop is written to
$HERMES_HOME/pubky/<profile-id>/backups/ first, as JSON you can read and
copy from. --prefer remote backs up both the cached local profile and the
queued writes; --prefer local backs up the remote profile. A base context
pinned only on the remote is carried over rather than lost.
Base context pinning
A base context is pinned by the SHA-256 of its raw bytes at the moment you
approve it. If the author later changes the document, the plugin keeps using
the approved copy and logs a warning; the new content is only adopted after you
review it with hermes pubky base refresh. This means someone whose context
you follow cannot silently change your agent's instructions.
Storage schema
Public base context
pubky://<author>/pub/hermes.pubky.app/v1/contexts/<context-id>.json
{
"schemaVersion": 1,
"id": "researcher",
"name": "Researcher",
"description": "Research-oriented agent instructions",
"instructions": "Markdown instructions"
}
Must be under /pub/ and end in .json. v0.1 reads public contexts; it does
not publish them.
Private profile
/priv/hermes.pubky.app/v1/profiles/<profile-id>.json
{
"schemaVersion": 1,
"profileId": "default",
"baseContext": {
"url": "pubky://.../context.json",
"sha256": "approved-raw-content-hash"
},
"user": ["portable user fact"],
"memory": ["portable agent memory"],
"revision": 1,
"updatedAt": "RFC3339 timestamp"
}
Local files
$HERMES_HOME/.env HERMES_PUBKY_GRANT_SECRET (0600)
$HERMES_HOME/pubky/<profile-id>/profile.json cached private profile
$HERMES_HOME/pubky/<profile-id>/context.json approved base context, raw bytes
$HERMES_HOME/pubky/<profile-id>/context.meta.json its URL, hash and approval time
$HERMES_HOME/pubky/<profile-id>/outbox.jsonl writes not yet mirrored
$HERMES_HOME/pubky/<profile-id>/state.json last synced revision, conflict flag
$HERMES_HOME/pubky/<profile-id>/backups/ discarded sides of conflicts
All of it lives under HERMES_HOME, so hermes backup already captures it.
Limits
Documents are capped at 64 KiB, entries at 4,000 characters, and each of
user / memory at 200 entries. Downloads are cut off mid-stream if a server
exceeds the cap, whatever its Content-Length says.
Not in v0.1
Publishing public contexts, semantic retrieval, chat/session archives, credential storage, Windows wheels, and multi-writer merge. The provider exposes no tools and performs no recall — it is a context provider, not a semantic-memory backend.
Development
uv venv --python 3.11 .venv
uv pip install --python .venv/bin/python maturin pytest pyyaml
PYO3_PYTHON="$PWD/.venv/bin/python" .venv/bin/maturin develop
.venv/bin/pytest # Python tests — no network, no homeserver
cargo test --lib # Rust unit tests
The end-to-end suite runs against a real Pubky v0.11 testnet, which needs a 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 e2e -- --ignored --test-threads=1
They are #[ignore]d and single-threaded because the static testnet binds
fixed ports — stop any testnet_fixture before running them.
To exercise the Hermes-facing flows by hand, start a testnet with a pre-authorized grant and drive the real CLI against it:
cargo run --example testnet_fixture # prints the grant secret as JSON
export HERMES_PUBKY_TESTNET=1
export HERMES_PUBKY_GRANT_SECRET="<grant_secret from the JSON>"
hermes-pubky install && hermes memory setup pubky
There is also a scripted version of that check:
HERMES_HOME=/tmp/hermes-home python scripts/check_hermes_integration.py
The Pubky Rust SDK is pinned to the v0.11.0 commit
6a14bdb8fa2e30ef4e4b241fcdd3992c453d2378 and Cargo.lock is committed.
License
MIT
Metadata
Release files for hermes-pubky 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| hermes_pubky-0.1.0.tar.gz | 110.5 kB | Details |
Built distributions (wheels)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| hermes_pubky-0.1.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl | CPython 3.11 | abi3 | Linux glibc 2.17+ x86-64 | Details |
| hermes_pubky-0.1.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl | CPython 3.11 | abi3 | Linux glibc 2.17+ ARM64 | Details |
| hermes_pubky-0.1.0-cp311-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl | CPython 3.11 | abi3 | macOS 11.0+ ARM64, macOS 10.12+ x86-64, macOS 10.12+ universal2 (ARM64, x86-64) | Details |
Total release size: 16.8 MB
Release files / hermes_pubky-0.1.0.tar.gz
| Download URL | hermes_pubky-0.1.0.tar.gz |
|---|---|
| Size | 110.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3b3e7ec0f028a5d6aeb5390a7f49a208cbf6600e6d7e808429a04e000af393c8
|
|
BLAKE2b-256 checksum How to use checksums |
b865ba8c53778524aca19d8731d3c6ccc42d4ba60464888d47856ffb0fa125e7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 9, 2026.
Transparency logRelease files / hermes_pubky-0.1.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | hermes_pubky-0.1.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 4.4 MB |
| Tags | CPython 3.11 Linux glibc 2.17+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
8cb7b290f606907afe6a020d2bbc0b04fa590307646c9d315776987260728616
|
|
BLAKE2b-256 checksum How to use checksums |
49bc854d3f414f0466e10cacd477b75dcd0f53ae13e0247ffb4615b1b6ffeff3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 9, 2026.
Transparency logRelease files / hermes_pubky-0.1.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
| Download URL | hermes_pubky-0.1.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl |
|---|---|
| Size | 4.1 MB |
| Tags | CPython 3.11 Linux glibc 2.17+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
8a73783c1beddff5a026036c027a6259bb9db1ac4d1616a6853c6f6496dac95f
|
|
BLAKE2b-256 checksum How to use checksums |
895f8c36570e2511e0c6533b528927ce8b31f639075ded479d4da9b8327fb8bd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 9, 2026.
Transparency logRelease files / hermes_pubky-0.1.0-cp311-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl
| Download URL | hermes_pubky-0.1.0-cp311-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl |
|---|---|
| Size | 8.1 MB |
| Tags | CPython 3.11 abi3 macOS 10.12+ universal2 (ARM64, x86-64) macOS 10.12+ x86-64 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
9c4ac56615ae8f7c7c585e657cd051d3b4437a97acfd641ec400c438b255be2b
|
|
BLAKE2b-256 checksum How to use checksums |
cb3bffcbf8ce4d4279e0572b0665e81fd20c69d6c52a4263f2a62ef31de8a928
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 9, 2026.
Transparency log