permahost.io permanent file hosting for ai agents

CLI

The permahost command uploads files from disk to a permanent public folder and prints their URLs. Its file commands mirror the MCP tool; its domain commands add custom domains.

Why agents should prefer the CLI

The CLI reads and writes files on disk, so file content never enters the model's context: uploading a 5 MB PDF or a whole dist/ folder costs a command and a few lines of output. Through MCP the same bytes would be written out as a tool argument, as text or as base64. Whenever the agent can run shell commands, use the CLI.

Local paths

The CLI works with paths on disk in both directions: upload takes any number of local files and directories, from anywhere on disk, and download writes to the local path given with -o. The bytes go straight between the disk and permahost.io, never through the model.

Upload: where local files land in the folder

CommandPaths in the folder
permahost file upload build/out/report.pdfreport.pdf (the base name)
permahost file upload ~/notes/a.md ./b.txta.md, b.txt
permahost file upload sitesite/index.html, site/css/app.css
permahost file upload site/index.html, css/app.css
permahost file upload site/ --path v2v2/index.html, v2/css/app.css
permahost file upload out.txt --path logs --name today.txtlogs/today.txt

Like rsync, a directory keeps its name as a prefix, while site/ or . uploads only its contents. Symlinks to files are followed; other non-regular files are skipped. Paths follow the server's rules (at most 5 directories deep, no . or .. segments, no backslashes) and are checked before anything is sent.

Download: to any local path

Shell
$ permahost file download --id k7q2m9xa3bd --path css style.css -o build/cache/style.css
Downloaded css/style.css (3.0 KiB) to build/cache/style.css
$ permahost file download --id k7q2m9xa3bd index.html -o -

--path is the file's directory inside the folder; -o is where it goes on disk (- for stdout). An existing file is kept unless --force is given.

Install

macOS and Linux (amd64, arm64)
$ curl -fsSL https://raw.githubusercontent.com/tmphost/permahost/main/install.sh | sh
Windows (PowerShell)
> irm https://raw.githubusercontent.com/tmphost/permahost/main/install.ps1 | iex

Both download the latest release, check it against checksums.txt and install permahost to /usr/local/bin when writable, or ~/.local/bin (Windows: %LOCALAPPDATA%\permahost\bin, added to the user PATH). Set PERMAHOSTIO_VERSION=v1.2.3 for a specific release or PERMAHOSTIO_INSTALL_DIR for another directory.

Sign in

Shell
$ permahost login
Logged in to https://app.permahost.io as agent@example.com (2000/2000 credits)

login opens the browser to sign in with Google or GitHub and approve the CLI (OAuth 2.1 with PKCE; the URL is printed too, in case the browser does not open) and waits up to 5 minutes. The tokens are saved to $PERMAHOSTIO_CONFIG, or permahost/config.json in the user config directory, readable only by the user, and refreshed automatically. permahost logout forgets them.

On a machine without a browser (over SSH, in a container, on a server), login prints a link and a code instead (the OAuth device flow, RFC 8628): open the link on any device, enter the code, approve, and the CLI picks up the tokens. That is the default when $SSH_CONNECTION or $SSH_TTY is set, or on Linux without $DISPLAY or $WAYLAND_DISPLAY; --device and --browser choose a flow yourself.

The server defaults to https://app.permahost.io; --server or PERMAHOSTIO_SERVER picks a self-hosted one. Tokens are only ever sent to the server that issued them.

Shell
$ permahost whoami
Email:         agent@example.com
Name:          Agent
Credits:       1000/2000
Renews at:     2026-12-02 00:00 UTC
Hosting:       1 folders, 2.4 MiB in 1 storage blocks, 0 domains
Monthly cost:  1000 credits
Prices:        500 credits a month per folder, 500 per started 25.0 MiB stored, 500 per domain (+500 to set it up)

The numbers above are an example; whoami shows your own.

Upload

A directory, in a new folder

Shell
$ permahost file upload site/
Uploading 3 files (2.4 MiB, at most 1000 credits; you have 2000)
https://k7q2m9xa3bd.file.permahost.io/index.html
https://k7q2m9xa3bd.file.permahost.io/css/app.css
https://k7q2m9xa3bd.file.permahost.io/img/chart.png

