Skip to content

Storage Providers

A new Compose installation includes RustFS, three private buckets and bootstrap for separate API and temporary-credential issuer identities. The API uses S3-compatible object operations and prefix-scoped STS credentials. Basic bucket access alone does not validate execution isolation.

The checked-in RustFS image is pinned by digest to 1.0.0-rc.5, a release candidate. Qualify the exact image, gateway and policies together before admitting tenants, and repeat that qualification when changing the pin.

VariableDefault or sourcePurpose
OBJECT_STORE_MODEbundledEnables the bundled topology
STORAGE_PROVIDER, RUNTIME_CREDENTIALS_PROVIDERawsS3 backing adapter and runtime credential implementation
S3_STS_PROVIDERrustfsRustFS session issuance
S3_PUBLIC_ENDPOINThttp://s3.localhost:9000Signed origin reachable by clients and execution services
S3_INTERNAL_ENDPOINThttp://object-store:9000Direct internal storage access
STS_ENDPOINT_URLhttp://object-store:9000Private credential issuance
AWS_USE_PATH_STYLEtrueBucket names appear in object paths
META_BUCKETflow-like-metaApp and Flow metadata
CONTENT_BUCKETflow-like-contentApp and user content
LOG_BUCKETflow-like-logsExecution logs
STS_SESSION_TTL_SECONDS7200Requested session lifetime

The default mode requires three distinct bucket names. CDN_BUCKET_NAME defaults to the content bucket in the S3 adapter; this does not make that bucket public.

Setup generates three independent identities:

  • RUSTFS_ROOT_USER and RUSTFS_ROOT_PASSWORD reach the store and bootstrap only.
  • AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY identify the API storage user.
  • STS_ISSUER_ACCESS_KEY and STS_ISSUER_SECRET_KEY identify the restricted issuer.

The API and issuer are regular IAM users because RustFS service accounts cannot issue STS sessions. Both users are denied administration; the API user is also denied AssumeRole. Bootstrap uses the pinned release’s native /rustfs/admin/v3 API with S3 SigV4.

Storage secrets support _FILE inputs. Unreadable or empty files fail, and readers remove only trailing line endings. The API requires exactly one of a value and its _FILE setting. Bootstrap and standalone storage readers give the file precedence, but configure only one form when sharing settings with the API.

The library accepts STS_SESSION_TTL_SECONDS from 900 to 43,200 seconds and defaults to 3,600; Compose and Helm request 7,200 for hour-long runs plus margins. The API uses the expiration actually returned by STS. RustFS rejects AWS SSE-KMS and S3 Express settings and omits AWS KMS policy clauses. Static API credentials do not require a fabricated session token.

Runners receive temporary credentials in their signed dispatch payloads. Session policies limit access to the required buckets and app, user, temporary or run-log prefixes. Killing a runner ends its processes; it does not instantly revoke credentials that were copied elsewhere. Provider expiration remains relevant.

Bootstrap requires distinct bucket names and exact, comma-separated browser origins in S3_CORS_ALLOWED_ORIGINS; wildcard origins fail. Existing bucket policies stop initialization for review. CORS and lifecycle rules expire incomplete multipart uploads after one day and content under tmp/ after two.

Existing IAM policies must match the expected definitions. Users must have only their expected policy and no group membership. Bootstrap verifies existing user passwords without replacing them. Changing a configured secret therefore needs an explicit IAM rotation or a new identity. Preserve the working identities and investigate drift before changing existing policies.

The API, compiler, browser/desktop clients and per-run gateways must all reach the exact S3_PUBLIC_ENDPOINT used for signatures. Changing a signed URL’s host, port or path breaks its signature.

For the local default, s3.localhost resolves to the data gateway inside Compose and to loopback on the client machine. Add a client hosts entry when needed. A client on another machine cannot use the operator’s loopback address.

The data gateway forwards only configured bucket paths. Root listing, STS and administration routes are denied. The private RustFS endpoint remains separate; do not publish it in place of the data gateway.

S3_INTERNAL_ENDPOINT serves API LanceDB access; AWS_ENDPOINT remains a legacy fallback. META_BUCKET_ENDPOINT, CONTENT_BUCKET_ENDPOINT and LOGS_BUCKET_ENDPOINT override exported per-bucket endpoints. Shared credentials carry the region, path-style and explicit HTTP settings. HTTPS certificate verification remains enabled. Install private CAs in each caller’s trust store; CA files are not carried in the credentials.

For https://storage.example.com, route your TLS proxy to the data gateway and configure:

S3_PUBLIC_ENDPOINT=https://storage.example.com
S3_GATEWAY_ALIAS=object-gateway
COMPILER_ALLOWED_STORAGE_HOSTS=https://storage.example.com,http://object-store:9000
EXECUTION_OBJECT_STORE_TLS_GATEWAY=true

