Skip to content
Hoody.com

Hoody Pipe streams data between senders and receivers over named “pipe paths” with no server-side storage. The web interface provides a browser-based upload form, the service info endpoints expose health checks and inline help, and the data transfer endpoints let scripts, curl, and other clients exchange files, text, and binary streams in real time.

Use the Web Interface endpoints to embed the pipe UI in a browser, the Service Info endpoints for health probes and copy-paste examples, and the Data Transfer endpoints to programmatically send and receive payloads.

Returns the Hoody Pipe web interface — an HTML page for sending files or text to a pipe path from the browser. Also accessible at / (root alias).

This endpoint takes no parameters.

Terminal window
curl https://{projectId}-{containerId}-pipe-1.{server}.containers.hoody.icu/api/v1/pipe

Returns a pure HTML form for file/text upload that works without JavaScript. Useful in restricted browser environments or when JavaScript is disabled. Also accessible at /noscript.

The form supports a path query parameter to pre-fill the pipe path and enable the send button, and a mode query parameter to switch between file (default) and text input. Path values are sanitized (leading slashes stripped, only URL-safe characters). The response uses a CSP style nonce to block scripts.

NameInTypeRequiredDescription
pathquerystringNoPre-fill the pipe path. Only URL-safe characters allowed.
modequerystringNoInput mode: file for file picker, text for textarea. Allowed values: file, text. Default: file.
Terminal window
curl "https://{projectId}-{containerId}-pipe-1.{server}.containers.hoody.icu/api/v1/pipe/noscript?path=mypath&mode=file"

Returns plain text usage instructions showing how to send and receive data using curl. The help text includes the server’s own URL (derived from the Host header) so the examples can be copied and run directly. Sections cover receiving data, sending files/text/directories with curl -T, ?download and ?filename control, ?video browser playback, ?progress transfer monitoring, and end-to-end encryption with OpenSSL. Also accessible at /help.

This endpoint takes no parameters.

Terminal window
curl https://{projectId}-{containerId}-pipe-1.{server}.containers.hoody.icu/api/v1/pipe/help

Returns the standardized 9-field health response. Unauthenticated. Only reachable at /api/v1/pipe/health — a bare /health returns 404 with the body [ERROR] '/health' is not a valid path. Use '/api/v1/pipe/health'.\n. Methods other than GET, HEAD, or OPTIONS return 405 with [ERROR] Method <verb> is not allowed.\n and Allow: GET, HEAD, OPTIONS.

This endpoint takes no parameters.

Terminal window
curl https://{projectId}-{containerId}-pipe-1.{server}.containers.hoody.icu/api/v1/pipe/health

Receive data from the specified pipe path. The response blocks until a sender connects and starts streaming. Once established, the response body contains the sender’s data with original headers forwarded.

Lifecycle:

  1. Receiver GETs a path → request blocks
  2. When a sender POSTs/PUTs to the same path with matching n, the pipe establishes
  3. Response starts streaming with the sender’s data
  4. Response completes when the sender finishes uploading

