Commit Graph

2 Commits (feat/plan-marketing)

Author SHA1 Message Date
nexxo 6998a22527 Repair ownership by looking inside, not at the door
tests / pest (push) Failing after 8m2s Details
tests / assets (push) Successful in 21s Details
tests / release (push) Has been skipped Details
The ownership repair added in 1.1.1 sampled the owner of each top-level
directory and skipped the recursion when it already matched. node_modules was
owned by www-data; node_modules/.vite-temp, left behind by an earlier root
build, was not. So the repair walked past it, `npm run build` failed with
EACCES, and the deployment stopped in maintenance mode — on the release whose
whole point was that this could not happen.

A directory's owner says nothing about its contents. It now walks each tree
once with find and changes only the entries that are wrong, which is also
cheaper than the detect-then-chown-everything it replaces.

Verified against the shape that actually failed: a root-owned file inside a
www-data-owned node_modules, repaired.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-28 23:53:25 +02:00
nexxo 00b9107ee3 Deploy as the application's user, and take back what root already took
tests / pest (push) Failing after 7m19s Details
tests / assets (push) Successful in 19s Details
tests / release (push) Has been skipped Details
The 500 on the VPN config download was never in the VPN code. storage/logs/
laravel.log was owned by root, mode 644, since 27.07 04:16 — the moment a
failed `artisan optimize` wrote its error there during a deployment. From then
on the application could not append to its own log: Monolog threw on every
attempt, and a throw while logging is a 500 on any page that logs, with nothing
written down to say why. The error page said "Er wurde protokolliert". It was
not.

It surfaced on the config download because that is one of the few pages writing
a log line on its way through — on success and on a wrong password alike. Pages
that log nothing were unaffected, which is exactly why it looked like a VPN
fault for days.

`docker compose exec` is root unless told otherwise, and every deployment
script relied on that default — including the agent's allowlist sync, which
runs every minute. docker/entrypoint.sh had it right all along and drops to
www-data for precisely these commands. Now so does everything else, help text
included: telling an operator to run artisan as root is how the file ends up
owned by root in the first place.

update.sh also repairs what an earlier run left behind, before its first
unprivileged step rather than after — a root-owned log file is not
self-healing, the page that trips over it is nowhere near the deployment that
caused it, and with in_app unprivileged a root-owned vendor/ would break the
next composer step outright.

tests/Feature/DeploymentRunsAsTheAppUserTest.php holds the line. It was checked
against the previous commit and finds all nine places that were wrong.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-28 22:52:09 +02:00