Use S3
capability_id: s3.use
display_name: Use S3
scope: s3:use
endpoint:
method: POST
path: /s3/presign
operation: s3.presign
billable: true
commands:
- GetObject
- PutObject
environment_selector: none
Purpose and authorization
s3:use authorizes an authenticated, application-scoped request for an AWS S3 presigned GetObject or PutObject request. The application must use Contract v1 and have the s3:use scope.
Gateman signs the native AWS SDK operation with the application's configured AWS credential and returns a URL for the application to call directly. Gateman does not proxy the object body, receive the subsequent S3 response, or return the AWS credential.
This is a narrow signing capability, not an object-storage abstraction. It does not register buckets, enforce prefixes, check ownership or existence, or authorize individual native S3 fields as separate Gateman privileges.
Implementation contract
The outer request is authenticated with Contract v1:
POST /s3/presign
Content-Type: application/json
X-GM-App: app_...
X-GM-Ts: 1710000000
X-GM-Sig: <Contract-v1-signature>
X-GM-Idempotency-Key: optional-gateman-key
The body is a JSON object:
{
"operation": "put",
"bucket": "example-bucket",
"key": "uploads/photo.jpg",
"region": "eu-west-1",
"expires": 900,
"parameters": {
"ContentType": "image/jpeg",
"ContentLength": 73421,
"Metadata": {
"source": "web"
}
}
}
The supported top-level fields are:
| Field | Required | Contract |
|---|---|---|
operation | Yes | get or put. These select GetObject and PutObject. |
bucket | Yes | A syntactically valid S3 bucket name. The caller chooses it. |
key | Yes | A nonempty S3 object key of at most 1,024 bytes without control characters. |
region | Conditional | A valid AWS region. Required for customer-managed credentials; managed access uses its recorded region. |
expires | Yes | Integer seconds from 1 through 3,600. |
parameters | No | A JSON object of native SDK input fields for the selected operation. |
content_type | No | Legacy PUT compatibility field. Equivalent to parameters.ContentType. |
Unknown top-level fields are rejected. Endpoint, host, scheme, and other provider-origin overrides are not supported.
Native parameters
parameters is validated against the installed AWS SDK operation model. It is not a static Gateman allowlist. Native parameter names and value types accepted by the current GetObject or PutObject model are therefore the source of truth, subject to the constraints below.
- Top-level
bucketandkeymap to the SDK'sBucketandKeyinputs. parameters.Bucket,parameters.Key, andparameters.Bodyare rejected.Bodyis unavailable because Gateman never receives the object body.- Unknown native names and SDK-invalid value types are checked locally through the SDK model before admission. These validation failures return HTTP
400without consuming an operation. The route controls the HTTP status and envelope, but the SDK-generated value-validation text is returned as the error string. ChecksumAlgorithmis rejected because Gateman cannot generate the object checksum for a body it never receives. Supply a supported explicit checksum value where the SDK model accepts it, and send the corresponding object bytes directly to S3.
The SDK model may produce signed request headers or query parameters from a native field. The response's headers must be used exactly for the direct S3 request. The parameters object is not an arbitrary JSON bag and does not grant permissions beyond the AWS credential's IAM policy.
For PUT, content_type is accepted for compatibility and is mapped to ContentType. Supplying both forms with different values is rejected. It is not accepted for GET; use the native GET response-content parameters where appropriate.
GET and PUT examples
Native GET
{
"operation": "get",
"bucket": "example-bucket",
"key": "downloads/report.pdf",
"region": "eu-west-1",
"expires": 300,
"parameters": {
"ResponseContentType": "application/pdf",
"Range": "bytes=0-1048575",
"IfMatch": "\"etag-value\""
}
}
The application sends the returned method, URL, and headers to S3. It reads the actual AWS response and remains responsible for range, content, and error handling.
Native PUT
{
"operation": "put",
"bucket": "example-bucket",
"key": "uploads/photo.jpg",
"region": "eu-west-1",
"expires": 900,
"parameters": {
"ContentType": "image/jpeg",
"Metadata": {
"source": "web"
},
"Tagging": "source=web&retention=short",
"StorageClass": "STANDARD"
}
}
The application sends the object bytes directly to S3 using the returned PUT URL and every header returned by Gateman. Gateman does not upload the bytes or confirm that AWS accepted them.
Response contract and signed headers
A successful response is HTTP 200:
{
"ok": true,
"url": "https://example-bucket.s3.eu-west-1.amazonaws.com/uploads/photo.jpg?...",
"method": "PUT",
"expires_at": "2026-10-16T12:15:00+00:00",
"headers": {
"Content-Type": "image/jpeg",
"x-amz-meta-source": "web"
}
}
url is the complete presigned URL, method is GET or PUT, and expires_at is the server-calculated UTC expiry. headers contains every non-Host header in the completed request returned by the presigner. The caller must send every header returned in Gateman's headers object when executing the presigned request against AWS S3. The URL, method, and returned headers must be used together; changing or omitting a returned header can cause AWS signature or request validation failure. The URL is a bearer-like capability until it expires, so do not expose it unnecessarily.
For PUT, ContentLength is accepted as a native SDK parameter when its value is valid. The completed request returns Content-Length, and the current presigner includes content-length in X-Amz-SignedHeaders, so the supplied value is part of the signature. This is request signing, not a separate Gateman upload-size policy.
The subsequent direct S3 transfer does not consume another Gateman operation. The application must interpret the actual AWS status and response body. A successful Gateman response does not prove that AWS will authorize or complete the operation.
Credentials, region, and IAM authority
AWS credentials are stored by Gateman in the encrypted application secret store. Customer-managed credentials are used as supplied. Gateman-managed access uses the recorded AWS region; do not send a different region to select another managed runtime context.
AWS IAM is authoritative for S3 resource permissions. The caller chooses the bucket and full object key. The route does not independently:
- register buckets;
- enforce key prefixes;
- check object ownership or object existence;
- validate effective IAM policy permissions; or
- authorize each native S3 field as a separate Gateman privilege.
Managed provisioning may configure restricted IAM permissions, but AWS enforces them. See Configure managed S3 access. The full provisioning and credential lifecycle remains separate from this runtime capability reference.
Operation accounting and concurrency
The billable operation is s3.presign. One operation is charged for each admitted invocation. Validation failures rejected before admission do not consume an operation. Credential, SDK, signing, or other presigning failures after admission remain billable as failed operations.
The subsequent S3 GET or PUT is not a Gateman operation. The optional X-GM-Idempotency-Key is part of Contract v1 signing but does not create an S3-specific replay ledger or deduplicate presigning calls. Repeated admitted calls consume separate operations.
Monthly allowance exhaustion returns HTTP 403 with monthly_operation_limit_reached. Application concurrency rejection returns HTTP 429 with Retry-After when applicable. An unavailable application concurrency guard returns HTTP 503. An unavailable operation-accounting service returns HTTP 503 with Operation accounting unavailable.
Do not call this route for every byte, browser render, download retry, or upload chunk. Request a presigned URL when the application needs one and use it until it expires or the operation must be reissued.
Errors
| Condition | HTTP response | Operation consequence |
|---|---|---|
| Contract v1 authentication failure | 401 | Rejected before capability execution; no operation. |
Missing s3:use scope | 401 | Rejected before capability execution; no operation. |
| Invalid JSON | 400 | Rejected before admission; no operation. |
| Missing required fields or unsupported operation | 400 | Rejected before admission; no operation. |
| Invalid bucket, key, region, or expiry | 400 | Rejected before admission; no operation. |
Invalid parameters shape | 400 | Rejected before admission; no operation. |
| Unknown top-level or native parameter | 400 | Rejected before admission; no operation. |
Reserved Bucket, Key, or Body | 400 | Rejected before admission; no operation. |
Conflicting content_type and ContentType | 400 | Rejected before admission; no operation. |
| Unsupported checksum behavior | 400 | Rejected before admission; no operation. |
| SDK parameter validation error during local model validation | 400 | Rejected before admission; no operation. The route controls the envelope, but the SDK's validation text is returned as the error string. |
| Application concurrency rejection | 429, with Retry-After when applicable | No operation. |
| Application concurrency guard unavailable | 503 | No operation. |
| Monthly operation allowance exhausted | 403 | No new operation is reserved. |
| Operation accounting unavailable | 503 | No confirmed operation is reserved. |
| Missing AWS credential or credential-store failure | 502 | Admitted operation is finalized as failed. |
SDK serialization or signing failure after admission with an InvalidArgumentException | 400 | Admitted operation is finalized as failed; the exception message is returned. |
| Unexpected presigning failure after admission | 502 with S3 presigning unavailable | Admitted operation is finalized as failed. |
These are Gateman presigning errors. They are distinct from an AWS status returned later when the caller uses the URL, such as an IAM denial, expired signature, missing object, checksum failure, or invalid request header.
Managed provisioning boundary
Managed setup and teardown are documented separately in Configure managed S3 access. In summary, managed access creates a dedicated runtime identity, applies the configured resource restrictions, encrypts the runtime credential, and revokes the temporary Gateman-Setup access key after setup. When managed access is removed, Gateman verifies and revokes the recorded key before removing local state; it can revoke access keys belonging to its own runtime IAM user only. If application deletion cannot verify cleanup, the application can remain in pending-deletion. Gateman does not delete the IAM user, policy, permissions boundary, buckets, bucket policies, or objects.
Implementation outcome
An independently correct Use S3 integration:
- authenticates with Contract v1 and the
s3:usescope; - selects
getorput, supplies the bucket, full key, region, and expiry; - represents native SDK inputs in
parametersand avoids reserved fields; - uses the returned URL, method, and signed headers for a direct S3 request;
- interprets the actual AWS response rather than fabricating a provider result;
- treats AWS IAM as the authorization boundary;
- protects the presigned URL and refreshes it only when needed; and
- accounts for one
s3.presignoperation per admitted Gateman invocation.
Implementation instructions for agents
- MUST use Contract v1, the
s3:usescope, andPOST /s3/presign. - MUST send
operationasgetorput, withbucket,key, and an integerexpiresfrom 1 through 3,600. - MUST provide
regionfor customer-managed credentials. Managed access uses its recorded region. - MAY send native AWS SDK inputs in the
parametersobject. The installed SDK operation model, not a hand-written exhaustive list, defines supported names and types. Local SDK model validation occurs before admission, but a later credentialed serialization or signing failure is billable. - MUST NOT send nested
Bucket,Key, orBody; object bytes never pass through Gateman. MUST NOT useChecksumAlgorithmwithout a body. - MAY use legacy PUT
content_type, but MUST NOT conflict withparameters.ContentTypeor use it for GET. - MUST send every header returned in Gateman's
headersobject, together with the returned URL and method, when making the direct S3 request. MUST interpret AWS's actual response. - MUST NOT implement bucket registration, prefix authorization, or object ownership checks inside Gateman. AWS IAM remains authoritative.
- MUST treat
X-GM-Idempotency-Keyas Contract v1 input, not an S3 replay ledger, and MUST account for each admitteds3.presigncall. - SHOULD avoid unnecessary presign calls and avoid exposing URLs beyond the code that needs to perform the transfer.
Reusable AGENTS.md block
<!-- gateman:begin capability=s3.use -->
- Use Contract v1 with the `s3:use` scope and target `POST /s3/presign`.
- Send `operation` as `get` or `put`, plus the caller-selected `bucket`, full
`key`, integer `expires` from 1 through 3,600, and `region` when using a
customer-managed AWS credential. Managed access uses its recorded region.
- Put native AWS SDK inputs in the JSON `parameters` object. Gateman derives
supported names and types from the installed `GetObject` or `PutObject`
model; it is not an exhaustive static allowlist.
- Do not send nested `Bucket`, `Key`, or `Body`. Object bytes never pass through
Gateman. `ChecksumAlgorithm` is rejected because no body is available.
- For PUT, `content_type` is legacy compatibility for `ContentType`; do not
use it for GET or conflict it with `parameters.ContentType`.
- Use the returned `url`, `method`, and every header in Gateman's `headers`
object unchanged for the direct S3 request. Interpret the actual AWS
response; do not filter or reconstruct the returned headers.
- AWS IAM is authoritative for bucket, key, prefix, ownership, existence, and
native-operation permissions. Do not add those checks to Gateman.
- One admitted call consumes one `s3.presign` operation. The direct S3 transfer
consumes no additional Gateman operation. `X-GM-Idempotency-Key` is not an
S3 replay ledger; avoid unnecessary presign requests.
- Managed AWS setup and credential lifecycle are separate from runtime signing:
https://docs.getgateman.com/capabilities/s3.provisioning/
- Full capability reference: https://docs.getgateman.com/capabilities/s3.use/
<!-- gateman:end capability=s3.use -->
