/r2-presigned-url
Generate presigned URLs for secure client-side uploads or downloads
$ npx -y skills add secondsky/claude-skills --agent claude-codeHow it fires
How this command gets triggered: by you, by Claude, or both.
- Fires itselfClaude auto-loads it when your prompt matches the work.
- You can call itInvoke it directly when you want it.
- Slash command
/r2-presigned-url
Context preview
What this command does when you run it.
Generate presigned URLs for secure client-side uploads or downloads
Command definition
r2-presigned-url.mdname: cloudflare-r2:presigned-url
description: Generate presigned URLs for secure client-side uploads or downloads
R2 Presigned URL Generator
Generate time-limited, signed URLs for secure client-side uploads or downloads without exposing R2 credentials.
Required Information
1. **Operation type**: `{{operation}}` (upload or download) 2. **Object key** (file path in bucket): `{{key}}` 3. **Expiry** (seconds, max 604800 = 7 days): `{{expiry}}` 4. **Custom headers** (optional, e.g., Content-Type): `{{headers}}`
What This Command Does
1. **Generates AWS v4 signed URL** using aws4fetch library 2. **Sets expiration time** (1 hour to 7 days) 3. **Provides curl test command** for verification 4. **Explains security considerations** 5. **Shows client-side usage example**
Implementation
import { AwsClient } from 'aws4fetch';
// Configure AWS client for R2
const r2Client = new AwsClient({
accessKeyId: env.R2_ACCESS_KEY_ID,
secretAccessKey: env.R2_SECRET_ACCESS_KEY,
});
// Generate presigned URL for upload
async function generateUploadURL(
bucket: string,
key: string,
expirySeconds: number
) {
const url = new URL(
`https://${bucket}.${env.ACCOUNT_ID}.r2.cloudflarestorage.com/${key}`
);
url.searchParams.set('X-Amz-Expires', expirySeconds.toString());
const signed = await r2Client.sign(
new Request(url, { method: 'PUT' }),
{ aws: { signQuery: true } }
);
return signed.url;
}
// Generate presigned URL for download
async function generateDownloadURL(
bucket: string,
key: string,
expirySeconds: number
) {
const url = new URL(
`https://${bucket}.${env.ACCOUNT_ID}.r2.cloudflarestorage.com/${key}`
);
url.searchParams.set('X-Amz-Expires', expirySeconds.toString());
const signed = await r2Client.sign(
new Request(url, { method: 'GET' }),
{ aws: { signQuery: true } }
);
return signed.url;
}Example Usage
// Generate upload URL (valid for 1 hour)
const uploadURL = await generateUploadURL('my-bucket', 'user/file.pdf', 3600);
// Client-side upload
fetch(uploadURL, {
method: 'PUT',
body: fileData,
headers: {
'Content-Type': 'application/pdf',
},
});
// Generate download URL (valid for 24 hours)
const downloadURL = await generateDownloadURL('my-bucket', 'user/file.pdf', 86400);
// Provide URL to user
return c.json({ downloadURL });Testing with curl
# Test upload URL
curl -X PUT "{{presigned_url}}" \
-H "Content-Type: text/plain" \
--data "Test content"
# Test download URL
curl "{{presigned_url}}" --output downloaded-file.txtSecurity Considerations
1. **Always set expiry** - Never create URLs without expiration 2. **Shortest practical TTL** - 1-24 hours for most use cases 3. **Never log URLs** - They contain credentials in query params 4. **HTTPS only** - Presigned URLs should always use HTTPS 5. **Validate before signing** - Check file size/type limits 6. **User-specific keys** - Consider per-user key prefixes 7. **Rotate credentials** - Change R2 access keys periodically
Common Expiry Times
- **Instant upload**: 300 seconds (5 minutes)
- **User upload**: 3600 seconds (1 hour)
- **Email link**: 86400 seconds (24 hours)
- **Share link**: 604800 seconds (7 days, maximum)
Read more
name: cloudflare-r2:presigned-url description: Generate presigned URLs for secure client-side uploads or downloads
R2 Presigned URL Generator
Generate time-limited, signed URLs for secure client-side uploads or downloads without exposing R2 credentials.
Required Information
1. **Operation type**: `{{operation}}` (upload or download) 2. **Object key** (file path in bucket): `{{key}}` 3. **Expiry** (seconds, max 604800 = 7 days): `{{expiry}}` 4. **Custom headers** (optional, e.g., Content-Type): `{{headers}}`
What This Command Does
1. **Generates AWS v4 signed URL** using aws4fetch library 2. **Sets expiration time** (1 hour to 7 days) 3. **Provides curl test command** for verification 4. **Explains security considerations** 5. **Shows client-side usage example**
Implementation
import { AwsClient } from 'aws4fetch';
// Configure AWS client for R2
const r2Client = new AwsClient({
accessKeyId: env.R2_ACCESS_KEY_ID,
secretAccessKey: env.R2_SECRET_ACCESS_KEY,
});
// Generate presigned URL for upload
async function generateUploadURL(
bucket: string,
key: string,
expirySeconds: number
) {
const url = new URL(
`https://${bucket}.${env.ACCOUNT_ID}.r2.cloudflarestorage.com/${key}`
);
url.searchParams.set('X-Amz-Expires', expirySeconds.toString());
const signed = await r2Client.sign(
new Request(url, { method: 'PUT' }),
{ aws: { signQuery: true } }
);
return signed.url;
}
// Generate presigned URL for download
async function generateDownloadURL(
bucket: string,
key: string,
expirySeconds: number
) {
const url = new URL(
`https://${bucket}.${env.ACCOUNT_ID}.r2.cloudflarestorage.com/${key}`
);
url.searchParams.set('X-Amz-Expires', expirySeconds.toString());
const signed = await r2Client.sign(
new Request(url, { method: 'GET' }),
{ aws: { signQuery: true } }
);
return signed.url;
}Example Usage
// Generate upload URL (valid for 1 hour)
const uploadURL = await generateUploadURL('my-bucket', 'user/file.pdf', 3600);
// Client-side upload
fetch(uploadURL, {
method: 'PUT',
body: fileData,
headers: {
'Content-Type': 'application/pdf',
},
});
// Generate download URL (valid for 24 hours)
const downloadURL = await generateDownloadURL('my-bucket', 'user/file.pdf', 86400);
// Provide URL to user
return c.json({ downloadURL });Testing with curl
# Test upload URL
curl -X PUT "{{presigned_url}}" \
-H "Content-Type: text/plain" \
--data "Test content"
# Test download URL
curl "{{presigned_url}}" --output downloaded-file.txtSecurity Considerations
1. **Always set expiry** - Never create URLs without expiration 2. **Shortest practical TTL** - 1-24 hours for most use cases 3. **Never log URLs** - They contain credentials in query params 4. **HTTPS only** - Presigned URLs should always use HTTPS 5. **Validate before signing** - Check file size/type limits 6. **User-specific keys** - Consider per-user key prefixes 7. **Rotate credentials** - Change R2 access keys periodically
Common Expiry Times
- **Instant upload**: 300 seconds (5 minutes)
- **User upload**: 3600 seconds (1 hour)
- **Email link**: 86400 seconds (24 hours)
- **Share link**: 604800 seconds (7 days, maximum)
142 production-ready skills for Claude Code CLI ๐ Platform / Harness Support These plugins ship as Claude Code marketplace plugins (.claude-plugin/ manifests) and Codex CLI plugins (.codex-plugin/ manifests).
Repo: secondsky/claude-skills
Other commands on secondsky-claude-skills.
- /better-auth-add-plugin
Add a better-auth plugin to an existing project. Configures server and client plugins with proper imports.
Open command - /better-auth-setup
Interactive setup wizard for better-auth authentication. Guides through database, framework, OAuth providers, and plugin configuration.
Open command - /explain-error
Explain Better Auth error codes and provide solutions with code examples
Open command - /providers
Display Better Auth available authentication providers and their configuration
Open command - /bun-debug
Type of issue to debug (runtime, test, build, memory, performance)
Open command - /bun-deploy
Target platform (docker, cloudflare, vercel, fly, railway)
Open command

