Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support TEE logging and support running eliza in Intel SGX #1470

Merged
merged 21 commits into from
Jan 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,8 @@ ZEROG_FLOW_ADDRESS=
TEE_MODE=OFF # LOCAL | DOCKER | PRODUCTION
WALLET_SECRET_SALT= # ONLY define if you want to use TEE Plugin, otherwise it will throw errors

ENABLE_TEE_LOG=false # Set to true to enable TEE logging, only available when running eliza in TEE

# Flow Blockchain Configuration
FLOW_ADDRESS=
FLOW_PRIVATE_KEY= # Private key for SHA3-256 + P256 ECDSA
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,7 @@ coverage
.eslintcache

agent/content

eliza.manifest
eliza.manifest.sgx
eliza.sig
59 changes: 59 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Copyright (C) 2024 Gramine contributors
# SPDX-License-Identifier: BSD-3-Clause

THIS_DIR := $(dir $(lastword $(MAKEFILE_LIST)))
NODEJS_DIR ?= /usr/bin

ARCH_LIBDIR ?= /lib/$(shell $(CC) -dumpmachine)

ifeq ($(DEBUG),1)
GRAMINE_LOG_LEVEL = debug
else
GRAMINE_LOG_LEVEL = error
endif

.PHONY: all
all: eliza.manifest
ifeq ($(SGX),1)
all: eliza.manifest.sgx eliza.sig
endif

.PHONY: eliza.manifest
eliza.manifest: eliza.manifest.template
gramine-manifest \
-Dlog_level=$(GRAMINE_LOG_LEVEL) \
-Darch_libdir=$(ARCH_LIBDIR) \
-Dnodejs_dir=$(NODEJS_DIR) \
$< >$@

# Make on Ubuntu <= 20.04 doesn't support "Rules with Grouped Targets" (`&:`),
# for details on this workaround see
# https://github.com/gramineproject/gramine/blob/e8735ea06c/CI-Examples/helloworld/Makefile
eliza.manifest.sgx eliza.sig: sgx_sign
@:

.INTERMEDIATE: sgx_sign
sgx_sign: eliza.manifest
gramine-sgx-sign \
--manifest $< \
--output $<.sgx

ifeq ($(SGX),)
GRAMINE = gramine-direct
else
GRAMINE = gramine-sgx
endif

# Start the default character:
# SGX=1 make start
# Start a specific character by passing arguments:
# SGX=1 make start -- --character "character/your_character_file.json"
.PHONY: start
start: all
$(GRAMINE) ./eliza --loader ts-node/esm src/index.ts --isRoot $(filter-out $@,$(MAKECMDGOALS))
.PHONY: clean
clean:
$(RM) *.manifest *.manifest.sgx *.sig

.PHONY: distclean
distclean: clean
2 changes: 2 additions & 0 deletions agent/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,9 @@
"@elizaos/plugin-giphy": "workspace:*",
"@elizaos/plugin-ton": "workspace:*",
"@elizaos/plugin-sui": "workspace:*",
"@elizaos/plugin-sgx": "workspace:*",
"@elizaos/plugin-tee": "workspace:*",
"@elizaos/plugin-tee-log": "workspace:*",
"@elizaos/plugin-tee-marlin": "workspace:*",
"@elizaos/plugin-multiversx": "workspace:*",
"@elizaos/plugin-near": "workspace:*",
Expand Down
8 changes: 8 additions & 0 deletions agent/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,9 @@ import { solanaPlugin } from "@elizaos/plugin-solana";
import { solanaAgentkitPlguin } from "@elizaos/plugin-solana-agentkit";
import { storyPlugin } from "@elizaos/plugin-story";
import { suiPlugin } from "@elizaos/plugin-sui";
import { sgxPlugin } from "@elizaos/plugin-sgx";
import { TEEMode, teePlugin } from "@elizaos/plugin-tee";
import { teeLogPlugin } from "@elizaos/plugin-tee-log";
import { teeMarlinPlugin } from "@elizaos/plugin-tee-marlin";
import { tonPlugin } from "@elizaos/plugin-ton";
import { webSearchPlugin } from "@elizaos/plugin-web-search";
Expand Down Expand Up @@ -664,6 +666,12 @@ export async function createAgent(
]
: []),
...(teeMode !== TEEMode.OFF && walletSecretSalt ? [teePlugin] : []),
getSecret(character, "SGX") ? sgxPlugin : null,
(getSecret(character, "ENABLE_TEE_LOG") &&
((teeMode !== TEEMode.OFF && walletSecretSalt) ||
getSecret(character, "SGX")))
? teeLogPlugin
: null,
getSecret(character, "COINBASE_API_KEY") &&
getSecret(character, "COINBASE_PRIVATE_KEY") &&
getSecret(character, "COINBASE_NOTIFICATION_URI")
Expand Down
84 changes: 84 additions & 0 deletions eliza.manifest.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Copyright (C) 2024 Gramine contributors
# SPDX-License-Identifier: BSD-3-Clause

