Architecture Review Document

OpenRally Architecture

Technical architecture for the OpenRally Protocol reference implementation in Setpoint. Covers system design, database schema, API routing, SSE push, Google Reserve integration, caching, security, and deployment.

Shippedv1.0Updated 2026-03-06

Technical architecture for the OpenRally Protocol reference implementation in Setpoint. Covers system design, database schema, API routing, SSE push, Google Reserve integration, caching, security, and deployment.

System Components

ComponentResponsibilityTechnology
API GatewayAuth, rate limiting, request validation, response cachingNext.js API routes
Discovery ServiceFacility and resource lookup, sport registryREST + DB queries
Availability ServiceSlot generation, availability queries, conflict detectionREST + DB + cache
Booking EngineHold management, booking CRUD, state machine enforcementREST + DB transactions
ID Registryor:// URI generation, alias mapping, provider registrationDB-backed registry
Event BusCapture state changes, fan out to SSE connectionsSupabase Realtime → SSE
Google Feed GeneratorExport Merchant/Service/Availability feeds for Actions CenterCron + feed formatter

Database Schema (11 tables)

or_providersnewRegistered namespace owners (e.g. 'setpoint', 'playtomic')
id (UUID PK)namespace (TEXT UNIQUE)nameurlapi_base_urlauth_methods (JSONB)capabilities (JSONB)is_activecreated_at
or_sportsnewCanonical sport registry with extensible IDs
id (UUID PK)canonical_id (TEXT UNIQUE)namecategoryis_core (BOOL)provider_id (FK)created_at
or_facilities (view)modifyMaps to existing facilities table + or_uri column
idprovider_idor_uri (TEXT UNIQUE)nameaddress_*geo_lat/lngtimezoneoperating_hours (JSONB)is_active
or_resources (view)modifyMaps to existing courts table + or_uri column
idfacility_id (FK)or_uri (TEXT UNIQUE)nameresource_typesurfaceis_indooris_active
or_resource_sportsnewJunction table: which sports a resource (court) supports
resource_id (FK)sport_id (FK)is_primary (BOOL)
or_slotsnewTime-based availability units — computed from schedules minus bookings
id (UUID PK)resource_id (FK)or_uri (TEXT UNIQUE)sport_id (FK)start_timeend_timeduration_minstatus (available|held|booked|blocked)price_amountcurrency
or_holdsnewTime-limited leases on slots with TTL enforcement
id (UUID PK)slot_id (FK)or_uri (TEXT UNIQUE)consumer_idconsumer_typestatus (active|confirmed|released|expired)idempotency_key (UNIQUE)expires_atcreated_at
or_bookings (view)modifyMaps to existing bookings table + or_uri column
idslot_id (FK)hold_id (FK)or_uri (TEXT UNIQUE)status (confirmed|completed|cancelled)customer_name/email/phoneplayers (JSONB)source_channelidempotency_keycreated_at
or_aliasesnewCross-platform identity mapping (same facility on multiple providers)
id (UUID PK)entity_typeprimary_urialias_uriprovider_id (FK)
or_eventsnewAppend-only event log — powers SSE replay, Google push, and cache invalidation
id (BIGSERIAL PK)event_typefacility_id (FK)resource_id (FK)entity_idpayload (JSONB)is_durable (BOOL)created_at
or_google_feed_exportsnewTracks Google feed export history and status
id (UUID PK)feed_type (merchant|service|availability)facility_id (FK)exported_atrecord_countstatuserror_message

Setpoint Database Mapping

Adapter pattern — OpenRally endpoints query existing Setpoint tables and transform results. No data duplication.

OpenRallySetpoint TableStrategy
or_facilitiesfacilitiesAdd or_uri column — 1:1 mapping
or_resourcescourtsAdd or_uri column — 1:1 mapping
or_slotscourt_schedules + bookingsComputed — generate from schedules, subtract bookings
or_holdsbookings (status='held')Map held bookings → OpenRally holds
or_bookingsbookings (status='confirmed')Map confirmed bookings → OpenRally bookings
or_sportsNew lookup tableSeed with 8 core sports
or_providersNew tableSeed with 'setpoint' as first provider
or_eventsNew tableDB triggers on bookings/slots write events

API Routes (20 total)

