htmlup
Share your generated reports with one command.
Point it at an HTML file — like a report your AI assistant just wrote — and get back a public URL you can send to anyone. Nothing to host, no secrets stored. Free and open source.
# Install
Install with Homebrew on macOS or Linux. Or grab a prebuilt binary or archive from the releases page.
brew tap truehhart/tap
brew trust truehhart/tap # one-time: Homebrew 6+ requires trusting non-official taps
brew install --cask htmlup
# Usage
The shape is always
htmlup publish <provider> <path>. Point it
at a single .html file or a directory of static assets
— directories upload recursively, preserving structure. On success
it prints a public URL per file and exits.
# branch & dir auto-detected from the repo's Pages settings htmlup publish github ./claude-html-reports --repo truehhart/share # manual targeting (skips auto-detect); preview with --dry-run htmlup publish github ./claude-html-reports --repo truehhart/share \ --branch gh-pages --dir reports --dry-run
# upload objects to a bucket htmlup publish s3 ./site --bucket my-bucket # with key prefix and explicit region htmlup publish s3 ./site --bucket my-bucket \ --prefix path/ --region us-east-1
--dry-run # enumerate what would upload + the resulting URLs; no writes -v, --verbose # per-file progress and SDK-level detail
# Two backends
Backends are pluggable behind one Provider interface —
adding a target is one new package plus registration, no edits to
the publish flow.
GitHub Pages github
Commits your file set to the target branch via the Git Data API,
creates the branch if missing, and flips Pages on if it isn't
already. Auto-detects where Pages serves from and reads a
CNAME (if present) to report the custom-domain URL.
You get a public URL back for every file.
S3 s3
One PutObject per file with content-type inferred
from the extension. No bucket-policy or website-config mutation
— you own public exposure, expected via CloudFront in front of
the bucket.
# Stateless by design
htmlup is single-purpose and boring on purpose. No daemon, no local database, no tracking of what was published.
No lifecycle management. Every invocation publishes and quits. (GitHub Pages expiry, if you want it, is an opt-in cron Action you copy into the target repo — never run by the CLI.)
htmlup never reads, prompts for, or caches secrets. Each official SDK owns its own credential resolution.
Every target implements one interface and self-registers. Contributors add a backend without touching core code.
Cobra commands parse flags and delegate; the real logic lives in provider packages and tested pure helpers.
# Password-protect a page
Pass --password and every .html is sealed
into a self-decrypting page before it leaves your machine. No
accounts, no link expiry, no server in the loop.
# prompt for the password, no echo htmlup publish github ./reports --repo truehhart/share --password - # or supply it via the environment HTMLUP_PASSWORD=hunter2 htmlup publish s3 ./reports --bucket my-bucket
The decryptor is plain JavaScript baked into the page — nothing to
install for whoever opens the link. Here's a real one,
htmlup-encrypted; the password is
hunter2:
--password# Authentication
Auth is delegated entirely to the official SDKs' standard credential chains. htmlup holds nothing.
# 1. $GITHUB_TOKEN → 2. $GH_TOKEN → 3. token from the gh CLI gh auth login # missing token? actionable error points you here
# env vars → ~/.aws/credentials → SSO → role / instance creds # --region only overrides the resolved region
# Also a Claude Code plugin
This repo doubles as a Claude Code plugin marketplace. It publishes the htmlup skill, which teaches Claude how to drive the CLI.
/plugin marketplace add truehhart/htmlup /plugin install htmlup@htmlup