# Node.js manifest file example

libos.entrypoint = "{{ nodejs_dir }}/node"

fs.start_dir = "/agent"

loader.log_level = "{{ log_level }}"

loader.env.LD_LIBRARY_PATH = "/lib:{{ arch_libdir }}:/usr/{{ arch_libdir }}"

# Insecure configuration for loading arguments and environment variables
# Do not set these configurations in production
loader.insecure__use_cmdline_argv = true
loader.insecure__use_host_env = true

fs.mounts = [
{ uri = "file:{{ gramine.runtimedir() }}", path = "/lib" },
{ uri = "file:{{ arch_libdir }}", path = "{{ arch_libdir }}" },
{ uri = "file:/usr/{{ arch_libdir }}", path = "/usr/{{ arch_libdir }}" },
{ uri = "file:{{ nodejs_dir }}/node", path = "{{ nodejs_dir }}/node" },
{ type = "tmpfs", path = "/tmp" },
{ type = "tmpfs", path = "/agent/content_cache" },
]

sys.enable_extra_runtime_domain_names_conf = true
sys.fds.limit = 65535

sgx.debug = false
sgx.remote_attestation = "dcap"
sgx.max_threads = 64

# Some dependencies of Eliza utilize WebAssembly (WASM).
# Initializing WASM requires a substantial amount of memory.
# If there is insufficient memory, you may encounter the following error:
# RangeError: WebAssembly.instantiate(): Out of memory: Cannot allocate Wasm memory for a new instance.
# To address this, we set the enclave size to 64GB.
sgx.enclave_size = "64G"

# `use_exinfo = true` is needed because Node.js uses memory mappings with `MAP_NORESERVE`, which
# will defer page accepts to page-fault events when EDMM is enabled
sgx.edmm_enable = {{ 'true' if env.get('EDMM', '0') == '1' else 'false' }}
sgx.use_exinfo = {{ 'true' if env.get('EDMM', '0') == '1' else 'false' }}

sgx.trusted_files = [
"file:{{ gramine.runtimedir() }}/",
"file:{{ arch_libdir }}/",
"file:/usr/{{ arch_libdir }}/",
"file:{{ nodejs_dir }}/node",
"file:characters/",
"file:agent/src/",
"file:agent/package.json",
"file:agent/tsconfig.json",
"file:package.json",
"file:.env",

# Add these files to sgx.trusted_files in production and remove them from sgx.allowed_files.
# Trusting these files requires a high-performance SGX machine due to the large number of files,
# which could significantly increase startup time.
# To mitigate startup time degradation, we use allowed_files in development.
#
# "file:node_modules/",
# "file:packages/",
# These files are symbolic links to node_modules,
# and Gramine does not support adding symbolic link directories to sgx.trusted_files.
# Therefore, we must add each directory individually to sgx.trusted_files.
# "file:agent/node_modules/@elizaos/adapter-sqlite/",
# "file:agent/node_modules/@elizaos/.../",
]

# Insecure configuration. Use gramine encrypted fs to store data in production.
sgx.allowed_files = [
"file:agent/data/",
"file:agent/model.gguf",

# Move these files to sgx.trusted_files in production.
"file:node_modules/",
"file:packages/",
"file:agent/node_modules/",
]

