# drop.blanket.party > A self-hosted drop-and-share service. Drop any file (or paste any text), get > an unlisted standalone URL with proper previews for images, video, audio, > code, markdown, and PDF. Each share auto-renders the best viewer per file > kind. Designed for friend-to-friend sharing AND for handing context to AI > assistants in a single URL. ## What an agent should know - All shares live at `/s/`. IDs are 10-character nanoids or 3–40 character custom slugs. - Reads are public; create/edit/delete are gated on either: - the per-share `drop_own_` cookie set on the creator's browser, OR - a bearer token via `Authorization: Bearer ` (root-level). - Shares default to a 60-day expiry, completed (locked) once the creator calls /complete. - Anything you upload is rendered server-side with a strict CSP: HTML/SVG/XML files are stored as text/plain so they can't execute. Images get on-demand WebP thumbnails via `?w=200|400|800|1200|1600`. ## Endpoint catalog ### Read (public) - GET / — landing page (drop UI) - GET /s/ — share view (auto-rendered viewers) - GET /s// — direct file (range-supported, immutable cache) - GET /s//?w= — WebP thumbnail for images - GET /s//raw — single text dump of the entire share for AI ingestion query: ?files=a.py,b.md ?text_only=1 - GET /s//manifest.json — structured catalog (id, files[], sizes, mime, urls) - GET /s//download — zip of all files - GET /s//qr.svg — QR code for the share URL - GET /s//og.png — branded 1200x630 OpenGraph card - GET /s/ (Accept: application/json) — returns the manifest, not HTML - GET /api/shares//zipls?path= — list entries inside a zip - GET /api/shares//zipls?path=&entry= — extract one entry - GET /api/my-shares — shares owned by the calling session (cookie-based) - GET /api/shares//render?path= — server-rendered shiki HTML for a code file ### Mutate (owner OR Bearer token) - POST /api/shares body: {"title"?:string, "description"?:string, "password"?:string, "expireDays"?:number (fractional ok, min 1/24 = 1h, max 365), "slug"?:string (custom URL: /s/, 3-40 chars [a-zA-Z0-9_-]), "maxViews"?:number (burn-after-read: expires after Nth non-owner view), "stripExif"?:bool (re-encode images, dropping EXIF/GPS), "encrypted"?:bool (E2E: upload AES-GCM ciphertext, key in URL fragment)} -> {id, expiresAt}; sets drop_own_ cookie unless Bearer auth errors: 400 bad_slug, 409 slug_taken - POST /api/shares//files?name=path/to/file.ext body: raw bytes -> {name, size, mime} - POST /api/shares//files?append=1&to= body: raw bytes — appends (chunked/resumable uploads; keep chunks <90MB) -> {ok, size} — replaces the .md with the current docs.blanket.party note content - POST /api/shares//complete -> {ok, id} ; share is now read-only - DELETE /api/shares/ - DELETE /api/shares//files/ - PUT /api/shares//files/ (text/code only) body: {"content": string} ### Private instance library (Bearer token) - POST /api/admin/shares/search body: {"q"?:string, "state"?:"incomplete"|"expired"|"expiring"|"active", "kind"?:"markdown"|"html"|"image"|"video"|"audio"|"pdf"|"archive"|"code_text"|"other", "created_after"?:ISO string, "created_before"?:ISO string, "limit"?:1..100, "cursor"?:string} -> {items, next_cursor, counts}; metadata only, no file-body indexing - POST /api/admin/library/migrate — bearer-only bounded, idempotent index migration - POST /api/admin/session body {"token":string} — exchange token for a signed 12-hour browser session - GET /library — private browser interface for the same metadata search MCP clients expose the library as `drop_list` and `drop_search`. ## Agent forms (interactive shares) Upload a self-contained HTML file (form / questionnaire / decision board) and open it via `/s//.html?app=1` — scripts run in an opaque sandboxed origin (no cookies, connect-src limited to this host). The page derives the share id from location.pathname and POSTs its result back: - POST /api/shares//responses — public; JSON body ≤256 KB; -> {ok, response_id} - GET /api/shares//responses — owner/Bearer; -> {count, responses:[{id, created_at, data}]} Publish → send the ?app=1 link to a human → poll GET responses until count > 0. Efficient asset handoff: fetch /s//manifest.json first, then curl only the files you need to disk — reading content into your context is the only token cost. ## Quickstart for an AI agent ```bash TOKEN="" SID=$(curl -s -X POST -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"title":"agent share"}' \ https://drop.blanket.party/api/shares | jq -r .id) # upload (path can include folders) curl -X POST -H "Authorization: Bearer $TOKEN" --data-binary @file.md \ "https://drop.blanket.party/api/shares/$SID/files?name=notes/file.md" # lock so no further uploads curl -X POST -H "Authorization: Bearer $TOKEN" \ "https://drop.blanket.party/api/shares/$SID/complete" # share URL echo "https://drop.blanket.party/s/$SID" # AI ingestion URLs echo "https://drop.blanket.party/s/$SID/raw" echo "https://drop.blanket.party/s/$SID/manifest.json" ``` ## Constraints / gotchas - 60-day default expiry; drops vanish after that. - Per-file render cap: 2 MB inlined, 2000 lines shiki-highlighted (rest available via download). - /raw response capped at 20 MB total + 2 MB per file. - Allowed thumbnail widths: 200, 400, 800, 1200, 1600 (snaps up). - Inline-safe MIMEs: image/(png|jpeg|gif|webp|avif|bmp|x-icon), video/*, audio/*, application/pdf, text/(plain|markdown|csv|tsv), application/json. Everything else served as Content-Disposition: attachment with X-Content-Type-Options: nosniff. ## Source Open source for the homelab owner; see /docs for human-friendly docs.