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-Versionheader, 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
- Fixed a dropped connection signing the user out - this was the highest-volume issue on the project, 90 events
- 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
ArgumentErrorin 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 proxyto 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
insertTicketWithPaymentIntentandtagPaymentIntentWithTickethelpers
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-admin11 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 /timetablesincluded, 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
getPaymentIntentcarried on into a null dereference instead of stopping
- Moved the database, Stripe and Twilio secrets out of the config files and into gitignored
.envfiles, 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.authUsertoreq.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
- Upgraded the whole dependency stack to match the Arranmore API - Express 4 to 5, MongoDB 4 to 7, Stripe 10 to 19,
- 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.mdandUPGRADE_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
- Added login functionality to the POS devices
- 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:
- Translated website to Gaeilge
- Full translations can be accessed: Hexa Translations
- 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
devandprodapp
- Updated POS Ticket / Machine
- Updated so that tickets with vehicles appear at the top
- Updated to use a new version of Shorebird
- Configured a
devandprodflavor - Updated to Flutter 3.22.0