Skip to content

EquiTrail — Developer Reference

Keep this file updated. Claude reads this at the start of each session. Last updated: 2026-05-31


Project

Item Value
App name EquiTrail
Package (Android) com.nossie.equitrail
Bundle ID (iOS) com.nossie.equitrail
Current version 3.5.0+50 (phone) · 1050 (watch)
Azure DevOps repo https://dev.azure.com/nossie/equitrail/_git/equitrail
Git user Paul Tierlier
Git email nossiej@gmail.com
Working directory /Users/nossie/app/equitrail
Flutter SDK 3.44 / Dart 3.12

Key File Locations (ALL in workdir — never in Downloads)

File Path Purpose
Android keystore android/equitrail.jks Release signing
Android key properties android/key.properties Keystore passwords
Play Console service account android/play_service_account.json API upload (gitignored)
Firebase Android android/app/google-services.json Firebase config (gitignored)
Firebase iOS ios/Runner/GoogleService-Info.plist Firebase config (gitignored)
Credentials/env credentials.env All passwords/keys (gitignored)
App constants lib/core/constants/app_constants.dart API keys, URLs, constants

android/key.properties content

storeFile=../equitrail.jks
storePassword=VYdUKs9MbUH5mFeegSMB
keyAlias=equitrail
keyPassword=VYdUKs9MbUH5mFeegSMB

Build Commands

Android phone AAB (release)

cd /Users/nossie/app/equitrail
bash scripts/build_release.sh --aab-only
# Output: build/app/outputs/bundle/release/app-release.aab

Android wearable AAB (release)

cd /Users/nossie/app/equitrail/android
./gradlew :wearable:bundleRelease
# Output: android/wearable/build/outputs/bundle/release/wearable-release.aab
# Then sign: jarsigner -verbose -sigalg SHA256withRSA -digestalg SHA-256 \
#   -keystore equitrail.jks -storepass VYdUKs9MbUH5mFeegSMB \
#   wearable/build/outputs/bundle/release/wearable-release.aab equitrail

iOS IPA (release)

cd /Users/nossie/app/equitrail
flutter build ipa --release
# Output: build/ios/ipa/equitrail.ipa
# Deploy to device: flutter install --device-id=00008030-0002196E11E3402E

Upload to Play Console

Upload script

cd /Users/nossie/app/equitrail
python3 scripts/play_upload.py \
  --package com.nossie.equitrail \
  --aab build/app/outputs/bundle/release/app-release.aab \
  --track internal \
  --key android/play_service_account.json

# Watch (wear:internal track)
python3 scripts/play_upload.py \
  --package com.nossie.equitrail \
  --aab android/wearable/build/outputs/bundle/release/wearable-release.aab \
  --track wear:internal \
  --key android/play_service_account.json

Or use the google-api-python-client directly

# Key: android/play_service_account.json
# Project: fleet-furnace-497817
# Package: com.nossie.equitrail
# Internal track: 'internal'
# Wear track: 'wear:internal'

Devices

Device ID OS Connection
iPhone SE (iNossie) 00008030-0002196E11E3402E iOS 26.5 Wireless
Galaxy Watch 6 (via Play Store wear:internal) Wear OS 3 Via phone

Deploy iOS to iPhone

flutter install --device-id=00008030-0002196E11E3402E
# OR for release build:
flutter run --release --device-id=00008030-0002196E11E3402E

Monitoring

Service URL Notes
Uptime Kuma http://192.168.1.111:3001 Synology NAS, Portainer stack
EquiTrail Dashboard (terminal) python3 scripts/dashboard.py Mac local, reads credentials.env
EquiTrail Dashboard (web) python3 scripts/dashboard_web.py → localhost:8088 Mac local
Azure DevOps Wiki https://dev.azure.com/nossie/equitrail/_wiki/wikis/EquiTrail-Wiki 8 pages
Integration tests bash scripts/run_tests.sh Boots Pixel_9, writes docs/bugtracker.md
Backup verify bash scripts/verify_backup.sh Monthly check

Uptime Kuma setup (first time)

  1. Open http://192.168.1.111:3001 → create admin account
  2. Settings → Backup → Import → upload docker/uptime-kuma-monitors.json
  3. Creates 8 monitors: website, routing, Firebase, Discord API, Discord bot, Oracle SSH, admin dashboard

