← Tisza Project · Shared Artifacts
Draft · 2026-04-10 · Revision 2

Data Model — Entity Map

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.

Tisza · 597 km 1 ─ N 1 ─ 1 N ─ 1 rental booking 1 ─ N optional N ─ 1 start = Stamp end = next Stamp → computed from river_km
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?