Bring your own S3
By default Amendable stores Git layer tarballs in platform S3. Pro and Platform can point those objects at a bucket you own.
Which mode
Section titled “Which mode”| Provider | Auth | Region / endpoint |
|---|---|---|
Amazon S3 (S3) | OIDC | Locked to us-west-2. Client region and endpoints are ignored |
Cloudflare R2 (R2) | STATIC_KEYS | You set region, endpoint_url, access key, secret |
MinIO (MINIO) or OTHER | STATIC_KEYS | Same as R2. Private/link-local endpoints are rejected |
Amazon S3 (OIDC)
Section titled “Amazon S3 (OIDC)”1. Copy the setup payload
Section titled “1. Copy the setup payload”UI: Settings → Bring-your-own storage
API (needs API_STORAGE):
curl -sS https://api.amendable.io/v1/storage-buckets/oidc-setup \ -H "access-token: $AMENDABLE_TOKEN"Fields you must use as-is:
| Field | Example |
|---|---|
issuer | https://oidc.amendable.io |
audience | amendable-byo-storage |
subject | account:<your-user-uuid> |
region | us-west-2 |
sample_trust_policy | IAM trust JSON with the correct hostname |
The issuer hostname must match the environment you registered the bucket with. Always copy from this payload. Staging uses https://stage.oidc.amendable.io. See Environments.
subject is account: plus your user UUID, not your username.
2. Bucket
Section titled “2. Bucket”- Region us-west-2
- Block public access
- SSE-S3 is enough
- Prefix, default
amendable/ - Do not use names that look like platform buckets (
amendable-repos-*,amendable-download-*)
3. IAM OIDC provider and role
Section titled “3. IAM OIDC provider and role”The module in examples/terraform/byo-s3 creates:
- the bucket
- the OIDC provider for
oidc.amendable.io - a role whose trust policy matches
audandsub - S3 permissions on
{prefix}*(canary + layers + multipart)
cd examples/terraform/byo-s3terraform initterraform apply \ -var aws_account_id=123456789012 \ -var bucket_name=my-amendable-layers \ -var amendable_user_id=YOUR_USER_UUIDThe thumbprint in that example is the one used against the current issuer chain. If AWS starts rejecting it, refresh from the issuer TLS chain (SHA-1 of the top intermediate CA).
# Values from GET /v1/storage-buckets/oidc-setupISSUER_HOST=oidc.amendable.ioBUCKET=my-amendable-layersACCOUNT=123456789012SUB=account:YOUR_USER_UUID
aws s3api create-bucket \ --bucket "$BUCKET" \ --region us-west-2 \ --create-bucket-configuration LocationConstraint=us-west-2
aws iam create-open-id-connect-provider \ --url "https://$ISSUER_HOST" \ --client-id-list amendable-byo-storage \ --thumbprint-list 06b25927c42a721631c1efd9431e648fa62e1e39Then put the sample trust policy on a new role (replace ACCOUNT_ID) and attach the S3 policy from the Terraform file (ListBucket on the prefix, object Get/Put/Delete/multipart on arn:aws:s3:::BUCKET/amendable/*).
- Create a
us-west-2bucket, block public access, default encryption. - IAM → Identity providers → Add provider → OpenID Connect. Provider URL
https://oidc.amendable.io(no path). Audienceamendable-byo-storage. - Create a role → Web identity → that provider and audience. Edit the trust policy so
subequalsaccount:<uuid>from the setup payload. - Attach an inline policy:
s3:ListBucketon the bucket withs3:prefixamendable/*, and object read/write/delete/multipart onbucket/amendable/*.
Minimum object actions: s3:GetObject, s3:PutObject, s3:DeleteObject, s3:AbortMultipartUpload, s3:ListMultipartUploadParts.
Verify writes {prefix}.amendable-canary/{uuid}.txt (Put, Head, Delete) then uses {prefix}layers/{layer-id}.tar.gz for Git.
4. Register and verify
Section titled “4. Register and verify”curl -sS -X POST https://api.amendable.io/v1/storage-buckets \ -H "access-token: $AMENDABLE_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "prod-layers", "provider": "S3", "auth_mode": "OIDC", "bucket_name": "my-amendable-layers", "key_prefix": "amendable/", "role_arn": "arn:aws:iam::123456789012:role/amendable-byo-storage", "is_default": true }'Status starts as PENDING_VERIFICATION. Then:
curl -sS -X POST \ https://api.amendable.io/v1/storage-buckets/BUCKET_ID/verify \ -H "access-token: $AMENDABLE_TOKEN"Success: status is ACTIVE and last_validated_at is set. Failure: FAILED, flash or 422, and a row in Error log / GET /v1/storage-buckets/errors.
5. Bind a repository
Section titled “5. Bind a repository”Pass storage_bucket_id on create, or set the bucket as account default. Binding cannot change later. You cannot delete a bucket that still has repos.
R2 / MinIO (static keys)
Section titled “R2 / MinIO (static keys)”curl -sS -X POST https://api.amendable.io/v1/storage-buckets \ -H "access-token: $AMENDABLE_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "r2-layers", "provider": "R2", "auth_mode": "STATIC_KEYS", "region": "auto", "bucket_name": "my-r2-bucket", "endpoint_url": "https://ACCOUNT_ID.r2.cloudflarestorage.com", "key_prefix": "amendable/", "access_key_id": "YOUR_ACCESS_KEY", "secret_access_key": "YOUR_SECRET", "is_default": true }'Then verify the same way. Secrets are envelope-encrypted at rest. They are never returned after create.
What Verify does not prove
Section titled “What Verify does not prove”Verify checks assume-role (OIDC) and canary Put/Head/Delete. It does not prove:
- that
layers/*.tar.gzobjects still exist - that
GetObjectonlayers/is still allowed
If you delete or overwrite layer objects, Git on a cold node can 500 (LoadImageError, S3 404, or digest mismatch). A warm node may still clone from cache. The BYO error log may stay empty. Treat layer keys as Amendable data. Do not tidy them.
After IAM changes, click Verify again. That is the check that writes a readable error.
Error log
Section titled “Error log”GET /v1/storage-buckets/errors and Settings → BYO storage → Error log.
Operations you will see: ASSUME_ROLE, VALIDATE, UPLOAD, and similar. Retention: 30 days / 500 rows per user.
Common Verify failures:
| Symptom | Cause |
|---|---|
AccessDenied on assume-role | Trust iss host, aud, or sub mismatch; bad role ARN |
S3 AccessDenied on canary | Missing Put/Head/Delete on {prefix}* |
| 422 OIDC required | Amazon S3 + static keys |
| 422 bucket name invalid | Platform bucket name |
| 403 | BYO requires Pro (feature flag) |