ElaRide — Software Requirements Specification

Version: 1.2
Date: June 2026
Status: Draft for Review


Table of Contents

  1. Introduction
  2. Product Overview
  3. User Roles & Access Control
  4. Functional Requirements
  5. Non-Functional Requirements
  6. System Architecture
  7. Technology Stack
  8. External Integrations
  9. Core Data Model (Entities Only)
  10. MVP vs Phase 2 Scope
  11. Open Items

1. Introduction

1.1 Purpose

This document is the authoritative specification for ElaRide MVP. It defines what the system does and how well it must do it. Implementation details — field names, API schemas, database column types — belong in the System Design Document (SDD).

1.2 MVP Philosophy

The client's wish list is large. We launch only the features that are core to the brand promise: a passenger can be safely booked, a driver can accept and complete the ride, and operations can manage everything in between. Everything else is Phase 2. This maximises speed-to-market, reduces initial cost, and lets real user feedback guide what gets built next.

1.3 MVP Scope Boundary

In scope:

Explicitly out of MVP:

1.4 Definitions

Term Definition
Guardian Adult who books and pays for rides — parent, caregiver, or self-booking adult
Dependent The passenger managed under a guardian account (typically a minor or care recipient)
Leg One direction of a journey. Round trip = 2 legs = 2 rides
ElaAbo Monthly subscription with a fixed leg quota
ElaRide+ Advance-reservation single trip (not on-demand)
Safe Handover Structured pickup/dropoff for minors, included in every minor ride
Pickup PIN 4-digit code set by guardian; validated by driver at boarding
Guardian Mode Real-time tracking, SOS, and safety tools active during a ride
Trusted Circle Up to 4 emergency contacts per dependent set by the guardian
Ops Dashboard Internal web application for admin, dispatcher, and support roles
PBefG Personenbeförderungsgesetz (German passenger transport law)
HGB Handelsgesetzbuch (German commercial code)
ArbZG Arbeitszeitgesetz (German Working Hours Act)

1.5 Assumptions


2. Product Overview

2.1 Services

ElaAbo — Subscription Rides

Recurring monthly subscription. Fixed routes, fixed times, same driver whenever possible. Designed for school runs, therapy, sports, and regular routines.

Plan Legs / Month Base price ≤8 km Monthly total Extra per km >8 km
ElaAbo 12 12 TBC ⚠️ TBC ⚠️ €2.20
ElaAbo 24 24 TBC ⚠️ TBC ⚠️ €2.20
ElaAbo 40 40 TBC ⚠️ TBC ⚠️ €2.20

Legs consumed beyond the monthly quota are billed at a configurable overage rate. The system supports configurable per-plan pricing; no values are hard-coded.

ElaRide+ — Single Trip

Advance-reservation only. Not on-demand. The UI must never present it as instant mobility.

Component Rate
Base fare (≤8 km) €24.00
Extra km (>8 km) €2.20 / km
Waiting time €0.50 / min

Booking windows:

Add-ons (ElaExtras)

Add-ons are independent selections — there is no combined type. If multiple add-ons are selected, the billing engine handles time-window interactions between them.

Add-on Free tolerance Billable rate
Safe Handover (minors only) 5 minutes Included — never charged
Wait+ None beyond Safe Handover tolerance TBC ⚠️ / 15-min block
Begleitung+ 10 minutes €12.00 flat, then TBC ⚠️ / 15-min block
Return+ Standard leg price for return

Safe Handover for minors includes: arrival confirmation, short handover at entrance or reception, confirmation of entry, app checkout, guardian notification, and PIN validation. The 5-minute tolerance is always included and never charged.

2.2 Booking Channels

Channel Flow
Rider app (mobile) Primary channel for all advance bookings
Rider web app (browser) Secondary channel; same booking flow via browser
Phone Guardian calls ops; operator creates booking in ops dashboard
WhatsApp Click-to-chat deep link opens WhatsApp to ops number; operator creates booking manually

2.3 Cancellation Policy

Time before pickup Guardian-initiated fee
≥24 hours Free
3–24 hours 50% of ride price
<3 hours or no-show 100%

If ElaRide cancels: full refund plus free rebooking to next available slot.


3. User Roles & Access Control

3.1 The Six Roles

The system has two distinct user populations: passengers and operators. Within passengers, there are three access levels. Within operators, there are three functional responsibilities. Six roles is the correct minimum — fewer would require ad-hoc permission flags; more would be over-engineering for a small team.

Role Who Access surface
guardian Parents, caregivers, self-booking adults. Billing owner. Rider app, rider web app, limited ops actions on own rides
dependent Passengers aged 12+ with guardian-granted limited login Rider app (restricted views only)
driver Female employee. Sees minimum necessary ride data. Driver app only
dispatcher Assigns drivers, manages live board, route optimization Ops dashboard
support Handles incidents, communicates with guardians, escalates Ops dashboard
admin Full system access — rides, drivers, billing, config, KPIs Ops dashboard

Account types vs. roles: "Parent", "caregiver", and "self-booking adult" are profile metadata (account_type field), not separate roles. All three use the guardian role and are treated identically by the system.

3.2 RBAC Implementation

No database permission tables are needed. Storing roles, permissions, and role-permission mappings in database tables is the correct pattern when an admin UI must configure permissions dynamically (multi-tenant SaaS, enterprise platforms). ElaRide's permissions are fixed business rules that do not change at runtime — they are encoded in the application. A role enum on the User record is sufficient for role storage.

Access control is enforced in three complementary layers:

Layer 1 — Route guards (NestJS, zero DB cost)
At login the JWT payload carries {userId, profileId, role, familyId}. Custom @Roles() decorators on controllers declare the permitted roles. A global RolesGuard validates the JWT claim before any database interaction. This handles coarse-grained access — a driver cannot reach a payment endpoint regardless of any other factor.