loader.env.SGX = "1"
1 change: 1 addition & 0 deletions packages/client-direct/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"dependencies": {
"@elizaos/core": "workspace:*",
"@elizaos/plugin-image-generation": "workspace:*",
"@elizaos/plugin-tee-log": "workspace:*",
"@types/body-parser": "1.19.5",
"@types/cors": "2.8.17",
"@types/express": "5.0.0",
Expand Down
145 changes: 145 additions & 0 deletions packages/client-direct/src/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,148 @@ curl -X GET "http://localhost:3000/fine-tune/8566c47a-ada8-441c-95bc-7bb07656c4c
-H "Content-Type: application/json" \
-H "Authorization: Bearer jvBpxrTNqGqhnfQhSEqCdsG6aTSP8IBL".
```

# TEE Logging

TEE Logging is a feature that allows you to log the activities of your agents. Through these logs, you can verify that the actions of the agents are protected by the TEE and that they are executed autonomously by Eliza, without any third-party interference.

## Setup

You need to setup the TEE log plugin first. Follow the [TEE Log Plugin](../plugin-tee-log/README.md) to setup the plugin.

## Get all TEE agents Information

```bash
curl -X GET --location "http://localhost:3000/tee/agents"
```

Example response when success:

```json
{
"agents": [
{
"id": "f18738bb-edab-45f6-805d-7f26dbfdba87",
"agentId": "75490f32-c06a-0005-9804-339453d3fe2f",
"agentName": "tea",
"createdAt": 1735222963153,
"publicKey": "02e1a9dde5462ee40bc2df7cc3f0dc88c6e582ea1c4ccf5a30e9dd7fbed736b0fe",
"attestation": "{\"quote\":\"0x03000200000000000...d2d2d2d0a00\",\"timestamp\":1735222963152}"
}
],
"attestation": "{\"quote\":\"0x0300020000000...4452d2d2d2d2d0a00\",\"timestamp\":1735223101255}"
}
```

Note that the user report included in the attestation contains the SHA256 hash of the value of the "agents" field. Specifically, it is calculated as follows: `SHA256(JSON.stringify(agents value))`. By verifying the attestation, you can retrieve this hash value and ensure the integrity of the agents' information.


Example response when error:

```json
{
"error": "Failed to get TEE agents"
}
```

## Get TEE agent Information by agentId

```bash
curl -X GET --location "http://localhost:3000/tee/agents/75490f32-c06a-0005-9804-339453d3fe2f"
```

Example response when success:

```json
{
"agent": {
"id": "f18738bb-edab-45f6-805d-7f26dbfdba87",
"agentId": "75490f32-c06a-0005-9804-339453d3fe2f",
"agentName": "tea",
"createdAt": 1735222963153,
"publicKey": "02e1a9dde5462ee40bc2df7cc3f0dc88c6e582ea1c4ccf5a30e9dd7fbed736b0fe",
"attestation": "{\"quote\":\"0x0300020...452d2d2d2d2d0a00\",\"timestamp\":1735222963152}"
},
"attestation": "{\"quote\":\"0x03000200000000000...d2d2d2d2d0a00\",\"timestamp\":1735223294916}"
}
```

Note that the user report included in the attestation contains the SHA256 hash of the value of the "agent" field. Specifically, it is calculated as follows: `SHA256(JSON.stringify(agent value))`. By verifying the attestation, you can retrieve this hash value and ensure the integrity of the agent's information.

Example response when error:

```json
{
"error": "Failed to get TEE agent"
}
```

## Get TEE log

```bash
curl -X POST --location "http://localhost:3000/tee/logs" \
-H "Content-Type: application/json" \
-d '{
"query": {
"agentId": "75490f32-c06a-0005-9804-339453d3fe2f"
},
"page": 1,
"pageSize": 10
}'
```

There are optional parameters in the `query` parameter:

- **agentId**: (string, optional) The ID of the agent whose logs you want to retrieve.
- **roomId**: (string, optional) The ID of the room associated with the logs.
- **userId**: (string, optional) The ID of the user related to the logs.
- **type**: (string, optional) The type of logs to filter.
- **containsContent**: (string, optional) A substring to search for within the log content.
- **startTimestamp**: (number, optional) The starting timestamp for filtering logs.
- **endTimestamp**: (number, optional) The ending timestamp for filtering logs.


Example response when success:

```json
{
"logs": {
"page": 1,
"pageSize": 10,
"total": 2,
"data": [
{
"id": "01aac44e-d482-42df-8acc-6e6bfbb798f0",
"agentId": "75490f32-c06a-0005-9804-339453d3fe2f",
"roomId": "322d5683-fe3c-056a-8f1a-6b002e0a5c22",
"userId": "12dea96f-ec20-0935-a6ab-75692c994959",
"type": "Action:CONTINUE",
"content": "Continue",
"timestamp": 1735222998263,
"signature": "0x304402201a5bd4eb5807293ba0612b835eaaa56742c04603dbe08e3c7d247cdae3dc4b6f022034a165e1d63f1d58cb0976f615f6acd052f5e11154cef76d7c14c8ba99249833"
},
{
"id": "6275e742-3ebf-477c-ab45-99d2c701c4b5",
"agentId": "75490f32-c06a-0005-9804-339453d3fe2f",
"roomId": "322d5683-fe3c-056a-8f1a-6b002e0a5c22",
"userId": "12dea96f-ec20-0935-a6ab-75692c994959",
"type": "Action:CONTINUE",
"content": "Continue",
"timestamp": 1735223036272,
"signature": "0x304402201a5bd4eb5807293ba0612b835eaaa56742c04603dbe08e3c7d247cdae3dc4b6f022034a165e1d63f1d58cb0976f615f6acd052f5e11154cef76d7c14c8ba99249833"
}
]
},
"attestation": "{\"quote\":\"0x0300020000000000...4154452d2d2d2d2d0a00\",\"timestamp\":1735223364956}"
}
```

Note that the user report included in the attestation contains the SHA256 hash of the value of the "logs" field. Specifically, it is calculated as follows: `SHA256(JSON.stringify(logs value))`. By verifying the attestation, you can retrieve this hash value and ensure the integrity of the logs

Example response when error:

```json
{
"error": "Failed to get TEE logs"
}
```
Loading
Loading