Bucket CORS
S3-style CORS configuration. See Core Concepts → CORS for the type signature.
| RPC | HTTP |
|---|---|
PutBucketCors | PUT /admin/buckets/:name/cors |
GetBucketCors | GET /admin/buckets/:name/cors |
DeleteBucketCors | DELETE /admin/buckets/:name/cors |
gRPC setup —
grpcurl, endpoints, reflection, and field-name casing — is covered once in Conventions → Using gRPC.
These rules bind one door. Per-bucket CORS is evaluated by k3-api’s
per_bucket_cors_middleware— i.e. onapi.data.dodil.ioonly. Theobject.uk-lon-1.dodil.iobyte plane carries a single fixed, fleet-wide origin allow-list instead (localhost,dodil.io,dodil.cloudby default) and never readsBucketCorsConfiguration. Setting a rule here will not make a browser on your own origin able to fetch from the object endpoint — point that traffic at the control-plane S3 route. Separately, the S3?corssubresource passes straight through to the storage backend and is never read by K3.
PutBucketCors
The HTTP body is the BucketCorsConfiguration value (proto body: "cors_configuration").
Validated before it is stored (services/storage/cors.rs, validate_cors_config):
| Rule | Failure |
|---|---|
cors_rules present, non-empty | INVALID_ARGUMENT — “cors_rules array is required” / “must have at least one rule” |
| at most 100 rules | INVALID_ARGUMENT — “cors_rules cannot exceed 100 rules” |
each rule has a non-empty allowed_origins | INVALID_ARGUMENT — “rule N: allowed_origins must have at least one origin” |
allowed_methods ⊆ GET, HEAD, PUT, POST, DELETE | INVALID_ARGUMENT — “rule N: invalid method ’…’” |
max_age_seconds ≥ 0 | INVALID_ARGUMENT — “rule N: max_age_seconds must be non-negative” |
(N is the zero-based index of the offending rule.)
A PUT replaces the whole configuration — there is no per-rule patch. Rules are then evaluated in order and the first origin match wins, so put your most specific rule first. An empty allowed_methods matches any method (S3’s permissive default) — be explicit. expose_headers is what the browser is allowed to read back: ETag is not CORS-safelisted, so an upload flow that reads the etag must list it.
Request
HTTP
curl -sS -X PUT "https://api.data.dodil.io/admin/buckets/kb-prod/cors" \
-H "Authorization: Bearer $DODIL_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"corsRules": [
{
"allowedOrigins": ["https://app.example.com"],
"allowedMethods": ["GET", "PUT"],
"allowedHeaders": ["*"],
"exposeHeaders": ["ETag"],
"maxAgeSeconds": 3600
}
]
}'Response
A BucketCorsConfiguration — see Core Concepts → CORS.
GetBucketCors
A bucket with no CORS configuration is NOT_FOUND (“no CORS configuration set on this bucket”), not an empty corsRules array. An unknown bucket is NOT_FOUND (“bucket not found”) on all three calls.
Request
HTTP
curl -sS "https://api.data.dodil.io/admin/buckets/kb-prod/cors" \
-H "Authorization: Bearer $DODIL_TOKEN"Response
A BucketCorsConfiguration — see Core Concepts → CORS.
DeleteBucketCors
Request
HTTP
curl -sS -X DELETE "https://api.data.dodil.io/admin/buckets/kb-prod/cors" \
-H "Authorization: Bearer $DODIL_TOKEN"Response
Empty (DeleteBucketCorsResponse {}).
See also
- Core Concepts → CORS — type signature
- Buckets · Policy · Objects
- Conventions — auth, headers, error envelope
grpcurlreference — full flag set + reflection-disabled fallbacks