Layer 2 — Resource-level authorization (CASL)
Route guards answer "can a driver reach this endpoint?" but not "can this guardian access this specific ride?" That requires attribute-based checks against resource ownership. The @casl/ability library is used via a CaslAbilityFactory that, given a user object, builds an ability rule set encoding conditions such as can('read', 'Ride', { familyId: user.familyId }). A PoliciesGuard in NestJS evaluates these rules in service-layer methods before any data is returned. This is the industry-standard approach for NestJS applications with family-scoped or user-scoped data.

Layer 3 — PostgreSQL Row Level Security (database baseline)
For the most sensitive tables — ride_live_locations, payments, driver_documents — RLS policies enforce access at the database engine level using the current_setting('app.current_user_id') pattern set per query. No application-layer bug can bypass this.

UI-level guards are supplementary only. They improve UX but are not relied upon for security.

3.3 Access Matrix

Resource Guardian Dependent Driver Dispatcher Support Admin
Own profile RW RW RW R RW
Dependent profiles RW (own family) own row R R RW
Rides RW (own family) R (own) R (assigned) RW R RW
Ride stops R (own family) R (own) R (assigned, labels only) RW R RW
Live locations R (active, family) R (active, own) W (active, assigned) R R R
Incidents RW (own family) R (own) R (assigned) R RW RW
Ride events (audit log) R (own family) R (own) R (assigned) R R R
Driver documents RW (own) RW
Payments / subscriptions RW R (limited†) R R RW
Driver shift data RW (own) R RW
System config RW

† Dependent payment view: amount, status, ride_id, date only. No Stripe identifiers or billing details.

Driver ride view exposes only: ride_id, stop_type, address_label (not full address), scheduled_at, passenger_first_name, pin_required (boolean), special_needs_label, seat_requirement. Never date of birth, full address, guardian PII, or payment data.


4. Functional Requirements

Format: [DOMAIN]-[NNN] Priority — Description
Priorities: P1 = MVP blocker · P2 = MVP important · P3 = MVP nice-to-have


4.1 Identity & Access Management (IAM)

IAM-001 P1 — The system shall support six roles with distinct data access scopes enforced at application and database layers: guardian, dependent, driver, dispatcher, support, admin.

IAM-002 P1 — Guardians and drivers shall register with email and password. Phone number verification via one-time code is mandatory for both before the account is activated.

IAM-003 P1 — Guardians shall be able to create, edit, and remove up to 3 dependent profiles under their account. Each profile stores: name, date of birth, special needs notes, saved places, and authorised handover persons.

IAM-004 P1 — Guardians may store named recurring locations per dependent (e.g. "School", "Therapy centre"). These saved places pre-populate the booking form and are encrypted at rest. They are part of the dependent profile, not a separate entity.

IAM-005 P1 — A dependent aged 12 or older may be granted a limited login by their guardian via an allow_login toggle. The system verifies the dependent's date of birth. Dependents with allow_login = false or age under 12 cannot log in.

IAM-006 P1 — Guardians may revoke dependent login access at any time. The dependent's active session is invalidated immediately upon revocation.

IAM-007 P1 — Driver onboarding follows a sequential approval flow before activation:

  1. Identity information submitted
  2. Documents uploaded: driving licence, Führungszeugnis (criminal record from Bundesamt für Justiz), vehicle insurance, training completion certificate
  3. Admin reviews and approves or rejects each document individually
  4. Admin activates the account once all documents are approved

This is a fully manual workflow: the driver obtains documents and uploads scans; admin reviews in the dashboard. No automated third-party verification API.

IAM-008 P1 — TOTP-based multi-factor authentication (authenticator app) shall be mandatory for admin, dispatcher, and support roles. Optional for guardians.

IAM-009 P1 — Guardians shall configure a Trusted Circle per dependent: up to 4 emergency contacts with name, phone number, and relationship. These contacts receive SMS alerts when SOS is triggered.

IAM-010 P1 — The system shall support password reset via email link, and phone number change via OTP verification.


4.2 Ride Booking

BK-001 P1 — Guardians and eligible dependents shall book rides via two products: ElaAbo (subscription) and ElaRide+ (single trip, advance-reservation).

BK-002 P1 — When a dependent initiates a booking, the guardian always receives a push notification immediately.

BK-003 P1 — The booking form shall capture: passenger selection, pickup location, dropoff location, date and time, time flexibility window (exact / ±15 min / ±30 min), ride direction (one-way / return), and add-on selection.

BK-004 P1 — ElaRide+ booking rules:

BK-005 P1 — ElaAbo subscription configuration: guardian sets days of week, pickup time, time flexibility window, whether a return leg is included, and add-on selection. The system generates recurring reservation records for the billing cycle.

BK-006 P1 — For any passenger under 18, the booking flow shall automatically apply the Minor Safety Layer: Guardian Mode, Pickup PIN, Live Tracking, and Safe Handover. These cannot be disabled for minors.

BK-007 P1 — A transparent fare estimate showing the full breakdown (base, distance, add-ons, total) shall be presented before the guardian confirms any booking. Prices are fixed; no surge pricing.

BK-008 P1 — Operators shall be able to create bookings on behalf of a guardian from the ops dashboard, to support phone and WhatsApp channel requests.

BK-009 P2 — Group rides across multiple families sharing one vehicle are [PHASE 2]. At MVP, each booking covers one family's passengers only.


4.3 Ride Lifecycle & State Machine

RL-001 P1 — All rides follow a single state machine. Entry points differ by product:

ElaRide+ path:
  pending_review → confirmed → assigned → accepted → en_route
                → arrived → in_progress → completed

