Skip to content

v0.4.0

Choose a tag to compare

@github-actions github-actions released this 23 Aug 20:24
· 23 commits to main since this release

Added

  • Email can now be delivered over Resend's HTTPS API instead of SMTP. Set a Resend
    API key under Settings → Email and mail goes out over port 443. This exists because
    several hosting platforms block outbound SMTP on their cheaper plans (Railway below
    Pro among them) by dropping the packets rather than refusing the connection - which
    looks like a hang, then like a wrong password, and cannot be fixed by changing any SMTP
    setting. Ports 25/465/587/2525 are all affected and it is not provider-specific.
  • The transport follows the credentials you supply: an API key selects HTTPS, otherwise
    SMTP, otherwise nothing. It does not probe and silently switch. Settings → Email
    badges which path is actually live, so filled-in SMTP fields are never mistaken for SMTP
    delivery, and "Remove key" switches back.

Security

  • All three image uploads now check dimensions before decoding. The 5 MB body limit
    bounds bytes on the wire, not pixels: a highly compressed PNG of 30000x30000 is a few
    hundred KB and decodes to gigabytes. Both the logo and banner endpoints now read the
    image header first and reject anything over 25 megapixels. The branding logo and banner
    are admin-only, but the user avatar upload is not - any authenticated member could
    send a ~160 KB file that decoded to hundreds of megabytes, and an out-of-memory kill
    takes down the process holding the single SQLite connection. It did not need a malicious
    user either: a genuine large camera photo is well under 5 MB compressed.

Fixed

  • Checkbox answers in the admin bookings list are matched liberally. Answers are
    canonicalised to yes/no on the way in, but rows created before that landed hold
    whatever the surface sent (the embed widget sent Yes), and a strict comparison rendered
    those as No - the opposite of what the guest ticked, which matters for consent
    checkboxes. Historic rows now display correctly without rewriting stored data.
  • Branding uploads read the content-type sniff buffer with io.ReadFull. A short read
    could hand the sniffer a truncated prefix and reject a valid image.
  • A failed SMTP dial could hang for ~2 minutes. defaultSMTPTimeout was applied only
    after the connection was established, so the dial itself fell back to the OS SYN-retry
    limit. Against a host that drops SMTP packets this stalled the background job queue,
    which shares a single SQLite connection, delaying every queued email behind it; the
    email test button also appeared to hang rather than fail.
  • The email test button now explains failures instead of reporting "failed to send test
    email". An unreachable server names the platform-block possibility and points at the API
    key; a timeout after connecting points at the port/TLS mode; provider rejections are
    shown verbatim.