An S3 client for Mac: browsing buckets like folders
Amazon S3 has no folders. A bucket is a flat list of objects, each with a key such as images/2026/header.png, and the slashes are just characters in the name. An S3 client on a Mac makes a bucket feel like a folder tree by asking S3 to group keys at each /, then showing those groups as folders. That works well for browsing, uploading and downloading. It shows its seams in a few places (renaming, permissions, very large files), and knowing where they are saves surprises.
How S3 fakes folders
Listing a "folder" is a ListObjectsV2 request with two parameters:
prefix=images/returns only keys starting withimages/.delimiter=/rolls up everything after the next slash into a singleCommonPrefixesentry.
So images/2026/header.png and images/2026/footer.png come back as one common prefix, images/2026/, which the client draws as a folder. You can see the same thing from the AWS CLI:
aws s3 ls s3://example-assets/images/
aws s3api list-objects-v2 --bucket example-assets \
--prefix images/ --delimiter /
Each response returns at most 1,000 keys. Larger folders come back in pages, each with a continuation token for the next, and a client that ignores the token silently shows a truncated folder.
An empty folder cannot exist on its own, because there is no key to hold it. When you choose "Create folder" in the AWS console, it writes a zero-byte object whose key ends in a slash, such as reports/, so the prefix has something in it. Most clients follow the same convention.
Where the folder illusion breaks
- Rename is copy plus delete. S3 has no rename operation. A client copies the object to the new key (a server-side copy, so the data does not pass through your Mac) and then deletes the original. A failure between the two steps leaves both keys in place. A single copy request handles objects up to 5 GB.
- Renaming or deleting a folder touches every object in it. A folder with 10,000 objects means 10,000 copies or deletions.
- No POSIX permissions. Access is controlled by IAM policies, bucket policies and S3 Object Ownership settings, not
chmod. A client offering a permissions dialog for S3 has nothing real to change. - You cannot set the modification time. S3 sets
Last-Modifiedto the moment an upload finishes. Tools that compare timestamps between your disk and a bucket have to allow for this.
The AWS guide to organising objects using folders describes the console's side of this.
A key for the job: a minimal IAM policy
Give a desktop client its own IAM user, or at least its own access key, with only the permissions it needs. For browsing, uploading, downloading and deleting in one bucket:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::example-assets"
},
{
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"],
"Resource": "arn:aws:s3:::example-assets/*"
}
]
}
Note the two resources. s3:ListBucket applies to the bucket ARN itself, and the object actions apply to bucket/*. Putting all four on one resource is the most common reason a listing works but downloads fail, or the other way round. Drop s3:DeleteObject for a key that should only add or replace files.
Regions and signatures
Every request is signed with Signature Version 4, and the signature includes the bucket's region. Sign for the wrong region and S3 either redirects (a 301 with PermanentRedirect) or rejects the request with an error naming the region it expected. To find out where a bucket lives:
aws s3api get-bucket-location --bucket example-assets
A null location constraint means us-east-1.
Large files: single PUT vs multipart
A single PUT request can upload an object of up to 5 GB. Beyond that, S3 requires multipart upload, which sends the file in parts and allows objects up to 5 TB. The AWS CLI's aws s3 cp switches to multipart automatically for large files. When choosing a desktop S3 client for big media or backups, check which method it uses.
Using Wharf as an S3 client on your Mac
Wharf treats a bucket as another site, next to your SFTP and FTP servers. In the site editor, choose Amazon S3 as the protocol and fill in:
- Bucket, and Region from the picker (choose Other… to type one that is not listed).
- Endpoint: leave it empty for Amazon S3. It is for S3-compatible services; see using Cloudflare R2 from a Mac.
- Put bucket in the path: off for Amazon, which prefers the bucket in the hostname. MinIO and most self-hosted servers need it on.
- Start folder, if you want to open somewhere below the top of the bucket.
- Access key ID and Secret access key. The secret goes in your Keychain, never in a configuration file.
When it connects, Wharf makes a zero-length list request, which checks the key, the region and the bucket in one go, so the policy above needs s3:ListBucket. If S3 answers with a redirect because the region is wrong, Wharf reports it as a wrong-region error and points you at the region setting, rather than leaving you with a bare status code.
From there the bucket behaves much like a server. Folders come from common prefixes, with continuation tokens followed so large folders list completely. New Folder… creates the zero-byte placeholder described above. Rename… copies on the server and then deletes the original. Permissions… reports that changing permissions is not supported by object storage, rather than pretending. Uploads get a Content-Type from the file extension (HTML, CSS, JavaScript, common image types and so on), so files served straight from the bucket arrive with a usable type.
Two limits are worth knowing. Wharf uploads each file with a single PUT, so the 5 GB per-object ceiling applies, and an interrupted upload to S3 starts again rather than resuming. Downloads do resume, using ranged requests.
If a request is refused, View › Server Log… (⌘⇧K) shows each S3 request and response, along with the canonical request and string to sign. It never shows your secret, and those are exactly the values to compare when S3 reports SignatureDoesNotMatch. The support page has more troubleshooting, and the features overview shows what else works across S3 and SFTP sites.