ElaAbo path:
  scheduled → confirmed → assigned → accepted → en_route
            → arrived → in_progress → completed

Terminal states (reachable from most active states):
  cancelled
  no_show (reachable from: arrived, in_progress)

State meanings:

RL-002 P1 — Every state transition shall be recorded as an immutable entry in the ride event log: transition type, actor, actor role, timestamp, and context metadata. This log is the authoritative audit trail and cannot be modified or deleted.

RL-003 P1 — Ops approval for ElaRide+: when a booking is in pending_review, an ops team member reviews it (payment method validity, passenger profile completeness) and either confirms or rejects it. Short-notice bookings (12–24 h) go through the same flow with a time-sensitive flag.

RL-004 P1 — Driver offer: the dispatcher assigns a driver, creating a time-limited offer (configurable window, default 5 minutes). The driver receives a push notification. On expiry or rejection the ride returns to confirmed for re-dispatch, and the dispatcher is alerted.

RL-005 P1 — If a driver cancels an accepted ride, the system immediately alerts the dispatcher, flags the ride as critical priority, and initiates the re-dispatch protocol:

Step Target time Action
Ops alert Immediate Dashboard high-priority flag
Guardian first notification ≤5 minutes Push + SMS: arranging replacement
Replacement search ≤10 minutes From available driver pool
Final resolution ≤15 minutes Replacement confirmed or no-replacement protocol

No-replacement message to guardian: direct apology, full refund offered, support contact. Driver cancellation is logged for reliability tracking.

RL-006 P1 — The Pickup PIN must be validated before the ride transitions from arrived to in_progress. The driver enters the PIN; the system validates against the stored hash server-side. The ride cannot start without a match or an approved override.

RL-007 P1 — PIN forgotten flow:

  1. Driver selects "PIN Forgotten" → guardian receives push and SMS requesting remote approval
  2. Guardian can: remotely approve, generate a temporary PIN, or call support
  3. If no guardian response within 3 minutes → driver is prompted for identity verification (passenger name and pickup address confirmation)
  4. For minor passengers: ride must not start without guardian confirmation
  5. If unresolved within 5 minutes → escalate to support; support can unlock the ride from the dashboard

RL-008 P1 — Safe Handover for minors at destination (in_progresscompleted):

  1. Driver confirms arrival at destination
  2. Short handover at entrance or reception (up to 5 minutes included, no charge)
  3. Driver confirms passenger has entered the location
  4. Driver checks out in app
  5. Guardian receives "Safe Arrival" push notification
  6. If handover exceeds 5 minutes: Wait+ timer starts; guardian and dispatcher are notified

RL-009 P2 — After completing a ride, the driver submits a brief status report: OK / delay / incident / extra wait. For incidents, a report is auto-opened.


4.4 Live Tracking & Guardian Mode

GT-001 P1 — Guardian Mode shall be the default and non-disableable setting for all rides involving a dependent. It is opt-in for adult self-bookings.

GT-002 P1 — Guardian Mode provides during an active ride: real-time driver location on a map, an SOS button, and Trusted Circle contact access.

GT-003 P1 — The driver app shall transmit GPS coordinates at an adaptive interval based on ride state:

State Interval Rationale
en_route Every 5 seconds Driver moving; position updates needed for guardian ETA
arrived Every 10 seconds Driver stationary; lower update rate acceptable
in_progress Every 4 seconds Passenger onboard; highest safety criticality

These are configured targets. The device may adapt further based on speed and battery conditions. The backend accepts updates at any frequency.

GT-004 P1 — GPS data flow: driver app transmits location → server validates (JWT and active assignment) → latest position cached for fast reads → location stored for anomaly detection → guardian and dependent apps receive real-time map update via WebSocket.

GT-005 P1 — Live location broadcast shall automatically cease when the ride reaches a terminal state (completed, cancelled, no_show). No further location events are emitted after this point.

GT-006 P1 — Live location data is not persisted beyond the active ride session unless an open incident investigation requires it.


4.5 Safety Systems

SF-001 P1 — An SOS button shall be accessible to dependents and guardians throughout any active ride (arrived or in_progress state). It is not available outside active rides.

SF-002 P1 — Pressing SOS triggers all of the following simultaneously:

  1. Support dashboard receives a high-priority alert requiring acknowledgement
  2. Guardian receives a high-priority push notification
  3. Trusted Circle contacts receive an SMS from the configured provider
  4. The app displays a prominent "Call 112" button (native phone dialler). Note: auto-dialling emergency services without user interaction is not permitted by iOS or Android. The one-tap button is the correct implementation.

SF-003 P1 — If support does not acknowledge an SOS alert within a configurable window (default: 3 minutes), the system escalates: SMS and email alert sent to the operations lead.

SF-004 P1 — Route deviation detection (background job comparing real-time GPS against planned route):

Level Trigger Response
1 >1.5 km from route OR ETA increased >5 min Ops dashboard alert. No passenger notification.
2 Level 1 sustained + driver contacted but reason unclear Driver must confirm status in app or by phone.
3 >3 km deviation OR >10 min delay OR driver unresponsive 2+ min Guardian receives calm informational message. Ops monitors closely.
4 Driver unreachable OR continued divergence after Level 3 Ride flagged Critical. SOS escalation path activated. 112 call button shown to guardian.

SF-005 P1 — Any ride participant (guardian, dependent, driver) shall be able to file an incident report during or after a ride, with severity (1–5), type, and free-text description.

SF-006 P1 — Incident workflow: open → in_review → resolved | escalated. Escalated incidents require admin acknowledgement and trigger an email to the operations lead.

