File storage on Cloudflare R2 with ECDSA auth, image optimization, and content-hash URLs.
One-line install (Node.js ≥ 18 required):
curl -sL https://filespot.42b.eu/install/setup.sh | bash
Check your version:
filespot --version
# Compare against deployed version:
curl -s https://filespot.42b.eu/api/version
# Initialize — generates ECDSA P-256 keypair, first user becomes admin
filespot init https://filespot.42b.eu
# Upload a local file (auto-optimizes images & SVGs)
filespot upload hero.png --category renders
# Upload directly from URL (worker fetches source)
filespot upload https://example.com/hero.png --category renders
# List your files
filespot ls
Uploaded files are served at:
https://filespot.42b.eu/builder/{category}/{filename}?ch={hash}&uk={userkey}
| Param | Meaning |
|---|---|
category | Namespace: materials, renders, templates, etc. |
ch | First 5 chars of SHA-256 of the original file (version ID / cache key) |
uk | User key — first 8 chars of SHA-256 of user’s ECDSA public key |
Same filename + different ch = different version. Cache-Control: immutable on all served files.
| Command | Description |
|---|---|
filespot init <url> | Configure server, generate keypair, register |
filespot upload <file|url> --category <cat> | Upload local files or remote URL sources |
filespot ls | List your uploaded files |
filespot info <key> | File metadata |
filespot rm <key> | Delete a file |
filespot download <url> | Download a file |
filespot whoami | Show identity & auth status |
filespot --version | Show local CLI version |
filespot requests | List pending access requests (admin) |
filespot requests approve <fp> | Approve user by fingerprint (admin) |
All commands output JSON when piped (non-TTY), making them usable by Claude, Cursor, and other AI agents.
| Method | Path | Auth | Description |
|---|---|---|---|
| POST | /api/register | — | Register ECDSA public key (first user = admin) |
| POST | /api/embed/session | ECDSA | Mint shareId + memberToken for iframe embedding |
| POST | /api/embed/upload/{shareId}?category=&filename= | Bearer | Upload via iframe using member token |
| GET | /embed/drop/{shareId}?lang=en#token=... | — | Embeddable drag-and-drop uploader |
| POST | /api/upload?category=&filename=&ch=&uk= | ECDSA | Upload file bytes |
| GET | /builder/{cat}/{file}?ch=&uk= | — | Serve file (public, immutable cache) |
| GET | /api/files | ECDSA | List files |
| GET | /api/info?key= | ECDSA | File metadata |
| DELETE | /api/files?key= | ECDSA | Delete file (own files only) |
| GET | /api/whoami | ECDSA | User identity |
| GET | /api/version | — | Deploy version info (JSON) |
| GET | /api/requests | Admin | Pending access requests |
| POST | /api/requests/approve | Admin | Approve user |
| POST | /api/requests/deny | Admin | Deny user |
Every authenticated request requires three headers:
X-Filespot-Key: {fingerprint} # 8-char hex, from SHA-256 of raw public key
X-Filespot-Timestamp: {unix_seconds} # must be within 60s of server time
X-Filespot-Signature: {base64url} # ECDSA-P256-SHA256 sign of: METHOD\nPATH+QUERY\nTIMESTAMP
# Register (first user gets admin)
curl -s -X POST https://filespot.42b.eu/api/register \
-H "Content-Type: application/json" \
-d '{"publicKey":"BASE64URL_RAW_PUBKEY","displayName":"alice"}'
# Upload a file (with auth headers)
curl -s -X POST "https://filespot.42b.eu/api/upload?category=materials&filename=logo.svg&ch=a1b2c&uk=fa73dcf4" \
-H "X-Filespot-Key: fa73dcf4" \
-H "X-Filespot-Timestamp: 1711670400" \
-H "X-Filespot-Signature: BASE64URL_SIG" \
-H "Content-Type: image/svg+xml" \
--data-binary @logo.svg
# Mint embed session (ECDSA auth) and upload with member token
EMBED_JSON=$(curl -s -X POST "https://filespot.42b.eu/api/embed/session" \
-H "X-Filespot-Key: fa73dcf4" \
-H "X-Filespot-Timestamp: 1711670400" \
-H "X-Filespot-Signature: BASE64URL_SIG" \
-H "Content-Type: application/json" \
-d '{"ttlSeconds":86400}')
SHARE_ID=$(printf '%s' "$EMBED_JSON" | jq -r '.shareId')
MEMBER_TOKEN=$(printf '%s' "$EMBED_JSON" | jq -r '.memberToken')
curl -s -X POST "https://filespot.42b.eu/api/embed/upload/${SHARE_ID}?category=drop&filename=drop.txt" \
-H "Authorization: Bearer ${MEMBER_TOKEN}" \
-H "Content-Type: text/plain" \
--data-binary "hello from embed flow"
# Fetch uploaded file (public, no auth needed)
curl -s "https://filespot.42b.eu/builder/materials/logo.svg?ch=a1b2c&uk=fa73dcf4"
Paste this iframe into any page (replace SHARE_ID and MEMBER_TOKEN):
<iframe
src="https://filespot.42b.eu/embed/drop/SHARE_ID?lang=en#token=MEMBER_TOKEN"
style="margin: 5%; border: none; border-radius: 16px;"
loading="lazy"
></iframe>
You can customize language, colors, and layout via query parameters (e.g. ?lang=sv&bg=%23f9fafb&border=%23bfdbfe&text=%23111827&accent=%233b82f6) and by adding inline style on the iframe in your own page.
The CLI auto-optimizes uploads:
svgo with SVGOMG-derived settings (falls back to built-in optimizer)--webp flag to upload as WebPffmpeg command for WebM conversionThreshold: ≥2% or ≥2KB savings. Skip with --no-optimize.
Powered by Cloudflare Workers + R2 + D1