Configure once
Two lines per project
A project names its URL and its build folder. The server comes from ~/.mason; the path on the server, the vhost, and the SSH details are derived. Mason finds the project file from any subfolder.
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. A project needs two lines of config — its URL and its build folder — and Mason works out the rest. No daemon runs on the server; the remote is just SSH, rsync, and 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, the vhost. 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.
Your server is named once, in ~/.mason. Each project's .mason says only which site it is and what to deploy. When the server changes, you fix it in one place.
Configure once
A project names its URL and its build folder. The server comes from ~/.mason; the path on the server, the vhost, and the SSH details are derived. Mason finds the project file from any subfolder.
No server daemon
Nothing runs on the VPS but ssh, rsync, and Caddy. Mason talks to a plain directory layout and Mason-managed Caddy snippets — nothing to keep alive, nothing to patch.
Plain & scriptable
Output is columnar and greppable; add --json for the machine-readable counterpart. mason status shows a deploy's plan, and --print shows any server script, 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.
mason deploy syncs the project's build folder to its site, 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.
One-command setup
mason setup <server> uses the SSH you already have, or creates a key and bootstraps the server — after asking. Re-run it anytime.
Careful with your server
Every Caddy change is validated, with the previous config restored if it fails. Vhosts you edited by hand are never overwritten without --force.
Self-checking
mason doctor checks what you've configured — the server, your share store, the current site — and says what to fix.
Quickstart
One command for this machine, one for each new site, then deploy whenever you build. Every step is safe to re-run.
mason setup reuses the SSH setup you already have when it works. Otherwise it creates ~/.ssh/mason and, after asking, bootstraps the server as root: an unprivileged mason user, this machine's key, and one Caddy import line. Add --print to read the script first.
In the project folder, mason init suggests the URL from the folder name and finds the build folder, writes a two-line .mason, then offers to provision the site: its web root and a Caddy vhost. Apex domains also get a permanent www → apex redirect.
Preview with mason status, then mason deploy. It works from any folder in the project. A project with several environments deploys the first by default — mason deploy live picks another.
Create your share store once with setup --share. Then drop a file on the server and get a link — use --as to choose its path.
Run mason setup me.dev on it. If it has no admin access, setup prints a one-liner — mason key add 'ssh-ed25519 AAAA… mason@laptop2' — to run on a machine that already publishes. mason key ls shows every authorized machine; mason key rm revokes one.
Configuration
~/.mason says where you publish. A project's .mason says what it is. Mason merges the two — and you rarely write either by hand: setup and init do it.
This machine
A project
With environments
A site can set its own path, server, or spa: true. Typos get a "did you mean", and old v1 files point straight to the migration guide.
Full reference: CONFIG.mdCore concepts
A server says where. Sites and shares say what. The server contract ties it together.
Concept · 1
A server is just an SSH destination. A site is a URL plus the folder that builds it, and its host is its identity — the vhost, the server path, and its name all follow from it.
Concept · 2
A share is a link to a content-addressed blob, not a copy. Two endpoints address it independently — a readable path and a short slug — and you can move, disable, or remove either.
Concept · 3
The server is a known directory layout plus Mason-managed Caddy snippets, marked # managed by mason. The share store 404s /objects/* and /.mason/*; sites 404 every dotfile.
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 — it only reports until you add -y.
CLI reference
Add --json to any command for machine-readable output. Commands that change the server ask first; -y answers yes.
mason init [url] [--build dir] [--no-provision]
Write the project's .mason (URL + build folder), then offer to provision.
mason provision [site] [--spa] [--print] [--force]
Create the site's web root and Caddy vhost — apex or subdomain, auto-detected. Validates, rolls back on failure, never overwrites a hand-edited vhost without --force.
mason status [site]
Show the deploy plan — no transfer.
mason deploy [site] [--delete] [-n]
Sync the build folder: plan → progress → summary. --delete mirrors exactly (confirmed).
mason share <file> [--as path]
Upload a file; print its long and short URLs.
mason ls [prefix] · info · url <ref>
List shares, show full detail, or print a URL (short by default).
mason setup <server> [--share url] [--print]
Set up this machine; bootstrap the server and share store as needed.
mason key ls · add · rm
See, authorize, and revoke the machines that can publish.
mason mv · cp <ref> <path>
Re-home a share's path, or duplicate it as a new path + slug.
mason disable · enable · rm <ref>
404 one endpoint reversibly, bring it back, or remove it for good.
mason prune [-y] · sync
Garbage-collect orphaned blobs (reports unless -y); refresh the local cache.
mason doctor [site] · version
Check the server, share store, and site; print the build you're running.
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.