Docs
Build With Tee Agent
Deploy contracts, run a Phala Cloud TDX oracle, mint ERC-8004 compatible agents with ERC-7857 private data, then validate and move them without exposing encrypted skills.
Contract Addresses
Feedback Verification
Verify feedback with its URI
Feedback rows can be checked against the on-chain validation registry by sending the `feedbackURI` to the dashboard verifier. The verifier decodes the ERC-8004 feedback JSON, reads the validation response, and confirms it came from the configured TEE verifier.
await fetch("/api/verify", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ feedbackURI }),
});Official agent identity
Deploy an ERC-8004 agent on the official registries, publish the teeOracle service on IPFS, and stay compatible with 8004scan.io.
TEE-backed reputation
Use a shared Validation Module with a TEE verifier/oracle to turn DCAP-backed results into Sybil-resistant feedback for the Reputation Module.
Encrypted private skills
Encrypt agent data such as skills, prompts, and models so only your oracle can decode it, uploaded to 0G Storage, with secure re-encryption during ownership transfer.
MCP
Operate agents from AI clients
`@tee-agent/mcp` exposes stdio and Streamable HTTP tools for creating metadata, minting, running oracles, requesting validation, submitting feedback, and verifying feedback. Write-oriented tools return calldata for the caller to submit with their own wallet.
{
"mcpServers": {
"tee-agent": {
"command": "node",
"args": ["apps/mcp/dist/index.js"]
}
}
}npm run mcp:start:http
POST /mcp
GET /health8004scan, With Validation, Proof And Private Skills
Start with official ERC-8004 discovery, run private encrypted skills through a Phala CVM oracle endpoint, verify TDX quotes, then turn validated runs into Sybil-resistant reputation.
WebApp / User
Deploy CVM + mint agent
Deploy the Phala CVM oracle first, then mint an official ERC-8004 agent with the printed HTTPS endpoint published as its teeOracle service.
Run your oracle and verify proof
Apps call the oracle endpoint for owner-signed runs, then pass the returned proof bundle to /verify when they need off-chain proof.
Request validation
The app writes a ValidationRequest that points at TeeVerifier and references the run payload, output, score, quote, and request hash.
Transfer ownership
When the agent is sold or transferred, the new owner chooses an oracle endpoint and signs the re-encryption flow.
Phala CVM / Oracle
Bring your handler
Copy an oracle example, replace the handler with your app logic, and deploy it as a Phala Cloud CVM with the repo scripts.
Run inside Intel TDX
The oracle checks ownership, decrypts ERC-7857 data only inside the TEE, runs code or model inference, and returns the result with a TDX quote.
Validate with AI
For validation, the oracle can rerun inference with another model at temperature 0 and compare the original result, score, and reasoning.
Re-wrap keys in TEE
The current oracle re-wraps ERC-7857 content keys to the new oracle key inside the TEE without exposing plaintext.
Blockchain
Publish identity + endpoint
AgentRegistry mints ERC-7857 encrypted skills, links the official ERC-8004 identity, and publishes metadata on IPFS. Encoded hash on 0G Storage.
Verify TEE proof
TeeVerifier and Automata DCAP verify Intel TDX quotes before trusting oracle registration, validation proofs, or transfer proofs.
Feed reputation
Validated runs can feed ERC-8004 reputation with TEE proof, reducing Sybil-prone self-reporting from arbitrary wallets.
Move identity + data
The ERC-7857 NFT and linked ERC-8004 identity move together, while encrypted data remains anchored on-chain.
Bring Your Own Oracle Handler
Copy one of the oracle examples under apps/oracle/src/examples, replace the handler with your app logic, then deploy it with npm run oracle:deploy. The printed Phala HTTPS endpoint is the URL published as the ERC-8004 teeOracle service and called by /run, /verify, /validate, and re-encryption flows.
Hardware Trust Boundary
The endpoint is for discovery and execution; the trust root is the TDX quote. Oracle registration binds reportData to the TEE-derived key, and validation quotes bind the agent id, request hash, and score before on-chain verification.
RTMR0 identifies the measured CVM hardware/firmware setup, RTMR1 the Linux kernel, RTMR2 kernel parameters plus initrd/rootfs, and RTMR3 the dstack app compose and runtime events. Matching RTMRs prove a run and verification came from the same measured environment.
Build On Top
Deploy one Phala CVM oracle, point ERC-8004 `teeOracle` services at it, then use the SDK from any app or backend. Remote oracle trust is enforced on-chain through Automata DCAP verification of Intel TDX quotes.
Launch An Agent
Deploy the contracts and oracle, then mint the agent with ERC-721 metadata, ERC-8004 services, and ERC-7857 private data.
Deploy
Deploy contracts and one Phala CVM oracle through the repo scripts. oracle:deploy prints the HTTPS endpoint to use as teeOracle.
# Deploy contracts and write deployments.json
npm run deploy:arbitrumSepolia --workspace=contracts
npm run setup-env --workspace=contracts
# Copy an example oracle or create your own handler
cp apps/oracle/src/examples/prediction-market.ts \
apps/oracle/src/prod/my-oracle.ts
# Fill root .env and apps/oracle/.env, then deploy it
npm run oracle:image
npm run oracle:deploy -- src/prod/my-oracle.ts
# oracle:deploy prints the HTTPS teeOracle endpointHandler
Copy an oracle example or create your own handler; /run receives the owner-signed payload and decrypted private blobs.
const handler = {
async run(payload, ctx) {
const skill = ctx.blobs[0];
const config = ctx.blobs[1];
return {
answer: await runModel({
prompt: skill,
input: payload.question,
config,
}),
};
},
};
await startOracle({ handler, deployments });Mint
Prepare metadata, ERC-8004 services, and ERC-7857 encrypted data, then call AgentRegistry.
const network = getNetworkConfig("arbitrumSepolia");
const deployment = deployments[String(network.chainId)];
const contracts = deployment.contracts;
const config = {
chain: network.chain,
registryAddress: contracts.agentRegistry,
teeVerifierAddress: contracts.teeVerifier,
validationRegistryAddress: contracts.validationRegistry,
identityRegistryAddress: network.identityRegistryAddress,
reputationRegistryAddress: network.reputationRegistryAddress,
rpcUrl,
pinataJwt,
privateKey,
zeroGRpcUrl,
zeroGIndexerUrl,
} satisfies AgentConfig;
const prepared = await prepareMint(config, {
name: "Prediction Agent",
description: "Runs inside my Phala CVM.",
imageUrl,
ownerAddress,
services: [{ name: "teeOracle", endpoint: oracleUrl }],
privateEntries: [{ name: "skill", data: systemPrompt }],
});
await walletClient.writeContract({
address: prepared.contractAddress,
abi: AGENT_REGISTRY_ABI,
functionName: "mint",
args: [
ownerAddress,
prepared.publicMetadataUri, // ERC-721 metadata
prepared.agentMetadataUri, // ERC-8004 services
prepared.intelligentData, // ERC-7857 private data
],
});Use And Transfer
Run the oracle, verify TDX quotes, respond to validation requests, and transfer the encrypted agent safely.
Run then Verify
Call the teeOracle /run endpoint with an owner signature, then verify the returned TDX proof bundle through /verify.
const payload = {
question: "Will ETH close above $4,000 on May 30, 2026?",
url:
"https://api.coingecko.com/api/v3/coins/ethereum/market_chart/range?vs_currency=usd&from=1780099200&to=1780185600",
};
const deadline = Math.floor(Date.now() / 1000) + 300;
const typedData = buildRunTypedData({
oracleAddress,
chainId: network.chainId,
agentId,
payload,
deadline,
});
const signature = await walletClient.signTypedData({
account: ownerAddress,
...typedData,
});
const run = await fetch(`${oracleUrl}/run`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
agentId: agentId.toString(),
registryAddress: contracts.agentRegistry,
payload,
signature,
deadline,
}),
}).then((res) => res.json());
const verified = await fetch(`${oracleUrl}/verify`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ proof: run.proof }),
}).then((res) => res.json());Validate
Write a ValidationRequest, then let the owner worker call /validate so the oracle submits the TEE-backed response.
const requestURI = toDataUri({
...
});
const validation = prepareValidation(config, {
agentId: erc8004AgentId,
validatorAddress: contracts.teeVerifier,
requestURI,
});
await walletClient.writeContract({
address: contracts.validationRegistry,
abi: VALIDATION_REGISTRY_ABI,
functionName: "validationRequest",
args: [
validation.validatorAddress,
BigInt(validation.agentId),
validation.requestURI,
validation.requestHash,
],
});
// Worker watches ValidationRequest events, signs as PRIVATE_KEY,
// then calls the agent teeOracle. The oracle submits validationResponse.
await fetch(`${oracleUrl}/validate`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(validateRequest),
});Transfer
Create an offer, let the recipient accept, then submit the combined transfer transaction.
const offer = await createTransferOffer(config, transferParams);
const toSign = getTransferAccessPayloadsToSign(offer);
const signatures = await wallet.signMessages(toSign);
const acceptance = buildTransferAcceptance(offer, signatures);
await walletClient.writeContract(buildTransferTxArgs(acceptance));