S3.Bucket
Examples below assume this module is imported with an
imports:entry under aliasS3. Kind references (S3.Bucket,S3.Put,S3.List) follow that alias — if you import the module under a different name, substitute your alias accordingly.
Declares an S3-compatible bucket. Constructs and owns the S3 client used by S3.Put, S3.List, and any other resource that references it via bucketRef. Works against AWS S3, Cloudflare R2, MinIO, RustFS, or any other S3-compatible endpoint.
S3.Bucket is a Telo.Provider — its snapshot exposes nothing publicly; other resources reach it through a bucketRef reference (bucketRef: !ref <bucket>, or !ref <Alias>.<bucket> for a bucket exported by an imported library).
Example
kind: Telo.Application
metadata: { name: storage, version: 1.0.0 }
imports:
S3: std/s3@0.6.0
---
kind: S3.Bucket
metadata:
name: ModuleStore
bucketName: "${{ resources.AppConfig.bucketName }}"
endpoint: "${{ resources.AppConfig.s3Endpoint }}"
forcePathStyle: true
accessKeyId: "${{ resources.AppConfig.accessKeyId }}"
secretAccessKey: "${{ resources.AppConfig.secretAccessKey }}"
createIfMissing: true
Fields
| Field | Type | Required | Description |
|---|---|---|---|
bucketName | string | yes | Name of the bucket. |
endpoint | string | yes | S3 endpoint URL. Examples: https://s3.amazonaws.com, https://<accountId>.r2.cloudflarestorage.com, http://storage:9000. |
accessKeyId | string | yes | Access key. Wire from a secret in the importing module. |
secretAccessKey | string | yes | Secret key. Wire from a secret in the importing module. |
forcePathStyle | boolean | no | Use path-style URLs (endpoint/bucket/key) instead of virtual-host style. Required for MinIO, RustFS, and most self-hosted S3. Defaults to false. |
createIfMissing | boolean | no | When true, the controller issues CreateBucket during init and silently tolerates BucketAlreadyOwnedByYou / BucketAlreadyExists. Intended for self-hosted/dev backends. Leave false on managed clouds where the app may lack CreateBucket permission. Defaults to false. |
publicRead | boolean | no | When true, the controller applies a bucket policy during init that grants anonymous s3:GetObject on all keys. Defaults to false. See publicRead. |
Credentials
S3.Bucket takes accessKeyId and secretAccessKey as its own fields — credentials are per-bucket, not configured on the import. Because S3.Bucket is a Telo.Provider, those fields are evaluated at load time, so wire them from a secret source in the importing module:
accessKeyId: "${{ resources.AppConfig.accessKeyId }}"
secretAccessKey: "${{ resources.AppConfig.secretAccessKey }}"
Wire them from a Config.Env provider, the application's secrets: block, or any other secret source in the importing module.
createIfMissing
A convenience for local development and self-hosted storage. When set, init calls CreateBucketCommand against the configured endpoint; if the bucket already exists (either owned by this account or globally, depending on backend), the error is swallowed and init proceeds.
Any other error (auth failure, network, endpoint misconfiguration) fails init — which is the desired behavior, since a broken bucket would otherwise only surface on the first S3.Put call at runtime.
Leave createIfMissing: false (the default) for production deployments on AWS S3 or Cloudflare R2, where bucket lifecycle is managed out-of-band and the runtime identity typically cannot create buckets.
publicRead
When true, the controller issues PutBucketPolicy during init with a policy granting anonymous s3:GetObject on all keys:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::<bucketName>/*"
}
]
}
The policy is applied regardless of createIfMissing, so it works for both newly created and pre-existing buckets. The policy grants read access only — ListBucket is not included, so the bucket index is not browsable; callers must know the object key.
Backend support:
- AWS S3 — works. Bucket-level "Block Public Access" must be disabled out of band; the controller does not touch that setting.
- MinIO, RustFS — works. Equivalent to
mc anonymous set downloadon MinIO. - Cloudflare R2 — not effective. R2 does not honor bucket policies via the S3 API; public access must be configured through the Cloudflare dashboard (custom domain / public bucket URL). Setting
publicRead: trueagainst R2 will either no-op or error depending on the region.