Change Log

Introduction

This documents aims to detail all the changes that have been made across the Tory Ferry platform.


Changes

6.7.0 Release | 2026-08-13

App
API
  • Released 6.7.0
    • A minor rather than a patch bump, as the release grew past its original scope - alongside the session and crash fixes it carries the profile fix, which changed how every sign-in path builds the user
    • The iOS build number skips 1073, which was already burned on a local test build - App Store Connect never lets a build number be reused
    • Corrected the version named in the auth-failure log comment, which is the reference point for judging any missing-token 403 seen after release

App Session and Profile Fixes (6.6.3) | 2026-08-12

App
API
  • App:
    • Fixed a dropped connection signing the user out - this was the highest-volume issue on the project, 90 events
      • refreshToken() caught every exception and signed out, and the token is refreshed over the network once it's an hour old, so opening the app on a weak connection destroyed a valid session outright
      • The request then went out with no auth token at all, came back 403, and the app showed that as the offline state - a passenger holding a valid ticket saw an empty list
      • It now signs out only for the four Firebase codes that mean the credential itself is dead. Keeping a dead credential costs one rejected request; dropping a live session makes someone log in again
      • The ticket calls bail on a missing token instead of sending a request that can only 403
    • Fixed the account screens crashing when there's no account - opening ticket details signed out took the screen down mid-purchase, on a screen that renders sign-in buttons further down
    • Fixed a sign-in erasing the name and phone number:
      • Reported as: sign out, sign in with Apple, sign out, sign in with Google - after which the account card showed the name as the word "null" and no phone number. The server still held both, so nothing was lost; the local copy was being overwritten with a worse one
      • The app took whichever provider Firebase listed first rather than the one being signed in with, and Apple only sends a name on the very first authorisation for an account
      • A null name was being converted to the four-character string "null", which is the literal word that appeared on screen. The API has scrubbed that on insert for years, a layer too late; it's now rejected at the source too
      • Firebase only fills in a phone number for phone sign-in, so signing in with Apple, Google or email was discarding the stored one every time
      • Sign-in now takes the account back from the API and uses it to fill gaps, since Logout clears the local copy before signing out
    • Google sign-in failing on a sideloaded or re-signed build is no longer reported as an error - the signature can't match anything registered against the Firebase project, so the refusal is correct. The same failure from a Play install still reports, because those do sometimes matter
    • Requests now send an X-App-Version header, e.g. 6.6.3+10663 - Dart's HTTP client sends the same user agent for every release ever shipped, so the access log couldn't tell one build from another
    • Added four signed-in Maestro flows - the existing ones all ran logged out, so none of them touched the code these fixes changed. They cover sign-in, an invalid password, My Tickets and the Edit Account screen
      • Also fixed dev builds being unable to reach a local API over plain HTTP at all, which targeting Android 16 had blocked
  • API:
    • The access log and the auth-failure log now record the app version, so a missing-token 403 after release can be told apart from a straggler still on 6.6.2

API Fix | 2026-08-09

API
  • Fixed two undeclared variables in the v1 test route

App and API Error Fixes (6.6.2) | 2026-08-03

