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 (cre command)
  • 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.json on 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

  1. Register an agent via CLI or SDK
  2. Submit a protected action
  3. Run ./simulate.sh <TX_HASH> to trigger CRE analysis
  4. Verify the action is resolved on-chain
  5. Check the agent's ENS text records for updated threat score