hostdocv1 · S3 + CloudFront
S3 website · CloudFront · self-hosted

Publish a doc.
Get a short link.

hostdoc uploads a local HTML file or folder to your own AWS and returns a short, shareable link — no platform account, no data leaving your cloud.

bash — publish
$ npm install -g hostdoc
$ hostdoc setup --bucket my-bucket --region us-east-1
$ hostdoc publish ./report.html --slug aws-design
# uploading 1 file…
http://my-bucket.s3-website-us-east-1.amazonaws.com/aws-design/
HTTPmy-bucket.s3-website-us-east-1.amazonaws.com/aws-design/
Overview

Three hosting modes, one upload path

hostdoc has three hosting modes that share one upload path. You pick a mode by how you configure it — the mode is derived, never stored.

HTTP

No domain

s3-website endpoint
LinkHTTP
HostPublic S3 website bucket
Set up withhostdoc setup (CLI)
Custom domainNo
Use whenQuick internal sharing, no HTTPS needed
HTTPS

Domain

cloudfront + custom domain
LinkHTTPS
HostPrivate S3, served via CloudFront (OAC)
Set up withhostdoc provision (Terraform)
Custom domainYes (Route53 hosted zone)
Use whenPublic-facing links that must be HTTPS
SELF-HOSTED

Self-hosted

your own Caddy / nginx
LinkHTTP / HTTPS
HostA directory on your NAS / PC
Set up withhostdoc setup --serve-root
Custom domainYour own host / DDNS
Use whenYou want no AWS — serve from your own box
Setup

Prerequisites & install

  • Node.js ≥ 22.12 — check with node --version.
  • For the two cloud modes: an AWS account with credentials available to the AWS SDK (see AWS credentials).
  • For domain mode only: Terraform installed and a Route53 hosted zone.
  • For self-hosted mode: your own Caddy or nginx (or use --install to auto-install Caddy) — no AWS needed.
bash
npm install -g hostdoc
Auth

AWS credentials

hostdoc never stores AWS keys. It uses the AWS SDK default credential chain (environment variables → SSO → shared ~/.aws profile). Pick a profile with --profile <name> and a region with --region <region>. Use any one of the three setups below.

bash
export AWS_ACCESS_KEY_ID=AKIA...
export AWS_SECRET_ACCESS_KEY=...
export AWS_REGION=us-east-1
Use a dedicated IAM user
Prefer a dedicated IAM user with a minimal policy over root credentials. See the AWS docs for creating an IAM user and attaching a policy. For domain mode, Terraform emits a ready-made minimal publisher_policy_json output (and can create the user with create_publisher_user = true).
No domain

Quick start (S3 website)

This mode serves content publicly over HTTP from an S3 static-website bucket (S3 website endpoints do not support HTTPS). For HTTPS, see Domain mode.

bash
# 1) create a public website bucket and save config
$ hostdoc setup --bucket my-unique-bucket --region us-east-1
# 2) publish a file or a folder
$ hostdoc publish ./report.html
$ hostdoc publish ./site/ --slug aws-design
$ hostdoc publish ./site/ --slug team/q1/report
# 3) manage
$ hostdoc list
$ hostdoc open aws-design
$ hostdoc rm aws-design --yes
--dry-run prints the URL it would publish to without uploading — and without any AWS call, so it works offline. open builds and opens the URL without checking the document exists. rm asks for confirmation; pass --yes to skip it (required when stdin is not a TTY).
HTTPS

Domain mode (CloudFront)

Domain mode serves your docs over HTTPS from a fully private S3 bucket fronted by CloudFront (OAC). It is provisioned with Terraform — no repo checkout needed: the templates ship inside the npm package and are extracted for you into $XDG_STATE_HOME/hostdoc/infra (i.e. ~/.local/state/hostdoc/infra).

Prerequisites: a Route53 hosted zone for your domain, AWS credentials, and Terraform installed.

bash
$ hostdoc provision \
$ --hosted-zone example.com \
$ --subdomain shared --region us-east-1
# extracts bundled Terraform, writes terraform.tfvars.json from the flags,
# runs terraform init + apply, and saves config (~15-30 min).
# non-interactive (e.g. driving hostdoc from an agent): add --approve
$ hostdoc publish ./mydoc
HTTPSshared.example.com/aws-design/

The single local terraform.tfstate lives in that per-user dir, so it is reused no matter where you run hostdoc from, and deprovision always finds it. Override the location with --dir. Re-running provision never clobbers a dir you have already edited. --price-class overrides the default PriceClass_100.

