hostthis - pipe a file in, get a public URL out
cat <file> | ssh -T hostthis.dev [<slug>] [-- <opts>]
tar czf - <dir> | ssh -T hostthis.dev [<slug>]
ssh hostthis.dev <command> [<args>]
Renders HTML, Markdown, diffs, Mermaid, PDF, CSV, JSON, folded stacks (flame graph), NDJSON logs and plain text at a random subdomain. A gzip-tar is a static site. No signup: your ssh key is your identity, and only it can change what it uploaded. Everything persists until you delete it.
cat <file> | ssh -T hostthis.dev new paste; URL on stdout, QR on stderr
cat <file> | ssh -T hostthis.dev <slug> new version; same URL
-- --name "<label>" set a label
-- --type <kind> force the renderer when detection misses (a
tiny CSV reads as text): html, markdown, diff,
mermaid, pdf, csv, json, flamegraph, log, text
-T silences ssh's pseudo-terminal warning. -- may go before or after the slug.
Each runs as ssh hostthis.dev <command>.
list [-o json] your pastes, most recently updated first
whoami [-o json] identity, count, quota
get <slug> content to stdout
url <slug> the URL
qr <slug> the URL and a QR code
rename <slug> [<label>] set the label; omit it to clear
versions <slug> [-o json] version history
pin <slug> <ver> serve <ver> until unpinned
unpin <slug> serve the latest version
delete <slug> delete everything
delete <slug> <ver> delete one version's bytes
have <slug> [-o json] a site's files as a keep list (see SITES)
pod link|status|unlink keep your bytes in your Solid pod (see PODS)
Sizes are stored bytes after compression, the unit of the quota.
<url>?raw source bytes (pages render in the browser, so
curl of the page itself gets the renderer)
#<heading> #page=3 #row=42 markdown, pdf, csv
#L42-L48 #F1L42-L48 text and log lines, diff lines of file 1
#focus=<stack> #q=<term> flame graph zoom, highlight
tar czf - <dir> | ssh -T hostthis.dev [<slug>] deploy, or redeploy in place
routing a missing path without a file extension serves
index.html; a missing asset is 404
layout one top-level directory is flattened
no index.html browsable knowledge base
redeploy stores only changed files
.hostthis-keep lines from `have` for files not sent, placed
next to index.html; its directory is the site
root. A stale base is refused: run have again
To send only what changed since the latest version:
ssh hostthis.dev have abc12345 > held
(cd dist && find . -type f | sed 's|^\./||' | sort | xargs sha256sum) > local
{ grep '^base ' held; grep -Fxf local held; } > dist/.hostthis-keep
grep -vFxf held local | cut -c67- | tar czf - -C dist -T - .hostthis-keep | ssh -T hostthis.dev abc12345
Key-value rooms at https://<slug>.hostthis.dev/api/, on your page's own origin
(not the apex). No auth: a room id, issued by POST, is the only key. Rooms are
deleted with their site.
POST /api/rooms new room: {"id": "<uuid>"}
GET /api/rooms/<id> all keys as one JSON object
GET /api/rooms/<id>/<key> one value, raw bytes
PUT /api/rooms/<id>/<key> write; body is the value
DELETE /api/rooms/<id>/<key> remove
In whole-room reads a value that parses as JSON nests as JSON; any other value is a string. 256 KiB and 256 keys per room, 64 MiB per app.
GET /api/rooms/<id>/ws {"type":"snapshot","seq","state"}, then
{"type":"put","seq","key","value"} and
{"type":"delete","seq","key"}
your frames relayed verbatim to the other clients; not
echoed, not stored, so treat them as untrusted
{"type":"ping"} every 20 s; answered with pong
close 4001 dropped for a newcomer after 2 min without
ping; reconnect
64 sockets per room; frames up to 32 KiB.
Peers connect browser to browser: data channels, audio, video, screen share. Signal over the room socket: send offers, answers and ICE candidates as your own frames, addressed with a per-tab id since every peer receives every frame. Pages you upload may call getUserMedia and getDisplayMedia; the browser still asks the visitor.
GET /api/rooms/<id>/ice {"iceServers": [...], "ttl": 3600}
pass iceServers to new RTCPeerConnection; STUN
always, a TURN credential when the operator set
one up and its monthly budget allows; ttl only
with TURN: fetch again before it runs out
GET /api/push/key VAPID public key
PUT|DELETE /api/rooms/<id>/push/subscriptions add or remove a PushSubscription
GET|PUT /api/rooms/<id>/push/schedule {"tz", "items": [{"id", "at": "HH:MM",
"days": [0-6, 0 = Sunday], "title",
"body" | "bodyKey", "url", "tag"}]}
POST /api/rooms/<id>/push/test send now
"when" (RFC 3339) fires once instead of at/days. bodyKey reads a room key
at send time. 16 devices and 16 items per room, 8 sends per device per day,
one test a minute. push and push/* are reserved keys.
Keep the bytes of your uploads in your own Solid pod.
pod link <webid> prints a URL valid for 15 minutes and returns. A
person opens it, signs in to the pod provider and
approves (an agent cannot); pod status then shows it
pod status [-o json] the linked folder, <pod>/hostthis/, or "no pod linked."
pod unlink new uploads are stored by hostthis again
The WebID is the profile URL your provider shows, e.g.
https://<you>.solidcommunity.net/profile/card#me. Only bytes move: slugs,
versions, URLs and quota stay on hostthis and work the same, delete included.
If the profile lists several pods, the link page asks which one; to switch,
run pod link again.
Every upload made while linked, a new version of an existing slug included, is
stored in the pod, except files a redeploy keeps unchanged, which stay where
they were. Earlier versions stay where they were, and pod versions stay in the
pod after unlink. hostthis can read and write <pod>/hostthis/ only. Unsharing
that folder revokes it, and the versions stored there then fail to load.
quota 1 GiB per key, after compression
paste 10 MiB after compression
site 1 GiB unpacked
0 ok 3 no ssh key
1 error 4 not found
2 usage 6 rate limited
NO_COLOR or TERM=dumb disables color.
MIT - see LICENSE.