Das Anlegen eines Hosts endete in der Konsole mit einem 500, bevor der
Betreiber den Einmal-Code je zu sehen bekam. Ursache war nicht das Anlegen,
sondern der Tunnel-Peer: HostEnrolment::issueWithKeys() rief `wg set wg0`
selbst auf — im Web-Request, also im `app`-Container. Der hat weder NET_ADMIN
noch /dev/net/tun; wg0 lebt im Provisioning-Container. Die Antwort war
"Unable to modify interface: Operation not permitted".
Der Peer geht jetzt als ApplyHostVpnPeer auf die Provisioning-Queue — genau
dorthin, wo ApplyVpnPeer es für die VPN-Zugänge längst richtig macht, mit
derselben `wireguard:hub`-Sperre und demselben Grundsatz: der Sollzustand
kommt beim Ausführen aus der Zeile, nicht aus dem beim Einreihen
festgehaltenen Wert. Der abgelöste Schlüssel reist als Wert mit, weil in der
Zeile zu diesem Zeitpunkt schon der neue steht.
Aufgefallen ist es nie, weil die Testsuite den Hub gegen FakeWireguardHub
tauscht — der bestehende Test blieb grün, während der echte Weg seit jeher
fehlschlug. Die zwei neuen Tests prüfen deshalb den WEG statt des Ergebnisses:
in der Anfrage bleibt der Hub unberührt, und der Auftrag liegt auf der
Provisioning-Queue.
Codex: 0 Fehler, 0 Sicherheitsbefunde. Dazu sein P2 — /.claude/ stand weder
im Index noch in .gitignore, ein `git add -A` hätte 153 MB als verschachteltes
Repo eingebettet.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
storage/archive is runtime output, and on a real server it is a mount point.
Committing it would put customer documents into git and make a deploy fight the
archive for the directory.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Everything the console has no field for is still real — MAIL_*, DB_*,
APP_KEY, whichever key nobody has built a form for yet — and without a way to
touch it from here, the operator needs a shell and the console page misses
its own point. EnvFileEditor is the net, not just the warning:
1. Validates before writing. A line that is neither blank, nor a comment, nor
KEY=value is rejected outright, and an empty file (syntactically "valid" by
that rule, but not survivable) is refused too. write() never touches the
file before checking the new content.
2. Backs up before every write that actually happens — a timestamped copy
beside .env, before the new content lands. Never pruned automatically; the
page says so, next to where it says where they land.
3. Names the keys a mistake here can lock an operator out with — APP_KEY,
DB_*, REDIS_*, SESSION_* — rather than a blanket warning nobody reads.
4. Gated by secrets.manage and the same confirmed password the vault entries
use — this is the one place on the page that can reach every credential
the vault otherwise keeps write-only.
5. Says plainly what saving does not do: queue, queue-provisioning, scheduler
and reverb only read .env at their own startup, and names the restart
command plus config:clear.
6. Marks which .env keys SecretVault currently overrides, so editing a line
that a stored vault value already shadows does not look broken.
Two issues surfaced by Codex review and fixed before this commit: the raw
file content stayed in the Livewire component snapshot after the password
confirmation window expired on its own (not only on an explicit re-lock), and
the first save on an installation with no .env yet failed trying to back up a
file that was never there.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
--env-file makes the whole run non-interactive, which is what a bare server
needs: install git, clone, run, done. The file also carries the operational
secrets (Hetzner DNS, Stripe, SMTP, the Proxmox key path); those are optional,
so a first install can happen before the Stripe account exists and the features
stay dark until the values are filled in.
The installer warns when the file is readable by more than its owner, and
clupilot.env is gitignored — it holds every secret an installation will need.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>