Self-Hosting Guide
This guide covers deploying your own ENShell infrastructure: the smart contract, relay service, CRE workflow, and SDK configuration. All components are open source.
Prerequisites
- Node.js >= 22.10.0
- An Ethereum wallet with Sepolia ETH (or target network ETH)
- An ENS domain (wrapped in NameWrapper)
- Chainlink CRE CLI (
crecommand) - An Anthropic API key (for Claude analysis)
1. Deploy the Smart Contract
Clone and Install
git clone https://github.com/0xenshell/contract
cd contract
npm install
Configure Deployment Parameters
Create ignition/parameters/your-network.json:
{
"AgentFirewall": {
"ensResolver": "0xYourENSResolverAddress",
"nameWrapper": "0xYourNameWrapperAddress",
"ensParentNode": "0xYourDomainNamehash",
"forwarder": "0xKeystoneForwarderAddress"
}
}
| Parameter | How to Get It |
|---|---|
ensResolver |
The ENS Public Resolver for your network |
nameWrapper |
The ENS NameWrapper for your network |
ensParentNode |
namehash('yourdomain.eth') -- compute with ethers.namehash() |
forwarder |
The Chainlink KeystoneForwarder address for your network |
Wrap Your ENS Domain
Your parent domain must be wrapped in the NameWrapper before the contract can create subdomains:
npx hardhat run scripts/wrap-ens.ts --network sepolia
Deploy
bash scripts/deploy.sh
This deploys the contract and runs approve-ens.ts to grant the contract operator permissions on:
- ENS Registry
- NameWrapper
- Public Resolver
Verify the Deployment
npx hardhat test --network sepolia
Note the deployed contract address from the output.
2. Deploy the Relay
The relay is a lightweight Node.js HTTP server that stores encrypted payloads and analysis results.
Clone and Install
git clone https://github.com/0xenshell/relay
cd relay
npm install
Configure
The relay has minimal configuration. Set the port via environment:
PORT=3001
Deploy
The relay can run on any Node.js hosting platform. For Railway:
railway up
For a basic server:
npm start
Endpoints
| Method | Path | Purpose |
|---|---|---|
PUT |
/relay/:hash |
Store encrypted payload |
GET |
/relay/:hash |
Retrieve encrypted payload |
POST |
/analysis/:actionId |
Store CRE analysis |
GET |
/analysis/:actionId |
Retrieve analysis |
POST |
/agents/:agentId |
Register/update agent |
PATCH |
/agents/:agentId |
Partial agent update |
GET |
/agents |
List all agents |
GET |
/stats |
Network statistics |
Persistence
- Agent data: Persisted to
agents.jsonon disk (30-second flush interval) - Encrypted payloads: In-memory with 24-hour TTL (lost on restart)
- Analysis results: In-memory with 24-hour TTL (lost on restart)
For production, consider adding a persistent storage backend for payloads and analyses.
3. Configure the CRE Workflow
Generate Oracle Key Pair
node -e "
const { getPublicKeyFromPrivate } = require('@enshell/sdk');
const crypto = require('crypto');
const privateKey = crypto.randomBytes(32).toString('hex');
console.log('Private:', privateKey);
console.log('Public:', getPublicKeyFromPrivate(privateKey));
"
Save the private key securely. The public key will be distributed in the SDK config.
Clone and Configure
git clone https://github.com/0xenshell/cre-workflow
cd cre-workflow
Create .env:
ANTHROPIC_API_KEY=sk-ant-...
ORACLE_PRIVATE_KEY=your-oracle-private-key-hex
CRE_ETH_PRIVATE_KEY=your-funded-signer-private-key
Update firewall-analyzer/config.staging.json:
{
"chainSelectorName": "ethereum-testnet-sepolia",
"firewallContractAddress": "0xYourDeployedContractAddress",
"relayUrl": "https://your-relay-url.com"
}
Fund the CRE Signer
The CRE signer needs ETH to pay gas for writeReport transactions:
# Send ~0.5 ETH to the CRE signer address derived from CRE_ETH_PRIVATE_KEY
Test the Workflow
./simulate.sh <TX_HASH>
Where <TX_HASH> is a transaction containing an ActionSubmitted event from your contract.
4. Configure the SDK
When using a custom deployment, override the default network config:
import { ENShell, Network } from '@enshell/sdk';
const client = new ENShell({
network: Network.SEPOLIA,
signer: yourSigner,
contractAddress: '0xYourDeployedContractAddress',
rpcUrl: 'https://your-rpc-endpoint.com',
});
For the encryption to work with your oracle, you need to use your oracle's public key when calling encryptForOracle() directly. The protect() method reads the public key from NETWORK_CONFIG, so for custom deployments you may need to call the lower-level methods:
import { encryptForOracle, RelayClient } from '@enshell/sdk';
import { keccak256, toUtf8Bytes } from 'ethers';
const relay = new RelayClient('https://your-relay-url.com');
const instruction = 'Swap 0.05 ETH for USDC';
const instructionHash = keccak256(toUtf8Bytes(instruction));
// Encrypt with YOUR oracle's public key
const encrypted = encryptForOracle(instruction, 'your-oracle-public-key-hex');
await relay.put(instructionHash, encrypted);
// Submit on-chain
const result = await client.submitAction(
'agent-id',
'0xTargetAddress',
'0.05',
'0x',
instructionHash,
);
5. Configure the CLI
Set environment variables for the CLI to point at your deployment:
# .env
ENSHELL_RPC_URL=https://your-rpc-endpoint.com
ENSHELL_CONTRACT_ADDRESS=0xYourDeployedContractAddress
ENSHELL_PRIVATE_KEY=0xYourPrivateKey
The CLI reads ENSHELL_CONTRACT_ADDRESS to override the default Sepolia address.
Architecture Checklist
After deploying all components, verify the full pipeline:
- Contract deployed and ENS permissions granted
- Relay running and accessible
- Oracle key pair generated (private in
.env, public in SDK config) - CRE signer funded with ETH
- CRE workflow configured with contract address and relay URL
- SDK/CLI configured to point at your contract and RPC
End-to-End Test
- Register an agent via CLI or SDK
- Submit a protected action
- Run
./simulate.sh <TX_HASH>to trigger CRE analysis - Verify the action is resolved on-chain
- Check the agent's ENS text records for updated threat score