The alias prevents Compose from resolving the public HTTPS hostname directly to a plaintext internal listener. Preserve the signed host and request path through the TLS proxy.

Enable EXECUTION_OBJECT_STORE_TLS_GATEWAY only after verifying that this TLS origin denies STS and administration. A sandbox’s HTTPS CONNECT tunnel hides request paths from its execution proxy, so that proxy relies on the TLS endpoint’s bucket-only contract.

After bootstrap, run the included conformance command against a disposable or backed-up store:

Terminal window
docker compose build object-store-init
docker compose up -d object-store object-gateway
docker compose run --rm object-store-init
docker compose run --rm --entrypoint python object-store-init /opt/object-store/conformance.py

The script uses unique test prefixes and removes its objects afterward. It checks permitted reads/writes, denied sibling prefixes and other buckets, list-prefix restrictions, copy-source authorization, multipart initiation and abort, missing/invalid session tokens, STS/admin denial and presigned GET/PUT.

The probes also check confusable prefixes, empty-prefix listing, explicit deny, nested STS issuance and API-user STS denial. A valid result must include both successful authorized operations and rejected unauthorized operations. Repeat the suite when changing the store digest, policies, public endpoint or issuer identity. Kubernetes runs the same contract through helm test <release> -n <namespace> --logs.

Complete additional tests for actual expiration, multipart completion and copy, application operations, restored IAM state and host failure. A passing smoke test does not cover those paths.

Include an hour-long workflow that can read and write near its deadline and operations that fail after real session expiry. Recheck old credentials after restore and rotation, and retain the store digest and probe results with the deployment release.

Back up both object data and RustFS IAM metadata. Restore them together with matching deployment identities on a clean host, then rerun authorization checks. Do not treat the named object_store_data volume as an off-host backup.

To check bootstrap logic without starting a store, run from the repository root:

Terminal window
python3 -m venv /tmp/flow-like-store-tests
/tmp/flow-like-store-tests/bin/pip install -r apps/backend/docker-compose/object-store/requirements.txt
PYTHONDONTWRITEBYTECODE=1 /tmp/flow-like-store-tests/bin/python -m unittest discover -s apps/backend/docker-compose/object-store -p 'test_*.py'

These offline tests do not validate a deployed storage provider.

Use the supplied overlay:

OBJECT_STORE_MODE=external
COMPOSE_FILE=docker-compose.yml:docker-compose.external-store.yml
S3_STS_PROVIDER=aws
S3_INTERNAL_ENDPOINT=https://your-storage-origin
S3_PUBLIC_ENDPOINT=https://your-storage-origin
STS_ENDPOINT_URL=https://your-sts-origin
COMPILER_ALLOWED_STORAGE_HOSTS=https://your-storage-origin

Provide the external API and issuer credentials, existing bucket names and provider-appropriate path-style settings. Use S3_STS_PROVIDER=rustfs for a separately hosted qualified RustFS installation.

The overlay removes bundled storage and bootstrap from the active graph. It does not create buckets or migrate data. The per-run TLS gateway restriction still applies: an unrestricted S3/administration endpoint does not satisfy the bucket-only contract.

The stock API already includes AWS runtime credentials. Set RUNTIME_ROLE_ARN to the execution role and configure the issuer’s permission to assume it. Flow-Like supplies an inline session policy that narrows the role’s access. Verify role and bucket policies as well as ordinary API access.

For hour-long executions, the role’s maximum session duration must cover the requested session and all execution budgets. AWS role chaining is limited to one hour, which cannot cover a full hour of execution plus queueing and grace. See the AWS AssumeRole duration contract. Automatic credential renewal is not implemented.

For customer-managed KMS encryption, the role also needs the relevant KMS permissions. Configure S3_KMS_KEY_ARN or the per-bucket KMS variables; details are in Storage Providers.

The Compose API includes explicit AWS credential environment entries. To use workload identity, supply a reviewed override that removes those entries and provides the platform’s identity material. Empty static keys are not a workload-identity configuration.

The API also includes Azure, GCP and R2 implementations. Their variables remain in .env.example; Storage Providers describes the provider contracts.

For R2, retain STORAGE_PROVIDER=aws for the backing adapter and select RUNTIME_CREDENTIALS_PROVIDER=r2 for scoped credentials. Configure the R2 account, API token and S3 keys. For Azure/GCP, set the provider-specific names explicitly where the Compose file injects empty overrides.

An alternative provider still needs the execution gateway, compiler, reachable signing endpoint and lifetime checks validated together. Generic S3 API support does not establish support for inline STS prefix policies.