Amit Jotwani

Amit Jotwani

Thoughts on code, workflows, and developer experience

January 20, 2026

Setup Guide: Upload to Spaces Skill

This is part of my Postbox workflow — when I’m writing and need to include a screenshot, I don’t want to leave the terminal. Open a browser, upload somewhere, copy a URL, paste it back… that breaks the flow.

So I made a small skill that uploads a file to DigitalOcean Spaces and gives me a public URL I can drop straight into Markdown. Upload, get link, keep writing.

This guide covers the technical setup: configuring s3cmd and creating the Claude Code skill.

What follows is the step-by-step setup.

Quick note on commands, skills, and MCP

When I started setting this up, Claude called these commands. Since then, it looks like commands have been folded into skills in Claude Code. I wrote more about creating these in Creating Reusable Prompts in Claude.

I’m still trying to figure out the practical difference between skills and MCP servers, and where each one really shines. That mental model is still forming for me. (I touch on how I use MCP servers in My current coding workflow.)

For now, this upload flow works well as a skill, so that’s what I went with.


Part 1: Install and Configure s3cmd

s3cmd is a lightweight CLI for interacting with S3-compatible storage, which includes DigitalOcean Spaces.

Step 1: Install s3cmd

On macOS:

brew install s3cmd

Verify it’s installed:

s3cmd --version

Step 2: Get Your Spaces Credentials

  1. Go to DigitalOcean Control Panel → API → Spaces Keys
  2. Click Generate New Key
  3. Copy both the Access Key and Secret Key

You can scope the key to:

  • All Spaces, or
  • A specific bucket (recommended)

Both options work for this setup.


Step 3: Configure s3cmd

Run the interactive configuration wizard:

s3cmd --configure

Example values for a bucket in nyc3 (adjust if you’re using another region):

PromptWhat to Enter
Access Key<your-spaces-access-key>
Secret Key<your-spaces-secret-key>
Default Regionnyc3
S3 Endpointnyc3.digitaloceanspaces.com
DNS-style bucket+hostname%(bucket)s.nyc3.digitaloceanspaces.com
Encryption passwordLeave blank
Path to GPG programLeave blank
Use HTTPS protocolYes
HTTP Proxy server nameLeave blank

Save the configuration when prompted.


Step 4: Test the Configuration (Important Gotcha)

During setup, s3cmd will try to list all buckets in your account. If your key is scoped to a specific bucket, you’ll likely see:

ERROR: Test failed: 403 (AccessDenied): Access Denied.
ERROR: Are you sure your keys have s3:ListAllMyBuckets permissions?

This is expected.

Scoped keys can’t list all buckets, but they can still access the bucket you allowed. Save the configuration anyway.


Step 5: Verify Access to Your Bucket

Test access directly against your bucket:

s3cmd ls s3://postbox/

If this works, your Spaces setup is complete.


Part 2: Create the Claude Code Skill

Now we wire this into Claude Code so uploads feel like a single command.

Step 1: Create the Command File

Create a new file:

~/.claude/commands/upload-to-spaces.md

Add the following content:

# Upload to Spaces

Upload a file to DigitalOcean Spaces and return the public URL.

## Arguments
$ARGUMENTS

## Instructions

1. Parse the arguments to find the file path to upload
2. Verify the file exists
3. Generate a unique filename by prepending a short UUID (8 chars) to the original filename
4. Upload the file using:
   s3cmd put "<filepath>" "s3://postbox/<unique-filename>" -P
5. Construct and return the public URL:
   https://postbox.nyc3.digitaloceanspaces.com/<unique-filename>
6. Display the URL prominently so it can be copied

Notes:

  • The -P flag makes the file publicly accessible
  • The UUID prefix avoids filename collisions
  • Adjust the bucket name and region to match your setup

Step 2: Use the Skill

In Claude Code, you can now run:

/upload-to-spaces /path/to/image.png

Or describe it naturally:

upload this screenshot to spaces: ~/Desktop/screenshot.png

Claude will upload the file and return the public URL, ready to paste into Markdown.


Configuration Reference

Example setup used in this guide:

  • Bucket: postbox
  • Region: nyc3
  • Endpoint: nyc3.digitaloceanspaces.com
  • Public URL format:
https://postbox.nyc3.digitaloceanspaces.com/<filename>

Useful s3cmd Commands

# List files in a bucket
s3cmd ls s3://postbox/

# Upload a file (public)
s3cmd put myfile.png s3://postbox/ -P

# Upload with a custom name
s3cmd put myfile.png s3://postbox/custom-name.png -P

# Delete a file
s3cmd del s3://postbox/filename.png

# Inspect a file
s3cmd info s3://postbox/filename.png

That’s it.

You now have a small, reusable building block: upload a file from Claude and immediately get a public URL, without leaving the flow you’re already in.