Infrastructure Locations

Tool/Service Where it runs Notes
Portainer Local network only (Mac/NAS) NOT on Oracle — never deploy Oracle services via Portainer
Uptime Kuma Local network (Portainer stack) Monitors external services
Docker Oracle Cloud (100.126.14.49) Plain Docker — deploy via docker compose over SSH
GraphHopper routing Oracle Cloud systemd service
Discord bot Oracle Cloud systemd user service equitrail-bot
Firebase Google Cloud europe-west4 Auth + Firestore + Functions
Website Plesk (equitrail.horse) FTP deploy only
Backup target Oracle Cloud ~/equitrail-backup/ via rsync

⚠️ Portainer is local only. Any Docker workload for Oracle must be deployed via docker compose over SSH (Tailscale), not via Portainer UI.

Cloud Infrastructure (ALL europe-west4 — NEVER us-central1)

Service Region URL
Firebase Auth + Firestore + Functions europe-west4 firebase.google.com
GraphHopper routing server Oracle Cloud NL (Amsterdam) routing.equitrail.horse
Website hosting Plesk (equitrail.horse) 136.144.178.202
Backup server Oracle Cloud 100.126.14.49 ~/equitrail-backup/

Routing server SSH

# ALWAYS use Tailscale IP — NEVER the public IP
ssh equitrail@100.126.14.49
# Public IP: 152.70.54.93 (blocked via UFW, never use directly)
# Routing API key: fFN-dJNv9x5SQ37yOCVyg67SP9ASksy5r0yjp5KPUyg

Deploy a Docker service to Oracle (NOT via Portainer)

# Copy compose file to server, then run over SSH
scp docker/some-stack.yml equitrail@100.126.14.49:~/
ssh equitrail@100.126.14.49 "docker compose -f some-stack.yml up -d"

Wearable Version Convention

  • Phone versionCode = N
  • Watch versionCode = N + 1000
  • Current: phone=45, watch=1045

Firebase

Item Value
Project ID equitrail
Android App ID 1:967800454287:android:845eaa6d9ef6d6f3a544fa
iOS App ID 1:967800454287:ios:b441f335f874398ca544fa
Package (correct, OLD was wrong) com.nossie.equitrail (NOT com.equitrail.equitrail)
SHA-1 (Play App Signing) In Firebase console

App Store Connect (iOS TestFlight)

  • Team ID: SAF6DV6MH7
  • Bundle ID: com.nossie.equitrail
  • Apple ID: nossiej@gmail.com
  • Privacy policy URL: https://equitrail.horse/privacy
  • Setup guide: docs/apple-developer-testflight-setup.md

Azure DevOps Boards

Item Value
Org https://dev.azure.com/nossie
Project equitrail
PAT name equitrail-code
PAT in credentials.env as AZURE_DEVOPS_PAT
Board https://dev.azure.com/nossie/equitrail/_boards/board/t/equitrail%20Team/Issues
Backlog https://dev.azure.com/nossie/equitrail/_backlogs/backlog/equitrail%20Team/Epics
Setup script scripts/setup_devops_board.py

Sprints

Sprint Dates Focus
Sprint 1 2026-06-01 → 2026-06-14 Hotfixes, Watch test, Apple enrollment
Sprint 2 - Social Discovery 2026-06-15 → 2026-06-28 Rider discovery map, iOS TestFlight
Sprint 3 — Monetisation 2026-07-01 → 2026-07-14 Google Play Billing, App Store IAP
Sprint 4 — Platform Expansion 2026-07-15 → 2026-07-28 Apple Watch, push notifications, security

