ElizaOS Plugin
Build an ElizaOS agent with decentralized storage capabilities. Three action handlers cover the full storage lifecycle: upload, list, and status.
Install
Plugin Structure
The plugin registers three actions that ElizaOS agents can invoke based on user messages.
// plugins/w3stor/index.tsimport type { Plugin } from "@elizaos/core";
import { storeOnFilecoin } from "./actions/store";import { listStoredFiles } from "./actions/list";import { checkStorageStatus } from "./actions/status";
export const w3storPlugin: Plugin = { name: "w3stor", description: "Decentralized storage via IPFS + Filecoin with x402 micropayments", actions: [storeOnFilecoin, listStoredFiles, checkStorageStatus], evaluators: [], providers: [],};Action Handlers
STORE_ON_FILECOIN
Handles file uploads. Validates user intent from keywords and attachments, then uploads via the w3stor API.
// plugins/w3stor/actions/store.tsimport type { Action, IAgentRuntime, Memory, State } from "@elizaos/core";
const W3S_API = process.env.W3S_API_URL ?? "https://api.w3s.storage";
export const storeOnFilecoin: Action = { name: "STORE_ON_FILECOIN", similes: ["UPLOAD_FILE", "SAVE_TO_IPFS", "STORE_DATA", "PIN_FILE"], description: "Upload a file to IPFS and replicate across Filecoin storage providers",
validate: async (_runtime: IAgentRuntime, message: Memory) => { // Check if message contains file reference or upload intent const text = message.content.text?.toLowerCase() ?? ""; return ( text.includes("upload") || text.includes("store") || text.includes("save") || text.includes("pin") || !!message.content.attachments?.length ); },
handler: async (runtime: IAgentRuntime, message: Memory, state?: State) => { const attachments = message.content.attachments ?? [];
if (attachments.length === 0) { return { text: "Please attach a file to upload to decentralized storage." }; }
const results = [];
for (const attachment of attachments) { const formData = new FormData(); const blob = new Blob([attachment.data], { type: attachment.mimeType }); formData.append("file", blob, attachment.name);
const res = await fetch(`${W3S_API}/upload`, { method: "POST", body: formData, }); const data = await res.json(); results.push(data); }
const summary = results .map((r) => `- ${r.cid} (${r.size} bytes)`) .join("\n");
return { text: `Uploaded ${results.length} file(s) to decentralized storage:\n${summary}\n\nFiles are pinned on IPFS and replicating to Filecoin SPs.`, data: results, }; },
examples: [ [ { user: "user", content: { text: "Store this research data on Filecoin" }, }, { user: "agent", content: { text: "Uploaded 1 file to decentralized storage. CID: bafkrei... Replicating to 3 SPs.", action: "STORE_ON_FILECOIN", }, }, ], ],};LIST_STORED_FILES
Lists the user's stored files with CID, status, and size.
// plugins/w3stor/actions/list.tsimport type { Action, IAgentRuntime, Memory } from "@elizaos/core";
const W3S_API = process.env.W3S_API_URL ?? "https://api.w3s.storage";
export const listStoredFiles: Action = { name: "LIST_STORED_FILES", similes: ["SHOW_FILES", "MY_FILES", "LIST_UPLOADS"], description: "List files stored in decentralized storage",
validate: async (_runtime: IAgentRuntime, message: Memory) => { const text = message.content.text?.toLowerCase() ?? ""; return text.includes("list") || text.includes("files") || text.includes("show"); },
handler: async () => { const res = await fetch(`${W3S_API}/files?limit=20`); const data = await res.json();
const table = data.files .map((f: { cid: string; status: string; size: number }) => `- ${f.cid.slice(0, 20)}... | ${f.status} | ${f.size} bytes` ) .join("\n");
return { text: `You have ${data.total} stored files:\n${table}`, data: data.files, }; },
examples: [ [ { user: "user", content: { text: "Show me my stored files" } }, { user: "agent", content: { text: "You have 4 stored files:...", action: "LIST_STORED_FILES" }, }, ], ],};CHECK_STORAGE_STATUS
Extracts a CID from the user message and checks its replication status.
// plugins/w3stor/actions/status.tsimport type { Action, IAgentRuntime, Memory } from "@elizaos/core";
const W3S_API = process.env.W3S_API_URL ?? "https://api.w3s.storage";
export const checkStorageStatus: Action = { name: "CHECK_STORAGE_STATUS", similes: ["FILE_STATUS", "CHECK_CID", "REPLICATION_STATUS"], description: "Check replication status for a specific CID",
validate: async (_runtime: IAgentRuntime, message: Memory) => { const text = message.content.text ?? ""; return text.includes("status") || text.includes("bafk") || text.includes("check"); },
handler: async (_runtime: IAgentRuntime, message: Memory) => { const cidMatch = message.content.text?.match(/bafk[a-z0-9]+/); if (!cidMatch) { return { text: "Please provide a CID to check. Example: check status of bafkrei..." }; }
const res = await fetch(`${W3S_API}/status/${cidMatch[0]}`); const data = await res.json();
return { text: `CID: ${data.cid}\nStatus: ${data.status}\nReplicas: ${data.replicationCount}/3\nProviders: ${data.providers?.join(", ") ?? "pending"}`, data, }; },
examples: [ [ { user: "user", content: { text: "Check status of bafkreihdwdcefgh..." } }, { user: "agent", content: { text: "CID: bafkrei... Status: stored, 3/3 replicas", action: "CHECK_STORAGE_STATUS" }, }, ], ],};Character Registration
Register the plugin with your ElizaOS character to enable storage capabilities.
// character.tsimport type { Character } from "@elizaos/core";import { w3storPlugin } from "./plugins/w3stor";
export const storageAgent: Character = { name: "StorageBot", plugins: [w3storPlugin], system: `You are a decentralized storage assistant. You help usersupload files to IPFS and Filecoin, check their storage status,and manage their data. You use x402 micropayments for paidoperations. Be concise and helpful.`, bio: [ "Decentralized storage agent powered by Filecoin and IPFS", "Supports x402 micropayments for trustless file operations", ], style: { all: ["concise", "technical but friendly", "always include CIDs in responses"], chat: ["helpful", "proactive about checking replication status"], },};Example Conversation
A typical multi-turn conversation with a w3stor-enabled ElizaOS agent:
// Example conversationUser: "Hey, can you store this CSV file for me?"Agent: "Of course! I'll upload it to IPFS and replicate across Filecoin SPs." [STORE_ON_FILECOIN] "Done! Your file is stored: - CID: bafkreihdwdcefgh4dqkjv67uzcmw7ojee6xedzd - Size: 245 KB - Status: pinned (replicating to 3 SPs) Cost: $0.0008 USDC via x402"
User: "How's the replication going?"Agent: [CHECK_STORAGE_STATUS] "Your file (bafkrei...) is fully replicated: - f01234: committed (sector 5891) - f05678: committed (sector 3204) - f09012: committed (sector 7712) All 3/3 SPs confirmed!"Alternative: A2A Integration
Instead of direct API calls, you can use the A2A protocol. This lets your ElizaOS agent communicate with w3stor as a peer agent via JSON-RPC.
// Alternative: Use A2A protocol instead of direct pluginimport type { Action, IAgentRuntime, Memory } from "@elizaos/core";
const A2A_ENDPOINT = "https://api.w3s.storage/a2a";
export const a2aStorage: Action = { name: "A2A_STORAGE", description: "Interact with w3stor via A2A protocol",
handler: async (_runtime: IAgentRuntime, message: Memory) => { const res = await fetch(A2A_ENDPOINT, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ jsonrpc: "2.0", id: crypto.randomUUID(), method: "tasks/send", params: { id: `task-${Date.now()}`, message: { role: "user", parts: [{ type: "text", text: message.content.text }], }, }, }), });
const data = await res.json(); const agentMessage = data.result?.status?.message; const textPart = agentMessage?.parts?.find( (p: { type: string }) => p.type === "text" ); return { text: textPart?.text ?? "Storage operation completed." }; },
validate: async () => true, similes: [], examples: [],};