bknd-storage-config
Use when configuring storage backends for file uploads. Covers S3-compatible storage (AWS S3, Cloudflare R2, DigitalOcean Spaces), Cloudinary media storage, local filesystem adapter for development, adapter configuration options, environment variables, and production storage setup.
What this skill does
# Storage Configuration
Configure storage backends for Bknd's media module.
## Prerequisites
- `bknd` package installed
- For S3: bucket created with appropriate permissions
- For Cloudinary: account with API credentials
- For R2: Cloudflare Workers environment with R2 binding
## When to Use UI Mode
- Admin panel doesn't support storage configuration
- Storage must be configured in code
## When to Use Code Mode
- All storage configuration (required)
- Setting up different adapters per environment
- Production storage with credentials
## Storage Adapter Overview
| Adapter | Type | Use Case |
|---------|------|----------|
| `s3` | S3-compatible | AWS S3, Cloudflare R2 (external), DigitalOcean Spaces, MinIO |
| `cloudinary` | Media-optimized | Image/video transformations, CDN delivery |
| `local` | Filesystem | Development only (Node.js/Bun runtime) |
| `r2` | Cloudflare R2 | Cloudflare Workers with R2 binding |
## Step-by-Step: S3 Adapter
### Step 1: Create S3 Bucket
Create bucket in AWS console or via CLI:
```bash
aws s3 mb s3://my-app-uploads --region us-east-1
```
### Step 2: Configure CORS (if browser uploads)
```json
{
"CORSRules": [{
"AllowedOrigins": ["https://yourapp.com"],
"AllowedMethods": ["GET", "PUT", "POST", "DELETE"],
"AllowedHeaders": ["*"],
"ExposeHeaders": ["ETag"]
}]
}
```
### Step 3: Get Access Credentials
Create IAM user with S3 access and get:
- Access Key ID
- Secret Access Key
### Step 4: Configure Bknd
```typescript
import { defineConfig } from "bknd";
export default defineConfig({
media: {
enabled: true,
adapter: {
type: "s3",
config: {
access_key: process.env.S3_ACCESS_KEY,
secret_access_key: process.env.S3_SECRET_KEY,
url: "https://my-bucket.s3.us-east-1.amazonaws.com",
},
},
},
});
```
### Step 5: Add Environment Variables
```bash
# .env
S3_ACCESS_KEY=AKIA...
S3_SECRET_KEY=wJalr...
```
## S3 URL Formats
Different S3-compatible services use different URL formats:
```typescript
// AWS S3
url: "https://{bucket}.s3.{region}.amazonaws.com"
// Example: "https://my-bucket.s3.us-east-1.amazonaws.com"
// Cloudflare R2 (external access via S3 API)
url: "https://{account_id}.r2.cloudflarestorage.com/{bucket}"
// Example: "https://abc123.r2.cloudflarestorage.com/my-bucket"
// DigitalOcean Spaces
url: "https://{bucket}.{region}.digitaloceanspaces.com"
// Example: "https://my-bucket.nyc3.digitaloceanspaces.com"
// MinIO (self-hosted)
url: "http://localhost:9000/{bucket}"
```
## Step-by-Step: Cloudinary Adapter
### Step 1: Get Cloudinary Credentials
From Cloudinary dashboard, copy:
- Cloud name
- API Key
- API Secret
### Step 2: Configure Bknd
```typescript
import { defineConfig } from "bknd";
export default defineConfig({
media: {
enabled: true,
adapter: {
type: "cloudinary",
config: {
cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
api_key: process.env.CLOUDINARY_API_KEY,
api_secret: process.env.CLOUDINARY_API_SECRET,
},
},
},
});
```
### Step 3: Add Environment Variables
```bash
# .env
CLOUDINARY_CLOUD_NAME=my-cloud
CLOUDINARY_API_KEY=123456789
CLOUDINARY_API_SECRET=abcdef...
```
### Optional: Upload Preset
For unsigned uploads or custom transformations:
```typescript
adapter: {
type: "cloudinary",
config: {
cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
api_key: process.env.CLOUDINARY_API_KEY,
api_secret: process.env.CLOUDINARY_API_SECRET,
upload_preset: "my-preset", // Optional
},
},
```
## Step-by-Step: Local Adapter (Development)
### Step 1: Create Upload Directory
```bash
mkdir -p ./uploads
```
### Step 2: Register and Configure
```typescript
import { defineConfig } from "bknd";
import { registerLocalMediaAdapter } from "bknd/adapter/node";
// Register the local adapter
const local = registerLocalMediaAdapter();
export default defineConfig({
media: {
enabled: true,
adapter: local({ path: "./uploads" }),
},
});
```
Files served at `/api/media/file/{filename}`.
### Note on Runtime
Local adapter requires Node.js or Bun runtime (filesystem access). It won't work in:
- Cloudflare Workers
- Vercel Edge Functions
- Browser environments
## Step-by-Step: Cloudflare R2 (Workers)
### Step 1: Create R2 Bucket
```bash
wrangler r2 bucket create my-bucket
```
### Step 2: Add R2 Binding to wrangler.toml
```toml
[[r2_buckets]]
binding = "MY_BUCKET"
bucket_name = "my-bucket"
```
### Step 3: Configure Bknd
```typescript
import { serve, type CloudflareBkndConfig } from "bknd/adapter/cloudflare";
const config: CloudflareBkndConfig = {
app: (env) => ({
connection: { url: env.DB },
config: {
media: {
enabled: true,
adapter: {
type: "r2",
config: {
binding: "MY_BUCKET",
},
},
},
},
}),
};
export default serve(config);
```
R2 adapter uses the Cloudflare Workers binding directly, no external credentials needed.
## Media Module Options
### Size Limit
```typescript
export default defineConfig({
media: {
enabled: true,
body_max_size: 10 * 1024 * 1024, // 10MB max upload
adapter: { ... },
},
});
```
### Default Size Behavior
If `body_max_size` not set, uploads have no size limit. Always set a reasonable limit in production.
## Environment-Based Configuration
Different adapters for dev vs production:
```typescript
import { defineConfig } from "bknd";
import { registerLocalMediaAdapter } from "bknd/adapter/node";
const local = registerLocalMediaAdapter();
const isDev = process.env.NODE_ENV !== "production";
export default defineConfig({
media: {
enabled: true,
body_max_size: 25 * 1024 * 1024, // 25MB
adapter: isDev
? local({ path: "./uploads" })
: {
type: "s3",
config: {
access_key: process.env.S3_ACCESS_KEY,
secret_access_key: process.env.S3_SECRET_KEY,
url: process.env.S3_BUCKET_URL,
},
},
},
});
```
## Verify Storage Configuration
### Check Media Module Enabled
```typescript
import { Api } from "bknd";
const api = new Api({ host: "http://localhost:7654" });
// List files (empty if no uploads yet)
const { ok, data, error } = await api.media.listFiles();
if (ok) {
console.log("Media module working, files:", data.length);
} else {
console.error("Media error:", error);
}
```
### Test Upload
```typescript
async function testStorage() {
const testFile = new File(["test content"], "test.txt", {
type: "text/plain"
});
const { ok, data, error } = await api.media.upload(testFile);
if (ok) {
console.log("Upload succeeded:", data.name);
// Clean up
await api.media.deleteFile(data.name);
console.log("Cleanup complete");
} else {
console.error("Upload failed:", error);
}
}
```
### Check via REST
```bash
# List files
curl http://localhost:7654/api/media/files
# Upload test file
echo "test" | curl -X POST \
-H "Content-Type: text/plain" \
--data-binary @- \
http://localhost:7654/api/media/upload/test.txt
```
## Complete Configuration Examples
### AWS S3 Production
```typescript
import { defineConfig } from "bknd";
export default defineConfig({
connection: {
url: process.env.DATABASE_URL,
},
config: {
media: {
enabled: true,
body_max_size: 50 * 1024 * 1024, // 50MB
adapter: {
type: "s3",
config: {
access_key: process.env.AWS_ACCESS_KEY_ID,
secret_access_key: process.env.AWS_SECRET_ACCESS_KEY,
url: `https://${process.env.S3_BUCKET}.s3.${process.env.AWS_REGION}.amazonaws.com`,
},
},
},
},
});
```
### Cloudflare R2 + D1
```typescript
import { serve, type CloudflareBkndConfig } from "bknd/adapter/cloudflare";
const config: CloudflareBkndConfig = {
app: (env) => ({
connection: { url: env.DB }, // D1 binding
config: {
media: {
enabled: true,
Related in Cloud & DevOps
appbuilder-action-scaffolder
IncludedCreate, implement, deploy, and debug Adobe Runtime actions with consistent layout, validation, and error handling. Use this skill whenever the user needs to add actions to an App Builder project, understand action structure (params, response format, web/raw actions), configure actions in the manifest, use App Builder SDKs (State, Files, Events, database), deploy and invoke actions via CLI, debug action issues, or implement patterns such as webhook receivers, custom event providers, journaling consumers, large payload redirects, action sequence pipelines, and Asset Compute workers. Also trigger when users mention serverless functions in Adobe context, action logging, IMS authentication for actions, or cron-style scheduled actions.
orchestrating-datacloud
IncludedSalesforce Data Cloud product orchestrator for connect→prepare→harmonize→segment→act workflows. Use this skill when the user needs a multi-step Data Cloud pipeline, cross-phase troubleshooting, or data space and data kit management. TRIGGER when: user needs a multi-step Data Cloud pipeline, asks to set up or troubleshoot Data Cloud across phases, manages data spaces or data kits, or wants a cross-phase sf data360 workflow. DO NOT TRIGGER when: work is isolated to a single phase (use the matching phase-specific skill), the task is STDM/session tracing/parquet telemetry (use observing-agentforce), standard CRM SOQL (use querying-soql), or Apex implementation (use generating-apex).
github-project-automation
IncludedAutomate GitHub repository setup with CI/CD workflows, issue templates, Dependabot, and CodeQL security scanning. Includes 12 production-tested workflows and prevents 18 errors: YAML syntax, action pinning, and configuration. Use when: setting up GitHub Actions CI/CD, creating issue/PR templates, enabling Dependabot or CodeQL scanning, deploying to Cloudflare Workers, implementing matrix testing, or troubleshooting YAML indentation, action version pinning, secrets syntax, runner versions, or CodeQL configuration. Keywords: github actions, github workflow, ci/cd, issue templates, pull request templates, dependabot, codeql, security scanning, yaml syntax, github automation, repository setup, workflow templates, github actions matrix, secrets management, branch protection, codeowners, github projects, continuous integration, continuous deployment, workflow syntax error, action version pinning, runner version, github context, yaml indentation error
sf-datacloud
IncludedSalesforce Data Cloud product orchestrator for connect→prepare→harmonize→segment→act workflows. TRIGGER when: user needs a multi-step Data Cloud pipeline, asks to set up or troubleshoot Data Cloud across phases, manages data spaces or data kits, or wants a cross-phase `sf data360` workflow. DO NOT TRIGGER when: work is isolated to a single phase (use the matching sf-datacloud-* skill), the task is STDM/session tracing/parquet telemetry (use sf-ai-agentforce-observability), standard CRM SOQL (use sf-soql), or Apex implementation (use sf-apex).
fabric-cli
IncludedUse this skill for Fabric.so CLI workflows with the `fabric` terminal command: diagnose/install/login, search or browse a Fabric library, save notes/links/files, create folders, ask the Fabric AI assistant, manage tasks/workspaces, generate shell completion, check subscription usage, produce JSON output, and use Fabric as persistent agent memory. Do not use for Microsoft Fabric/Azure/Power BI `fab`, Daniel Miessler's Fabric framework, Python Fabric SSH, Fabric.js, or textile/fashion fabric.
lark
IncludedLark/Feishu CLI skills: lark-cli operations for docs, markdown, sheets, base, calendar, im, mail, task, okr, drive, wiki, slides, whiteboard, apps, approval, attendance, contact, vc, minutes, event. Use when the user needs to operate Lark/Feishu resources via lark-cli, send messages, manage documents, spreadsheets, calendars, tasks, OKRs, deploy web pages, or any Feishu/Lark workspace operations.