SF-007 P2 — Periodic passenger check-in prompts during active rides — [PHASE 2]. At configurable intervals the dependent receives an automated "Are you okay?" prompt. No response within a timeout triggers a guardian alert, followed by a support alert. GPS tracking and the SOS button cover the MVP safety promise.


4.6 Driver Management & Shift Operations

DM-001 P1 — Driver onboarding status progression: pending_documents → documents_submitted → under_review → approved | action_required. Each status change triggers push and email notifications to the driver and admin.

DM-002 P1 — Required documents for activation: driving licence, Führungszeugnis (manual workflow — driver obtains, uploads scan), vehicle insurance certificate, training completion certificate. Admin approves each document individually.

DM-003 P1 — The system shall issue automated reminders to drivers whose Führungszeugnis is approaching its 1-year expiry (30 days before). If not renewed by expiry, driver status transitions to action_required and they are removed from the dispatch pool until renewed.

DM-004 P1 — Drivers shall set and manage availability blocks, including recurring weekly patterns. Availability blocks are visible to dispatchers on the driver schedule board.

DM-005 P1 — Shift management: drivers check in at the start of each shift and check out at the end. The system records shift start, end, and pause events. Checking in requires confirming vehicle safety, personal health readiness, and fitness to work. If a driver marks themselves unfit, the shift is blocked and ops receives an alert.

DM-006 P1 — German labour law compliance (ArbZG): before any ride offer is sent to a driver, the system shall verify the driver is not in violation of mandatory rest or break requirements. If a legal break is overdue or the 10-hour shift limit would be exceeded, the ride offer is blocked and the dispatcher is notified with the reason. Checks enforced: required break compliance, shift duration limit (10 hours), and minimum 11-hour rest between shifts.

DM-007 P2 — Same-driver preference for recurring ElaAbo routes: the system notes the last assigned driver per route and prioritises them when available. This is a preference, not a guarantee. [PHASE 2]

DM-008 P2 — Advanced fatigue scoring, break health engine, and in-app earnings summary. [PHASE 2]


4.7 Notifications

NT-001 P1 — Notification channels: push notifications (Expo Push Notification Service, wrapping FCM and APNs), SMS (provider to be selected — see Open Items), and email (Brevo).

NT-002 P1 — Guardian notification triggers: ride requested by dependent, all ride status changes, driver assigned and accepted, SOS triggered, incident created and updated, and all payment events.

NT-003 P1 — Dependent notification triggers (when allow_login = true): driver assigned, driver en route, driver arrived, boarding confirmed, ride completed or cancelled.

NT-004 P1 — Driver notification triggers: new ride offer, ride status updates relevant to their assignment, ops messages. Drivers never receive family PII, guardian contact details, or payment data in notifications.

NT-005 P1 — Safety-critical notifications (SOS, incident escalation) shall be delivered via push and SMS simultaneously. For non-SOS notifications, SMS is a fallback if push is unacknowledged within 60 seconds.

NT-006 P1 — Notification content shall not include full minor names or full street addresses in preview text, in compliance with GDPR privacy requirements.

NT-007 P1 — SOS alerts cannot be disabled by any user. All other notification preferences are user-configurable.


4.8 Billing & Payments

PY-001 P1 — The billing entity is always the guardian's Stripe customer account, regardless of who initiated the booking.

PY-002 P1 — ElaAbo billing: monthly recurring charge via Stripe Subscriptions. Legs consumed are tracked against the monthly quota. Overage legs are billed at a configurable per-leg rate at cycle end.

PY-003 P1 — ElaRide+ billing: Stripe PaymentIntent created and authorised at booking confirmation. Captured on ride completion. Released on free-window cancellation.

PY-004 P1 — Apple Pay and Google Pay shall be supported via Stripe Payment Element.

PY-005 P1 — No raw card data shall be stored or logged in the ElaRide system. All card handling is delegated exclusively to Stripe.

PY-006 P1 — Every completed ride shall have a pricing record capturing: pricing mode (subscription or single), base amount, distance component, add-on charges, and total. This record is immutable after creation.

PY-007 P1 — Payment failure: guardian receives high-priority push and email. A configurable grace period (default: 48 hours) applies before new rides are blocked. Existing confirmed bookings are not cancelled during the grace period.

