Skip to content

feat(deploy): make /opt/moshpit a checkout, so merging ships - #15

Merged
ralyodio merged 2 commits into
mainfrom
feat/self-updating-deploy
Aug 3, 2026
Merged

feat(deploy): make /opt/moshpit a checkout, so merging ships#15
ralyodio merged 2 commits into
mainfrom
feat/self-updating-deploy

Conversation

@ralyodio

@ralyodio ralyodio commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #14, which fixed a template that then had to be copied to the box by hand.

The problem

/opt/moshpit is a hand-copied nginx/ and scripts/. A merged fix reaches a box only when somebody remembers to copy it — and forgetting produces no error, just a fix that appears not to work.

That cost an afternoon today. #14 removed the HTTPS redirect from the origin template; the box still had the old one, and moshpit-reconcile faithfully re-applied it every 60 seconds over vhosts that had been fixed by hand. The debugging looked like "the fix didn't take" rather than "the fix isn't here". The same shape bit the DNS resolver deployment twice more the same day.

What this adds

scripts/moshpit-update.sh plus a 15-minute timer.

It converts a hand-copied prefix into a checkout on first run rather than asking anyone to do it by hand — an upgrade path nobody has to read about is the only kind that gets taken. The superseded tree is kept beside the new one, because if somebody edited a script in place and never upstreamed it, that is the only copy holding it.

Safety, ordered by how badly each would fail

  • Uncommitted changes abort the update. Overwriting them silently is how a fix applied on the box at 3am vanishes without trace.
  • Fast-forward only. A diverged prefix is a person's problem, not a script's.
  • A failed clone or fetch leaves the prefix exactly as it was — an update that half-lands is worse than one that does not run.
  • nginx -t before reload, and a failing test warns rather than aborts: the update has already landed, and dying here would strand the checkout mid-way.

Why 15 minutes and not one

Reconcile runs every minute because an unserved name is urgent. This reaches the network and a merge is not. RandomizedDelaySec so a merge does not have every origin fetch at once; Persistent=true so a box that was off catches up rather than waiting a full interval.

Install

Copy scripts/moshpit-update.sh into /opt/moshpit/scripts/ and the two units into /etc/systemd/system/, then systemctl daemon-reload and systemctl enable --now moshpit-update.timer — all as root.

The first run converts /opt/moshpit into a checkout; every run after is a fast-forward. --dry-run says what it would do without touching anything.

ralyodio and others added 2 commits August 3, 2026 09:59
…body can verify

The template redirected :80 to :443, with a comment saying the only
problem was a missing 443 block. That reasoning was incomplete: it
assumes the client can verify what it lands on, and the two biggest
classes cannot.

No CA will issue for an ending outside the DNS root, so the 443 block
presents a self-signed certificate. TronBrowser, moshpit-proxy and the
SDKs check it against the registry's published pin and are fine. curl and
a stock browser have no pin to check, so they reject it — and the
redirect leaves them nowhere:

    curl chovy.hacker -L
    curl: (60) SSL certificate problem: self-signed certificate

Through the gateway it is worse: pit.moshcode.sh forwards the status and
not the Location, so a visitor gets a 301 pointing nowhere at all.

Now :80 serves the site. Both ports serve it, neither redirects to the
other, and each client takes the one it can verify — pin-checking clients
still get TLS, everyone else gets the page instead of an error that reads
as "Moshpit is broken".

HTTPS-only remains right for a name in the public DNS. It is not
available for a name outside it, and pretending otherwise costs the
visitor the site.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
/opt/moshpit was a hand-copied nginx/ and scripts/. A merged fix reached
a box only when somebody remembered to copy it, and forgetting does not
raise an error — it produces a fix that appears not to work.

That cost a full afternoon today: the origin template still redirected to
HTTPS on a box whose repo had stopped doing so, and every reconcile pass
faithfully re-applied the stale copy over the hand-edited vhosts. The same
shape bit the DNS resolver deployment twice more.

Adds an updater and a 15-minute timer. It converts a hand-copied prefix
into a checkout on first run rather than asking anyone to do that by hand,
because an upgrade path nobody has to read about is the only kind that
gets taken. The superseded tree is kept beside the new one: if somebody
edited a script in place and never upstreamed it, that is the only copy
holding it.

Safety, in order of how badly each fails:

- uncommitted changes in the prefix abort the update. Overwriting them
  silently is how a fix applied on the box at 3am vanishes without trace
- fast-forward only; a diverged prefix is a person's problem, not a
  script's
- a failed clone or fetch leaves the prefix exactly as it was
- `nginx -t` before reload, and a failing test warns rather than aborts —
  the update has already landed and failing here would strand the checkout

15 minutes, not one: this reaches the network, and a merge is not urgent
where an unserved name is. Randomised so a merge does not have every
origin fetch at once, and Persistent so a box that was off catches up.
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown

vu1nz Security Review

0 finding(s) in PR #?

No security issues found.

@ralyodio
ralyodio merged commit 54bd476 into main Aug 3, 2026
3 checks passed
@ralyodio ralyodio mentioned this pull request Aug 3, 2026
@ralyodio
ralyodio deleted the feat/self-updating-deploy branch August 3, 2026 10:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant