@origintrail-official/dkg-adapter-hermes
v10.0.3
Published
Hermes Agent adapter — connects Hermes AI agents to a DKG V10 node for verifiable shared memory
Readme
DKG V10 Hermes Adapter
@origintrail-official/dkg-adapter-hermes connects a local
Hermes Agent profile to a DKG
V10 node.
The adapter is a thin bridge into the DKG node. It does not run its own DKG
node or own Hermes runtime state. The DKG daemon owns graph state, wallets,
auth, context graphs, /.well-known/skill.md, the local-agent registry, Node
UI chat routing, and DKG-backed chat persistence. Hermes owns its profile
directory, config.yaml, .env, session state, tools, plugins, and runtime
logs.
This package contains:
src/- TypeScript setup helpers, daemon client helpers, and Hermes channel payload/client contracts.hermes-plugin/- Python Hermes memory provider plugin and DKG daemon client.setup-entry.mjs- setup-safe package entry used by the DKG CLI and daemon.
What It Does
- installs the DKG memory provider plugin into a selected Hermes profile
- elects DKG as Hermes' external memory provider
- exposes the DKG tool surface listed in
packages/cli/skills/dkg-node/SKILL.mdplus the Hermes-nativedkg_memoryhelper - stores provider memory facts in the
memoryassertion of theagent-contextcontext graph by default - syncs completed Hermes turns into DKG Working Memory with stable turn IDs and duplicate-turn protection
- bridges the DKG Node UI right-panel chat to Hermes' OpenAI-compatible API server
- keeps connected-agent chat history persisted in DKG memory so Node UI reloads do not lose the conversation
- registers Hermes as a DKG local-agent integration for status, connect, refresh, and disconnect flows
Scope Boundaries
- it does not run its own DKG node;
dkg hermes setupbootstraps the DKG config when missing (no separatedkg initneeded) and starts the daemon by default (--no-startopts out) - it does not start Hermes for you; run the Hermes gateway separately
- it does not copy DKG API tokens into Hermes config files
- it does not expose standalone HTTP route stubs from the adapter package; Hermes channel routes are served by the DKG CLI daemon
Quick Start
Install the DKG CLI and set up the default Hermes profile:
npm install -g @origintrail-official/dkg
dkg hermes setupEnable Hermes' API server and start the gateway:
echo 'API_SERVER_ENABLED=true' >> ~/.hermes/.env
hermes gateway run --replace -vThen open the DKG Node UI at http://127.0.0.1:9200/ui, choose Agents in
the right panel, and connect Hermes. A healthy setup lets Hermes run
dkg_status, search memory, write DKG memory, and use the DKG tool table from
the node skill file.
For a named Hermes profile:
dkg hermes setup --profile researchThe named profile resolves to ~/.hermes/profiles/research. If HERMES_HOME
is set, setup uses that exact profile home.
dkg hermes setup flags
| Flag | Default | Purpose |
| --- | --- | --- |
| --profile <name> | default profile | Target ~/.hermes/profiles/<name> instead of ~/.hermes. |
| --daemon-url <url> | http://127.0.0.1:9200 | DKG daemon URL. First-wins over --port when both are set. |
| --gateway-url <url> | http://127.0.0.1:8642 | Hermes OpenAI-compatible API server URL for Node UI chat. |
| --bridge-url <url> | unset | Custom same-host Hermes bridge URL. Loopback only; use --gateway-url for WSL2 or remote transports. |
| --bridge-health-url <url> | derived from transport | Optional health URL override. Must belong to the configured bridge/gateway base. |
| --port <port> | 9200 | Shortcut for --daemon-url http://127.0.0.1:<port>. |
| --memory-mode <mode> | primary | primary elects DKG as the Hermes memory provider; tools-only skips provider election. |
| --network <name> | mainnet-gnosis (fresh node) | Network to set up on (mainnet-gnosis | mainnet-base | testnet), persisted as config.networkConfig. Applies to a FRESH node only; an existing node keeps its current network (switch with dkg init --network). |
| --no-start | off (daemon starts) | Skip starting the DKG daemon. Best-effort daemon registration still fires against an already-running daemon. |
| --no-fund / --fund | --fund | Fund the node's generated admin and operational wallets through the testnet faucet (testnet only — mainnet has none). --no-fund skips. Faucet failures are non-fatal. |
| --preserve-provider | off (replace) | Refuse to replace an existing non-DKG memory.provider. Restores the pre-#386 throw-on-conflict behavior. Aliased as --no-replace-provider. |
| --no-verify | off | Skip the post-setup verification pass. |
| --dry-run | off | Preview planned changes without writing files, starting the daemon, calling the faucet, or taking a config backup. |
dkg hermes disconnect flags
| Flag | Default | Purpose |
| --- | --- | --- |
| --profile <name> | default profile | Target the named profile's Hermes home. |
| --dry-run | off | Preview planned changes without writing. |
| --restore-provider | off (disconnect-only) | After removing the managed DKG block, restore the prior memory.provider captured at first setup. UI Disconnect always restores; the CLI requires this opt-in. |
dkg hermes uninstall and dkg hermes reconnect
uninstall always restores the prior memory.provider before removing
adapter-owned files; the captured config.yaml.bak.<ts> backup is left in
place for operator rollback. reconnect reuses persisted daemon and bridge
settings and accepts the same transport flags as setup.
Verification
A healthy setup should satisfy all of the following:
dkg hermes verifyreports the selected profile as configured- Hermes gateway logs show
Memory provider 'dkg' registered dkg_statusworks from Hermes- the DKG Node UI loads at
http://127.0.0.1:9200/ui - the right-side chat surface can connect to Hermes and send a message
- the conversation survives Node UI reload because turns are persisted in DKG memory
dkg_memorywrites can be read from thememoryassertion inagent-context
Config Files
| File | Owner | Purpose |
| --- | --- | --- |
| ~/.dkg/config.json | DKG node | node config: networking, chain, auth, API |
| $HERMES_HOME/config.yaml | Hermes | active Hermes provider selection; setup writes a managed DKG memory provider block, replacing any prior memory.provider after taking a <configPath>.bak.<unix-ts-ms> sibling backup |
| $HERMES_HOME/dkg.json | DKG adapter | daemon URL, resolved DKG home, memory assertion, tool guards, and transport config |
| $HERMES_HOME/plugins/dkg/ | DKG adapter | installed Hermes memory provider plugin |
| $HERMES_HOME/skills/dkg-node/SKILL.md | DKG adapter | Hermes profile copy of the node skill file |
| $HERMES_HOME/.dkg-adapter-hermes/ | DKG adapter | setup state and ownership metadata |
Adapter Config
These keys live in $HERMES_HOME/dkg.json. dkg hermes setup writes the file
with ownership metadata and leaves a non-managed file untouched.
| Key | Default | Purpose |
| --- | --- | --- |
| daemon_url | http://127.0.0.1:9200 | DKG daemon HTTP URL. Env DKG_DAEMON_URL overrides at runtime. |
| dkg_home | resolved from the target daemon | DKG config home used to read auth.token; supports monorepo .dkg-dev and packaged .dkg installs. |
| bridge.gatewayUrl | http://127.0.0.1:8642 | Hermes OpenAI-compatible API server base used by Node UI chat. |
| bridge.url | unset | Optional custom loopback /health, /send, /stream bridge. |
| bridge.healthUrl | derived | Optional health check URL tied to the configured transport base. |
| context_graph | agent-context | Default context graph for provider memory facts. Env DKG_CONTEXT_GRAPH overrides at runtime. |
| memory_assertion | memory | Working Memory assertion used by dkg_memory. Env DKG_MEMORY_ASSERTION overrides at runtime. |
| memory_mode | provider | Stored setup mode for status/reconnect/uninstall. |
| publish_tool / allow_direct_publish | direct / true | Controls exposure of the dkg_knowledge_asset_publish Verifiable Memory publish tool. Env DKG_ALLOW_DIRECT_PUBLISH=false hides it. |
| allow_context_graph_admin_tools | true | Controls mutating project-admin tools. Env DKG_ALLOW_CONTEXT_GRAPH_ADMIN_TOOLS=false hides them. |
| import_roots | [] | Optional safe roots for dkg_knowledge_asset_import_file; env import-root settings also apply. |
Environment token override order is DKG_API_TOKEN, DKG_AUTH_TOKEN, the
setup-resolved dkg_home, DKG_HOME, then ~/.dkg.
Hermes Memory Provider
Hermes uses DKG as its memory provider. Setup installs and selects DKG by
writing a managed memory.provider: dkg block. Replacement is the
default: if the target profile already has another provider configured,
setup snapshots it and elects DKG. The replacement is reversible — pass
--preserve-provider to opt out and keep the pre-#386 throw-on-conflict
behavior.
When replacing a non-DKG provider, setup performs writes in this order:
<hermesHome>/.dkg-adapter-hermes/setup-state.jsonis written withpriorMemoryProvider = { provider, configBackupPath, capturedAt }recording the intent to swap. This write happens before any destructive change.<hermesHome>/config.yaml.bak.<unix-ts-ms>is written as a sibling ofconfig.yaml, holding the pre-replacement bytes verbatim.<hermesHome>/config.yamlis rewritten with the managed# BEGIN/END DKG ADAPTER HERMES MANAGEDblock selectingmemory.provider: dkg.
The intent-first ordering is load-bearing for downstream-package authors: a
SIGINT between steps 1 and 2, or between steps 2 and 3, leaves a recoverable
state on disk. A re-run sees the persisted priorMemoryProvider and routes
restore to the captured backup path even if the rewrite itself never
completed. priorMemoryProvider is first-wins — the original snapshot is
never overwritten by re-runs or interrupted re-attempts.
Restore is invoked by dkg hermes disconnect --restore-provider,
dkg hermes uninstall (unconditional), and the Node UI Disconnect button
(unconditional). It tries surgical line-rewrite of memory.provider first
(preserving unrelated config.yaml edits), then falls back to atomic rename
of the captured backup file. Restore failure does not roll back disconnect —
the integration stays disconnected and the restore error surfaces as a
warning on the Node UI row.
Once DKG is the active provider, Hermes receives DKG-backed memory recall,
dkg_memory, memory_search, dkg_query, the
dkg_knowledge_asset_* lifecycle tools (create -> write -> finalize -> share ->
publish — a full share seals by default and create can write quads and
optionally share them in one call; plus pull_from / query / history / discard /
import_file), sub-graph helpers, and status/wallet/network helpers.
Node UI Connect, Refresh, And Disconnect
The Node UI Connect Hermes button registers Hermes in the local-agent
registry and probes the configured Hermes API server/bridge. If Hermes is
online, the panel becomes chat-ready. If Hermes is offline, the panel records a
degraded state and tells the user to run dkg hermes setup or refresh after
Hermes starts.
Refresh re-probes Hermes health and updates ready/degraded state. It does not reinstall the adapter.
Disconnect runs Hermes reverse setup for the stored profile metadata, then
disables the local-agent integration. It removes only adapter-owned provider
election/artifacts and preserves Hermes sessions, logs, .env, and unrelated
profile data.
Local-Agent Routes
Hermes uses Hermes-specific daemon routes for this release. These routes are supported by the DKG CLI daemon; this adapter package provides the setup, client, and payload contracts that call into them.
| Route | Purpose |
| --- | --- |
| GET /api/hermes-channel/health | Probe configured Hermes bridge/gateway health and update local-agent readiness. |
| POST /api/hermes-channel/send | Forward a non-streaming Node UI message to Hermes. |
| POST /api/hermes-channel/stream | Forward a streaming Node UI message and proxy SSE frames back to the UI. |
| POST /api/hermes-channel/persist-turn | Persist a completed Hermes turn through DKG chat memory with duplicate-turn protection. |
The daemon forwards Node UI chat to Hermes' OpenAI-compatible API server at
http://127.0.0.1:8642 by default. Since Hermes v0.15.0 that API server
refuses to start without API_SERVER_KEY, even on loopback. For the local
loopback transport, dkg hermes setup provisions this automatically: it writes
API_SERVER_ENABLED=true and a generated API_SERVER_KEY into the active
Hermes profile .env (an existing key is never overwritten), and the daemon
forwards Authorization: Bearer <API_SERVER_KEY> to /v1/chat/completions.
You only need to restart hermes gateway run --replace -v so Hermes picks up
the key — the same restart the provider install already requires.
Use dkg hermes setup --gateway-url <url> when the Hermes API server is
reachable through WSL2 or a remote gateway. In that case Hermes' .env lives on
another host, so DKG does not write it; set DKG_HERMES_API_SERVER_KEY in the
daemon environment to the key configured on the remote Hermes and DKG forwards
it as the bearer. --bridge-url is reserved for a custom loopback bridge that
implements /health, /send, and /stream.
Attachment references are node-owned assertion refs. The daemon verifies their provenance before forwarding them to Hermes.
Auth And Security
- Non-public DKG daemon routes use the existing bearer token auth.
- The Python client reads the DKG token from token environment variables first,
then the setup-resolved
dkg_homewritten to$HERMES_HOME/dkg.json, thenDKG_HOME/~/.dkg; it does not copy the token into Hermes config. - Setup registration uses the same bearer source.
- Standalone loopback Hermes bridge calls use a route-scoped
x-dkg-bridge-tokenheader. Non-loopbackbridgeUrlvalues are rejected; usegatewayUrlfor remote transports. Gateway targets do not receive that bridge token. - Hermes
sendandstreamroutes fail closed when the Hermes integration is not enabled in the DKG local-agent registry.persist-turnremains daemon-authenticated so the active Hermes provider can persist completed turns even when UI chat registration is unavailable. - The
dkg_knowledge_asset_publishVerifiable Memory publish tool is model-callable by default to match the node skill surface. Publishing Verifiable Memory is permanent and may cost TRAC; operators can hide publish exposure withDKG_ALLOW_DIRECT_PUBLISH=false. - Context-graph admin mutation tools are enabled by default for collaboration;
operators can hide them with
DKG_ALLOW_CONTEXT_GRAPH_ADMIN_TOOLS=false. dkg_knowledge_asset_import_filerequires an operator-approved import root. UseDKG_HERMES_IMPORT_ROOTS,HERMES_DKG_IMPORT_ROOTS,DKG_IMPORT_ROOTS, or adapterimport_rootsto approve document locations explicitly.
Troubleshooting
Provider conflict (with --preserve-provider)
If setup is invoked with --preserve-provider against a profile that already
has another memory.provider configured, setup exits with
Refusing to replace existing Hermes memory.provider: <name>. Drop the flag
to take the default replace-by-default path; the prior provider is captured
into setup-state.json and a config.yaml.bak.<ts> backup is written before
the swap. Restore via dkg hermes disconnect --restore-provider or
dkg hermes uninstall.
Hermes chat offline
If Node UI says Hermes is degraded or offline:
- Confirm Hermes is running for the same profile.
- Confirm
API_SERVER_ENABLED=trueis present in the active$HERMES_HOME/.env. - Confirm
http://127.0.0.1:8642/healthresponds, or configure the DKG local-agent integration with the correct gateway URL. - Run
dkg hermes doctor --profile <name>. - Refresh the Hermes connected-agent panel in the Node UI.
Windows and WSL2
Hermes does not support native Windows. Run Hermes inside WSL2. If the DKG daemon runs on Windows, use a daemon URL reachable from WSL:
dkg hermes setup --profile research --daemon-url http://<windows-host-ip>:9200Uninstall and reconnect
disconnect is reversible:
dkg hermes disconnect --profile research
dkg hermes reconnect --profile researchUse uninstall when you want to remove adapter-owned files:
dkg hermes uninstall --profile researchProgrammatic Entrypoint
Downstream packages can import the setup-safe entrypoint directly:
import { runHermesSetup, restoreHermesProfile } from '@origintrail-official/dkg-adapter-hermes';
const result = await runHermesSetup({
profile: 'research',
start: false, // daemon already running
fund: false, // skip faucet
verify: true,
invokedBy: 'ui',
});
if (!result.ok) {
console.error(result.errors.join('\n'));
} else if (result.providerSwap) {
console.log(`Replaced ${result.providerSwap.previousProvider}; backup at ${result.providerSwap.backupPath}`);
}runHermesSetup is the single setup-safe entrypoint shared by dkg hermes
setup (CLI) and the daemon's runHermesUiSetup shim (Node UI Connect). It
returns a HermesSetupResult with:
ok: boolean—truewhen setup landed cleanly (verify passed or skipped).status: 'configured' | 'degraded' | 'error'— daemon route maps these toruntime.statusofready/degraded/error.profile: HermesProfileMetadata— always populated, even on error.daemonStarted: boolean—truewhenstart !== falseandstartDaemonsucceeded or the daemon was already reachable on the resolved port.fundedWallets: string[]— empty whenfund: false, no faucet configured,dryRun: true, or when the faucet returned no funded wallets (non-fatal).transport: { kind, bridgeUrl?, gatewayUrl?, healthUrl? }— always populated; lifts straight into the daemon'sLocalAgentIntegrationTransportshape.providerSwap?: { previousProvider, backupPath }— present only when setup actually swapped a non-DKG provider on this invocation; first-wins on re-runs (omitted on idempotent re-attaches).warnings: string[]/errors: string[]— surfaced intoruntime.lastErrorby the daemon route.state?: HermesSetupState— fullsetup-state.jsonsnapshot for callers that want to persist or inspect.
restoreHermesProfile({ profile, hermesHome, signal? }) returns
{ ok, path: 'surgical' | 'backup-file' | 'noop' | 'failed', restoredFrom?,
restoredProvider?, restoreError? }. See
agent-docs/hermes-parity/setup-entrypoint-contract.md
for the full shape, including dryRun: true strict side-effect-freeness and
the AbortSignal cancellation semantics.
SIGINT-safe intent semantics
setupHermesProfile (the synchronous half called by runHermesSetup) writes
setup-state.json with the priorMemoryProvider intent before the
destructive config.yaml rewrite. Downstream-package authors composing
runHermesSetup into larger setup flows can rely on this ordering: a partial
interrupt (Ctrl-C, kill -INT, supervisor restart) between the state write and
the config.yaml rewrite leaves a recoverable state on disk. A re-run sees
the persisted priorMemoryProvider and routes restore to the captured backup
path even if the rewrite never completed. priorMemoryProvider is first-wins
— never overwritten by re-runs or interrupted re-attempts.
Development
pnpm --filter @origintrail-official/dkg-adapter-hermes run build
pnpm --filter @origintrail-official/dkg-adapter-hermes test
python -m py_compile packages/adapter-hermes/hermes-plugin/__init__.py packages/adapter-hermes/hermes-plugin/client.pyMore Setup Detail
See Hermes setup.
License
Apache-2.0