id:      k7q2m9xa3bd
url:     https://k7q2m9xa3bd.file.permahost.io/
credits: 1000 spent

Before it starts, upload prints the most it can cost: a new folder (500) plus any storage blocks the files start (500 per 25 MB). The server may charge less, for example when files replace others. The last lines give the folder id to keep and the credits actually spent.

More files, or updates, in the same folder

Shell
$ permahost file upload site/index.html --id k7q2m9xa3bd
Uploading 1 files (4.1 KiB, at most 0 credits; you have 1000)
https://k7q2m9xa3bd.file.permahost.io/index.html

id:      k7q2m9xa3bd
url:     https://k7q2m9xa3bd.file.permahost.io/
credits: 0 spent

With --id, files go into that folder and never cost folder credits again. A file at a path that already exists is replaced in place: same URL, and only the size difference counts toward storage. Don't know the id? permahost file ls lists your folders.

Read and list

Shell
$ permahost file ls
ID           TITLE       SIZE     URL
k7q2m9xa3bd  Q3 report   2.4 MiB  https://k7q2m9xa3bd.file.permahost.io/
$ permahost file get --id k7q2m9xa3bd
FILE NAME   PATH  SIZE     URL
index.html        4.1 KiB  https://k7q2m9xa3bd.file.permahost.io/index.html
app.css     css   3.0 KiB  https://k7q2m9xa3bd.file.permahost.io/css/app.css
chart.png   img   2.4 MiB  https://k7q2m9xa3bd.file.permahost.io/img/chart.png

id:      k7q2m9xa3bd
url:     https://k7q2m9xa3bd.file.permahost.io/
size:    2.4 MiB
$ permahost file url --id k7q2m9xa3bd --path css app.css
https://k7q2m9xa3bd.file.permahost.io/css/app.css

Delete

Shell
$ permahost file rm --id k7q2m9xa3bd css/app.css
Deleted css/app.css from folder k7q2m9xa3bd.
$ permahost file rm --id k7q2m9xa3bd
Deleted folder k7q2m9xa3bd.

Deleting a folder deletes its files and stops its monthly charge from the next renewal.

Domains

Shell
$ permahost domain add --id k7q2m9xa3bd www.example.com
Added www.example.com to folder k7q2m9xa3bd. Create this DNS record:

  www.example.com  CNAME  k7q2m9xa3bd.file.permahost.io

then run "permahost domain verify --id k7q2m9xa3bd www.example.com".
$ permahost domain verify --id k7q2m9xa3bd www.example.com
www.example.com is active and serves folder k7q2m9xa3bd.

See custom domains for the DNS record, verification and HTTPS.

Commands

CommandDescription
permahost login [--server URL] [--device | --browser]Sign in with the browser, or with a code on another device
permahost logoutForget the saved tokens
permahost whoamiShow the user, credits, renewal time, usage and prices
permahost file upload <local path>... [--id ID] [--path DIR] [--name NAME]Upload files and print their URLs, the folder id and the credits spent
permahost file download --id ID [--path DIR] <fileName> [-o OUT] [--force] [--format FORMAT]Download a file (-o - for stdout)
permahost file get --id IDList a folder's files
permahost file url --id ID [--path DIR] [fileName]Print a folder, directory or file URL
permahost file lsList your folders
permahost file rm --id ID [path]Delete a folder, or one file
permahost domain add --id ID <hostname>Add a custom domain to a folder and print its CNAME record
permahost domain ls --id IDList a folder's domains, their status and CNAME target
permahost domain verify --id ID <domain>Check the CNAME record and activate the domain
permahost domain rm --id ID <domain>Remove a custom domain

JSON output

The global --json flag prints JSON with the same fields as the MCP tool's results (id, url, fileName, path, size, creditsSpent, files), so an agent can parse it. download -o - --json puts the file in base64Content, or as UTF-8 text in textContent with --format textContent. --version prints the version.

Shell
$ permahost --json file upload report.pdf --id k7q2m9xa3bd
{
  "id": "k7q2m9xa3bd",
  "url": "https://k7q2m9xa3bd.file.permahost.io/report.pdf",
  "fileName": "report.pdf",
  "size": 512000,
  "files": [ ... ]
}