These guides are primarily for agent readers. They explain how agents use Sails. If you are a person, start at sails.app or Add Sails.

Send a file

upload and download print a one-time browser URL. The person uses their own machine. You never need their local disk, and you should not paste binaries into chat.

There is no upload --to. The path is the first argument. It is relative to the current working directory and must stay under $HOME.

MCP write_file / read_file are UTF-8 text only. Use transfer for CSVs, zips, images, and anything awkward to paste.

Done looks like

  • In: upload poll prints success and the file exists at the path you named.
  • Out: the person opens the URL once and the browser saves the file (or a .tar.gz for a directory). No poll step.

Worked example: get a spreadsheet into the repo

Job: a human has q2-leads.csv on their laptop. You need it at inbox/q2-leads.csv.

upload inbox/q2-leads.csv

Expected output (shape, not exact host):

Open this URL in your browser to upload (repository members):
  https://…/sails/transfer/…
Target: ~/inbox/q2-leads.csv
Transfer session: 3f2a9c1e-…
  1. Send that URL to the person.
  2. They sign in as a repository member and drop the file.
  3. You wait:
upload poll 3f2a9c1e-…

When poll returns success, the file is in the live workspace and autosave has it. Then you can read it, publish it, or hand it to another command.

Do not pass --wait from bash_exec. That call buffers until the upload finishes, so you never see the URL. Print the URL, then upload poll ID.

Worked example: hand someone a report

download sites/sails/pages/blog/launch.page.md

That prints a one-time URL. First successful GET downloads the file and consumes the link. There is no download poll.

A directory is packed as gzip tar:

download ./site
# browser saves site.tar.gz

Upload a folder

upload assets --dir
upload poll TRANSFER_ID

The browser can pick a folder, or they can upload a .tar / .tar.gz / .tgz and it extracts into assets.

Flags

CommandWhat it does
upload PATHOne-time URL for a single file at PATH
upload PATH --dirOne-time URL for a directory (folder picker or archive)
upload poll IDBlock until that upload finishes, fails, or expires
upload PATH --waitBlock in one command — avoid inside bash_exec
download PATHOne-time URL; directories become .tar.gz

Common flag: --timeout SECS (default 900). After that the session expires; run the command again.

upload help
download help

Failure modes

What you seeWhat it meansWhat to do
Browser asks to sign in / 403Transfer URLs are for repository membersThey must be signed in as a member of this repo
Link dead on second openSingle-use (download completes on first GET)Run upload / download again
upload poll times outNobody finished the browser step, or --timeout elapsedNew upload PATH, new URL
Path rejectedOutside $HOME / the active repo mountUse a path under the workspace
File missing after “I uploaded”You never polled, or they hit a different sessionConfirm the transfer id from the upload output, then upload poll
100 MiB exceededHard limitSplit the archive, or fetch a public URL with curl -o instead
You used upload --to DIRThat flag does not existupload DIR/filename or upload DIR --dir

When not to use this

JobUse instead
Small text edit already in chatMCP write_file / edit_file
File is already on the public internetcurl -o path https://…
API key, token, DSNsecret open — see Store a secret
Public URL for a folderPublish a site
Another repository should copy a folderShare a directory with another repo

See File transfer.