Headers forwarded from sender:

  • Content-Type — sender’s content type (dangerous types rewritten to text/plain; foreign params dropped except a safe charset)
  • Content-Length — only when the sender provided a valid ^\d{1,19}$ value AND the body is non-multipart
  • Content-Disposition — if provided; a sender-supplied inline is upgraded to attachment unless the effective Content-Type is on the inline-safe allowlist (image/*, audio/*, video/*, text/plain, application/pdf, excluding image/svg+xml)
  • X-Piping — custom metadata from sender
  • X-Hoody-Pipe — custom metadata from sender

Forwarded headers are CRLF-sanitized to prevent header injection.

Download control via query parameters:

  • ?download — force attachment disposition (triggers browser download)
  • ?download=false — suppress Content-Disposition entirely (always display inline)
  • ?filename=custom.txt — set a custom download filename (implies ?download)

These work per-receiver — with n=2, one receiver can download while the other displays inline.

Multi-receiver: When n exceeds 1, all receivers get identical copies via lockstep fan-out — each chunk is written to every receiver before the next chunk is read from the sender. Memory is bounded to roughly one chunk per receiver. The slowest receiver paces the entire transfer — its backpressure applies to the sender and transitively to all other receivers.

Connection ordering: Either party can connect first — the server holds the early party until counterparts arrive (up to a 5-minute TTL).

Security headers on response: X-Robots-Tag: none, X-Content-Type-Options: nosniff, and CORS headers reflecting the receiver’s Origin.

Also accessible without prefix (e.g. GET /myfile).

NameInTypeRequiredDescription
pathpathstringYesPipe path name to receive from — must match the path used by the sender. Reserved paths (/help, /noscript, etc.) return their own content on GET instead of acting as pipe receivers.
nqueryintegerNoExpected number of receivers. Must match the sender’s n value exactly — a mismatch returns 400. When n exceeds 1, the pipe waits for all n receivers and the sender before streaming. Default: 1.
downloadquerystringNoControl whether the response triggers a browser download. ?download, ?download=true, ?download=yes, ?download=1 force Content-Disposition: attachment. ?download=false, ?download=no, ?download=0 suppress Content-Disposition entirely. Absent passthroughs the sender’s Content-Disposition as-is.
filenamequerystringNoSet a custom download filename. Implies ?download. Null bytes, CRLF, path separators, leading dots, and control characters are stripped. Truncated to 255 characters. Non-ASCII filenames use RFC 5987 filename*=UTF-8''... encoding. Filenames that sanitize to empty fall back to bare attachment.
videoquerystringNoReturn an HTML page with an embedded MSE video player instead of raw pipe data. The player page fetches the raw stream internally — no pipe receiver slot is consumed by the page itself. Only serves the HTML player when the client sends Accept: text/html; non-browser clients fall through to normal pipe receiver behavior. ?video, ?video=true, ?video=yes, ?video=1 show the player; ?video=false, ?video=no, ?video=0 return normal pipe data.
progressquerystringNoReturn real-time transfer progress as an SSE stream or HTML dashboard. Does NOT consume a pipe receiver slot. Accept header routing: text/event-stream for SSE; text/html for dashboard; */* or missing defaults to SSE. ?progress, ?progress=true, ?progress=yes, ?progress=1 show progress; ?progress=false, ?progress=no, ?progress=0 return normal pipe data.
Terminal window
curl https://{projectId}-{containerId}-pipe-1.{server}.containers.hoody.icu/api/v1/pipe/mypath

Send data to the specified pipe path. The sender’s request body is streamed directly to receiver(s) when they connect — no server-side storage.

Lifecycle:

  1. Sender POSTs to a path → gets back a streaming response with [INFO] status messages
  2. Server waits for n receivers to connect (default: 1)
  3. Once all receivers connect, data streams from sender to all receivers simultaneously
  4. Sender receives [INFO] Upload complete. then [INFO] Transfer complete.

Status messages streamed to the sender as text/plain:

[INFO] Waiting for 1 receiver(s) to connect...
[INFO] Streaming to 1 receiver(s)...
[INFO] Upload complete.
[INFO] Transfer complete.

Multipart uploads: When Content-Type matches multipart/form-data (case-insensitive), the server extracts the first file part (non-file form fields are drained and skipped) and streams its contents. The part’s Content-Type and Content-Disposition are forwarded to receivers. Content-Length is NOT forwarded for multipart inputs (the response uses chunked transfer encoding). A multipart body containing no file part is rejected with [ERROR] Transfer failed and receivers receive 500. Slow-loris bound: parsing aborts if no part arrives within 30 s.

Custom headers: Set X-Hoody-Pipe or X-Piping request headers to forward arbitrary metadata to receivers. Each header is capped at 8 KiB (over-cap headers are dropped) and CRLF/control chars are stripped. The receiver response carries Access-Control-Expose-Headers: X-Piping, X-Hoody-Pipe only when at least one was supplied.

Content-Type safety: Dangerous MIME types that execute scripts in browsers (text/html, image/svg+xml, application/javascript, the full WHATWG JS-essence list, XHTML/XML) are rewritten to text/plain. Parameters are stripped except for a single safe charset (utf-8/us-ascii/iso-8859-1).

Connection ordering: Either sender or receiver(s) can connect first.

Limits:

  • Path length: max 1024 characters
  • Receiver count (n): 1–256
  • Pending transfers: max 1000 server-wide
  • Active transfers: max 1000 server-wide
  • Unestablished pipe TTL: 5 minutes

Also accessible without prefix (e.g. POST /myfile).

NameInTypeRequiredDescription
pathpathstringYesUnique pipe path name. Must not be a reserved path (/, /help, /noscript, /favicon.ico, /robots.txt). Examples: myfile, transfer123, secret.png, logs/today.
nqueryintegerNoNumber of receivers to wait for before starting the transfer. All receivers get identical copies of the data (fan-out). Must be a positive integer, max 256. Default: 1.

Data to stream to receiver(s). Any content type is accepted.

  • Binary files: Use application/octet-stream or the file’s actual MIME type.
  • Text: Use text/plain.
  • Multipart: Use multipart/form-data for browser uploads — only the first file part is streamed; leading non-file form fields are drained and skipped.
  • No body: An empty POST is valid — receivers get an empty response.
Terminal window
curl -X POST https://{projectId}-{containerId}-pipe-1.{server}.containers.hoody.icu/api/v1/pipe/mypath \
--data-binary "@myfile.png" \
-H "Content-Type: application/octet-stream"

Identical to POST — send data to the specified pipe path. PUT is provided as an alias because curl -T file URL uses PUT, making it natural for file transfers.

curl usage:

Terminal window
# Send a file (uses PUT)
curl -T myfile https://pipe.example.com/api/v1/pipe/mypath
# Send stdin
echo 'hello' | curl -T - https://pipe.example.com/api/v1/pipe/mypath
# Send a directory as tar.gz
tar czf - ./mydir | curl -T - https://pipe.example.com/api/v1/pipe/mydir.tar.gz

All parameters, request body handling, status messages, and error codes are identical to POST.

NameInTypeRequiredDescription
pathpathstringYesUnique pipe path name (same rules as POST — no reserved paths, max 1024 chars).
nqueryintegerNoNumber of receivers to wait for (must match receivers’ n, max 256). Default: 1.

Data to stream — any content type. Multipart/form-data is supported (first file part extracted; non-file fields are skipped).

Terminal window
curl -T myfile.png https://{projectId}-{containerId}-pipe-1.{server}.containers.hoody.icu/api/v1/pipe/mypath

Handles CORS preflight requests for cross-origin browser access. Returns permissive CORS headers reflecting the request Origin.

Headers returned:

  • Access-Control-Allow-Origin — reflects Origin (or * if none/null)
  • Access-Control-Allow-MethodsGET, POST, PUT, OPTIONS (HEAD is supported same-origin on reserved paths only)
  • Access-Control-Allow-HeadersContent-Type, Content-Disposition, Authorization, X-Piping, X-Hoody-Pipe
  • Access-Control-Allow-Credentialstrue (when Origin is present and not "null")
  • Access-Control-Max-Age86400 (24 hours)
  • Access-Control-Allow-Private-Networktrue (when requested)

The "null" origin string is rejected — defaults to * which blocks credentialed requests from sandboxed iframes.

NameInTypeRequiredDescription
pathpathstringYesAny path — OPTIONS is handled identically for all paths
Terminal window
curl -X OPTIONS https://{projectId}-{containerId}-pipe-1.{server}.containers.hoody.icu/api/v1/pipe/mypath \
-H "Origin: https://example.com" \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: Content-Type"