Caddy glob-imports Caddyfile.d/ once, at startup, and its own unit does not
change when a fragment does — so NixOS left it running and the vhost simply
did not exist. No listener, no certificate, and nothing in the log to say why,
because Caddy had never been told to look. The first deploy of this module hit
exactly that: it had to be reloaded by hand before ACME would even try.
reloadTriggers makes the fragment's content part of what decides whether Caddy
is reloaded, so this cannot recur the next time the domain or the port moves.
Also answer unmatched paths with 404. Caddy's default for a request that
matched no route is an empty 200, which made every stray path — /config.yml
among them — look like an empty file that exists. Nothing was ever served
either way, but 404 is the honest answer.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The deployment detail that used to be here was dropped from the history
rather than just from the tip, so it is not recoverable from an earlier
revision of a public repo. It lives with the server configuration now.
Also ignore MANAGE.md.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The header named the serving host's secret store and age key path. A usage
comment is the second thing someone opening this repo reads, and the script
does not need to know where the secret is kept — it reads it from the
environment either way. The concrete invocation lives with the server config.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The scaffold here was a manual pipeline: copy an APK into repo/, run
`fdroid update`, rsync the result somewhere. Every app already builds and
signs its own APK in CI and publishes it as a Forgejo release, so all of
that copying was re-doing by hand what the forge already had.
Both halves of a listing now come from the same release: the APK from the
release asset, and the title, summary, description, changelog, icon and
screenshots from the fastlane/ tree at the same tag. A listing can no longer
describe a build other than the one it ships, because there is no second copy
of the text to drift.
fdroid-sync.py is the whole reconcile. It is a pure function of what the forge
currently holds — work out what should be published, make it so, re-sign the
index only if something moved — which is what makes it safe to drive from a
webhook that has no delivery guarantee and no retry. A missed delivery costs
latency, not correctness.
Three things were only found by running the real thing with an empty
environment, and all three fail late and confusingly:
- fdroidserver bundles apksigner but no JDK, and shells out to jar, keytool
and jarsigner by bare name. The PATH is now set outright rather than
inherited, because the bundled apksigner is a Gradle-style launcher that
also needs xargs and sed — and without xargs fdroidserver reads every APK
as having an invalid signature and quarantines the lot into archive/.
- index-v2 only carries a changelog for a version matching the last Builds
entry, and a repo of prebuilt APKs has no build recipes. Without a declared
entry every release shipped an empty "What's New".
- fdroidserver does not fall back to the APK's launcher icon, so an app whose
fastlane tree has no images/icon.png was listed with no icon at all, which
is two of these four. The sync now recovers one from the APK.
module.nix keeps the keystore password out of the Nix store via
fdroidserver's {env: ...} indirection, and serves the repo rooted at repo/
rather than at the working directory, which would also have handed out
config.yml and metadata/.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>