Object Storage (S3) — Agentuity Documentation

Object Storage (S3)

Durable file storage using Bun's native S3 APIs

Object storage provides durable file storage for documents, images, media, and binary content using Bun's native S3 APIs.

When to Use Object Storage

Storage TypeBest For
Object (S3)Files, images, documents, media, backups
Key-ValueFast lookups, caching, configuration
VectorSemantic search, embeddings, RAG
DatabaseStructured data, complex queries, transactions
Durable StreamsLarge exports, audit logs

Setup

Object storage requires a storage bucket linked to your project.

New Projects

When you run agentuity project create, the CLI prompts you to create or select a storage bucket. If you opt in, the bucket is linked and credentials are written to .env automatically.

Existing Projects

For projects that don't have a bucket yet:

  1. Create a bucket using the CLI or by signing in to the Agentuity web app:
agentuity cloud storage create
  1. Link it to your project:
agentuity project add storage

agentuity project add storage links the bucket and writes credentials to .env. agentuity cloud storage create also writes credentials if run from a project directory.

The credentials written to .env:

  • S3_ACCESS_KEY_ID
  • S3_SECRET_ACCESS_KEY
  • S3_BUCKET
  • S3_ENDPOINT

Quick Start

import { s3 } from 'bun';
 
// Create a file reference
const file = s3.file('documents/report.pdf');
 
// Write content
await file.write('Hello, World!');
await file.write(JSON.stringify({ createdAt: new Date().toISOString() }), {
  type: 'application/json',
});
 
// Read content
const text = await file.text();
const json = await file.json();
const bytes = await file.bytes();
 
// Check existence and delete
if (await file.exists()) {
  await file.delete();
}

Using in Agents

import { createAgent } from '@agentuity/runtime';
import { s } from '@agentuity/schema';
import { s3 } from 'bun';
 
const agent = createAgent('FileProcessor', {
  schema: {
    input: s.object({ userId: s.string() }),
    output: s.object({
      data: s.unknown().optional(),
      error: s.string().optional(),
    }),
  },
  handler: async (ctx, input) => {
    const file = s3.file(`uploads/${input.userId}/data.json`);
 
    if (!(await file.exists())) {
      return { error: 'File not found' };
    }
 
    const data = await file.json();
    ctx.logger.info('File loaded', { userId: input.userId });
    return { data };
  },
});

Using in Routes

import { Hono } from 'hono';
import type { Env } from '@agentuity/runtime';
import { s3 } from 'bun';
 
const router = new Hono<Env>();
 
// File upload
router.post('/upload/:filename', async (c) => {
  const filename = c.req.param('filename');
  const file = s3.file(`uploads/${filename}`);
 
  const buffer = await c.req.arrayBuffer();
  await file.write(new Uint8Array(buffer), {
    type: c.req.header('content-type') || 'application/octet-stream',
  });
 
  return c.json({ success: true, url: file.presign({ expiresIn: 3600 }) });
});
 
// File download (redirects to S3)
router.get('/download/:filename', async (c) => {
  const file = s3.file(`uploads/${c.req.param('filename')}`);
  if (!(await file.exists())) {
    return c.json({ error: 'Not found' }, 404);
  }
  return new Response(file);
});
 
export default router;

Presigned URLs

Generate time-limited URLs for direct client access:

import { s3 } from 'bun';
 
// Download URL (default: GET, 24 hours)
const downloadUrl = s3.presign('uploads/document.pdf', {
  expiresIn: 3600,
});
 
// Upload URL
const uploadUrl = s3.presign('uploads/new-file.pdf', {
  method: 'PUT',
  expiresIn: 900,
  type: 'application/pdf',
});

Custom S3 Clients

For multiple buckets or external S3-compatible services:

import { S3Client } from 'bun';
 
function requireEnv(name: string): string {
  const value = process.env[name];
  if (!value) {
    throw new Error(`${name} is required`);
  }
  return value;
}
 
// Cloudflare R2
const r2 = new S3Client({
  accessKeyId: requireEnv('R2_ACCESS_KEY'),
  secretAccessKey: requireEnv('R2_SECRET_KEY'),
  bucket: 'my-bucket',
  endpoint: `https://${requireEnv('R2_ACCOUNT_ID')}.r2.cloudflarestorage.com`,
});
 
// AWS S3
const aws = new S3Client({
  accessKeyId: requireEnv('AWS_ACCESS_KEY_ID'),
  secretAccessKey: requireEnv('AWS_SECRET_ACCESS_KEY'),
  bucket: 'my-bucket',
  region: 'us-east-1',
});

Bun S3 Documentation

For complete API documentation including streaming, multipart uploads, file metadata, listing objects, and partial reads, see the Bun S3 documentation.

Next Steps