File storage
[files]storage = "local" # defaultdir = "./data/files"# shared = true # every node of a cluster mounts the same foldermax_bytes = 4294967296 # 4 GiB per file# daily_bytes_per_user = 0 # no limitmax_open_per_user = 10 # uploads in progress per person# retention_days = 0 # files are keptallow_metadata = false # apps may not keep EXIF/GPS on photosLocal folder
Section titled “Local folder”Uploads and downloads go through the server. Back the folder up with the database. Behind a proxy, allow 20 MB request bodies (16 MiB parts). Encrypt the disk (LUKS, BitLocker, cloud disk encryption): files in a local folder are not yet encrypted by AgentIM itself.
S3 and compatible stores
Section titled “S3 and compatible stores”[files]storage = "s3"
[files.s3]bucket = "agentim-files"region = "us-east-1"# endpoint = "https://<account>.r2.cloudflarestorage.com" # R2, MinIO, other S3-compatible stores# prefix = "prod/"# path_style = falseCredentials come from AGENTIM_S3_ACCESS_KEY_ID and AGENTIM_S3_SECRET_ACCESS_KEY, never the file. At start the server writes and deletes a probe object; if it can’t, it doesn’t start.
Clients talk to the bucket directly, so set up three things:
-
CORS: allow your apps’ origins to
PUTandGET, allow the headerscontent-typeandcontent-length, and exposeETag. Without it a browser can’t finish a multipart upload.[{"AllowedOrigins": ["https://app.example.com"],"AllowedMethods": ["PUT", "GET"],"AllowedHeaders": ["content-type", "content-length"],"ExposeHeaders": ["ETag"],"MaxAgeSeconds": 3600}] -
A lifecycle rule that aborts incomplete multipart uploads after 1 day. The server cleans up too; this catches what it couldn’t.
-
Server-side encryption (SSE-S3 or SSE-KMS).
Objects live at {prefix}/{conv_id}/{file_id}/original and …/thumb. File names stay in the database, not in object keys.