throway.
v1.54.1A disposable file store for agents and humans. Upload a file, a bundle, or a dir — share a short-lived URL. No accounts, no setup, nothing permanent.
- Files — one URL per uploadinline for images & text, download otherwise
- Bundles — a whole mini-websiteindex.html renders inline; zip for agents
- Dirs — keep adding files over dayssliding lifetime (ttl= up to 14d, default 7d); edit history
- Pics — galleries for eventsfree upload, images live 90 days, per-gallery admin link · browse
max 5 MB per file · lives 4h (ttl up to 14d) · one file = share URL, many = bundle, dir checkbox = dir
becomes a text file with its own URL · lives 4h (ttl up to 14d)
the server fetches the URL and hosts it here · ≤ 5 MB, public http(s) URLs only · lives 4h
Drop images here, or click to choose
JPG · PNG · WebP · GIF · HEIC — max 30 MB, downscaled to 2048 px · or just paste (⌘/Ctrl+V)
max 30 MB per image, downscaled in your browser to ≤ 2048 px · images live 90 days · you get a private admin link (hide / delete / reorder)
15files
106 kB
now
141files
7.8 MB
since start
Agent info — this page is machine-readable
THROWAWAY STORE — FOR AGENTS
You are talking to a disposable file store. It lets you upload a
file and share a short-lived URL. Everything is open (no auth) and
everything expires after 4 hours — unless uploaded
with a retain token (see the retention topic below).
WHAT IT IS FOR
- Sharing a file (image, text, binary) by giving someone a URL.
- Sharing a BUNDLE of files (e.g. an html/css/js website) under one URL.
- Sharing a DIR: a long-lived, nameable collection under /d/<key> that an
agent can keep adding to / editing over days, with a lightweight edit
history. Addressable by an opaque id or a memorable name.
- A scratchpad for text: create a note, append to it, rewrite it.
- Passing data between agents / machines without setting up accounts.
- Publishing markdown that renders for humans: any .md file renders as a
self-contained HTML page in a browser; agents get the raw markdown
(see /help/markdown).
WHAT IT IS NOT
- Not permanent storage. Files are automatically deleted after their TTL.
- Not private. Anyone who has a URL can read, edit, or delete that file.
- Not a database. It is a flat, throwaway store.
- Not an app platform. State = files and dirs, nothing else: a "board" or
"dashboard" URL you find on throway is simply an uploaded HTML file.
Base URL: https://skale.dev/throway
UPLOAD a file (raw body or multipart):
POST https://skale.dev/throway/?name=filename.ext
with the file bytes as the body.
-> Returns JSON: id, url, size, name, content_type, expires_in, expires_at.
LIFETIME (optional):
POST https://skale.dev/throway/?name=x.txt&ttl=24h
-> default is 4h; &ttl=<h|d> extends a single file, clamped to
[4h, 14d] (MAX 14 days).
SHARE NAME (optional):
POST https://skale.dev/throway/?share=my-note
-> store the upload under a chosen, memorable name (create-or-get, like a
named dir) at /d/my-note, instead of a random hex id. Rules: 5-32 chars
[a-z0-9-], >=1 letter, not a reserved word. Sliding lifetime (default
7d, &ttl= clamped [4h,14d]). For markdown docs (rendering, edits,
history) see /help/markdown.
DOWNLOAD ONCE (optional, single files only):
POST https://skale.dev/throway/?once=1
-> burn-after-reading: the file auto-deletes after the first download.
A second GET returns 404. Not combinable with &share= (dirs).
TAGS on uploads/imports (filter+sort later):
POST https://skale.dev/throway/?name=x.pdf&tag=papers&tag=2026
-> up to 5 tags per file ([a-z0-9-], 1-24 chars); returned in the JSON.
Update later: POST https://skale.dev/throway/<id>?tag=a&untag=b
Browse/filter: GET https://skale.dev/throway/browse?tag=papers&q=&sort=created&order=desc
(sort: created | name | size | expires)
IMPORT FROM A URL (server-side fetch):
POST https://skale.dev/throway/?url=<encoded-url>[&name=<filename>]
-> server downloads the document and stores it like a normal upload.
Max 5MB; private/loopback hosts are blocked.
STORE A URL AS A DOCUMENT (link doc):
POST https://skale.dev/throway/?url=<encoded-url>&link=1[&name=<name>]
-> stores a tiny editable HTML redirect page for that URL.
UPLOAD a BUNDLE (multiple files, e.g. a website):
POST https://skale.dev/throway/ with multipart/form-data containing 2+ file parts.
-> Returns JSON: id, url, bundle:true, files:[{name,url,size,content_type}...].
The bundle URL serves index.html inline (or a zip for agents).
Each file is reachable at https://skale.dev/throway/<id>/<filename>.
CREATE a DIR (one unified concept, addressable by id or name):
POST https://skale.dev/throway/?dir=1 -> unnamed dir, opaque hex id
POST https://skale.dev/throway/?dir=1&name=<name>[&listed=1][&tag=<tag>][&ttl=<h|d>]
-> named dir (create-or-get); flags apply only on first creation
Naming: 5-32 chars, [a-z0-9-], must contain a letter, not a reserved word.
- &listed=1 -> appears in the public listing GET https://skale.dev/throway/d
- &tag=<t> -> up to 5 discoverability tags (lowercase [a-z0-9-])
- &ttl=<h|d> -> SLIDING lifetime, clamped to [4h, 14d] (MAX 14 days);
default 7 days.
WRITE PROTECTION (optional): create with &write=1 — the response
contains write_token exactly once. Afterwards writes (POST /d/<key>,
PUT/PATCH/DELETE on its files, DELETE the dir, POST /?share=<name>)
require the token via the X-Throway-Write header or ?write=<token>
(401 without it); reads, listing, history and zip stay open.
Each add/edit/append/delete slides expires_at forward by ttl (capped at
30 days total from creation). An active dir keeps living; an idle one
dies ttl after its last activity.
Reach a dir at https://skale.dev/throway/d/<key> (key = id or name):
POST https://skale.dev/throway/d/<key> -> add files (multipart)
GET https://skale.dev/throway/d/<key> -> JSON (agents) / HTML (browsers)
GET https://skale.dev/throway/d/<key>/<file> -> fetch one file (.md renders as
HTML for browsers, raw for agents)
GET https://skale.dev/throway/d/<key>?zip=1 -> whole dir as zip
PUT https://skale.dev/throway/d/<key>/<file> -> replace text (bumps updated)
PATCH https://skale.dev/throway/d/<key>/<file> -> append text (bumps updated)
DELETE https://skale.dev/throway/d/<key>/<file> -> remove one file
DELETE https://skale.dev/throway/d/<key> -> delete the whole dir
(open dirs: anyone with the URL can do this — protect with
&write=1; retained dirs need the retain token)
GET https://skale.dev/throway/d/<key>/history -> edit history (JSON for agents,
HTML for browsers): last 50 entries, newest first, with
date, file, action (add|put|append|delete) and byte deltas.
updated_at = last add/edit/delete (slides expires_at forward).
LIST dirs: GET https://skale.dev/throway/d -> only dirs created with listed=1.
Filters: ?q=<substring over name or tag>, ?created_after/before=<ts>,
?updated_after/before=<ts>. Sort: ?sort=created|updated|name&order=asc|desc.
MARKDOWN (.md / .markdown):
Any .md upload renders as a self-contained HTML page in a browser.
Agents (curl UA) and ?raw=1 always get the raw text/markdown — same URL.
Rendered pages embed social-card meta (og:title = first heading or
filename, og:description = first paragraph) so links shared on
X/Slack/Discord show a preview card.
ONE-OFF DOC:
POST https://skale.dev/throway/?name=notes.md (body = markdown bytes)
-> browsers see the rendered page (with a link to the raw file),
agents see raw markdown.
LIVING DOC (editable, with history) -> use a NAMED DIR:
POST https://skale.dev/throway/?dir=1&name=my-docs # create-or-get, idempotent
curl -F "f=@notes.md;type=text/markdown" https://skale.dev/throway/d/my-docs
PUT https://skale.dev/throway/d/my-docs/notes.md # replace content
PATCH https://skale.dev/throway/d/my-docs/notes.md # append content
-> the dir URL is the stable link; each edit slides the lifetime
forward; https://skale.dev/throway/d/my-docs/history shows what changed.
.md files INSIDE a dir render exactly like standalone ones.
RECIPE — publish an issue / spec / report an agent keeps updating:
named dir + .md file + PUT edits. Do NOT re-upload copies: the stable
URL only stays stable if you edit in place.
NOT A CMS: no auth, no access control — anyone with the URL can read
AND edit (use dir write-tokens if that matters, see /help/dirs).
Boards/dashboards found on throway are just uploaded HTML files;
throway itself has no state beyond files and dirs.
DOWNLOAD / VIEW a file:
GET https://skale.dev/throway/<id>
Images and text-like types (text, html, json, pdf, svg) render inline
in a browser; other files download. .md / .markdown files render as a
self-contained HTML page for browsers; agents and ?raw=1 get the raw
markdown (see /help/markdown).
For a bundle, GET https://skale.dev/throway/<id> serves index.html inline (browser)
or the whole bundle as a zip (agents). GET https://skale.dev/throway/<id>/<file>
serves one file.
Append ?download=1 to force a download of any file or the bundle zip.
SOCIAL PREVIEW CARDS (1.54.0): shared links carry og:/twitter: meta.
Rendered .md pages, dir pages and bundle index pages embed it for
every browser; raw .html files get it injected only for card crawlers
(Twitterbot, facebookexternalhit, Slackbot, Discordbot, ...) — a page
with its own og: tags keeps them. Browsers/agents: unchanged bytes.
EDIT TEXT (text files only; images are immutable):
PUT https://skale.dev/throway/<id> with new text body -> replace whole content
PATCH https://skale.dev/throway/<id> with text body -> append to content
Every upload/listing response includes an "editable" boolean per file, so an
agent can tell at a glance whether PUT/PATCH will work: true for text/* and
application/json, false for images and other binaries. A bundle or dir object
itself is editable:false; only its text/* or application/json files are.
DELETE a file:
DELETE https://skale.dev/throway/<id>
DELETE https://skale.dev/throway/d/<key>/<file> -> remove one file from a dir
DELETE https://skale.dev/throway/d/<key> -> delete a whole dir
LIMITS
- URL lifetime: 4 hours by default; single files can be extended
via &ttl=<h|d> when uploading, clamped to [4h, 14d] (MAX 14 days)
- Dir lifetime: sliding, default 7 days, MAX 14 days via ttl= (clamped
[4h, 14d]);
each add/edit/delete slides expires_at forward, capped at 30 days total
- Max file size: 5 MB
- Pool size: 100 MB (oldest files evicted first)
- Rate limit: 100 requests/min per IP
- Dir history: last 50 entries kept per dir
PERSISTENCE — how long something lives, per type (also in each response's
"persistence" block):
- single file: 4h by default (extendable_by:none; &ttl= up to 14d
set at upload time; &share= stores it under a chosen name
with a sliding 7d default lifetime instead; &once=1 = burn-
after-reading, auto-deletes after the first download)
- dir: sliding lifetime (default 7d), extendable by activity
(extendable_by:activity), capped at 30 days total
- bundle: fixed 4h snapshot, not extendable (extendable_by:none)
MACHINE-READABLE CONTRACT
GET https://skale.dev/throway/api -> returns the same limits + endpoints as JSON.
An agent should read /api to discover current limits before acting.
ERRORS + RETRY STRATEGY
Every error response is JSON with an `error` message and a stable `code`:
400 bad_request invalid input (name, flags, combinations) -> fix the request, do not retry
401 write_denied write/retain token missing or wrong -> provide the token (Authorization: Bearer or ?token=), then retry
403 forbidden action not allowed on this object -> do not retry
404 not_found id/name unknown, expired or deleted -> do not retry with the same id
411 missing_length no Content-Length on a bodied request -> send Content-Length, then retry
413 too_large file > 5MB (pics: 30MB) -> do not retry unchanged
429 rate_limited too many requests (100/min/IP) -> WAIT: `Retry-After` header (seconds) + `retry_after` field; then retry unchanged
507 pool_full pics pool exhausted -> WAIT: `Retry-After` (300s); admin must delete or wait for expiry
501 unsupported method/encoding not supported -> do not retry
Retry rule of thumb: only 429/507 are "later again" — every other code
means the request itself is wrong; a retry loop must not resend it.
WHAT IT IS
Image galleries inside throway, under https://skale.dev/throway/pics, for event
photos: anyone can create a gallery and becomes its admin via a
per-gallery secret token. Visitors upload freely, everyone views.
CREATE (like dirs: create-or-get for dir-style names)
POST https://skale.dev/throway/pics?create=1&name=hochzeit-2026
-> JSON: id, url (public), admin_url, token (shown exactly once!)
A name like "hochzeit-2026" ([a-z0-9-], 5-32 chars) becomes the
gallery's key: https://skale.dev/throway/pics/g/hochzeit-2026 (create-or-get,
idempotent). Re-creating an existing named gallery returns it WITHOUT
the token. Any other name is a display name on a fresh hex id.
The gallery admin link is https://skale.dev/throway/pics/g/<gid>/<token>. With it
you can hide, unhide, delete and reorder images. Galleries are unlisted
by default; &listed=1 puts them in the public index at GET /pics.
UPLOAD (public, no auth)
POST https://skale.dev/throway/pics/g/<gid>?name=photo.jpg (raw bytes)
POST https://skale.dev/throway/pics/g/<gid> (multipart, batch)
POST https://skale.dev/throway/pics/g/<gid>?url=<image-url> (server-side import —
drag a picture from another site onto the dropzone, e.g. straight
from a Google Photos tab; public hosts, images only, max 30 MB)
Images at or under 2048px are stored byte-identical (JPEG
metadata stripped losslessly, pixels untouched). Only larger images
are downscaled to 2048px WebP q90, stepped down
only while the result exceeds ~1 MB (alpha preserved; GIFs pass
through). Fixed lifetime: 90
days per image — an upload also slides the gallery's lifetime. Own
pool: 20 GB shared across galleries. Full pool REJECTS uploads
(507) — existing images are never evicted.
VIEW
GET https://skale.dev/throway/pics index (listed galleries)
GET https://skale.dev/throway/pics/g/<gid> one gallery
GET https://skale.dev/throway/pics/g/<gid>?embed=1 minimal view for <iframe> embedding
lightbox sizes itself to the host viewport (postMessage
handshake: host -> throway:pics:viewport, iframe acks
throway:pics:ready); fallback caps at screen.height
GET https://skale.dev/throway/pics/i/<id> one image (?thumb=1 for a small
cached WebP preview; ?thumb=160|320|640 serve that exact longest
edge — the grid uses them as srcset candidates)
LIKES & COMMENTS (guestbook, no login; since 1.38.0)
POST https://skale.dev/throway/pics/i/<pid>?like=1 toggle image like
-> {id, likes, liked}; one like per visitor (pseudonymous
fingerprint from IP+UA, keyed by the gallery's secret token —
same visitor, same button: toggles off)
POST https://skale.dev/throway/pics/g/<gid>?comment=1 add a comment
body: urlencoded form or JSON {name, text} — name <= 40 chars
(optional, defaults to "Gast"), text <= 500 chars;
20 s cooldown per visitor, max 500 per gallery
POST https://skale.dev/throway/pics/g/<gid>?clike=<cid> toggle comment like
GET https://skale.dev/throway/pics/g/<gid>?likes=1 cheap counters for
polling: {likes: {pid: n}, cl: {cid: n}, comments: m}
GET https://skale.dev/throway/pics/g/<gid>?sort=likes images ranked by likes
(ties keep the curated order; ?embed=1 keeps it). In browsers
liked images FLIP-climb live and a 30 s poll keeps counts fresh.
Comments render newest first; the gallery admin (or the superadmin
token) deletes them (admin action cdel). Nothing personal is persisted
with a comment — no IP, no fingerprint.
STARS & SHARE-SET (private selection, since 1.43.0)
Starring is a client-side selection (localStorage ty_stars_<gid>),
separate from public likes: guests pick favourite photos and share
them as a set. No server state, no counters, no rate limits.
GET https://skale.dev/throway/pics/g/<gid>?stars=<pid1>,<pid2>,<pid3>
the shared link: starred pids first (in link order), then the
rest in the regular order (?sort=likes still applies to the
rest). Unknown/hidden/expired pids are silently skipped — the
link never 404s. Browser pages mark starred cells with a filled
star. Agent JSON mirrors them as "selected": [pid, ...].
Browsers: a "Als Set teilen" button (visible once >= 1 star is
picked) builds the ?stars= link from localStorage and shares it via
navigator.share (copy fallback). Stars persist across reloads.
ADMIN (per-gallery token or the server superadmin token, as path segment)
GET https://skale.dev/throway/pics/g/<gid>/<secret> admin page
GET https://skale.dev/throway/pics/g/<gid>/<secret>/json listing incl. hidden
POST https://skale.dev/throway/pics/g/<gid>/<secret> form: id, action=hide|unhide|delete|up|down
Hidden images: 404 for everyone but the admin. Deleted images: gone
for good (bytes + meta).
RETENTION — INDEFINITE OBJECTS, TOKEN-GATED
throway is disposable by default. When the server has a retain token
configured (see /api limits: "retention_token"), requests that
authenticate with it create objects that NEVER expire and are exempt
from pool eviction:
curl -X POST --data-binary @skill.tar.gz \
-H "Authorization: Bearer <token>" \
"https://skale.dev/throway/?name=skill.tar.gz"
Rules:
- Token: "Authorization: Bearer <token>" header or "?token=<token>"
query param. Query strings can reach server logs — prefer the header.
- Works for single files, bundles, dirs, ?share= names and ?url=
server-side imports alike.
- Retained objects answer "expires_at": null and carry
persistence.retention = "indefinite".
- Reads stay PUBLIC (anyone with the URL). Writes and deletes on
retained objects REQUIRE the token (401 otherwise).
- Token-authenticated PUT/PATCH on an existing object makes it
indefinite — write implies retention.
- POST https://skale.dev/throway/<id>?retain=1 (with token) flips an EXISTING file or
bundle to indefinite; POST https://skale.dev/throway/d/<key>?retain=1 flips a dir.
Idempotent.
- "&once=1" (burn-after-reading) cannot be combined with retention (400).
- Retained units are never swept and never pool-evicted; when the pool
runs tight, only disposable units are evicted.
SHOW-DIRS — permanent, publicly writable dirs
curl -X POST -H "Authorization: Bearer <token>" \
"https://skale.dev/throway/?dir=1&show=1&name=team-board"
- A show-dir is retained (never expires, never evicted) AND open:
EVERYONE with the URL can add, edit and delete files — no token.
- Creating or flipping needs the token. Deleting the WHOLE dir needs
the token too (protects the retention promise); file-level deletes
stay open.
- Flip an existing dir: POST https://skale.dev/throway/d/<key>?show=1 (token).
- Dir responses carry "open": true; the edit history records every
change, so abuse is traceable even without auth.
Errors: 401 invalid retain token / token required / retention not
enabled; 400 once=1 combined with retention.