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.
LLM-friendly version (Markdown): https://permahost.io/docs/cli.md
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
| Command | Paths in the folder |
|---|---|
| permahost file upload build/out/report.pdf | report.pdf (the base name) |
| permahost file upload ~/notes/a.md ./b.txt | a.md, b.txt |
| permahost file upload site | site/index.html, site/css/app.css |
| permahost file upload site/ | index.html, css/app.css |
| permahost file upload site/ --path v2 | v2/index.html, v2/css/app.css |
| permahost file upload out.txt --path logs --name today.txt | logs/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
$ 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
$ curl -fsSL https://raw.githubusercontent.com/tmphost/permahost/main/install.sh | sh
> 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
$ 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.
$ 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
$ 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
$ 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
$ 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
$ 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
$ 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
| Command | Description |
|---|---|
| permahost login [--server URL] [--device | --browser] | Sign in with the browser, or with a code on another device |
| permahost logout | Forget the saved tokens |
| permahost whoami | Show 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 ID | List a folder's files |
| permahost file url --id ID [--path DIR] [fileName] | Print a folder, directory or file URL |
| permahost file ls | List 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 ID | List 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.
$ permahost --json file upload report.pdf --id k7q2m9xa3bd { "id": "k7q2m9xa3bd", "url": "https://k7q2m9xa3bd.file.permahost.io/report.pdf", "fileName": "report.pdf", "size": 512000, "files": [ ... ] }