Configure once
The context travels with the work
Named targets hold the host, user, remote root, and base URL. A project .mason picks one; Mason walks up from the cwd to find it, falling back to ~/.mason.
Open source · MIT · Single Go binary
Mason is a small local CLI for publishing to a personal server behind Caddy: deploy static sites and share files. It remembers the host, the paths, the user, and the rsync flags — so you don't. No daemon runs on the server; the remote is just a filesystem contract plus Caddy.
→ go install github.com/norlinga/mason/cmd/mason@latest
The premise
Building a static site is a single command. Then comes the friction: the hostname, the user, the remote path, the SSH key, the exact rsync flags. Put it in a Makefile and every project re-implements the same script — and they all go stale the moment the server changes.
The idea
A mason assembles blocks into something useful — even beautiful. Mason assembles your files onto your server, turning common publishing tasks into single, memorable commands.
Configuration lives in a .mason file — per-project, or in your home directory — so the context travels with the work. When the server changes, you fix it in one place.
Configure once
Named targets hold the host, user, remote root, and base URL. A project .mason picks one; Mason walks up from the cwd to find it, falling back to ~/.mason.
No server daemon
Nothing runs on the VPS but ssh, rsync, and Caddy. Mason talks to a plain filesystem layout and a Mason-owned Caddy snippet — nothing to keep alive, nothing to patch.
Plain & scriptable
Output is columnar and greppable. Add --json to any command for the machine-readable counterpart, and --dry-run to see the plan before anything moves.
What Mason does
Deploy a static site, or share a single file. Both land on the same server, behind the same Caddy, addressed by clean URLs.
Point Mason at a build directory and a target. mason deploy rsyncs the tree, showing the plan, live progress, and a summary. Use --delete to mirror exactly — removals are confirmed before they happen.
mason share ./talk.mp3 uploads the file and hands back a shareable URL. Send a recording or a PDF without routing it through a chat app — it lives at a clean link on your own domain.
Local-first
One binary on your machine. No agent, no control plane — just ssh and rsync under the hood.
Idempotent setup
remote-init prints a script that converges the server to a known state. Re-run it anytime — it's safe.
Self-checking
mason doctor verifies SSH, permissions, the manifest, private-prefix 404s, and Caddy reconciliation.
Quickstart
Onboard locally, bootstrap the server, verify, and you're deploying sites and sharing files.
mason setup generates an SSH key if needed, writes ~/.mason, and tests the connection. Run it without flags for an interactive prompt.
remote-init prints an idempotent script: it creates the mason user, installs your key, lays out the trees, and adds a Mason-owned Caddy snippet. Review it, then pipe it to an admin login.
Scaffold a project .mason, preview the plan, then deploy the build tree.
Drop a file on the server and get a link. Use --as for an exact object path, or let Mason choose a default.
Core concepts
Targets say where. Deploys and shares say what. The server contract ties it together.
Concept · 1
A named destination: host, user, remote root, and base URL. Stored in ~/.mason or a project .mason. One is the default; override per command with -t.
Concept · 2
A share is a link to a content-addressed blob, not a copy. Two endpoints address it independently — an object path and a short slug — and you can move, disable, or remove either.
Concept · 3
The server is a known directory layout plus a Mason-owned Caddy snippet. Private prefixes like /objects/* and /.mason/* return 404 — the blob store stays out of view.
Blobs are addressed by their content. Share identical bytes to a new path and Mason adds a link only — no second copy on disk, no second upload over the wire.
disable 404s an endpoint without deleting it; enable brings it back. prune garbage-collects orphaned blobs — dry-run by default, -y to commit.
CLI reference
Add --json to any command for machine-readable output, -n/--dry-run to preview, and -t to pick a target.
mason init
Scaffold a project .mason.
mason status
Show the deploy plan — no transfer.
mason deploy [target] [--delete]
Rsync the build tree: plan → progress → summary. --delete mirrors exactly (confirmed).
mason share <file> [--as path]
Upload a file; print its shareable URL.
mason ls [prefix] · info · url s/<slug>
List shares, show full detail, or print a URL (short by default).
mason mv · cp <path> <path>
Re-home an object path, or duplicate it as a new path + slug.
mason disable · enable s/<slug>
404 an endpoint, reversibly — then bring it back.
mason rm s/<slug> · prune [-y]
Remove an endpoint, or GC orphaned blobs (dry-run unless -y).
mason target ls · add · use · rm
Manage named destinations and pick the default.
mason setup · remote-init · doctor · sync
Onboard, bootstrap the server, health-check, and refresh the local cache.
Mason is a single Go binary, MIT-licensed, and small enough to read end-to-end. Clone it, build it, and point it at your own VPS.