Epics (#6–#16)

🐛 Bugs · 🐴 Tracking · 🌐 Social · 👥 Rider Discovery · 💰 Monetisation · 📱 iOS · ⌚ Galaxy Watch · 🍎 Apple Watch · 🗺️ Navigation · 🔒 Security · 🌍 Website

Work item types (Basic process)

Epic → Issue (User Story) → Task

Commit linking

Use #<id> in commit messages to link commits to work items. Example: fix: GPS crash on pause #33


Key Architecture Files

File Purpose
lib/router.dart All app routes
lib/main.dart App entry point, Hive box registrations
lib/shared/widgets/scaffold_with_bottom_nav.dart Bottom nav bar
lib/shared/l10n/app_localizations.dart NL/EN/FR/DE translations
lib/core/constants/app_constants.dart All constants, API URLs
lib/core/services/calorie_service.dart Calorie calculation
lib/core/services/routing_service.dart GraphHopper routing
lib/core/services/weather_service.dart wttr.in weather
android/wearable/ Galaxy Watch Wear OS app

Hive Box Names & Type IDs

Box Key Type ID
Settings settings
Horses horses 0
Rides rides 1
TrackPoints track_points 2
Routes route_library

Important Rules (from user)

  1. ALL cloud infrastructure in europe-west4 / Netherlands — NEVER us-central1
  2. SSH to routing server ONLY via Tailscale (100.126.14.49), NEVER public IP
  3. Never commit: credentials.env, google-services.json, GoogleService-Info.plist, *.jks, play_service_account.json
  4. After every significant change: update CHANGELOG, README, ARCHITECTURE, SECURITY
  5. All features must work on both Android AND iOS
  6. Service account, keys, all credentials live in the workdir (not in Downloads or elsewhere)

Website (equitrail.horse)

  • Host: 136.144.178.202 (Plesk)
  • SFTP user: equitrail.horse_8u2g2uuryqe
  • SFTP pass: in credentials.env
  • Remote dir: /httpdocs
  • Deploy: bash scripts/deploy_website.sh
  • Preview auth: user=equiteam pass=Paard2026!
  • Privacy policy: /privacy (bypasses auth via .htaccess)

Discord

Item Value
Bot EquiTrail#3566 (1510599021276692641)
Server 1510601145465438289
Token in credentials.env as DISCORD_BOT_TOKEN
Invite https://discord.gg/sRcJuTQzyZ (permanent) — credentials.env + AppConstants.discordInvite
Bot files /home/equitrail/equitrail-bot/ on Oracle server · discord_bot/ in repo
Restart ssh equitrail@100.126.14.49 "systemctl --user restart equitrail-bot"
Logs ssh equitrail@100.126.14.49 "journalctl --user -u equitrail-bot -f"
RouteHelper Merged into main bot — handles #share-routes GPX uploads + /route /gpx
GPX storage /home/equitrail/equitrail-bot/routes/ + metadata.json

Website

Item Value
Host equitrail.horse (Plesk, IP 136.144.178.202)
SFTP user in credentials.env as WEBSITE_SFTP_USER
SFTP pass in credentials.env as WEBSITE_SFTP_PASS
Remote dir /httpdocs
Deploy cmd lftp ftp://equitrail.horse -u "$WEBSITE_FTP_USER,$WEBSITE_FTP_PASS" -e "set ftp:ssl-force false; put FILE -o /httpdocs/FILE; quit"
⚠️ Protocol FTP only — SFTP does NOT work on this Plesk server
Full deploy bash scripts/deploy_website.sh
Single file bash scripts/deploy_website.sh --file terms.html
PRO page equitrail.horse/pro — NL/EN/DE/FR pricing comparison
Privacy equitrail.horse/privacy — bypasses .htaccess auth

PRO Pricing

Channel Monthly Annual Commission
Website €10.00 €90.00 0%
Google Play €11.99 €109.99 15%
App Store (SBP) €11.99 €109.99 15%
App Store (standard) €14.99 €129.99 30%

Apple SBP: apply at developer.apple.com/app-store/small-business-program/ (< $1M revenue, qualifies)

Play Console Upload History

Date Phone versionCode Watch versionCode Track
2026-05-28 43 1042 internal / wear:internal
2026-05-31 45 1045 internal / wear:internal
2026-05-31 46 internal — cloud sync bugfixes
2026-05-31 47 internal — rider discovery, roles, Discord badge
2026-05-31 48 internal — Discord invite, RouteHelper, PRO pricing page
2026-06-01 49 internal — grey screen fix, copyright headers, IP protection
2026-06-01 50 internal — hidden badges, in-app notifications, GPX request, block/report, health disclaimer