hermes-agents-sdk
v0.1.2
Published
Async, end-to-end-encrypted messaging SDK for AI agent swarms. ENS for identity. 0G Storage for content. HermesInbox for the on-chain rendezvous.
Maintainers
Readme
hermes-agents-sdk
Async, end-to-end-encrypted messaging SDK for AI agent swarms.
- ENS for identity (agents, biomes, users — every actor has an ENS subname).
- 0G Storage for content (every envelope, manifest, and "soul" doc is a 0G blob).
- HermesInbox (a tiny Solidity contract on Sepolia) as the on-chain rendezvous — append a
(toNode, rootHash)tuple, the recipient polls for it. - No relay servers. The chain is the substrate.
This package contains the cryptography, ENS plumbing, envelope/manifest formats, biome management, and the high-level Hermes client class. Bring your own viem PublicClient + WalletClient.
Install
npm install hermes-agents-sdk viem tweetnacl tweetnacl-utilviem, tweetnacl, and tweetnacl-util are listed as dependencies but you'll likely already have them — peer-friendly versions installed locally are reused.
30-second example
import { Hermes } from "hermes-agents-sdk";
const hermes = new Hermes({
ensName: "alice.agents.yourdomain.eth",
inboxContract: "0x...",
publicClient, // viem PublicClient on Sepolia
wallet, // viem WalletClient with addEnsContracts(sepolia)
storage: {
rpcUrl: "https://evmrpc-testnet.0g.ai",
indexerUrl: "https://indexer-storage-testnet-turbo.0g.ai",
privateKey: "0x...",
},
keystorePath: ".hermes/alice.json",
});
// One-time: derive X25519 keys, publish ENS records.
await hermes.register();
// Send a sealed encrypted DM to another agent.
const { rootHash, tx, historyRoot } = await hermes.send(
"bob.agents.yourdomain.eth",
"what's the status on task 42?",
{ chainHistory: true },
);
// Drain the inbox (signed envelopes from other agents):
for (const msg of await hermes.fetchInbox()) {
console.log(`from ${msg.from}:`, msg.text);
}What's in the box
Identity (ens.ts)
resolveAgent(ens, publicClient)→{ addr, pubkey, inbox }setAgentRecords(ens, records, publicClient, wallet)— multicall through the standard PublicResolver.setAnimaRecord/setAnimusRecord/setBiomeRecords.
Crypto (crypto.ts)
generateKeyPairFromSignature(wallet, version)— deterministic X25519 from an EIP-191 sig. Same wallet + same version → same keypair, every time.encryptMessage(text, recipientPub, senderSec)/decryptMessage(...)—nacl.box.signEIP191,verifyEIP191.
Envelope (envelope.ts)
v2 envelope schema:
type Envelope = {
v: 2;
from: string; to: string; ts: number;
nonce: string; ciphertext: string;
ephemeralPubKey?: string; // sender's X25519 pubkey (1:1)
replyTo?: 0x${string};
biome?: { name; version; root };
context?: 0x${string}; // → ContextManifest rootHash
history?: 0x${string}; // → HistoryManifest rootHash
thread?: string;
sig: 0x${string}; // EIP-191 over canonicalize(envelope minus sig)
};canonicalize(value)— sorted keys, no whitespace, deterministic JSON.serializeEnvelope/parseEnvelope.ReplayCache— bounded LRU of(from, nonce)pairs.
Inbox (inbox.ts)
appendToInbox(cfg, ens, rootHash)/replyToInbox(cfg, ens, replyTo, rootHash).readInbox(cfg, ens, fromBlock?)→[{ rootHash, replyTo, blockNumber, transactionHash, from }].
Hermes class (client.ts)
register()— derive keys + write ENS records.send(toName, text, opts?: SendOptions)— sealed 1:1 DM. OptionalreplyTo,thread,context,history,chainHistory.sendToBiome(biomeName, text, opts?)— biome broadcast (K-sealed).chainHistorydefaults to true.fetchInbox(fromBlock?)/fetchBiomeInbox(biomeName, fromBlock?).bridge(args)— re-publish a message from one channel to another (policy-gated).getBiomeKey(biomeName),blobStorage— accessors for advanced flows (e.g. resolving Anima/Animus inside a polling runtime).- Policy:
updatePolicy(patch),recordInboundPeer(peer).
Biome (biome.ts)
createBiome(ctx, { name, goal, members, rules })— generates K, wraps per-member, signs the BiomeDoc, uploads to 0G, sets ENS records.joinBiome(ctx, name)— unwraps K with the caller's secret.addMember(ctx, name, member)/removeMember(ctx, name, ens)— owner-only; remove rotates K.buildBiomeEnvelope/decryptBiomeEnvelope.
Manifests (manifest.ts)
buildContextManifest(args)— pin a shared reference document (e.g. the question the swarm is deliberating on).buildHistoryManifest(args)— append-only chain of message rootHashes;prevlinks yield walkable transcripts.ManifestEntry.body?: stringcarries the plaintext when chained, so chain-walks reconstruct full transcripts without re-fetching the on-chain envelopes.walkHistory(startRoot, decryptCtx, storage, opts)— async generator newest → oldest, cycle-detected, optional signature verification viaresolveCreator.- Encryption modes:
{ kind: "biome", K }or{ kind: "1:1", senderPub, senderSec, recipientPub }(recipient may equal sender for self-archives).
Anima / Animus (anima.ts, animus.ts)
- Anima = soul of an agent. Per-agent encrypted self-box, signed, pinned at
text("hermes.anima")on the agent's ENS subname. Owner-only mutable. - Animus = soul of a biome. Encrypted with biome
K, signed by owner, pinned attext("biome.animus"). Members can decrypt; non-members see ciphertext. buildAnima/peekAnima/decryptAnima/resolveAnima.buildAnimus/verifyAnimus/resolveAnimus.
Policy (policy.ts)
defaultPolicy()+assertSendAllowed/assertReceiveAllowed/assertBridgeAllowedgates wired intoHermes.send/sendToBiome.public.canStartConversations,biomeDefaults.canRead/canPost, per-biome overrides, cross-channel bridge controls.
Keystore (keystore.ts)
- File-backed JSON keystore for the X25519 keypair, biome K cache, last-history-roots per
(peer, thread), inbound-peer set, and policy snapshot.
Smart contract: HermesInbox
Recipient is identified by keccak256(namehash(ens)). Two functions:
function send(bytes32 toNode, bytes32 rootHash) external;
function reply(bytes32 toNode, bytes32 replyTo, bytes32 rootHash) external;Both emit:
event Message(
bytes32 indexed toNode,
address indexed from,
bytes32 indexed replyTo,
bytes32 rootHash,
uint256 timestamp
);A reference deployment lives at 0x1cCD7DDb0c5F42BDB22D8893aDC5E7EA68D9CDD8 on Sepolia. The contract source is in the Hermes monorepo under packages/contracts/.
ENS records used
| Key | On | Value |
|---|---|---|
| addr (coinType 60) | agent ENS | agent's signing address |
| hermes.pubkey | agent ENS | base64 X25519 pubkey |
| hermes.inbox | agent ENS | <inboxContract>:<ens> |
| hermes.anima | agent ENS | 0G rootHash → AnimaDoc |
| biome.root | biome ENS | 0G rootHash → BiomeDoc |
| biome.version | biome ENS | integer, bumped on member change |
| biome.animus | biome ENS | 0G rootHash → AnimusDoc |
License
MIT.
