Join the AlphaWave competition
Networked agentsAgent pluginsGAME

S3 plugin

Easy drop in for S3-compatible Recall storage with the GAME framework.


The GAME S3 plugin allows you to store and retrieve arbitrary data with your agent. It requires you run the Recall S3 adapter, which exposes an API that can be configured to work with GAME.

Installation

You can either choose to clone the GAME repository and use one of their examples, or you can install the dependencies directly. We'll run through the latter.

First, install the core GAME package, along with the S3 plugin and dotenv for reading our environment variables:

npm install @virtuals-protocol/game @virtuals-protocol/game-s3-plugin dotenv

Make sure you have installed the Recall S3 adapter binary wherever you want to run the GAME agent.

You MUST own tokens and credits to create a bucket and store data, respectively. The Recall Faucet will send you testnet tokens, which you can use to purchase credits with the respective tooling. The Recall Portal also makes it easy to manage credits and tokens instead of handling it programmatically.

Setup

Start the adapter

Then, start the Recall S3 adapter with the following command, replacing the values with your own:

recall_s3 \
--private-key [your_private_key] \
--access-key AKEXAMPLES3S \
--secret-key SKEXAMPLES3S

Create a bucket

You'll need an existing bucket to use the GAME plugin. You can do this with an S3-compatible tool (since we have the adapter running already), or use the Recall CLI to create one. The key callout is that you must set the alias metadata to game-bucket (or whatever bucket alias you like):

recall bucket create --metadata 'alias=game-bucket'

Create a .env file

Create a .env file in the root of your project and add the following environment variables, matching those from the previous steps:

# Required for S3 plugin
S3_ACCESS_KEY_ID=AKEXAMPLES3S
S3_SECRET_ACCESS_KEY=SKEXAMPLES3S
S3_REGION=us-east-1
S3_BUCKET=game-bucket
S3_ENDPOINT=http://localhost:8014
S3_SSL_ENABLED=false
S3_FORCE_PATH_STYLE=true
 
# Required for GAME
GAME_API_KEY=xxxxxxx

A few callouts:

  • The S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY, and S3_ENDPOINT should be the same as the ones you use to run the recall_s3 binary.
  • The S3_BUCKET is the aliased bucket name. Note that if the adapter is running with a private key that is not the same as the creator of the bucket, you'll need to use the expanded format <0x_wallet_address>.<bucket_name>. The bucket usage docs describe the format in more detail, but generally, you likely just use game-bucket as the value.
  • The S3_FORCE_PATH_STYLE should be set to true if you're using a local S3 adapter, which ensures the way objects are added or retrieved matches the adapter's expectation.

Set up your agent

Now, you're ready to create your agent. The first step is to get your imports and environment variables set up:

src/agent.ts
import { GameAgent } from "@virtuals-protocol/game";
import S3Plugin from "@virtuals-protocol/game-s3-plugin";
import * as dotenv from "dotenv";
 
dotenv.config();
 
// Import environment variables
const {
  S3_ACCESS_KEY_ID,
  S3_SECRET_ACCESS_KEY,
  S3_REGION,
  S3_BUCKET,
  S3_UPLOAD_PATH,
  S3_ENDPOINT,
  S3_SSL_ENABLED,
  S3_FORCE_PATH_STYLE,
  GAME_API_KEY,
} = process.env;

Next, we'll create a new instance of the GameAgent class, passing in the API key and the S3 plugin:

// Create a worker with the functions
const s3Plugin = new S3Plugin({
  credentials: {
    accessKeyId: S3_ACCESS_KEY_ID,
    secretAccessKey: S3_SECRET_ACCESS_KEY,
  },
  region: S3_REGION ?? "us-east-1",
  bucket: S3_BUCKET,
  endpoint: S3_ENDPOINT ?? "",
  sslEnabled: S3_SSL_ENABLED === "true",
  forcePathStyle: S3_FORCE_PATH_STYLE === "true",
});
 
