eigen-skills 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +753 -0
- package/index.js +23 -0
- package/package.json +29 -0
- package/scripts/demo.js +93 -0
- package/skills/eigen-avs/SKILL.md +92 -0
- package/skills/eigen-avs/scripts/avs-api.js +131 -0
- package/skills/eigen-compute/SKILL.md +198 -0
- package/skills/eigen-compute/scripts/compute-api.js +153 -0
- package/skills/eigen-da/SKILL.md +148 -0
- package/skills/eigen-da/scripts/da-api.js +128 -0
- package/skills/eigen-delegation/SKILL.md +98 -0
- package/skills/eigen-delegation/scripts/delegation-api.js +160 -0
- package/skills/eigen-restaking/SKILL.md +92 -0
- package/skills/eigen-restaking/scripts/eigen-api.js +186 -0
- package/skills/eigen-rewards/SKILL.md +83 -0
- package/skills/eigen-rewards/scripts/rewards-api.js +108 -0
package/index.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* eigen-skills ā EigenLayer data & infra skills for AI agents
|
|
3
|
+
*
|
|
4
|
+
* Install: npx skills add zeeshan8281/eigen-skills
|
|
5
|
+
* Usage: Agents discover SKILL.md files in skills/ automatically.
|
|
6
|
+
* For programmatic use, require this module.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const EigenAPI = require('./skills/eigen-restaking/scripts/eigen-api');
|
|
10
|
+
const AVSAPI = require('./skills/eigen-avs/scripts/avs-api');
|
|
11
|
+
const RewardsAPI = require('./skills/eigen-rewards/scripts/rewards-api');
|
|
12
|
+
const DelegationAPI = require('./skills/eigen-delegation/scripts/delegation-api');
|
|
13
|
+
const EigenCompute = require('./skills/eigen-compute/scripts/compute-api');
|
|
14
|
+
const EigenDA = require('./skills/eigen-da/scripts/da-api');
|
|
15
|
+
|
|
16
|
+
module.exports = {
|
|
17
|
+
EigenAPI,
|
|
18
|
+
AVSAPI,
|
|
19
|
+
RewardsAPI,
|
|
20
|
+
DelegationAPI,
|
|
21
|
+
EigenCompute,
|
|
22
|
+
EigenDA,
|
|
23
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "eigen-skills",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Agent Skills for EigenLayer ā restaking, AVS, operators, rewards, delegation data powered by EigenExplorer API",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"agents": {
|
|
7
|
+
"skills": "skills/"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "node scripts/test-all.js",
|
|
11
|
+
"demo": "node scripts/demo.js"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"eigenlayer",
|
|
15
|
+
"eigen",
|
|
16
|
+
"restaking",
|
|
17
|
+
"avs",
|
|
18
|
+
"agent-skills",
|
|
19
|
+
"ai-agent",
|
|
20
|
+
"claude",
|
|
21
|
+
"openclaw",
|
|
22
|
+
"eigenexplorer"
|
|
23
|
+
],
|
|
24
|
+
"author": "zeeshan8281",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"axios": "^1.7.0"
|
|
28
|
+
}
|
|
29
|
+
}
|
package/scripts/demo.js
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Demo ā run all eigen-agent-skills to verify they work.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* EIGEN_API_KEY=your_key node scripts/demo.js
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const EigenAPI = require('../skills/eigen-restaking/scripts/eigen-api');
|
|
11
|
+
const AVSAPI = require('../skills/eigen-avs/scripts/avs-api');
|
|
12
|
+
const RewardsAPI = require('../skills/eigen-rewards/scripts/rewards-api');
|
|
13
|
+
const DelegationAPI = require('../skills/eigen-delegation/scripts/delegation-api');
|
|
14
|
+
|
|
15
|
+
const API_KEY = process.env.EIGEN_API_KEY;
|
|
16
|
+
|
|
17
|
+
if (!API_KEY) {
|
|
18
|
+
console.error('ā Set EIGEN_API_KEY env var. Get a free key at https://developer.eigenexplorer.com');
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async function run() {
|
|
23
|
+
console.log('\nš āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā');
|
|
24
|
+
console.log(' Eigen Agent Skills ā Demo');
|
|
25
|
+
console.log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n');
|
|
26
|
+
|
|
27
|
+
const eigen = new EigenAPI(API_KEY);
|
|
28
|
+
const avs = new AVSAPI(API_KEY);
|
|
29
|
+
const rewards = new RewardsAPI(API_KEY);
|
|
30
|
+
const delegation = new DelegationAPI(API_KEY);
|
|
31
|
+
|
|
32
|
+
// 1. Health check
|
|
33
|
+
console.log('š” Health check...');
|
|
34
|
+
const health = await eigen.healthCheck();
|
|
35
|
+
console.log(` Status: ${health.ok ? 'ā
Healthy' : 'ā Failed'}`);
|
|
36
|
+
if (health.tvl) console.log(` EigenLayer TVL: ${JSON.stringify(health.tvl)}`);
|
|
37
|
+
console.log('');
|
|
38
|
+
|
|
39
|
+
// 2. Ecosystem metrics
|
|
40
|
+
console.log('š Ecosystem Metrics...');
|
|
41
|
+
const metrics = await eigen.getMetrics();
|
|
42
|
+
console.log(` ${JSON.stringify(metrics, null, 2).substring(0, 500)}`);
|
|
43
|
+
console.log('');
|
|
44
|
+
|
|
45
|
+
// 3. Top 5 operators by TVL
|
|
46
|
+
console.log('š„ Top 5 Operators by TVL...');
|
|
47
|
+
const ops = await eigen.getOperators({ sortByTvl: 'desc', take: 5 });
|
|
48
|
+
if (ops.data) {
|
|
49
|
+
ops.data.forEach((op, i) => {
|
|
50
|
+
console.log(` ${i + 1}. ${op.metadataName || op.address} ā TVL: ${JSON.stringify(op.tvl?.tvl || 'N/A')} ā Stakers: ${op.totalStakers}`);
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
console.log('');
|
|
54
|
+
|
|
55
|
+
// 4. Top 5 AVS by TVL
|
|
56
|
+
console.log('š”ļø Top 5 AVS by TVL...');
|
|
57
|
+
const avsList = await avs.getAllAVS({ sortByTvl: 'desc', take: 5 });
|
|
58
|
+
if (avsList.data) {
|
|
59
|
+
avsList.data.forEach((a, i) => {
|
|
60
|
+
console.log(` ${i + 1}. ${a.metadataName || a.address} ā TVL: ${JSON.stringify(a.tvl?.tvl || 'N/A')}`);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
console.log('');
|
|
64
|
+
|
|
65
|
+
// 5. Top 5 operators by APY
|
|
66
|
+
console.log('š° Top 5 Operators by APY...');
|
|
67
|
+
const topApy = await rewards.getTopOperatorsByAPY(5);
|
|
68
|
+
if (topApy.data) {
|
|
69
|
+
topApy.data.forEach((op, i) => {
|
|
70
|
+
console.log(` ${i + 1}. ${op.metadataName || op.address} ā APY: ${op.maxApy || 'N/A'}`);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
console.log('');
|
|
74
|
+
|
|
75
|
+
// 6. Top 5 operators by delegation
|
|
76
|
+
console.log('š¤ Top 5 Operators by Delegation (staker count)...');
|
|
77
|
+
const topDel = await delegation.getTopDelegatedOperators(5);
|
|
78
|
+
if (topDel.data) {
|
|
79
|
+
topDel.data.forEach((op, i) => {
|
|
80
|
+
console.log(` ${i + 1}. ${op.metadataName || op.address} ā Stakers: ${op.totalStakers} ā AVS: ${op.totalAvs}`);
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
console.log('');
|
|
84
|
+
|
|
85
|
+
console.log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā');
|
|
86
|
+
console.log(' ā
Demo complete');
|
|
87
|
+
console.log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
run().catch(err => {
|
|
91
|
+
console.error('Demo failed:', err.message);
|
|
92
|
+
process.exit(1);
|
|
93
|
+
});
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: eigen-avs
|
|
3
|
+
description: "Query EigenLayer AVS (Actively Validated Services) ā list services, operators per AVS, stakers per AVS, registration events, and operator-sets"
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
metadata:
|
|
6
|
+
emoji: "š”ļø"
|
|
7
|
+
tags: ["eigenlayer", "avs", "services", "operators", "security"]
|
|
8
|
+
user-invocable: true
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# EigenLayer AVS Skill
|
|
12
|
+
|
|
13
|
+
Query live data about Actively Validated Services (AVS) on EigenLayer: service listings, operators securing each AVS, staker delegations, registration events, and operator-sets.
|
|
14
|
+
|
|
15
|
+
## Data Source
|
|
16
|
+
|
|
17
|
+
**EigenExplorer API** ā `https://api.eigenexplorer.com`
|
|
18
|
+
- Auth: `x-api-token` header (free key at https://developer.eigenexplorer.com)
|
|
19
|
+
|
|
20
|
+
## When to use this skill
|
|
21
|
+
|
|
22
|
+
Use when the user asks about:
|
|
23
|
+
- AVS (Actively Validated Services) on EigenLayer
|
|
24
|
+
- Which operators are securing a specific AVS
|
|
25
|
+
- Stakers delegated to a specific AVS
|
|
26
|
+
- AVS registration/deregistration events
|
|
27
|
+
- Operator-sets for an AVS
|
|
28
|
+
- Comparing AVS by TVL, staker count, or APY
|
|
29
|
+
|
|
30
|
+
## How to query
|
|
31
|
+
|
|
32
|
+
### Get all AVS (sorted by TVL)
|
|
33
|
+
```bash
|
|
34
|
+
curl -s "https://api.eigenexplorer.com/avs?withTvl=true&sortByTvl=desc&take=10" -H "x-api-token: $EIGEN_API_KEY"
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Search AVS by name
|
|
38
|
+
```bash
|
|
39
|
+
curl -s "https://api.eigenexplorer.com/avs?searchByText=eigenda&withTvl=true" -H "x-api-token: $EIGEN_API_KEY"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Get a specific AVS
|
|
43
|
+
```bash
|
|
44
|
+
curl -s "https://api.eigenexplorer.com/avs/0xAVS_ADDRESS?withTvl=true" -H "x-api-token: $EIGEN_API_KEY"
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Get operators registered to an AVS
|
|
48
|
+
```bash
|
|
49
|
+
curl -s "https://api.eigenexplorer.com/avs/0xAVS_ADDRESS/operators?take=20" -H "x-api-token: $EIGEN_API_KEY"
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Get stakers for an AVS
|
|
53
|
+
```bash
|
|
54
|
+
curl -s "https://api.eigenexplorer.com/avs/0xAVS_ADDRESS/stakers?take=20" -H "x-api-token: $EIGEN_API_KEY"
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Get registration events for an AVS
|
|
58
|
+
```bash
|
|
59
|
+
curl -s "https://api.eigenexplorer.com/avs/0xAVS_ADDRESS/events/registration?take=20" -H "x-api-token: $EIGEN_API_KEY"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Get operator-sets for an AVS
|
|
63
|
+
```bash
|
|
64
|
+
curl -s "https://api.eigenexplorer.com/avs/0xAVS_ADDRESS/operator-sets?take=20" -H "x-api-token: $EIGEN_API_KEY"
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Get all AVS addresses (lightweight)
|
|
68
|
+
```bash
|
|
69
|
+
curl -s "https://api.eigenexplorer.com/avs/addresses" -H "x-api-token: $EIGEN_API_KEY"
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Response Format
|
|
73
|
+
|
|
74
|
+
Format results for the user with:
|
|
75
|
+
- **Bold AVS names** and abbreviated addresses
|
|
76
|
+
- TVL in human-readable form (e.g., "$500M")
|
|
77
|
+
- Number of operators securing the AVS
|
|
78
|
+
- Number of stakers
|
|
79
|
+
- APY if available
|
|
80
|
+
- Active/inactive status for operator registrations
|
|
81
|
+
- Use bullet points, never tables in chat
|
|
82
|
+
|
|
83
|
+
## Programmatic Usage
|
|
84
|
+
|
|
85
|
+
```javascript
|
|
86
|
+
const AVSAPI = require('eigen-agent-skills/skills/eigen-avs/scripts/avs-api');
|
|
87
|
+
const api = new AVSAPI('YOUR_API_KEY');
|
|
88
|
+
|
|
89
|
+
const allAVS = await api.getAllAVS({ sortByTvl: 'desc', take: 10 });
|
|
90
|
+
const operators = await api.getAVSOperators('0xAVS_ADDRESS');
|
|
91
|
+
const stakers = await api.getAVSStakers('0xAVS_ADDRESS');
|
|
92
|
+
```
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EigenLayer AVS (Actively Validated Services) API Client
|
|
3
|
+
*
|
|
4
|
+
* Data source: EigenExplorer REST API
|
|
5
|
+
* Covers: AVS listing, detail, operators per AVS, stakers per AVS, registration events
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const axios = require('axios');
|
|
9
|
+
|
|
10
|
+
const MAINNET = 'https://api.eigenexplorer.com';
|
|
11
|
+
const HOLESKY = 'https://api-holesky.eigenexplorer.com';
|
|
12
|
+
|
|
13
|
+
class AVSAPI {
|
|
14
|
+
constructor(apiKey, { network = 'mainnet' } = {}) {
|
|
15
|
+
if (!apiKey) throw new Error('EigenExplorer API key required. Get one free at https://developer.eigenexplorer.com');
|
|
16
|
+
this.client = axios.create({
|
|
17
|
+
baseURL: network === 'holesky' ? HOLESKY : MAINNET,
|
|
18
|
+
headers: {
|
|
19
|
+
'x-api-token': apiKey,
|
|
20
|
+
'Content-Type': 'application/json',
|
|
21
|
+
},
|
|
22
|
+
timeout: 30000,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// āāā AVS Listing āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Get all AVS with optional filtering and sorting.
|
|
30
|
+
* @param {object} opts
|
|
31
|
+
* @param {boolean} [opts.withTvl=true]
|
|
32
|
+
* @param {string} [opts.searchByText] - search AVS by name
|
|
33
|
+
* @param {'asc'|'desc'} [opts.sortByTvl]
|
|
34
|
+
* @param {'asc'|'desc'} [opts.sortByApy]
|
|
35
|
+
* @param {'asc'|'desc'} [opts.sortByTotalStakers]
|
|
36
|
+
* @param {number} [opts.skip=0]
|
|
37
|
+
* @param {number} [opts.take=12]
|
|
38
|
+
*/
|
|
39
|
+
async getAllAVS(opts = {}) {
|
|
40
|
+
const params = {
|
|
41
|
+
withTvl: opts.withTvl !== false ? 'true' : 'false',
|
|
42
|
+
skip: opts.skip || 0,
|
|
43
|
+
take: opts.take || 12,
|
|
44
|
+
};
|
|
45
|
+
if (opts.searchByText) params.searchByText = opts.searchByText;
|
|
46
|
+
if (opts.sortByTvl) params.sortByTvl = opts.sortByTvl;
|
|
47
|
+
if (opts.sortByApy) params.sortByApy = opts.sortByApy;
|
|
48
|
+
if (opts.sortByTotalStakers) params.sortByTotalStakers = opts.sortByTotalStakers;
|
|
49
|
+
|
|
50
|
+
const { data } = await this.client.get('/avs', { params });
|
|
51
|
+
return data;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Get AVS addresses only (lightweight).
|
|
56
|
+
*/
|
|
57
|
+
async getAVSAddresses() {
|
|
58
|
+
const { data } = await this.client.get('/avs/addresses');
|
|
59
|
+
return data;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Get detailed info for a single AVS.
|
|
64
|
+
* @param {string} address - AVS contract address
|
|
65
|
+
* @param {boolean} [withTvl=true]
|
|
66
|
+
*/
|
|
67
|
+
async getAVS(address, withTvl = true) {
|
|
68
|
+
const { data } = await this.client.get(`/avs/${address}`, {
|
|
69
|
+
params: { withTvl: withTvl ? 'true' : 'false' },
|
|
70
|
+
});
|
|
71
|
+
return data;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// āāā AVS ā Operators āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Get operators registered to a specific AVS.
|
|
78
|
+
* @param {string} avsAddress
|
|
79
|
+
* @param {object} opts
|
|
80
|
+
*/
|
|
81
|
+
async getAVSOperators(avsAddress, opts = {}) {
|
|
82
|
+
const { data } = await this.client.get(`/avs/${avsAddress}/operators`, {
|
|
83
|
+
params: { skip: opts.skip || 0, take: opts.take || 12 },
|
|
84
|
+
});
|
|
85
|
+
return data;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// āāā AVS ā Stakers āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Get stakers delegated to a specific AVS.
|
|
92
|
+
* @param {string} avsAddress
|
|
93
|
+
* @param {object} opts
|
|
94
|
+
*/
|
|
95
|
+
async getAVSStakers(avsAddress, opts = {}) {
|
|
96
|
+
const { data } = await this.client.get(`/avs/${avsAddress}/stakers`, {
|
|
97
|
+
params: { skip: opts.skip || 0, take: opts.take || 12 },
|
|
98
|
+
});
|
|
99
|
+
return data;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// āāā AVS Registration Events āāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Get registration events for a specific AVS.
|
|
106
|
+
* @param {string} avsAddress
|
|
107
|
+
* @param {object} opts
|
|
108
|
+
*/
|
|
109
|
+
async getAVSRegistrationEvents(avsAddress, opts = {}) {
|
|
110
|
+
const { data } = await this.client.get(`/avs/${avsAddress}/events/registration`, {
|
|
111
|
+
params: { skip: opts.skip || 0, take: opts.take || 12 },
|
|
112
|
+
});
|
|
113
|
+
return data;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// āāā Operator-Sets per AVS āāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Get operator sets for a specific AVS.
|
|
120
|
+
* @param {string} avsAddress
|
|
121
|
+
* @param {object} opts
|
|
122
|
+
*/
|
|
123
|
+
async getAVSOperatorSets(avsAddress, opts = {}) {
|
|
124
|
+
const { data } = await this.client.get(`/avs/${avsAddress}/operator-sets`, {
|
|
125
|
+
params: { skip: opts.skip || 0, take: opts.take || 12 },
|
|
126
|
+
});
|
|
127
|
+
return data;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
module.exports = AVSAPI;
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: eigen-compute
|
|
3
|
+
description: "Deploy and manage applications on EigenCompute TEE (Trusted Execution Environment) ā deploy, monitor, attest, and manage lifecycle via ecloud CLI"
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
metadata:
|
|
6
|
+
emoji: "š"
|
|
7
|
+
tags: ["eigenlayer", "eigencompute", "tee", "tdx", "deploy", "attestation"]
|
|
8
|
+
user-invocable: true
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# EigenCompute Skill
|
|
12
|
+
|
|
13
|
+
Deploy, manage, and attest applications running inside EigenCompute TEE (Trusted Execution Environment) powered by Intel TDX.
|
|
14
|
+
|
|
15
|
+
## What is EigenCompute?
|
|
16
|
+
|
|
17
|
+
EigenCompute runs your Docker containers inside **hardware-isolated Intel TDX TEEs**. Each deployed app gets:
|
|
18
|
+
- **Encrypted memory** ā the host cannot read your app's data
|
|
19
|
+
- **Unique wallet** ā cryptographic identity per deployment
|
|
20
|
+
- **KMS signing key** ā at `/usr/local/bin/kms-signing-public-key.pem`
|
|
21
|
+
- **Sealed secrets** ā env vars are unsealed inside the TEE at runtime
|
|
22
|
+
- **Cryptographic attestation** ā verifiable proof of what code is running
|
|
23
|
+
|
|
24
|
+
## Prerequisites
|
|
25
|
+
|
|
26
|
+
Install the ecloud CLI:
|
|
27
|
+
```bash
|
|
28
|
+
npm install -g @layr-labs/ecloud-cli
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## When to use this skill
|
|
32
|
+
|
|
33
|
+
Use when the user asks about:
|
|
34
|
+
- Deploying to EigenCompute / TEE / EigenCloud
|
|
35
|
+
- Checking app status, logs, or info
|
|
36
|
+
- TEE attestation or verification
|
|
37
|
+
- Managing EigenCompute apps (start, stop, terminate)
|
|
38
|
+
- Dockerfile setup for TEE deployment
|
|
39
|
+
- KMS signing or sealed secrets
|
|
40
|
+
- EigenCompute troubleshooting
|
|
41
|
+
|
|
42
|
+
## How to use
|
|
43
|
+
|
|
44
|
+
### Authentication
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
# Login with existing key
|
|
48
|
+
ecloud auth login
|
|
49
|
+
|
|
50
|
+
# Or generate a new key
|
|
51
|
+
ecloud auth generate --store
|
|
52
|
+
|
|
53
|
+
# Check who you're authenticated as
|
|
54
|
+
ecloud auth whoami
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Create a new app from template
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
ecloud compute app create --name my-app --language typescript
|
|
61
|
+
# Languages: typescript, python, golang, rust
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Deploy from Dockerfile (recommended)
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
ecloud compute app deploy
|
|
68
|
+
```
|
|
69
|
+
- Select **"Build and deploy from Dockerfile"** (most reliable method)
|
|
70
|
+
- Choose **Linux/AMD64** (standard TEE architecture)
|
|
71
|
+
- Estimated cost: ~0.008 ETH per deploy (Sepolia testnet)
|
|
72
|
+
|
|
73
|
+
**IMPORTANT:** "Deploy from registry" method is unreliable ā apps often end up in `Status: Unknown` with no error. Always use "Build from Dockerfile".
|
|
74
|
+
|
|
75
|
+
### Check app status
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
# List all your apps
|
|
79
|
+
ecloud compute app list
|
|
80
|
+
|
|
81
|
+
# Get info for a specific app
|
|
82
|
+
ecloud compute app info <APP_ID>
|
|
83
|
+
|
|
84
|
+
# View logs (may require admin permissions)
|
|
85
|
+
ecloud compute app logs <APP_ID>
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Set environment variables (sealed secrets)
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
ecloud compute app env set \
|
|
92
|
+
MY_SECRET="value" \
|
|
93
|
+
API_KEY="key"
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**Note:** You cannot inspect sealed secrets after they're set. Verify through your app's logging.
|
|
97
|
+
|
|
98
|
+
### App lifecycle
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
# Start a stopped app
|
|
102
|
+
ecloud compute app start <APP_ID>
|
|
103
|
+
|
|
104
|
+
# Stop a running app
|
|
105
|
+
ecloud compute app stop <APP_ID>
|
|
106
|
+
|
|
107
|
+
# Terminate (permanent ā creates new App ID on redeploy)
|
|
108
|
+
ecloud compute app terminate <APP_ID>
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Upgrade (update running app)
|
|
112
|
+
```bash
|
|
113
|
+
ecloud compute app upgrade <APP_ID>
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## TEE Container Internals
|
|
117
|
+
|
|
118
|
+
Inside the TEE container, these are available:
|
|
119
|
+
|
|
120
|
+
| Path | What |
|
|
121
|
+
|------|------|
|
|
122
|
+
| `/usr/local/bin/compute-source-env.sh` | Sources sealed env vars at runtime |
|
|
123
|
+
| `/usr/local/bin/kms-signing-public-key.pem` | KMS signing public key |
|
|
124
|
+
| `/usr/local/bin/kms-client` | KMS signing client binary |
|
|
125
|
+
|
|
126
|
+
### Entrypoint pattern for TEE
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
#!/bin/bash
|
|
130
|
+
# Source sealed secrets
|
|
131
|
+
if [ -f "/usr/local/bin/compute-source-env.sh" ]; then
|
|
132
|
+
source /usr/local/bin/compute-source-env.sh
|
|
133
|
+
fi
|
|
134
|
+
|
|
135
|
+
# Start your app
|
|
136
|
+
node server.js
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Dockerfile pattern for TEE
|
|
140
|
+
|
|
141
|
+
```dockerfile
|
|
142
|
+
FROM node:20-slim
|
|
143
|
+
WORKDIR /app
|
|
144
|
+
COPY package*.json ./
|
|
145
|
+
RUN npm ci --production
|
|
146
|
+
COPY . .
|
|
147
|
+
EXPOSE 3000
|
|
148
|
+
ENTRYPOINT ["bash", "entrypoint.sh"]
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## TEE Attestation
|
|
152
|
+
|
|
153
|
+
Collect attestation data to prove your app runs in a real TEE:
|
|
154
|
+
|
|
155
|
+
```javascript
|
|
156
|
+
const crypto = require('crypto');
|
|
157
|
+
const fs = require('fs');
|
|
158
|
+
|
|
159
|
+
function getAttestation() {
|
|
160
|
+
return {
|
|
161
|
+
appId: process.env.ECLOUD_APP_ID || null,
|
|
162
|
+
platform: 'Intel TDX (EigenCompute)',
|
|
163
|
+
kmsKeyFingerprint: getKMSFingerprint(),
|
|
164
|
+
nodeVersion: process.version,
|
|
165
|
+
uptimeSeconds: Math.floor(process.uptime()),
|
|
166
|
+
timestamp: new Date().toISOString(),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function getKMSFingerprint() {
|
|
171
|
+
try {
|
|
172
|
+
const pem = fs.readFileSync('/usr/local/bin/kms-signing-public-key.pem', 'utf-8');
|
|
173
|
+
return 'sha256:' + crypto.createHash('sha256').update(pem.trim()).digest('hex');
|
|
174
|
+
} catch { return null; }
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Verify at: `https://verify-sepolia.eigencloud.xyz`
|
|
179
|
+
|
|
180
|
+
## Known Issues & Workarounds
|
|
181
|
+
|
|
182
|
+
- **429 rate limiting** ā The API rate-limits aggressively after deploys. Wait 30-60s before running `app list` or `app info`.
|
|
183
|
+
- **Logs 403** ā Even with "admin viewable" selected, `app logs` may return 403. Add logging within your app and expose it via HTTP as a workaround.
|
|
184
|
+
- **IP changes on every deploy** ā No static IP. Don't hardcode IPs in DNS or webhooks.
|
|
185
|
+
- **App ID changes on terminate + redeploy** ā No persistent identity across deploys.
|
|
186
|
+
- **Secret rotation requires full redeploy** ā No way to update env vars without terminate + deploy.
|
|
187
|
+
|
|
188
|
+
## Programmatic Usage
|
|
189
|
+
|
|
190
|
+
```javascript
|
|
191
|
+
const EigenCompute = require('eigen-skills/skills/eigen-compute/scripts/compute-api');
|
|
192
|
+
const compute = new EigenCompute();
|
|
193
|
+
|
|
194
|
+
// These are wrappers around the ecloud CLI
|
|
195
|
+
const apps = await compute.listApps();
|
|
196
|
+
const info = await compute.getAppInfo('APP_ID');
|
|
197
|
+
const attestation = compute.collectAttestation();
|
|
198
|
+
```
|