chore: drop the plain-HTTP route now the panel is proven on TLS

The transport log settles it: the device reaches the server over https
and validates the Let's Encrypt chain without trouble. The dedicated
port, the Traefik entrypoint and the plain-HTTP router were all built on
a hypothesis the evidence has since refused.

DEVICE_ALLOW_HTTP goes back to false, so the device routes refuse an
unencrypted request again.

DEPLOY.md is rewritten around the real cause — the firmware does not
follow redirects, and a trailing slash was answered with a 308 — and
records the three hypotheses that were wrong, so nobody spends another
evening on them. It also names the trap that made this slow: Traefik,
a production Next server and tcpdump were each read as saying "no
traffic" when all three were simply silent by default.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012cSY9pVhZmJUKNN7wf1Myd
This commit is contained in:
2026-09-21 23:19:45 +02:00
co-authored by Claude Opus 5
parent 235af90aed
commit 13abcf3242
3 changed files with 67 additions and 81 deletions
+3 -31
View File
@@ -38,40 +38,12 @@ services:
traefik.http.routers.horaires.tls.certresolver: ${TRAEFIK_CERTRESOLVER:-myresolver}
traefik.http.routers.horaires.middlewares: horaires-hsts
traefik.http.services.horaires.loadbalancer.server.port: "3010"
# The admin is only ever served over TLS; say so to the browsers.
# Everything is served over TLS, the panel included: its firmware
# validates the chain without trouble. A plain-HTTP route existed here
# for a while, on the belief that it could not — see DEPLOY.md.
traefik.http.middlewares.horaires-hsts.headers.stsSeconds: "31536000"
traefik.http.middlewares.horaires-hsts.headers.stsIncludeSubdomains: "true"
# --- The panel, in clear, on four paths only ---
#
# The e-ink firmware carries a certificate-authority bundle fixed when it
# was built, so it cannot validate a chain rooted in an authority created
# afterwards — which is exactly the case with Let's Encrypt's ISRG Root YR
# (May 2026). The handshake fails before a request is ever sent, which is
# why neither Traefik nor the application sees anything at all.
#
# This router therefore serves the four device paths over plain HTTP. The
# administration stays on TLS. The trade-off is real and bounded: the
# device token travels in clear, it is used for nothing else, and it can
# be revoked from Paramètres → Appareils. The image URL is an unguessable
# content hash.
#
# It listens on its own entrypoint rather than on :80. Traefik applies an
# entrypoint's HTTP→HTTPS redirection before routing, so no router
# priority can escape it — the port has to be one that never redirects.
# That also makes the restriction structural: nothing but these four
# paths is reachable on 2300, and it is Traefik that guarantees it rather
# than application code.
#
# Requires the matching `device` entrypoint in the shared traefik.yml.
# Remove this block the day the firmware learns the new roots, and set
# DEVICE_ALLOW_HTTP=false — the application refuses plain requests
# without it.
traefik.http.routers.horaires-device.rule: >-
Host(`${APP_DOMAIN}`) && (PathPrefix(`/api/setup`) || PathPrefix(`/api/display`)
|| PathPrefix(`/api/log`) || PathPrefix(`/api/device/`))
traefik.http.routers.horaires-device.entrypoints: device
traefik.http.routers.horaires-device.service: horaires
backup:
# A nightly dump kept for two weeks. Small, boring, and the only thing