PathMethodsDescription
/api/openrally/v1/GETProtocol metadata + capabilities
/api/openrally/v1/facilitiesGETList facilities
/api/openrally/v1/facilities/[id]GETFacility details
/api/openrally/v1/facilities/[id]/resourcesGETList courts/resources
/api/openrally/v1/facilities/[id]/resources/[id]GETResource details
/api/openrally/v1/facilities/[id]/availabilityGETQuery available slots
/api/openrally/v1/facilities/[id]/pricingGETGet pricing for a specific slot
/api/openrally/v1/sportsGETList sports
/api/openrally/v1/holdsPOSTCreate hold
/api/openrally/v1/holds/[id]GET, DELETEGet/release hold
/api/openrally/v1/bookingsGET, POSTList/create bookings
/api/openrally/v1/bookings/[id]GET, PATCH, DELETEGet/modify/cancel booking
/api/openrally/v1/streamGETSSE event stream
/api/google/v3/HealthCheckGETGoogle health check
/api/google/v3/CheckAvailabilityPOSTGoogle availability check
/api/google/v3/BatchAvailabilityLookupPOSTGoogle batch availability
/api/google/v3/CreateBookingPOSTGoogle create booking
/api/google/v3/UpdateBookingPOSTGoogle update booking
/api/google/v3/GetBookingStatusPOSTGoogle booking status
/api/google/v3/ListBookingsPOSTGoogle list bookings

Amber rows = Google Booking Server (adapter over OpenRally endpoints)

Google Reserve Integration

OpenRallyGoogle ConceptMapping
or_facilitiesMerchant Feedor_uri → merchant_id, geo/address/name direct
Sport booking serviceService FeedOne service per sport per facility
or_slots (available)Availability Feedstart_time → start_sec (epoch), duration → duration_sec, court → room_id
or_bookingsBooking Statusconfirmed → CONFIRMED, cancelled → CANCELED
POST /holds + /bookingsCreateBookingGoogle calls → we create hold + confirm atomically
PATCH /bookings/{id}UpdateBookingGoogle calls → we modify booking
GET /bookings/{id}GetBookingStatusGoogle calls → we return booking status

SSE Push Architecture

DB Trigger

INSERT/UPDATE on bookings/slots

or_events

Append-only event log

Supabase Realtime

Listens to inserts

SSE Adapter

Formats + filters + sends

Event Bus: DB trigger → or_events table → Supabase Realtime → SSE stream to clients

Caching Strategy

EndpointCache-ControlRationale
GET /max-age=3600Metadata rarely changes
GET /facilitiesmax-age=3600, stale-while-revalidate=600Facility list changes rarely
GET /availabilitymax-age=30, stale-while-revalidate=60Critical freshness — 30s max
GET /sportsmax-age=86400Effectively static
GET /holds/{id}no-storeTime-critical, never cache
GET /bookings/*no-cacheMust revalidate every time
GET /streamno-cacheSSE — never cache

Security Model

Layer 1

API Key (X-API-Key)

All requests. Format: ork_live_{32-char}. SHA-256 hashed in DB.

Layer 2

OAuth 2.0 (optional)

Channel managers. Client Credentials grant for elevated access.

Layer 3

Google Basic Auth

Google Booking Server endpoints only. Credentials rotated every 6 months.

Migration Path (6 weeks)

1

Schema

Week 1
  • Create new tables: or_providers, or_sports, or_events, or_aliases, or_google_feed_exports
  • Add or_uri columns to facilities and courts
  • Backfill or_uri values for existing records
  • Create triggers for auto-generation of or_uri on insert
  • Create event emission triggers on bookings/slots
  • Seed or_sports with 8 core sports, or_providers with 'setpoint'
2

Core Endpoints

Week 2-3
  • Protocol metadata endpoint (GET /)
  • Discovery: facilities, resources, sports
  • Availability: query with date/sport/time/duration filters
  • Holds: create, get, release with advisory locks
  • Bookings: create (from hold + direct), get, modify, cancel, list
  • Auth layer, rate limiting, error formatting
3

Push + Google

Week 4-5
  • SSE stream endpoint with Supabase Realtime adapter
  • Event replay via or_events + Last-Event-ID
  • Heartbeat (30s) and reconnection handling
  • Google Merchant/Service/Availability feed generators (daily cron)
  • Google Booking Server (7 adapter endpoints)
  • Maps Booking API client for real-time push to Google
4

Test + Certify

Week 6
  • Compliance test suite against Setpoint
  • E2E tests for OpenRally + Google endpoints
  • Performance benchmarks (availability < 200ms p50)
  • Self-certification at OpenRally Full level
  • OpenAPI 3.1 spec + documentation

Open Questions

Should slots be materialized or computed on-the-fly?

Options: Materialized (faster) vs. Computed (always fresh)Impact: Performance vs. freshness

SSE stream: Vercel or Supabase Edge?

Options: Vercel streaming (simpler, 300s limit) vs. Edge Function (unlimited)Impact: SSE reliability

Slot generation for facilities without schedules?

Options: Require schedules vs. default 7am-11pm with 1hr slotsImpact: Onboarding friction

Google Actions Center onboarding timing?

Options: Start now (12-16 weeks) vs. after protocol v1.0Impact: Time to market