Skip to content

S3 Storage

Overview

S3-compatible object storage handles two categories of files in the Lota SDK: user-uploaded attachments (files attached to conversation messages) and generated documents (artifacts produced by the runtime, such as exported reports or pipeline outputs). The storage layer is owned by @lota-sdk/core and accessed exclusively through runtime service accessors.

Public Runtime Boundary

ts
const runtime = await createLotaRuntime(config)
await runtime.connect()

const { attachmentService, generatedDocumentStorageService } = runtime.services

Host code should use runtime.services.attachmentService and runtime.services.generatedDocumentStorageService.

Ownership

  • core/src/storage/attachment-storage.service.ts
  • core/src/storage/generated-document-storage.service.ts
  • core/src/storage/attachment-parser.ts
  • core/src/storage/attachments.utils.ts

Attachment Upload Flow

When a user attaches a file to a conversation message, the following sequence occurs:

  1. Upload. The file is uploaded to S3 via the attachment service. The storage key is structured by organization, user, and workstream to ensure tenant isolation: {orgId}/{userId}/{workstreamId}/{filename}.

  2. Metadata persistence. A workstreamAttachment record is created in SurrealDB containing the storage key, original filename, MIME type, file size, and a reference to the associated message.

  3. Presigned URL generation. A presigned download URL is generated with a configurable TTL (default: 30 minutes). This URL grants temporary read access to the object without exposing S3 credentials.

  4. URL re-signing on load. Every time messages are loaded for a workstream, attachment URLs are re-signed with fresh expiry times. This ensures users never encounter expired download links, regardless of how long the conversation has been idle.

  5. Content parsing. The attachment-parser module extracts text content from supported file types (PDF, plain text, code files, etc.) so that the content can be included in the agent's context window.

Generated Documents

Runtime-generated artifacts are stored via generatedDocumentStorageService. These are separate from user attachments and include:

  • Exported documents produced by agent tools
  • Pipeline outputs from execution plans
  • Any file artifacts created during agent execution

Generated documents follow the same presigned URL pattern for retrieval but use a distinct storage key namespace to keep them separate from user uploads.

S3-Compatible Services

The SDK uses the standard AWS S3 API (@aws-sdk/client-s3 and @aws-sdk/s3-request-presigner), which means it works with any S3-compatible object storage service:

ServiceNotes
AWS S3Native support
MinIOCommon for local development and self-hosted deployments
GarageLightweight, used in the default docker-compose stack
DigitalOcean SpacesDrop-in S3-compatible
Cloudflare R2S3-compatible with no egress fees
Backblaze B2S3-compatible API available

The only requirement is that the bucket specified in the configuration must already exist. The SDK does not create buckets automatically.

Configuration Details

ts
s3: {
  endpoint: 'http://localhost:3910',     // S3-compatible endpoint URL
  bucket: 'lota-attachments',           // Bucket name (must already exist)
  region: 'garage',                      // Region identifier (default: 'garage')
  accessKeyId: 'your-access-key',       // S3 access key
  secretAccessKey: 'your-secret-key',   // S3 secret key
  attachmentUrlExpiresIn: 1800,          // Presigned URL expiry in seconds (default: 30 min)
}
OptionRequiredDefaultDescription
endpointYesThe S3-compatible service endpoint URL
bucketYesTarget bucket for all stored objects
regionNo'garage'AWS region or equivalent identifier
accessKeyIdYesAccess key for authentication
secretAccessKeyYesSecret key for authentication
attachmentUrlExpiresInNo1800Presigned URL TTL in seconds

The lower-level storage modules remain implementation detail surfaces for the runtime. The supported host boundary is the runtime service accessor.