App
API
  • App - fixed the error handling behind the top Bugsink issues:
    • The auth token returned an empty string on failure and every caller sent it to the API, which rejects it with a 403 - so a client-side token problem surfaced as a server error and an empty ticket list
    • Dropped the forced token refresh, a network round trip to Google on every API call that added latency and signed people out whenever it failed on a weak connection
    • Replaced 23 broken error handlers - each threw an ArgumentError in place of the real error, destroying the status code and URL, and 17 of them had no surrounding try at all
    • Fixed all 5 misuses of the request timeout, which threw instead of degrading
    • Dismissing the Google account picker crashed instead of cancelling
    • About half the app's reported error volume wasn't defects - dismissed sign-in and payment sheets, devices with no connectivity and ordinary password validation are no longer reported
      • The largest single source was the update check running at cold start before the device had brought the network up; it now checks connectivity first
      • Cancelling an Apple or Google sign-in no longer shows an error banner either - telling someone "couldn't log in" for something they chose to do is wrong
      • Timeouts, every HTTP error response and the sign-in failures that indicate a configuration problem are all deliberately still reported, and there are tests to keep it that way
    • The ticket service now checks the response shape before reading it - a non-JSON reply from the API or a proxy in front of it crashed, was swallowed, and showed to the user as "offline"
    • Removed the admin check-in button from the My Ferry page - check-in is handled in the web admin panel
    • Slowed the shimmer on the ticket buttons, which read as a flicker rather than a highlight
  • API:
    • Fixed admin authorisation 500ing when the account lookup finds nothing - a soft-deleted account, a uid from another environment's database or a stale header. Reported against the admin ticket lookup, which is why the admin app couldn't load ticket data
    • 403s are now diagnosable - both auth paths log a structured reason (no token, expired token, no account, not an admin) through winston into Loki, where before the only trace was a console line inside the container
    • The token itself is never logged, only whether one was present
    • Set trust proxy to one hop, so the access log records the real client address instead of Traefik's address on the Docker network for every request

Email and Privacy Update | 2026-07-29

API
Web
  • Updated the receipt and updated-receipt emails, and the shared email styles
  • Improved the privacy policy page on the website

Statistics Update | 2026-07-26

API
  • Ported the Arranmore statistics work over, adapted for Tory:
    • Statistics can now be narrowed to a single sailing - all three endpoints take an optional journey and time, and the return leg matches the opposite sailing
    • New totals endpoint returning tickets and passengers. No vehicle breakdown, unlike Arranmore, as Tory is passenger only
    • Soft-deleted tickets are now excluded from every statistics aggregation, so a single-sailing view agrees with the dashboard for the same ferry. Whole-day totals may drop slightly as a result
    • Checked against the local database for one day - 124 tickets and 265 passengers, matching an independent count, and summing all eight sailings reconciles to the whole-day figure exactly

Stripe Metadata Update | 2026-07-25

API
  • Stripe PaymentIntents are now tagged with the ticket id, ticket ref, user id and payment method
    • Payments are traceable back to a ticket from the Stripe dashboard for every ticket, not just ones that complete payment
    • All 8 PaymentIntent creation flows now go through the same insertTicketWithPaymentIntent and tagPaymentIntentWithTicket helpers

App and API Overhaul | 2026-07-22

App
API

Huge Update

  • API:
    • Upgraded the whole dependency stack to match the Arranmore API - Express 4 to 5, MongoDB 4 to 7, Stripe 10 to 19, firebase-admin 11 to 13, react-email 1 to 6, TypeScript 5.3 to 5.8. Node 20.19+ is now required
    • Migrated the v1 controllers off the MongoDB callback API that was dropped in driver 5 - 46 call sites. On the new driver those requests hung forever with no error, GET /timetables included, which the app calls on every launch
    • Fixed crashes found along the way:
      • Cancelling a sailing with tickets on it crashed the API and emailed nobody - the cancellation email was being called with one argument against a four-argument signature
      • An unknown user on getPaymentIntent carried on into a null dereference instead of stopping
    • Moved the database, Stripe and Twilio secrets out of the config files and into gitignored .env files, with a documented .env.example
      • This uncovered a dotenv ordering bug the hardcoded values had been masking - the config was read before the env file had loaded
      • The old credentials are still in the git history and need rotating
    • Error reporting added via Sentry and Bugsink, along with Loki log shipping and a request logger
    • The auth token moved from req.body.authUser to req.authUser - Express 5 leaves the body undefined on GETs, which would have 500'd every authenticated ticket read
    • Added a local test harness - 88 v2 routes are probed for the Express 5 upgrade and all nine email templates are rendered
  • App:
    • Fixed the sold-out guard - it compared seats against a car count, which is always 0 on Tory, so passengers could be added to a full sailing with the Buy button still live. Only the server-side check at payment stopped it
    • The ticket card now shows the price actually paid rather than recalculating it on the phone, so discounts and islander rates match the receipt
    • Fixed tickets greying out 30 minutes before departure instead of after - the grace period was added to the wrong side, so the QR code read as expired exactly when the crew needed to scan it
    • Free passenger bookings now go through the normal booking path - they were being sent to a reservation endpoint that doesn't exist in the API and failed with "Reservation Failed"
      • Free baskets are re-priced against the server price list before a ticket is issued, so a spoofed zero total can't mint a valid ticket
      • The genuinely free fares are islander children under 14 - the smartpass is charged for
    • Timetable parsing is now typed and guarded - one bad record used to take the whole timetables page down instead of being skipped
    • Translations now come from Tolgee as the source of truth, with a pull script that runs before a build
    • Now targeting Android 16 (API 36) ahead of the Google Play deadline on 31 August 2026
    • Added a Maestro device test suite - four flows passing on iOS 26.2 and Android 16
  • Wrote up the Tory/Arranmore parity audit and the upgrade in PARITY_WITH_ARRANMORE.md and UPGRADE_NOTES.md