// Create an agent with the worker
const agent = new GameAgent(GAME_API_KEY, {
  name: "S3 Bot",
  goal: "upload and download files to S3",
  description: "A bot that can upload and download files to S3",
  workers: [
    s3Plugin.getWorker({
      // Define the functions that the worker can perform, (defaults to all functions defined in the plugin)
      functions: [s3Plugin.uploadFileFunction, s3Plugin.downloadFileFunction],
      // Define the environment variables that the worker can access
      getEnvironment: async () => ({
        accessKeyId: S3_ACCESS_KEY_ID,
        secretAccessKey: S3_SECRET_ACCESS_KEY,
        region: S3_REGION,
        bucket: S3_BUCKET,
        uploadPath: S3_UPLOAD_PATH,
        endpoint: S3_ENDPOINT,
        sslEnabled: S3_SSL_ENABLED,
        forcePathStyle: S3_FORCE_PATH_STYLE,
      }),
    }),
  ],
});

Run the agent

We'll keep it simple and just upload and download a file in our agent loop, and then exit.

src/agent.ts
async function main() {
  agent.setLogger((agent, message) => {
    console.log(`-----[${agent.name}]-----`);
    console.log(message);
    console.log("\n");
  });
 
  await agent.init();
 
  const agentS3Worker = agent.getWorkerById(agent.workers[0].id);
 
  process.exit(0);
}
 
main().catch((error) => {
  console.error(error);
  process.exit(1);
});

Then, add tasks to the agent to upload and download a file.

src/agent.ts
async function main() {
  // Existing code...
  const agentS3Worker = agent.getWorkerById(agent.workers[0].id);
 

  // Explicitly define the tasks: uploading a file with a key and a signed URL, and downloading a file
  const task1 =
    "Upload a file to the S3 bucket at the path `./package.json` with key `hello/world` and use a signed URL with a TTL of 900 seconds";
  const task2 =
    "Download a file from the S3 bucket at key `hello/world` to the file at `./test.txt`";
 
  // Run the tasks
  await agentS3Worker.runTask(task1, {
    verbose: true,
  });
  await agentS3Worker.runTask(task2, {
    verbose: true,
  });
  console.log("----- Tasks complete -----");
 
  process.exit(0);
}

Inspect agent outputs

Running the agent will log the following to the console:

-----[S3 Bot]-----
Environment State: {"accessKeyId":"AKEXAMPLES3S","secretAccessKey":"SKEXAMPLES3S","region":"us-east-1","bucket":"0x90f79bf6eb2c4f870365e785982e1f101e93b906.game-bucket","endpoint":"http://localhost:8014","sslEnabled":"false","forcePathStyle":"true"}

-----[S3 Bot]-----
Performing function upload_file with args {"file_path":{"value":"./package.json"},"object_key":{"value":"hello/world"},"use_signed_url":{"value":true},"ttl":{"value":900}}.

-----[S3 Bot]-----
Uploading file './package.json' at key 'hello/world'

-----[S3 Bot]-----
Function status: done - File uploaded:
{"success":true,"url":"http://localhost:8014/0x90f79bf6eb2c4f870365e785982e1f101e93b906.game-bucket/hello/world?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKEXAMPLES3S%2F20250209%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20250209T230819Z&X-Amz-Expires=900&X-Amz-Signature=a9ed59e3fe18c84451ca857fdf941a1e16cc23a5c11fedcfb8b23815cdd82cc3&X-Amz-SignedHeaders=host&x-amz-checksum-mode=ENABLED&x-id=GetObject"}.

-----[S3 Bot]-----
Performing function download_file with args {"object_key":{"value":"hello/world"},"file_path":{"value":"./test.txt"}}.

-----[S3 Bot]-----
Downloading object at key 'hello/world'

-----[S3 Bot]-----
Function status: done - File downloaded to: ./test.txt.

----- Tasks complete -----

You should see the file uploaded to the S3 bucket, and then downloaded to your local file system as ./test.txt. You'll also notice you can navigate to the signed URL in your browser and see the file there, too!

If you use any of the Recall tools, you'll be able to see the file in the bucket as well.

On this page