Object storage
Layout
Section titled “Layout”| Bucket | Owner | Contents |
|---|---|---|
Platform bucket (AWS_BUCKET, e.g. ulams) |
Platform | Platform files, h5p/ for the platform’s H5P content |
ulams-<slug> (TENANCY_BUCKET) |
One tenant | Course files, images, videos and HLS renditions, LiaScript assets, H5P content and temporary files under h5p/, and SCORM/cmi5 packages when their disk is s3 |
ulams:tenant:create creates the tenant bucket and attaches a public-read bucket policy
(S3BucketProvisioner): files are served to browsers by URL. The bucket’s public base URL is
TENANCY_STORAGE_PUBLIC_URL plus /<bucket> and is written to the tenant’s AWS_URL.
All tenants use the platform’s access key: isolation is per bucket, not per credential (a known
limit recorded in api/docs/multidomain.md).
Variables
Section titled “Variables”| Where | Variables |
|---|---|
api (platform) |
FILESYSTEM_DRIVER=s3, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_DEFAULT_REGION, AWS_BUCKET, AWS_ENDPOINT, AWS_URL, AWS_USE_PATH_STYLE_ENDPOINT |
api (bucket creation) |
TENANCY_S3_ENDPOINT, TENANCY_S3_KEY, TENANCY_S3_SECRET, TENANCY_S3_REGION (default to the AWS_* values), TENANCY_BUCKET, TENANCY_STORAGE_PUBLIC_URL |
api (package disks) |
SCORM_DISK (default local), CMI5_DISK (default: the value of SCORM_DISK), LIASCRIPT_DISK (default FILESYSTEM_DRIVER) |
h5p |
S3_ENDPOINT, S3_REGION, S3_KEY, S3_SECRET, S3_BUCKET, S3_PREFIX (h5p), S3_FORCE_PATH_STYLE; per tenant, the AWS_* values of its env file win |
AWS_ENDPOINT is the address the containers use; AWS_URL and TENANCY_STORAGE_PUBLIC_URL are
what browsers use. They differ when the store sits on the internal network (the example’s MinIO:
http://minio:9000 inside, https://storage.<content domain> outside).
Choosing a store
Section titled “Choosing a store”- A managed S3-compatible service (AWS S3, Cloudflare R2, Backblaze B2, Scaleway, OVH and
others). The access key needs
CreateBucketandPutBucketPolicyfor tenant provisioning. On AWS, S3 Block Public Access rejects public bucket policies by default; tenant provisioning fails at thebucketstep until you allow public policies for these buckets (or pre-create them and serve files through a CDN). Not tested by the project. - Self-hosted. The development stack uses MinIO, and the example offers it behind
--profile minio. Recommended evaluate SeaweedFS or RustFS instead: the project plans to move to a permissively licensed default.
Serving files safely
Section titled “Serving files safely”Uploaded files are untrusted. The API stores SVG, HTML and XML outside package paths with
Content-Disposition: attachment and sets Content-Type from the extension
(ActiveContentSafeS3Adapter), and the storage host in the Caddyfile adds nosniff and a
script-src 'none'; sandbox policy for active file types. Put the public storage host on the
content domain, not on the application domain. See Security.