Email Update | 2026-06-22

API
  • Removed "All tickets are non-refundable but fully transferable" from the cancellation emails

API Updates | 2026-06-13

API
Kiosk
  • Fixed a kiosk timezone bug - the weekday was worked out on the server's UTC clock, so a date picked at midnight rolled back a day and returned the wrong timetable
  • Ticket history now records who made the change, and sorts in the right order

API Updates | 2026-05-31

API
  • Email logging now includes the ticket reference and the recipient address

App Update | 2026-05-01

App
  • Updated to the latest Stripe plugin

App and Kiosk Update | 2026-04-29

App
Kiosk
  • App:
    • Updated Firebase Auth for reCAPTCHA 6.5.0
    • Improved profile editing
  • Kiosk:
    • Updated to the latest Next.js version

Admin Reroute Functionality | 2025-10-23

Admin
API
  • Added reroute functionality to the Admin panel
  • Added all reroute API endpoints
    • When rerouting a ferry, no emails are sent, just marked as "Requires Attention"
  • Added cancellation - "Mark as Requies Attention" to the cancellation panel

App Declaration Update | 2025-10-07

App
  • App Financial Features Declaration Completed

Web and Admin Updates | 2025-07-25

Admin
  • Added a new git branch for admin

Web and Admin Updates | 2025-06-18

Admin
Web
API
Kiosk

Huge Update

  • Server Updates:
    • Moved servers from Heineken to Rex (Hetzner)
    • Deployed using CI/CD pipeline
    • Moved from bitbucket to GitHub
  • POS Devices:
    • Added login functionality to the POS devices
      • Each device has a device_id registered in POS Devices tables
      • Always included in the POS headers
    • Improved the Revenue APIs
      • POS devices can now print out the full data
  • API:
    • Promo Code functionality exists in the API
    • Ready for Kiosk deployment
    • Device IDs
    • Revenue is available in admin and per device
  • Admin Panel:
    • Brought the admin panel in line with latest version, complete rewrite.
    • Updated the admin panel update functionality
      • Only makes changes to specific fields in the ticket
    • Added the "Push To Device"
      • Tickets can be push from admin
      • Tickets can be picked up on the admin till
  • Web:
  • Kiosk Updates:
    • Removed all references to snow in the kiosk
    • Added a preferred payment method when selecting the payment device from Stripe

App Update | Up to 2024-10-09

App
  • Support for iOS Dark and Tinted Icons and iOS 18.0
  • Support for Themed Icons on Android
  • All Flutter plugins up to date (Flutter v3.24.3)

App, Admin & API | Up to 2024-07-10

App
POS
  • Updated App
    • Updated styles for Stripe
    • Target Android Version 34
    • Configured a dev and prod app
  • Updated POS Ticket / Machine
    • Updated so that tickets with vehicles appear at the top
    • Updated to use a new version of Shorebird
    • Configured a dev and prod flavor
    • Updated to Flutter 3.22.0

hexa studios

mobile app and web developers

All rights reserved.