Already provisioned the infra yourself? Import it without applying: hostdoc init --from-terraform <dir>. Tear it all down with hostdoc deprovision (reuses the saved terraform.tfvars.json; add --approve for non-interactive). Overwriting (--force) and hostdoc rm automatically invalidate /<code>/* on the distribution.

External (non-Route53) DNS

Automated ACM validation and alias records require a Route53 hosted zone. If your domain is hosted elsewhere (e.g. Cloudflare), provisioning is manual: add the ACM validation CNAME shown by AWS, then point your subdomain at the CloudFront distribution domain via a CNAME/ALIAS record.
No AWS

Self-hosted mode (your own NAS/PC)

Self-hosted mode publishes to a local directory served by your own web server — no AWS at all. hostdoc copies files into <serve-root>/<code>/ and returns a link on your own host.

bash
# configure the serve directory + print a Caddy (default) or nginx snippet
$ hostdoc setup --serve-root /srv/hostdoc --host docs.example.com
# paste the printed snippet into your web-server config, then reload it
$ hostdoc publish ./report.html --slug aws-design
http://docs.example.com/aws-design/

--host is your public host (a DDNS name, domain, or static IP). Add --port for a non-standard port and --scheme https if your server terminates TLS. --nginx emits an nginx server block instead of a Caddyfile (nginx is also auto-selected when it is on PATH); --caddy forces a Caddyfile.

Auto-install (Caddy only)

Add --install and hostdoc downloads Caddy and registers a boot service (Linux/macOS/Windows) so it survives reboot:

bash
hostdoc setup --serve-root /srv/hostdoc --host docs.example.com --install

Non-privileged prep (config, Caddyfile, binary) runs as your user; the privileged service step runs automatically when you are root/admin, otherwise hostdoc prints the exact sudo (or elevated PowerShell) commands to finish. An --nginx choice falls back to the manual snippet — nginx is not a single static binary. Tear it down with hostdoc deprovision (stops and unregisters the service; published files and the Caddy binary are left in place).

Network exposure is your responsibility
Only the serve directory is served — directory listing is off and /_* (hostdoc's _meta sidecars) is blocked. Port-forwarding, firewall, and router configuration are up to you; expose only what you intend.
Reference

Configuration & precedence

Settings resolve in this order — earlier wins. This lets you point hostdoc at bring-your-own infrastructure.

1 · highest
CLI flags
--bucket, --region, --profile
2
Environment variables
HOSTDOC_BUCKET, HOSTDOC_REGION, HOSTDOC_DISTRIBUTION
3 · lowest
Config file
~/.config/hostdoc/config.json

Inspect the saved config with hostdoc config.

Help

Troubleshooting

No configuration found. Run hostdoc setup …
Not set up yet — run hostdoc setup (no domain), hostdoc provision (domain), or hostdoc setup --serve-root <dir> (self-hosted). You can also pass --bucket/--region (or HOSTDOC_BUCKET/HOSTDOC_REGION).
setup requires --bucket and --region (s3-website), or --serve-root (self-hosted).
Give setup a mode: --bucket + --region for S3 website, or --serve-root for self-hosted.
Ambiguous config: serveRoot (self-hosted) is set alongside bucket/domain/distribution (AWS).
Pick one mode. Unset serveRoot/HOSTDOC_SERVE_ROOT for a cloud mode, or clear the AWS fields for self-hosted.
Invalid config at <path>: not valid JSON / expected a JSON object
The config file is corrupted. Fix or delete ~/.config/hostdoc/config.json and re-run setup.
Incomplete cloudfront config: 'domain' set without 'distributionId'
Domain is set but the distribution id is missing. Set --distribution / HOSTDOC_DISTRIBUTION, run hostdoc init --from-terraform <dir>, or unset the domain for s3-website mode.
Incomplete cloudfront config: bucket and region are required
Run hostdoc init --from-terraform <dir> to import the bucket and region.
Could not load credentials from any providers
No usable AWS credentials. Configure env vars, a --profile, or SSO (see AWS credentials).
terraform is not installed or not on PATH …
Install Terraform (e.g. brew install terraform) and retry.
No terraform.tfvars in "<dir>". Pass --hosted-zone … --subdomain …
Run provision/deprovision from the provisioned dir, or pass --hosted-zone, --subdomain, and --region (all three required together).
Could not read terraform outputs from "<dir>" …
Ensure Terraform is installed and terraform apply has run in that dir before init --from-terraform.
Slug "<x>" already exists. Use --force to overwrite.
Pick a different --slug or pass --force (force also invalidates the CloudFront path in domain mode).
Path not found / Folder is empty / Document not found
Check the file/folder path you published, or the id you passed to open/rm.
Automation

Use with an agent (skill)

hostdoc ships an installable agent skill so coding agents can drive it conversationally — “publish this folder”, “list my docs”, “remove that slug” — without memorizing flags.

bash
npx skills add jkas2016/hostdoc

The skill shells out to the hostdoc CLI (preferring a global install, falling back to npx -y hostdoc), so no global install is required. It runs an AWS-free preflight and turns missing config/credentials into guidance instead of raw errors.