Eight entities for the MVP. The river structure (Stamp, Section, Place)
is the spine. Operator is first-class because it is where revenue happens. Trip
lifecycle is flagged for deeper modeling in the next pass.
Core
User
id, email, name, locale
profile, photo
skill_level (default)
last_gps, last_gps_at
rank + badges derived from history
Core · needs deeper work
Trip
plan (JSONB): route, camps, gear
operator_id, rental_details
status: draft → paid → active → done
snapshots: weather, water level
lifecycle phases = next conversation
System
Payment
trip_id, amount, currency
provider: stripe
provider_ref, status
Stripe only — no HU providers
Revenue · NEW first-class
Operator
id, name, village, contact
fleet: canoe count, types, prices
pickup_kms[], seasonal_availability
onboarding_status, payout_info
partner · not a Place sub-type
Gamification
Checkin
user_id, stamp_id, trip_id?
gps_lat, gps_lon, accuracy
happened_at, photo, note
~1 km geolocation radius
River Structure
Section
name, slug, km_start, km_end
stamp_start_id, stamp_end_id
character, difficulty, typical_days
hero_photo, description, highlights
Kéktúra model: stretches carry content
River Structure
Stamp
name, slug, river_km
lat, lon
stamp_artwork, badge_meta
village_ref, hero_photo
~42 placeholder · flexible
River Structure
Place (POI)
type, name, river_km
lat, lon
extras (JSONB): type-specific
source, verified_at, active
shop, well, beach, sight, portage, ...
Changes in revision 2
Stamp is now a first-class entity — each Section starts at a Stamp; the next Stamp ends it. Each Stamp carries its own stamp artwork and badge metadata. ~42 is a placeholder; real count and placement still open.
Operator is now first-class — own entity, own onboarding, own payout info. Revenue flows through here; it cannot be a sub-type of Place.
Place no longer stores section_id — section membership is computed on the fly from river_km falling inside a section's km_start / km_end range. Keeps POIs decoupled from the section boundaries we happen to choose today.
Dropped Report for MVP — come back post-launch.
Payment = Stripe only, explicit. No Hungarian providers.
Self-sufficient mode dropped from User prefs; it is a per-trip choice.
Next conversation — Trip lifecycle
Trip is the contentious one. A trip at the time of planning, while you are on it, and when it is finished are three
different shapes with three different information needs. Three options to explore:
(1) one fat Trip with phase fields — simplest, everything in one row, JSONB for per-phase data;
(2) Trip + TripEvent log — event-sourcing style, the plan is stable and a clean append-only log
captures what actually happened (check-ins, GPS pings, reports, photos);
(3) TripPlan + TripRecord split — two joined entities, explicit before/after. Tradeoffs for each get their own round.
Open questions for the call
Does the Operator entity cover the rental partner fully, or do we need a separate "rental transaction" entity once a Trip books one?
Do Operators log in and manage their own Places (rental kiosks, dropoffs) or is that curated centrally?
How does the trip planner split revenue between platform fee and operator payout? Single Payment or split?
Is there anything in the Kéktúra passport book experience we are not capturing yet?