Architecture, integration contracts and security model – for anyone evaluating or building against the suite.
Overview
Two schematics before the prose: which services the suite actually consists of, and where the data comes from. Every box traces back to a real file in the relevant repository.
The suite as services
Nine modules, five deployed services. The boxes are services; the names inside them are the modules they deliver.
Hosting and data sources
What runs where, and which sources the app actually asks. Dashed = premium mode only, with your own key.
Architecture
Waybound Studio is a suite of ten parts, not a single monolithic system. That's a deliberate architectural choice, not inherited history: each module owns its own responsibility, its own data where possible, and talks to its neighbours through stated contracts rather than shared databases.
The suite as repositories
WB core (Travel Guide, Route Planner, On The Road, Quests, Happy Trails) is a FastAPI/Python backend with a vanilla JS/CSS frontend, Supabase as its database, deployed on Render. It's the suite's only stateful backend – identity, saved trips and the quest catalogue live here.
Open Places is its own service: FastAPI + PostgreSQL/PostGIS, a traceable open places database built from OpenStreetMap extracts. It only ever answers where, never when – route logic and timing don't belong there.
Explorer and Car Bingo are static sites with no backend of their own. They call Open Places directly from the browser (Explorer) or run fully offline against a shared seed (Car Bingo, with a thin Supabase service used only for sharing custom boards).
Android Auto is a native Android app that reads an already-planned trip directly from WB core's Supabase data. It never plans a route itself and never draws its own map.
Why the split looks like this
The boundary between Route Planner and Open Places is deliberate and defended: Route Planner owns the journey and knows when to stop, Open Places owns the places and knows what's there. Any attempt to move route intelligence down into the places layer ends with two systems disagreeing about the truth.
Modules as contracts
Every internal module in WB core declares its own contract: the exported surface, what each call promises, and what a caller therefore never needs to check itself. It isn't a document that can drift from the code – the contract is a test, not a comment.
The same principle holds between repositories: the interface between WB core and Open Places, and between WB core and the Android Auto app, is written and tested contracts owned by whichever side has the most to lose from breaking them. An Android Auto app already sitting in a car can't be updated the same afternoon a bug is found – so WB core owns that contract, tests against it, and changes it before the app is ever touched.
Operating model
Every service runs on Render. Static sites (Explorer, Car Bingo, Open Places) deploy straight from `main` with no build step; WB core runs a full test suite on every change.
One deliberate exception to "always over an API": the Android Auto app reads WB core's Supabase data directly – the same traveller account, the same saved trips – rather than going through a separate API. That's a tradeoff, not a shortcut: the boundary is enforced by Supabase Row Level Security (each traveller only ever sees their own rows) and by the written contract between the two repositories, not by a network call. Open Places, by contrast, is fully standalone and is always reached through its public HTTP API, never by reading its database directly.
Contracts and integration
For anyone evaluating the suite as a developer: here's what the interfaces actually look like between the parts that talk to each other across a repository boundary.
Open Places — an open HTTP API
A public, read-only HTTP API with no authentication, built to be callable directly from a browser. Core surfaces:
Place listing and radius search, with category and facility filters
Clustered counts for wide map views
Route corridor search: send a route line, get places within a given distance of it, sorted and with distance to both the corridor and an optional anchor point
Category, source and stats listings for anyone building their own view on top of the catalogue
Facilities are deliberately three-state – present, unknown, or explicitly absent – rather than a binary flag. The default mode never hides an unsurveyed place just because a facility is missing; a stricter mode exists for anyone who'd rather filter hard.
CORS is open but scoped: `GET`/`OPTIONS` only, no cookies or credentials, and an explicit allowlist of origins rather than a wildcard. It's built for a standalone static site with no server of its own – like Explorer – to call it directly.
WB core ↔ Open Places
The boundary between the route engine and the places database is a written contract, not an implicit agreement: WB core owns timing and decides *when* a stop is needed, Open Places owns geography and answers *where* something exists. Stop types (overnight, lunch, fuel …) are mapped to a set of place categories on WB core's side of the boundary.
WB core ↔ Android Auto
The car app reads a trip that's already planned – never a calculation it initiates itself. The interface is written and tested before the app is ever touched, for a simple reason: an app that's already sitting in a car can't be updated the same afternoon a bug is found. Change the contract, watch the tests that prove it fail, then — and only then — change the app.
What's deliberately not a contract
No external integration exists today for Travel Guide, Quests or Happy Trails – they're internal surfaces in WB core, reached through the same logged-in session as the rest of the app, not through a separate public API. That could change, but isn't something to build against today.
Licensing and attribution when integrating
Building something on top of Open Places: the attribution that travels with every place (OpenStreetMap, and Trafikverket where applicable) must be preserved wherever you display that data onward. See Data Sources and Licenses for exactly what terms apply to each source.
Security and guardrails
Security is architecture, not a layer added at the end. Here's how we think about it, for developers, architects or security reviewers evaluating the suite.
Core principles
Secure by default. The free tier never carries a Google key – that's a property of the request itself, not a setting anyone could forget.
Least privilege. Every traveller only ever sees and touches their own data, enforced at the database level (Row Level Security) rather than in application code alone.
Explicit authentication and authorization. Sign-in via Google, Microsoft, email, or a fully local mode with no account. Admin functions (the quest catalogue, error logs) are gated by the same explicit permission check regardless of which surface they're reached from.
Fail securely. A service that doesn't respond – Open Places during a brief outage, for example – returns an empty result rather than crashing the app or leaking an internal error.
What the platform is built to withstand
Traffic and route data isn't sensitive by itself, but the account, saved trips, and where you currently are, is. The architecture is built with the following in mind:
Cross-Site Scripting (XSS) – all dynamic text shown in the UI is escaped before it's inserted into the page.
Broken Object Level Authorization – a traveller can never read or modify another traveller's trip by guessing an ID.
JWT manipulation and unauthorized project access – the session is validated on every call, not only at sign-in.
API abuse and cost control – see the guardrails below.
Information leakage – error messages shown to the user never reveal internal system or database detail.
Clickjacking and content injection – locked down through HTTP security headers (Content Security Policy and equivalents).
Guardrails that are product decisions, not just engineering
A cost ceiling per account. Both route calculation and Travel Guide's searches carry a budget, and a long search call can be cancelled by the traveller mid-flight – verified against a real, long search, not just against the code.
The search radius can never shrink below the detour a traveller has already said they'll accept. A narrow search radius that still technically honours a stated preference would be correctness that makes the trip worse – better the extra mile to civilization.
No user-generated content in anything shared publicly. Happy Trails stories are built only from the traveller's own choices during the trip, never from free text or uploaded images. That's not convenience – it removes an entire category of moderation and privacy risk at once, since the threshold for publishing approaches zero without ever needing to review content.
Quests are honor-based by design. No geofencing, no photo required. That's a deliberate choice away from collecting more location data than necessary, not a limitation we happened to land on.
Deletion is self-service, not a process that requires someone to read an email and act on it manually.
Data handling in short
Place data in Open Places is honestly incomplete, and says so: a missing fact like a toilet usually means "not yet surveyed," never a guess presented as fact. See Data Sources and Licenses for exactly which data sources are used and under what terms.
*This is an architecture-level overview, not a complete security audit, and not a promise of the absence of future vulnerabilities. If you find something that looks wrong, reach out through the app's system menu.*
How do we handle data?
Data sources, privacy and terms — what we've actually reviewed, sourced and dated where relevant.