Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: mhoennig <michael@hoennig.de> Reviewed-on: #18
7.2 KiB
WARNING: This document describes only the change applied in this PR. It may already be outdated once the next PR is merged. Historic PR-documentation is not maintained along with new PRs — treat it as a snapshot, not as current documentation.
The Problem
tools/remote grew with the Hostsharing Managed Webspace rollout (steps 21 and 23) and encoded that rollout's layout as if it were the only one.
The watched repository had to be $WERKATOR_PATH/werkator, the runtime $WERKATOR_PATH/.werkator/werkator, and the systemd unit was spelled out as werkator-werkator.service; a werkdock binary and a bwrap rootfs archive were built and uploaded unconditionally.
vm4006, the Docker host that has been running Werkator since long before the script existed, matches none of that: its watched repository is ~/hs.hsadmin.ng (so its unit is werkator-hs.hsadmin.ng.service), its runtime lives in ~/opt/werkator, and it has neither werkdock nor a rootfs because its builds run in Docker.
So that host could only be deployed by hand, and had drifted nine releases behind — the question that started this PR was whether the Werkdock and multi-repo work had broken it, which it had not: what was broken was the deployment tooling.
The second problem surfaced while deploying: instance-update stopped the systemd unit and then started the upload.
The 66 MB transfer to vm4006 died with scp: Connection closed, leaving the host with no running Werkator and nothing new to start.
Non-Goals
instance-startfor a Docker host: it places a Hostsharing.htaccessinto adoms/<domain>/docroot, which only exists on a Managed Webspace.vm4006uses the managed nginx container instead, and keeps its existing units.repo-initforvm4006— the repository has been cloned and configured there for months; onlyinstance-updatewas needed.- Making the machine configuration of
vm4006current: it still carries the pre-renamegitTally:meta key, which is read by nothing today. Harmless whileConfigVersions.FORMAT_BROKE_INis empty, a trap on the day it is not.
The Scenarios
Feature: one deployment command for every host layout
Background
- The env file carries transport values only; the three new keys describe where things are on the host, not what Werkator does.
- The defaults are exactly the layout
instance-installcreates, so an env file naming none of them resolves as before.
Scenario#18.01: A host that predates the script can be deployed with it
So that an installation is not condemned to hand-typed scp sequences because it was set up before the tooling existed.
- Given an env file with
WERKATOR_REPO_DIR=hs.hsadmin.ng,WERKATOR_INSTALL_DIR=/home/tallyman/optandWERKATOR_SANDBOX=docker - When
tools/remote --env-file .env.vm4006 werkator instance-updateruns - Then it addresses
werkator-hs.hsadmin.ng.service, unpacks into~/opt, and uploads neither a werkdock binary nor a rootfs archive
Verified by
- Live on
vm4006(2026-09-03):check-prerequisitesreported the Docker daemon instead of runningwerkdock doctor; the update swapped~/opt/werkatorfrom v1.0.1 to v1.1.2, the unit came upactive,/api/watcherpolls without errors, and/api/systemstill reports real disk figures (diskSource.kind: volume) rather than the quota shape from PR#16.
Scenario#18.02: The webspace hosts are unaffected
So that making the script layout-aware does not break the deployment path that is actually in production.
- Given
.env.mih09and.env.mih34, neither naming any of the new keys - When the layout is resolved
- Then repository directory, install directory, unit name and sandbox are identical to the hardcoded values they replace
Verified by
- Resolution measured for both env files (2026-09-03):
REPO_DIR=$WERKATOR_PATH/werkator,INSTALL_DIR=$WERKATOR_PATH/.werkator,UNIT=werkator-werkator.service,SANDBOX=bwrap. - Live on
mih09(2026-09-03): a fullinstance-updateran through the bwrap path — werkdock uploaded,werkdock 0.1.0-devreported after the swap, serviceactive,https://werkator.javagil.de/answering 200.
Scenario#18.03: A failed transfer does not take the service down
So that a dropped connection costs the upload and nothing else.
- Given an instance whose service is running
- When the runtime bundle cannot be transferred
- Then the service is still running, because the upload happens before the stop, and a partially transferred file is never moved into place
Verified by
- The failure itself on
vm4006(2026-09-03), which is what this scenario is written from: with the old order,scp: Connection closedleft the unit stopped and the host without Werkator. - Live on
mih09(2026-09-03): the bundle already on the host was recognised by its sha256 and skipped, so the upload step cost nothing and the stop followed only after it.
The Solution
Three optional env keys replace three hardcoded assumptions.
WERKATOR_REPO_DIR and WERKATOR_INSTALL_DIR are resolved absolute-or-relative-to-WERKATOR_PATH, and the unit name is now derived from the repository directory the way SystemdServiceFiles.unitName derives it (basename, every character outside [A-Za-z0-9_.-] replaced by a dash) instead of being spelled out — one rule, in two places, with the script naming the Kotlin function it mirrors.
WERKATOR_SANDBOX=docker skips everything bwrap-shaped: no werkdock build, no werkdock upload, no rootfs archive, and check-prerequisites asks the Docker daemon instead of running werkdock doctor.
repo-add clones beside the watched repository rather than into WERKATOR_PATH, which is the same directory whenever the default layout is used.
deploy_instance is split into upload_instance_artifacts and swap_instance_runtime, and instance_update calls the first before stopping the unit.
Every artifact is uploaded to <name>.part, compared by sha256 with the local file, and only then moved into place; three attempts, and an unchanged artifact is skipped entirely.
The checksum is not belt-and-braces: a truncated tarball would unpack into a runtime that starts and misbehaves, which is far worse than the failed transfer it came from.
Open Questions
- Should
instance-startlearn the Docker-host shape too? Not answered here.vm4006keeps its existing units and its managed nginx; the day it needs regenerating,init --systemdon the host is the documented path.
Additional Changes
- None beyond the feature itself.
Follow-up work discovered while deploying this
https://vm4006.hostsharing.net:8443/is not reachable from outside the host, whilehttps://127.0.0.1:8443/answers 200 and thewerkator-nginx-hs.hsadmin.ngcontainer publishes both ports. Pre-existing and unrelated to this PR — the host's own firewall, not Werkator.- The machine configuration on
vm4006still declaresgitTally: version: since: "0.9.20"; the current code reads onlywerkator:, so the file's version claim is silently ignored (werkator.version.sinceprints empty). It costs nothing whileConfigVersions.FORMAT_BROKE_INis"", and stops protecting that host the moment plan step 18 sets it.
Prerequisite PRs
- None; it changes only
tools/remoteand the documentation.
Follow-up PRs
- None planned.