← all posts

Using Claude Code over SSH — view HTML, screenshots, and reports

Running Claude Code on a remote box is a great setup: the VPS has the fast network, the real dataset, and the production-like environment, and your laptop just carries the terminal. People run agents this way on dev servers, devcontainers, and bare VPSes — there's a whole HN thread about the pattern.

It has exactly one recurring annoyance: the box has no screen. The moment the agent produces anything visual — an HTML report, a Playwright screenshot, a rendered chart, a 4,000-line log — you're back to the SSH artifact shuffle:

  • scp user@box:/tmp/report.html . after every iteration
  • ssh -L 8000:localhost:8000 plus python -m http.server, then remembering which port maps where
  • committing throwaway artifacts to git just to view them in the GitHub UI
  • letting the agent describe the screenshot to you in prose

Each of these costs more than it looks, because you pay it every single time the agent produces output. The Claude Code repo has open requests for a built-in publish step (#27792, #58255) precisely because of workflows like this.

The pattern: artifacts become URLs

pail is an MCP server you add to Claude Code on the remote box. Every tool takes content in and returns a public URL out. The terminal stays a terminal; everything visual moves to your browser — laptop or phone, no tunnel.

HTML reports. "Share the coverage report" → the agent uploads htmlcov/ as a folder share and replies with one URL. The HTML is sanitized and rendered as a readable page; relative assets inside the folder resolve. (More detail in Share HTML from Claude Code.)

Screenshots. The agent took a Playwright screenshot to verify a layout fix? share uploads the PNG and the URL renders it as an image page with an OG card — paste it straight into Slack.

> take a screenshot of the login page after the fix and share it with pail

Long logs and generated files. Markdown renders as an article, CSVs as tables, code with syntax highlighting, notebooks as notebooks. A raw link on every page serves the original bytes when you want them.

Long-running jobs. For a migration or test suite that runs 30 minutes, ask for a status page instead of watching the terminal: a live-updating URL (SSE-driven) with progress, log lines, and a final success/failure state. Close the laptop; check it from your phone.

Is it safe to put work artifacts on a URL?

Shares are public-with-secret-id: the random ~10-character base62 id is the bearer, the same model as an unlisted gist. For anything sensitive, pass an access_token (the page gates on it) and a short ttl — shares expire on their own, and revoke_share kills one immediately. Don't upload secrets at all; that rule is the same as it ever was.

Make the remote box do it automatically

Drop this into AGENTS.md in the project on the remote machine:

This machine is headless. Whenever you produce something I would need to
open — HTML, screenshots, reports, logs over ~100 lines — share it with
pail and give me the URL. Use a status page for tasks longer than a few
minutes.

After that, the SSH session output is sentences and URLs, which is what a terminal is good at.

Setup

On the remote box (or anywhere — the key is per-account, not per-machine), sign in at pail.thalos.ai, copy your API key, and run:

claude mcp add --transport http pail https://pail.thalos.ai/mcp \
  --header "Authorization: Bearer $PAIL_API_KEY"

The free tier is 30 actions/day and 5 GB of storage — see pricing. The docs cover all ten tools, and Upload a file and get a URL from an MCP server is the two-minute reference for the core flow.