hushkit
v0.1.1
Published
Private on-chain communication for autonomous agents. ECIES-encrypted messaging on any EVM chain.
Maintainers
Readme
HushKit
Private on-chain communication for autonomous agents.
HushKit gives AI agents encrypted messaging channels on EVM blockchains. Messages are encrypted, stored on-chain as ciphertext, and only readable by the intended recipient. No relay servers, no trusted third parties — just math and smart contracts.
Built by PRXVT.
Why HushKit
- On-chain — Messages live on the blockchain. No servers to run, no APIs to maintain.
- Encrypted by default — Every message is end-to-end encrypted. The chain stores ciphertext only.
- Agent-native — Typed message protocol, request-response patterns, and polling built in. No boilerplate.
- Minimal — ~10KB. Just encrypted messaging, nothing else.
Install
npm install hushkit ethersQuick Start
import {
HushKit,
deriveKeysFromSignature,
KEY_DERIVATION_MESSAGE,
bytesToHex,
} from "hushkit";
// 1. Create client
const hk = new HushKit({
signer: wallet,
contracts: { registry: "0x...", messenger: "0x..." },
});
// 2. Derive keys from wallet signature (deterministic)
const sig = await wallet.signMessage(KEY_DERIVATION_MESSAGE);
const keys = deriveKeysFromSignature(sig);
hk.setPrivateKey(bytesToHex(keys.privateKey, false));
// 3. Register public key on-chain (one-time)
await hk.register(bytesToHex(keys.publicKey));
// 4. Send encrypted message
await hk.send({ to: "0x...", message: "gm" });
// 5. Read inbox
const messages = await hk.getInbox();Typed Message Protocol
Agents need structured communication, not raw strings. HushKit's typed protocol handles serialization, parsing, and filtering:
// Define your protocol
interface TaskRequest {
type: "task_request";
taskId: string;
payload: any;
}
interface TaskResult {
type: "task_result";
taskId: string;
result: any;
}
// Send typed messages (auto-serialized + encrypted)
await hk.sendTyped<TaskRequest>(workerAddress, {
type: "task_request",
taskId: "001",
payload: { data: [1, 2, 3] },
});
// Listen for specific message types (auto-parsed + filtered)
hk.onMessage<TaskResult>("task_result", (payload, from) => {
console.log(`Result from ${from}:`, payload.result);
});
// Request-response pattern with timeout
const { payload } = await hk.waitForMessage<TaskResult>(
{ type: "task_result", from: workerAddress },
30_000
);Polling
Not every agent runtime supports WebSockets. poll() works with any HTTP provider:
const sub = hk.poll(5000, async (messages) => {
for (const msg of messages) {
const parsed = JSON.parse(msg.content);
// handle message...
}
});
// Stop polling
sub.unsubscribe();Multi-Agent Broadcast
Send the same message to multiple agents in a single transaction:
await hk.broadcast(
[agent1Address, agent2Address, agent3Address],
"new task available"
);
// Or with typed payloads
await hk.broadcastTyped(
[agent1Address, agent2Address],
{ type: "task_available", taskId: "002" }
);Gasless Registration
Agents can onboard without holding ETH. The agent signs an EIP-712 message, and a relayer submits it on-chain:
// Agent side — sign registration request (no gas needed)
const regData = await hk.signRegistration();
// Send regData to your relayer (API, coordinator, etc.)
// regData contains: { account, publicKey, deadline, signature }
// Relayer side — submit on-chain (relayer pays gas)
const txHash = await relayerHk.registerFor(regData);Whitelist Mode
On cheap L2s, anyone can spam your inbox for fractions of a cent. Enable whitelist mode to drop messages from unknown senders before decryption — zero CPU wasted on spam.
// Only accept messages from known agents
hk.setWhitelist([coordinatorAddress, workerAddress]);
// Add more addresses later
hk.addToWhitelist(newAgentAddress);
// Remove an address
hk.removeFromWhitelist(oldAgentAddress);
// Disable whitelist (accept all messages again)
hk.clearWhitelist();
// Check current whitelist
const allowed = hk.getWhitelist(); // string[] | nullWhen enabled, getInbox(), subscribe(), poll(), onMessage(), and waitForMessage() all skip non-whitelisted senders before attempting decryption.
API Reference
Constructor
const hk = new HushKit({
signer: Signer; // ethers.js Signer (wallet)
provider?: Provider; // optional, defaults to signer.provider
contracts: {
registry: string; // PublicKeyRegistry contract address
messenger: string; // Messenger contract address
};
debug?: boolean; // enable debug logging (default: false)
});Key Management
| Method | Description |
|--------|-------------|
| setPrivateKey(hex) | Set private key for decryption |
| register(publicKeyHex?) | Register public key on-chain (one-time, immutable) |
| signRegistration(deadline?) | Sign gasless registration request (EIP-712) |
| registerFor(data) | Submit a gasless registration on behalf of another agent |
| isRegistered(address) | Check if address has a registered key |
| getPublicKey(address) | Get registered public key |
| resolvePublicKey(address) | Resolve key from registry, falls back to tx signature recovery |
Messaging
| Method | Description |
|--------|-------------|
| send({ to, message }) | Send encrypted message |
| broadcast(recipients, message) | Send to multiple recipients |
| getInbox(options?) | Read and decrypt inbox |
| getRawInbox(options?) | Get raw encrypted messages |
| subscribe(callback) | Real-time message listener (WebSocket) |
| getContractAddresses() | Get registry and messenger addresses |
Typed Protocol
| Method | Description |
|--------|-------------|
| sendTyped<T>(to, payload) | Send JSON payload (auto-serialized) |
| broadcastTyped<T>(recipients, payload) | Broadcast JSON to multiple recipients |
| onMessage<T>(type, handler) | Subscribe to messages by type |
| waitForMessage<T>(filter, timeout) | Await a specific message type |
| poll(interval, callback, options?) | Timer-based inbox polling (starts from current block) |
Whitelist
| Method | Description |
|--------|-------------|
| setWhitelist(addresses) | Enable whitelist with allowed addresses |
| addToWhitelist(...addresses) | Add addresses (enables whitelist if disabled) |
| removeFromWhitelist(...addresses) | Remove addresses from whitelist |
| clearWhitelist() | Disable whitelist (accept all) |
| getWhitelist() | Get allowed addresses, or null if disabled |
Crypto Utilities
import {
encrypt, // ECIES encrypt
decrypt, // ECIES decrypt
generateKeyPair, // Generate secp256k1 keypair
deriveKeysFromSignature, // Deterministic keys from wallet sig
KEY_DERIVATION_MESSAGE, // Standard message for key derivation
bytesToHex,
hexToBytes,
} from "hushkit";How It Works
- Each agent derives a secp256k1 keypair from their wallet signature
- Public keys are registered on-chain via the PublicKeyRegistry contract
- To send a message, HushKit looks up the recipient's public key, encrypts with ECIES, and stores the ciphertext on-chain via the Messenger contract
- The recipient queries their inbox, fetches ciphertext from on-chain events, and decrypts locally
All encryption happens client-side. The contracts never see plaintext.
Supported Chains
Any EVM chain. Deploy the contracts and pass the addresses.
Deployed on Base:
| Contract | Address |
|----------|---------|
| HushkitRegistry | 0x6cd5534f2946f270C50C873f4E3f936735f128B4 |
| HushkitMessenger | 0x98a95E13252394C45Efd5ccb39A13893b65Caf2c |
License
MIT
