bunny-net — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited bunny-net (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Bunny.net is a comprehensive CDN and media platform offering content delivery, storage, video streaming, image optimization, AI image generation, edge computing, DNS, and security services.
API Key: Set as environment variable BUNNY_API_KEY
Storage Key: Set as environment variable BUNNY_STORAGE_KEY (per storage zone)
Base URLs:
| Service | Base URL |
|---|---|
| API | https://api.bunny.net |
| Storage (Frankfurt) | https://storage.bunnycdn.com |
| Storage (UK) | https://uk.storage.bunnycdn.com |
| Storage (NY) | https://ny.storage.bunnycdn.com |
| Storage (LA) | https://la.storage.bunnycdn.com |
| Storage (Singapore) | https://sg.storage.bunnycdn.com |
curl -H "AccessKey: $BUNNY_API_KEY" \
https://api.bunny.net/storagezoneUses storage zone password as API key (different from main API key):
curl -H "AccessKey: $BUNNY_STORAGE_KEY" \
https://storage.bunnycdn.com/<zone-name>/<path>Store and serve files globally via CDN.
| Zone | ID | Password | CDN URL |
|---|---|---|---|
| your-storage-zone | YOUR_ZONE_ID | YOUR_STORAGE_PASSWORD | https://your-zone.b-cdn.net |
curl -X PUT "https://storage.bunnycdn.com/your-storage-zone/images/photo.jpg" \
-H "AccessKey: $BUNNY_STORAGE_KEY" \
-H "Content-Type: image/jpeg" \
--data-binary @photo.jpgcurl "https://storage.bunnycdn.com/your-storage-zone/images/photo.jpg" \
-H "AccessKey: $BUNNY_STORAGE_KEY" \
-o photo.jpgcurl "https://storage.bunnycdn.com/your-storage-zone/images/" \
-H "AccessKey: $BUNNY_STORAGE_KEY"curl -X DELETE "https://storage.bunnycdn.com/your-storage-zone/images/photo.jpg" \
-H "AccessKey: $BUNNY_STORAGE_KEY"import os
import requests
from pathlib import Path
def upload_to_bunny(local_path: str, remote_path: str):
"""Upload file to Bunny.net storage"""
STORAGE_KEY = os.environ.get("BUNNY_STORAGE_KEY")
ZONE = os.environ.get("BUNNY_STORAGE_ZONE", "your-storage-zone")
CDN_URL = os.environ.get("BUNNY_CDN_URL", "https://your-zone.b-cdn.net")
url = f"https://storage.bunnycdn.com/{ZONE}/{remote_path}"
with open(local_path, "rb") as f:
response = requests.put(
url,
headers={"AccessKey": STORAGE_KEY},
data=f
)
if response.status_code == 201:
cdn_url = f"{CDN_URL}/{remote_path}"
return cdn_url
else:
raise Exception(f"Upload failed: {response.status_code}")Content delivery network with global edge locations.
curl -X POST "https://api.bunny.net/pullzone" \
-H "AccessKey: $BUNNY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"Name":"my-cdn","StorageZoneId":YOUR_ZONE_ID,"OriginType":2}'# Purge single URL
curl -X POST "https://api.bunny.net/purge?url=https://your-zone.b-cdn.net/image.jpg" \
-H "AccessKey: $BUNNY_API_KEY"Real-time image optimization and transformation via URL parameters.
https://your-zone.b-cdn.net/<path>?<transformations>
| Parameter | Description | Example |
|---|---|---|
width | Resize width | ?width=800 |
height | Resize height | ?height=600 |
aspect_ratio | Force aspect | ?aspect_ratio=16:9 |
crop | Crop region | ?crop=x,y,width,height |
focus_crop | Focus point crop | ?focus_crop=x,y,scale,aspect |
quality | JPEG quality (1-100) | ?quality=85 |
format | Output format | ?format=webp |
blur | Blur effect | ?blur=10 |
sharpen | Sharpen image | ?sharpen=true |
brightness | Adjust brightness | ?brightness=4 |
contrast | Adjust contrast | ?contrast=4 |
saturation | Color saturation | ?saturation=20 |
gamma | Gamma correction | ?gamma=25 |
tint | Color tint | ?tint=ffeeee |
flop | Horizontal flip | ?flop=true |
# Resize to 800px width, WebP format
https://your-zone.b-cdn.net/photo.jpg?width=800&format=webp
# Thumbnail with aspect ratio
https://your-zone.b-cdn.net/photo.jpg?width=400&aspect_ratio=1:1
# Optimized with quality and blur
https://your-zone.b-cdn.net/photo.jpg?quality=75&blur=5Generate images via URL using AI models.
https://your-zone.b-cdn.net/.ai/img/<engine>/<blueprint>/<seed>/<prompt>.jpg| Engine | Code | Resolution | Cost |
|---|---|---|---|
| DALLE-2 256px | dalle-256 | 256x256 | $0.016 |
| DALLE-2 512px | dalle-512 | 512x512 | $0.018 |
| DALLE-2 1024px | dalle-1024 | 1024x1024 | $0.020 |
| Stable Diffusion v1.5 | sd15-512 | 512x512 | $0.001 |
| Stable Diffusion v2.1 | sd21-512 | 512x512 | $0.001 |
| Stable Diffusion v2.1 | sd21-768 | 768x768 | $0.030 |
# Generate avatar with DALLE
https://your-zone.b-cdn.net/.ai/img/dalle-512/default/12345/cute-pixel-bunny.jpg
# Stable Diffusion landscape
https://your-zone.b-cdn.net/.ai/img/sd21-768/default/67890/tropical-island-sunset.jpgbunny-eating-carrots -> "bunny eating carrots"Professional video hosting with transcoding, DRM, and analytics.
# Create video library
curl -X POST "https://api.bunny.net/videolibrary" \
-H "AccessKey: $BUNNY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"Name":"my-videos"}'# 1. Create video entry
curl -X POST "https://video.bunnycdn.com/library/{libraryId}/videos" \
-H "AccessKey: {library-api-key}" \
-H "Content-Type: application/json" \
-d '{"title":"My Video"}'
# 2. Upload video file
curl -X PUT "https://video.bunnycdn.com/library/{libraryId}/videos/{videoId}" \
-H "AccessKey: {library-api-key}" \
--data-binary @video.mp4curl "https://video.bunnycdn.com/library/{libraryId}/videos/{videoId}" \
-H "AccessKey: {library-api-key}"<iframe src="https://iframe.mediadelivery.net/embed/{libraryId}/{videoId}"
loading="lazy"
style="border:none;width:100%;aspect-ratio:16/9"
allow="accelerometer;gyroscope;autoplay;encrypted-media;picture-in-picture"
allowfullscreen>
</iframe>Run JavaScript at the edge for custom logic.
curl -X POST "https://api.bunny.net/edgescript" \
-H "AccessKey: $BUNNY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"Name":"my-script","Type":1}'// Redirect based on country
export default {
async fetch(request, env) {
const country = request.headers.get('cf-ipcountry');
if (country === 'NO') {
return Response.redirect('https://no.example.com' + request.url.pathname);
}
return fetch(request);
}
}Run Docker containers at edge locations.
Comprehensive security suite.
| Feature | Description |
|---|---|
| WAF | Web Application Firewall with custom rules |
| DDoS | Layer 3/4/7 DDoS protection |
| Bot Detection | AI-powered bot filtering |
| Rate Limiting | Per-IP and custom rate limits |
| Access Lists | IP allowlist/blocklist |
| Upload Scanning | Malware detection |
curl -X POST "https://api.bunny.net/shield/zones" \
-H "AccessKey: $BUNNY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"PullZoneId":YOUR_PULLZONE_ID}'Managed DNS with global anycast.
curl -X POST "https://api.bunny.net/dnszone" \
-H "AccessKey: $BUNNY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"Domain":"example.com"}'| Method | Endpoint | Description |
|---|---|---|
| GET | /storagezone | List all zones |
| POST | /storagezone | Create zone |
| GET | /storagezone/{id} | Get zone details |
| DELETE | /storagezone/{id} | Delete zone |
| Method | Endpoint | Description |
|---|---|---|
| GET | /pullzone | List all zones |
| POST | /pullzone | Create zone |
| GET | /pullzone/{id} | Get zone details |
| POST | /pullzone/{id} | Update zone |
| DELETE | /pullzone/{id} | Delete zone |
| Method | Endpoint | Description |
|---|---|---|
| POST | /purge?url=<url> | Purge single URL |
| Method | Endpoint | Description |
|---|---|---|
| GET | /videolibrary | List libraries |
| POST | /videolibrary | Create library |
| GET | /videolibrary/{id} | Get library |
"""
Bunny.net Media Service
"""
import os
import requests
from pathlib import Path
from typing import Optional
import mimetypes
class BunnyService:
"""Bunny.net CDN and Storage Service"""
def __init__(self, api_key=None, storage_key=None, storage_zone=None, cdn_url=None):
self.api_key = api_key or os.environ.get("BUNNY_API_KEY")
self.storage_key = storage_key or os.environ.get("BUNNY_STORAGE_KEY")
self.storage_zone = storage_zone or os.environ.get("BUNNY_STORAGE_ZONE", "your-storage-zone")
self.cdn_url = cdn_url or os.environ.get("BUNNY_CDN_URL", "https://your-zone.b-cdn.net")
self.storage_url = "https://storage.bunnycdn.com"
def upload(self, local_path: str, remote_path: str) -> str:
"""Upload file and return CDN URL"""
url = f"{self.storage_url}/{self.storage_zone}/{remote_path}"
content_type = mimetypes.guess_type(local_path)[0] or "application/octet-stream"
with open(local_path, "rb") as f:
response = requests.put(
url,
headers={
"AccessKey": self.storage_key,
"Content-Type": content_type
},
data=f
)
response.raise_for_status()
return f"{self.cdn_url}/{remote_path}"
def delete(self, remote_path: str) -> bool:
"""Delete file from storage"""
url = f"{self.storage_url}/{self.storage_zone}/{remote_path}"
response = requests.delete(url, headers={"AccessKey": self.storage_key})
return response.status_code == 200
def list_files(self, path: str = "") -> list:
"""List files in directory"""
url = f"{self.storage_url}/{self.storage_zone}/{path}/"
response = requests.get(url, headers={"AccessKey": self.storage_key})
return response.json()
def purge_cache(self, path: str):
"""Purge CDN cache for path"""
url = f"https://api.bunny.net/purge?url={self.cdn_url}/{path}"
response = requests.post(url, headers={"AccessKey": self.api_key})
return response.status_code == 200
def get_optimized_url(self, path: str, width: int = None, format: str = None, quality: int = None) -> str:
"""Get optimized image URL with transformations"""
url = f"{self.cdn_url}/{path}"
params = []
if width:
params.append(f"width={width}")
if format:
params.append(f"format={format}")
if quality:
params.append(f"quality={quality}")
if params:
url += "?" + "&".join(params)
return url
# Usage
bunny = BunnyService()
# Upload image
cdn_url = bunny.upload("photo.jpg", "images/photo.jpg")
print(f"Uploaded: {cdn_url}")
# Get optimized versions
webp_url = bunny.get_optimized_url("images/photo.jpg", width=800, format="webp", quality=85)
thumb_url = bunny.get_optimized_url("images/photo.jpg", width=200, format="webp")~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.