PY-008 P1 — All payment-related emails are sent via Brevo (not Stripe's built-in delivery — this is disabled in Stripe Dashboard settings to ensure full German-language brand control). After each completed ElaRide+ ride: auto-receipt email. After each ElaAbo billing cycle: monthly invoice email.

PY-009 P1 — Dependent payment view: dependents with login access see a restricted summary (amount, status, ride reference, date) with no Stripe identifiers or full billing details.


4.9 Cancellation & Refunds

CA-001 P1 — Cancellation policy (guardian-initiated):

Time before pickup Fee
≥24 hours Free
3–24 hours 50% of ride price
<3 hours or no-show 100%

CA-002 P1 — When ElaRide cancels (driver unavailable, no replacement found): guardian receives full refund and is offered a free rebooking to the next available slot.

CA-003 P1 — Cancellation fees are processed via Stripe: for ElaRide+ via PaymentIntent partial capture; for ElaAbo as a deduction applied in the next billing cycle.

CA-004 P1 — All cancellations are logged in the ride event log with actor attribution and timestamp.


4.10 Add-ons (ElaExtras)

AO-001 P1 — Three add-on types: wait_plus, begleitung_plus, return_plus. Each is an independent boolean selection on a booking. If multiple are selected, the billing engine handles their interactions independently — there is no combined type.

AO-002 P1 — Billing rules:

AO-003 P1 — For ElaAbo subscriptions, add-ons are configured per subscription (boolean flags on the subscription record), not selected per ride.

AO-004 P2 — Dispatchers may waive add-on charges on individual rides with a mandatory reason note.


4.11 Route Optimization & Dispatch

RO-001 P1 — Route optimization is dispatcher-triggered (semi-manual). There is no auto-dispatch at MVP.

RO-002 P1 — Dispatcher workflow: selects a time window and confirmed rides, selects available drivers, sets constraints, triggers an optimization run, reviews the proposed plan (map and list view), modifies if needed, then approves. Driver offers are only created after dispatcher approval.

RO-003 P1 — The optimization engine must respect: vehicle capacity, child seat requirements, driver availability blocks, and maximum detour percentage. It returns a proposed assignment plan which the dispatcher can modify before approving.

RO-004 P1 — Graceful degradation: if the optimization service is unavailable, dispatchers can continue with fully manual ride assignment without interruption.


4.12 Operations Dashboard

The ops dashboard is a Next.js web application, designed for desktop and tablet use (1024 px and above). It serves three roles: admin, dispatcher, and support.

OD-001 P1 — Role-based dashboard views:

OD-002 P1Reservation Board: shows all bookings grouped by status (pending review, confirmed, today, next 7 days, no driver assigned). Dispatcher and admin can approve, assign a driver, or reject bookings from this view.

OD-003 P1Live Ride Board: real-time view of all active rides (en_route, arrived, in_progress) with status, passenger name, driver name, and ETA. Dispatcher can intervene: contact driver, notify guardian, or flag as incident.

OD-004 P1Driver Schedule Board: per-driver view of assigned rides, availability blocks, open time windows, and planned breaks. Dispatcher can insert ride offers into open windows.

OD-005 P1Safety & Incidents Board: all open incidents grouped by status (open, in review, escalated). Support handles incidents and escalates when required. Escalated incidents alert the operations lead via email.

OD-006 P1Driver Management (Admin): full driver pipeline — application review, document status, per-document approve/reject, training status, shift records, suspension and activation.

OD-007 P1System Configuration (Admin): cancellation windows, PIN rules, ElaAbo plan prices and quota, overage rates, add-on rates, notification templates, grace period settings.

OD-008 P1Booking on behalf of user: any ops user can create a booking in the dashboard on behalf of a guardian, to handle phone and WhatsApp channel requests.

OD-009 P2KPI Analytics (Admin): on-time rate, cancellation rate, incident rate, driver reliability score, re-booking rate, revenue by plan type. Queries run against a read replica. [PHASE 2 — basic stats only at MVP]


5. Non-Functional Requirements

5.1 Security & Privacy

NFR-SEC-001 P1 — Row Level Security enforced at the database layer on sensitive tables. Application-layer guards are supplementary.

NFR-SEC-002 P1 — All data in transit: TLS 1.3 minimum. Pickup PINs stored as bcrypt hashes (cost factor ≥12). Never in plaintext.

NFR-SEC-003 P1 — Dependent PII (date of birth, special needs notes, saved places) encrypted at field level in the database. Führungszeugnis documents stored in a private encrypted bucket with admin-only access.

NFR-SEC-004 P1 — No raw card data stored or logged. All payment handling delegated to Stripe.

NFR-SEC-005 P1 — JWT access tokens: 15-minute lifetime. Refresh tokens: 7-day TTL, rotated on use, revocable server-side via Redis.

NFR-SEC-006 P1 — All secrets (database credentials, API keys, JWT signing key) managed via environment secret management. Never committed to version control.

NFR-SEC-007 P1 — Driver-facing data views expose only the minimum necessary data (see Section 3.3). No dependent date of birth, full address, guardian PII, or payment data ever reaches the driver.

NFR-SEC-008 P2 — Penetration test before public launch. Particularly important given handling of minor data and GDPR obligations.

5.2 Performance & Scalability

NFR-PERF-001 P1 — Live location update latency (driver transmit to guardian map update): ≤2 seconds P95.

NFR-PERF-002 P1 — Core API responses (booking creation, status update): ≤500 ms P95 under normal load.

NFR-PERF-003 P1 — Push notifications delivered within 5 seconds of the triggering event (P90).

NFR-PERF-004 P1 — High-frequency GPS writes are cached first for fast reads and persisted asynchronously, preventing write saturation on the primary database.

NFR-PERF-005 P1 — Analytics and KPI queries run against a read replica, never the primary database.

NFR-PERF-006 P2 — Architecture shall support scaling from the Berlin pilot (approximately 50 concurrent rides) to 500+ concurrent rides without structural changes to the backend.

5.3 Availability & Reliability

NFR-AVL-001 P1 — Core ride operations (booking, live tracking, SOS): target 99.9% uptime.

NFR-AVL-002 P1 — If route optimization is unavailable, manual dispatch continues uninterrupted.

NFR-AVL-003 P1 — SOS notification paths have no single point of failure. Push and SMS are sent in parallel; failure of one does not prevent the other.

NFR-AVL-004 P1 — Background job failures retry with exponential backoff (max 5 attempts). Permanently failed jobs alert on-call via dead-letter queue.

NFR-AVL-005 P1 — Database: daily automated backups with point-in-time recovery. RPO ≤1 hour; RTO ≤4 hours.

5.4 Accessibility & Usability

NFR-ACC-001 P1 — Rider and guardian interfaces shall meet WCAG 2.1 AA standards.

NFR-ACC-002 P1 — Mobile apps support iOS 16+ and Android 9+.

NFR-ACC-003 P1 — German and English at launch. Internationalisation architecture ready for additional locales without code changes.

NFR-ACC-004 P1 — All API endpoints documented via OpenAPI 3.0.

5.5 Legal & Regulatory Compliance

NFR-COM-001 P1 — GDPR: privacy by design, explicit guardian consent for minor data, configurable data retention, right to erasure for non-mandatory records, data portability export. Data Processing Agreements required with all sub-processors before launch.

NFR-COM-002 P1 — PBefG §49: all ride operations comply with Mietwagenunternehmen rules. Single-family rides only at MVP.

NFR-COM-003 P1 — Minor data handling reviewed with legal counsel before launch (Jugendschutz).

NFR-COM-004 P1 — HGB §257: billing and accounting records retained for minimum 10 years.

NFR-COM-005 P1 — ArbZG: driver shift engine enforces mandatory break requirements and the 10-hour shift limit.

5.6 Data Retention Policy

Data type Retention Action at expiry
Live GPS (active ride) Real-time only Purged on ride end
Detailed GPS route traces 90 days Anonymised
Aggregated route data 12 months Location detail removed
Trip metadata and billing records 10 years Archive; delete after (HGB §257)
Incident reports (general) 3–5 years Configurable
Incident reports (accident/insurance) Up to 10 years Manual admin confirmation required
Driving licence verification Contract + 3 years Admin must confirm deletion
Criminal record (Führungszeugnis) Contract + 3 years Admin must confirm deletion
Training records Contract + 5 years Admin must confirm deletion
Dependent profiles Until guardian deletion or erasure request GDPR erasure applies

Automated retention jobs run as weekly scheduled background tasks. All executions are logged to a separate audit table.


6. System Architecture

6.1 Application Surfaces & Platform Decisions

Surface Framework Rationale
Rider mobile app Expo (React Native) — iOS and Android Cross-platform, background GPS via expo-location, push notifications, app store distribution
Rider web app Next.js 14 (App Router) Proper desktop-first web experience; sharing shadcn/ui and TailwindCSS with the ops dashboard; booking and schedule management
Driver mobile app Expo (React Native) — separate app from rider Persistent background GPS, different OS permissions, different UX, independent release cadence
Ops dashboard Next.js 14 (App Router) Complex desktop UI: drag-and-drop boards, dense data tables, multi-panel layouts
Backend API NestJS (TypeScript) Modular monolith, strong DI, built-in WebSocket, OpenAPI generation

Why two separate Expo apps (rider and driver):
Industry standard — Uber, Bolt, Lyft, and Grab all maintain separate apps. The driver app requires always-on background GPS (expo-task-manager), a different OS permission model, a completely different navigation structure, and an independent release cadence. Combining into one app with role switching increases the security surface area, bundle size, and operational complexity.

Why Next.js for the rider web app and not Expo Router web:
The user's primary web context is a guardian booking from a laptop or desktop browser. The desktop web experience requires a fundamentally different UI than a mobile screen: multi-column layouts, sidebar navigation, proper web form patterns, hover states, and keyboard interactions. Expo Router's web output is built around React Native components rendered via react-native-web, which produces a mobile-app-like experience that does not translate naturally to desktop — it accumulates Platform.OS === 'web' hacks as the UI grows. Next.js 14 is already in the stack for the ops dashboard; the team knows it, the design system (shadcn/ui, TailwindCSS) is shared, and types and API clients are shared via monorepo packages. There is no meaningful code saving from using Expo web for this surface, and the UX compromise is significant. Next.js is the correct choice.

Why Next.js for the ops dashboard and not Expo:
The ops dashboard is a complex desktop management tool with drag-and-drop kanban boards, dense data tables, multi-panel layouts, and complex filter UIs. This use case is far outside what React Native for Web is designed for.

Why modular monolith over microservices for NestJS:
At MVP scale (~50 concurrent rides in Berlin), microservices add network latency, distributed tracing overhead, multiple deployment pipelines, and significant operational complexity with no meaningful benefit. NestJS module boundaries (RideModule, AuthModule, TrackingModule, SafetyModule, PaymentModule, NotificationModule, DriverModule, DispatchModule) provide clean separation that can be extracted into independent services when load actually demands it.

6.2 High-Level Architecture

┌────────────────────────────────────────────────────────────────────┐
│ CLIENT LAYER                                                       │
│                                                                    │
│  ┌──────────────┐  ┌──────────────┐  ┌────────────┐  ┌─────────┐  │
│  │ Rider        │  │ Rider        │  │ Driver     │  │ Ops     │  │
│  │ Mobile App   │  │ Web App      │  │ Mobile App │  │ Dash-   │  │
│  │ Expo         │  │ Next.js 14   │  │ Expo       │  │ board   │  │
│  │ iOS/Android  │  │ Desktop/Web  │  │ iOS/Android│  │ Next.js │  │
│  └──────┬───────┘  └──────┬───────┘  └─────┬──────┘  └────┬────┘  │
└─────────┼────────────────┼────────────────┼────────────────┼───────┘
          │       HTTPS + WSS (TLS 1.3)     │                │
          └────────────────┬────────────────┘────────────────┘
                           │
┌──────────────────────────▼──────────────────────────────────────┐
│  BACKEND — NestJS (Modular Monolith)                            │
│                                                                  │
│  REST Endpoints                  WebSocket Gateway (Socket.io)   │
│  /auth  /rides  /drivers         Ride tracking rooms             │
│  /dispatch  /safety              Ops live board                  │
│  /payments  /docs                Safety alert room               │
│                                                                  │
│  Middleware: JWT → Role guard (Layer 1) →                        │
│             CASL policy check (Layer 2) → Zod validation         │
│                                                                  │
│  Background Workers (BullMQ):                                    │
│  GPS anomaly · billing webhooks · data retention                 │
│  driver eligibility · document renewal reminders                 │
└───────┬──────────────────────────────────┬──────────────────────┘
        │                                  │
┌───────▼─────────────┐        ┌───────────▼────────┐
│  PostgreSQL 16      │        │  Redis (Upstash)    │
│  + PostGIS          │        │                     │
│  (Neon serverless)  │        │  GPS position cache │
│                     │        │  JWT token store    │
│  Primary: writes    │        │  BullMQ queues      │
│  Read replica: KPIs │        │  Rate limiting      │
└─────────────────────┘        └────────────────────┘

6.3 Key Data Flows

GPS tracking:

Driver app → POST /rides/:id/location (adaptive interval)
  → JWT + assignment validation
  → Redis SET ride:{id}:loc (latest, fast read for dashboard)
  → Async insert to ride_live_locations (anomaly detection)
  → Socket.io emit to room ride:{id}
      (guardian and dependent subscribers receive real-time map update)

SOS:

Guardian or dependent taps SOS
  → POST /safety/sos → validate active ride membership
  → Create incident (severity=5, type=sos)
  → Log ride event (sos_triggered)
  → Parallel:
      Socket.io → ops safety room (acknowledgement required)
      Expo push → guardian (HIGH PRIORITY)
      SMS provider → all Trusted Circle contacts
  → App shows 112 call button (one tap, native dialler)
  → Timer: if unacknowledged within T minutes → SMS + email to ops lead

Booking → Confirmed → Dispatch:

Guardian books ElaRide+
  → POST /bookings → validation → ride status: pending_review
  → Stripe PaymentIntent authorised
  → Ops dashboard: new reservation for review

Ops approves
  → ride status: confirmed
  → Push to guardian: "Booking confirmed"

Dispatcher assigns
  → Route optimization (Vroom) or manual selection
  → Ride assignment offer created → ride status: assigned
  → Push to driver: ride offer

Driver accepts
  → ride status: accepted → push to guardian and dependent
  → en_route → arrived → in_progress → completed
  → Each transition: ride event logged, notifications sent
  → On completion: Stripe charge captured, receipt email via Brevo

7. Technology Stack

Layer Choice Rationale
Rider mobile app Expo SDK 51+ (React Native) Cross-platform iOS/Android. expo-location for GPS, expo-task-manager for background, expo-notifications for push.
Rider web app Next.js 14 (App Router, TypeScript) Proper desktop-first web UI. Shares TailwindCSS, shadcn/ui, and design system with ops dashboard. Types and API client shared via monorepo packages.
Driver app Expo SDK 51+ — separate app Persistent background GPS via expo-task-manager. Separate app store listing, permissions, and UX.
Ops dashboard Next.js 14 (App Router, TypeScript) Complex desktop UI: full web capabilities, TailwindCSS + shadcn/ui.
Backend NestJS 10 (TypeScript) Modular, strong DI, built-in WebSocket, Passport guards, auto-generated OpenAPI.
Authorization CASL (@casl/ability) Resource-level attribute-based authorization in NestJS service layer. Handles ownership and family scoping conditions. Paired with custom @Roles() guard for route-level checks. No database permission tables — permissions are fixed business rules coded in the application.
ORM Prisma Type-safe queries, excellent migration tooling, auto-generated types shared via monorepo.
Database PostgreSQL 16 + PostGIS (Neon) Industry standard for ride-hailing (used by Uber, Lyft, Grab). PostGIS for geo queries. Neon: serverless, scales-to-zero for MVP cost, PITR, per-environment branching, migrates to AWS RDS as volume grows with no code changes.
Cache & Queue Redis via Upstash Serverless Redis. GPS position cache, JWT revocation store, BullMQ backing.
Job queue BullMQ Redis-backed, reliable retries, dead-letter queues, cron scheduling.
Real-time Socket.io (@nestjs/platform-socket.io) Room-based broadcasting per ride, handles reconnection, compatible with both Expo and Next.js clients.
File storage Cloudflare R2 S3-compatible API, zero egress fees (significant saving vs S3), private buckets with signed URLs for driver documents.
Maps Google Maps Platform Maps SDK for React Native (mobile apps), Maps JavaScript API (Next.js web apps). Directions API, Geocoding API, Distance Matrix API.
Route optimization Vroom (self-hosted Docker) Open-source VRPTW solver, REST API, handles fleet routing constraints. Self-hosted alongside the API.
Payments Stripe PaymentIntents (ElaRide+), Subscriptions (ElaAbo), Payment Element (Apple/Google Pay), webhooks.
Push notifications Expo Push Notification Service Free, wraps FCM (Android) and APNs (iOS), delivery receipts API.
SMS To be selected — shortlist: seven.io (German company, GDPR-native), Plivo (30–40% cheaper than Twilio), GatewayAPI (Danish, EU-hosted) All provide GDPR-compliant EU coverage. Twilio excluded due to US jurisdiction and GDPR complexity for Germany. Final selection after pricing benchmarks against Germany rates.
Email Brevo French/EU company, EU data hosting by default, GDPR and ISO 27001 certified. 300 emails/day free (sufficient for Berlin MVP). German and multilingual template support. Stripe's automatic email delivery is disabled; all ElaRide emails go through Brevo.
Authentication Custom (Passport.js + JWT + TOTP via otplib) No external auth vendor. JWT (access 15 min, refresh 7 days rotating), Redis token store with revocation. TOTP for ops MFA.
Monorepo Turborepo Single repo: apps/rider-mobile, apps/rider-web, apps/driver, apps/admin, packages/api-client, packages/shared-types, packages/ui. Shared types prevent API/client drift; shared UI primitives between the two Next.js apps.
Error monitoring Sentry SDK for NestJS, Next.js, and Expo. Free tier covers MVP.
CI/CD GitHub Actions Lint, type-check, unit and integration tests on every PR. Staging on develop merge, production on main merge.

8. External Integrations

Service Purpose
Stripe Subscriptions (ElaAbo), PaymentIntents (ElaRide+), Payment Element (Apple/Google Pay), webhook events
SMS provider (TBD) Phone OTP at registration; SOS alert SMS to Trusted Circle; re-dispatch SMS to guardian
Expo Push Notification Service Mobile push notifications (wraps FCM and APNs)
Google Maps Platform Maps SDK (React Native mobile), Maps JS API (Next.js web), Directions, Geocoding, Distance Matrix
Brevo All transactional email — receipts, invoices, onboarding, incident alerts, password reset, document status
Vroom Route optimisation (VRPTW), self-hosted
Cloudflare R2 Driver document storage (private bucket, signed URLs)
Bundesamt für Justiz Führungszeugnis: manual document workflow only. No API. Driver obtains and uploads scan.
Sentry Error tracking and performance monitoring
WhatsApp (click-to-chat) wa.me deep link; no automation. Ops manually handles messages.

9. Core Data Model (Entities Only)

This section names key entities and their relationships. Field-level design (column names, types, indices) belongs in the System Design Document.

User
  One-to-one with: Guardian | Dependent (12+) | Driver
  Has: role (enum), account_type (profile metadata)

Guardian
  Has many: Dependents (max 3)
  Has many: TrustedCircle entries (per dependent)
  Has one: Stripe customer
  Has many: Subscriptions
  Has many: Payments

Dependent
  Belongs to: Guardian
  Has: optional login (User)
  Has many: SavedPlaces (encrypted addresses)
  Has many: AuthorizedHandoverPersons

Driver
  Has one: User
  Has many: DriverDocuments
  Has many: AvailabilityBlocks
  Has many: DriverShifts

Ride
  Belongs to: Guardian (billing)
  Belongs to: Dependent or self (passenger)
  Has many: RideStops (pickup, dropoff, waypoints)
  Has one: RideAssignment (current)
  Has one: RidePricing (immutable after creation)
  Has many: RideEvents (immutable audit log)
  Has many: RideLiveLocations (purged post-ride)
  Has many: AddonUsages
  Has zero-or-one: Incident

RideAssignment
  Belongs to: Ride, Driver
  Tracks: offer status, acceptance, expiry

RideLiveLocation
  Belongs to: Ride, Driver
  Purged when ride reaches a terminal state

RideEvent (immutable)
  Belongs to: Ride
  Records: event type, actor, actor role, timestamp, metadata

Incident
  Belongs to: Ride
  Has many: IncidentEvents (status transitions)

Subscription
  Belongs to: Guardian, Dependent
  Links to: Stripe subscription
  Generates: Rides on recurring schedule

Payment
  Belongs to: Guardian
  Links to: Ride or Subscription
  Links to: Stripe payment intent or invoice

RoutePlan
  Created by: Dispatcher
  Has many: RoutePlanItems (driver-ride assignments, proposed)
  Status: proposed → approved | discarded

10. MVP vs Phase 2 Scope

MVP (Berlin Launch)

Everything in Section 4 marked P1 or P2, excluding items explicitly marked [PHASE 2].

MVP includes: booking (ElaAbo + ElaRide+), ride lifecycle and state machine, live GPS tracking, Guardian Mode, SOS, pickup PIN and forgotten PIN flow, route deviation detection (all levels), Safe Handover, add-ons (Wait+, Begleitung+, Return+), driver onboarding and document management, shift management, German labour law compliance enforcement, ops dashboard (Reservation Board, Live Ride Board, Driver Schedule Board, Safety & Incidents Board, Driver Management, System Configuration), all notification channels, and payments.

Phase 2 (Post-Launch)

Feature Reason for deferral
Multi-family seat pooling PBefG compliance review required; complex billing split
WhatsApp Business API automation Volume must justify the cost
B2B partner portal Separate product surface; not needed for consumer launch
Driver benefits management Internal HR feature; can be tracked externally at MVP
Periodic passenger check-in prompts Complex UX; GPS and SOS cover the MVP safety promise
Trusted Circle live tracking access Additional feature; guardian tracking covers MVP
Demand signal (last-minute availability) Only needed when last-minute rides are offered
Full KPI analytics dashboard Basic ops statistics sufficient at MVP
Same-driver preference for ElaAbo Preference feature; dispatch works without it
Goodwill credit ledger Can be handled manually at MVP
Advanced fatigue scoring Basic ArbZG enforcement covers compliance
Psychological follow-up flag Support team can track externally at MVP
In-vehicle dashcam or audio recording Requires GDPR consent flow design first
Face ID and biometric login Later UX enhancement

11. Open Items

ID Item Status Notes
OI-001 ElaAbo per-plan monthly prices Open ⚠️ Use placeholder prices for development; confirm before billing module goes live
OI-002 Wait+ per-15-min-block rate Open ⚠️ Same as above
OI-003 ElaAbo overage rate per leg Open ⚠️ Same as above
OI-004 Begleitung+ extra-time rate Open ⚠️ Same as above
OI-005 Languages beyond German/English Resolved German and English only at launch
OI-006 Payment failure grace period Resolved 48 hours default; configurable
OI-007 Ops support hours Resolved Normal German business hours
OI-008 Jugendschutz and GDPR consent flows for minor data Open ⚠️ Legal review required before launch
OI-009 Data Processing Agreements with sub-processors Open ⚠️ Must be completed before launch: Stripe, SMS provider, Brevo, Google, Cloudflare
OI-010 SMS provider final selection Open ⚠️ Benchmark seven.io, Plivo, and GatewayAPI on Germany pricing and GDPR terms; select before notification module is built
OI-011 Exact German copy for all safety notifications Open ⚠️ Required for notification templates before go-live

End of document — ElaRide SRS v1.2