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: [],};