Skip to content
OxyHQPublic

About

No description, website, or topics provided.

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

GoWay

GoWay is Oxy's open map and geographic platform, built for web, iOS and Android with Expo.

It provides both the consumer map experience at https://goway.to and the reusable @goway.to/sdk used by FairCoin, Moovo, Mercaria, Homiio, Mention, Clarity and other Oxy or third-party apps.

Getting started

Bun only — never npm, yarn or pnpm, and bun.lock is committed with the package.json change that moved it.

bun install

# PostgreSQL 17 + PostGIS 3.5 on 127.0.0.1:5440, loopback only.
# Both versions are pinned: a floating PostGIS minor is a floating set of
# function volatilities, and a generated `geography` column needs ST_MakePoint
# to stay IMMUTABLE.
docker compose -f docker-compose.postgres.yml up -d --wait postgres

cp packages/backend/.env.example packages/backend/.env
bun run db:migrate --target-database=goway_dev

bun run dev            # backend + Expo together
bun run dev:backend    # backend alone, on :3000
bun run dev:frontend   # Expo alone

Port 5440 rather than 5432: an Oxy developer machine routinely has several backends' local databases up at once, and 5432–5439 and 5441 are already spoken for (5432 oxy-api, 5433 Mention, 5434 Homiio/Syra, 5435 Mercaria, 5436 CrowdSource, 5437 Moovo, 5438 Noted, 5439 Peable, 5441 Schedio). A port collision does not fail loudly once a container is running — it connects you to somebody else's database and migrates it.

CREATE EXTENSION postgis is privileged. On a brand-new database a role with rds_superuser has to run it once before GoWay's migration role can migrate; packages/backend/README.md explains why IF NOT EXISTS hides that.

Directions in development

A route is never a fixture. With fixtures on, places, search and geocoding are answered locally, but POST /routes goes to the API at EXPO_PUBLIC_API_URL exactly as it does with fixtures off (a fixture place is sent as its point, because the API has never heard of its id). So the line on the map is always an engine's, and where there is no engine the planner says "GoWay can't work out this route right now" and draws nothing.

EXPO_PUBLIC_API_URL Who routes
unset / https://api.goway.to GoWay's production Valhalla. Works from localhost signed out; signed in, the browser sends a bearer, which only the origins in production's CORS_APP_ORIGINS may, and the request is refused.
a local backend Whatever its ROUTING_VALHALLA_URL names. Unset, every route answers service_unavailable.

A local backend routes with GoWay's own engine run locally — the same tiles production serves (packages/routing-engine/README.md → "Running it locally"), then ROUTING_VALHALLA_URL=http://127.0.0.1:8002/route in packages/backend/.env. The public community instance in .env.example is a documented fallback for one developer within its fair-use policy, never more. bun run check:routing with GOWAY_API_URL=http://127.0.0.1:<port> proves a local backend routes every mode.

Checks

bun run typecheck        # bun check (TypeScript 7) on contracts, backend, its tools program and the SDK; tsc on the frontend
bun run lint             # Biome (lint + format check) everywhere, then the frontend's minimal Expo ESLint
bun run lint:fix         # apply Biome's safe fixes and formatting
bun run format           # Biome formatter only
bun run test             # bun test
bun run check:migrations # deploy-phase markers + no $1 placeholders in generated SQL
bun run check:routing    # api.goway.to returns REAL drive, walk and bike routes (Barcelona, Madrid), never a straight line
bun run check:lockfile   # bun.lock matches the manifests it describes
bun run test:gates       # proves the routing, migration, OpenAPI and JSON Schema gates can fail
bun run check:openapi    # packages/contracts/openapi.json is complete and fresh
bun run check:contracts  # every named contract schema converts to JSON Schema
bun run openapi          # regenerate openapi.json after a contract change

CI runs all of these, and the AWS deploy is a JOB of the CI workflow with needs: those jobs — not a workflow with its own push trigger, which would race CI and always win. The Cloudflare frontend deploy is a second such job (deploy-frontend), for the same reason and on the same gates.

Deploying the web app

https://goway.to is served by a Cloudflare Worker serving static assets, published by .github/workflows/deploy-frontend.yml on every push to main that passes CI. The build is an expo export --platform web of packages/frontend; the Worker is configured entirely by packages/frontend/wrangler.toml.

It runs against the live API. A production build never uses the fixture layer (fixturesOn, packages/frontend/lib/goway/mockTransport.ts), so goway.to talks to api.goway.to: real search, real geocoding, and real turn-by-turn routing from GoWay's own Valhalla (packages/routing-engine). A development build DEFAULTS fixtures on, so bun run dev:frontend works with no backend running — for everything except directions, which are never a fixture (below).

One thing is knowingly empty: GoWay Places has no rows in production, so /places/bounds answers an empty page and the GoWay-owned pin layer is blank until it is seeded. That was chosen over keeping the fixtures live, because those fixtures are real Barcelona names carrying invented facts, and fabricated places on a public map are the same defect as a fabricated route. The basemap still draws every POI it has, every name on it is tappable, search is real and directions are real.

The seed now exists and has not been run. packages/backend/src/import/osm/ imports OpenStreetMap POIs into places — 771,515 of them for Spain, measured — and packages/backend/README.md has the one-shot aws ecs run-task that applies it, plus the IAM grant oxy-goway-github-deploy still needs before .github/workflows/import-osm-pois.yml can do it instead. Switching the basemap's own poi-* layers off is the change AFTER that, not before: turning them off over an empty table is an empty map.

A Worker, not Pages — and why workers_dev = false is the point

A Cloudflare Pages project ALWAYS serves <project>.pages.dev and offers no way to turn it off (wrangler pages project has only list, create, delete). Every Oxy app that was on Pages therefore had a second, indexable copy of itself on a hostname in no CORS allowlist and no Oxy application's redirectUris — it rendered the shell and failed every call it made. GoWay is born on Workers so it never has that hostname, and workers_dev = false in wrangler.toml is the line that keeps it that way. Do not "simplify" this to wrangler pages deploy.

The trade that comes with it: Pages adds x-content-type-options: nosniff and referrer-policy: strict-origin-when-cross-origin to every response; Workers Static Assets does not. Nothing in this repository asks for them, so the difference is invisible to every build, test and deploy. They are declared explicitly in packages/frontend/public/_headers, which expo export copies into dist/ verbatim, and the deploy workflow asserts both are on the live response afterwards.

One-time human setup

Everything below is done ONCE, by a human with access to Oxy's Cloudflare account and the GitHub org. The deploy is fully automatic afterwards.

1. Actions secrets — and the shadowing trap.

The workflow reads exactly two values from secrets:

Secret Value
CLOUDFLARE_API_TOKEN The Oxy org-level token — the same one every sibling frontend (Clarity, Inbox, Mention, Allo, Noted) deploys its Worker with. It must be Workers-capable: Workers Scripts: Edit on the account, and Zone: Read plus Workers Routes: Edit on the goway.to zone, which is what lets wrangler attach the custom domain and have Cloudflare write the record.
CLOUDFLARE_ACCOUNT_ID The Oxy Cloudflare account id.

If the first deploy fails with Authentication error [code: 10000], the token is Pages-scoped rather than Workers-capable — which is the failure the shadowing note below describes.

Do NOT add these as repository secrets if the org already provides them. A repo-level Actions secret silently SHADOWS the org-level one of the same name, and nothing reports it. OxyHQ/Noted carried its own CLOUDFLARE_API_TOKEN from 2026-07-15, scoped to Pages only; when the org token was rotated to a Workers-capable one on 2026-09-05 the repo copy kept winning and Noted's deploy failed Authentication error [code: 10000] while every sibling repo deployed fine. Check the org secrets first. If a repo-level copy already exists, delete it rather than updating its value.

Optionally set the repository variable EXPO_PUBLIC_OXY_CLIENT_ID to GoWay's production Oxy client id. A variable, not a secret: the id ships inside the bundle, so it is public by construction. Unset, the map still works — only sign-in is disabled, and silently.

2. DNS, in this order: DNS → deploy. This order is not a preference.

A Worker custom domain REFUSES a hostname that already has externally managed DNS records (code: 100117). Cloudflare writes and manages the record for a custom domain itself, so the apex must be empty of address records when wrangler deploy first runs.

  1. In the goway.to zone, delete any existing A / AAAA / CNAME record at the apex. Type-scoped — a TXT/SPF/DMARC record at the same name must survive; deleting those breaks mail, not the website.
  2. If goway.to is attached to a Cloudflare Pages project, detach it there too.
  3. Run the workflow (merge to main, or workflow_dispatch on main). wrangler creates the Worker, attaches goway.to as a custom domain and writes the DNS record.

The downtime is that window — roughly 30 seconds when the build is ready first. api.goway.to is a separate record in front of the AWS ALB and is not touched by any of this.

What the deploy proves before it reports success

wrangler deploy exiting 0 means an upload was accepted, not that goway.to serves this build — and the SPA fallback makes the naive check actively misleading, because with not_found_handling = "single-page-application" every path that misses an asset answers 200 with index.html. A bare status check would pass against a deployment containing nothing but a stale shell.

So the final step asserts a size floor plus positive markers: the shell is at least 1 KB and carries the React root, the title and three or more hashed bundle references; both _headers security headers are on the live response; the entry bundle this run just built, addressed by the content hash read out of the local dist/, comes back as JavaScript rather than text/html (the one assertion that cannot pass against the previous deployment or against the fallback); and a route that exists only inside the bundle answers with the shell, proving deep links work.

Monorepo

GoWay follows the Oxy packages/* convention:

packages/
  frontend/                 Expo Router app for web, iOS and Android
  backend/                  GoWay API, Places persistence and provider adapters
  sdk/                      public @goway.to/sdk package
  contracts/                the zod contracts: every request, response, error and value set, the route registry and openapi.json
  reconstruction-worker/    Python/CUDA Street 3D worker

The frontend uses the same Oxy application foundation as the rest of the ecosystem: Expo Router, React Native/react-native-web, @oxy.so/app-preset, Bloom, Oxy Services/contracts/core and Oxy authentication. Street 3D contribution UI stays inside this same Expo app rather than becoming a separate product.

Core platform

  • Maps — MapLibre rendering over GoWay's own vector tiles: Planetiler builds one PMTiles archive from an OpenStreetMap extract, it lives in Cloudflare R2, and the Cloudflare Worker reads byte ranges out of it. No tile server, and no third-party tile origin. See packages/frontend/README.md → "The tiles".
  • Map performance — every 3D layer reads one quality profile (Low/Medium/High, chosen per device or in Settings → Performance), animates through one clock that pauses when the map is idle, and is held to frame budgets by a CI gate; ?perf=1 shows the HUD and bun run --cwd packages/frontend bench:map measures. See docs/MAP_PERFORMANCE.md.
  • Places — a first-class GoWay Places schema backed by PostgreSQL + PostGIS, with stable place IDs, source provenance, business relationships and extensible capabilities such as FairCoin acceptance.
  • Search — provider-neutral place/address search, geocoding and reverse geocoding.
  • Routing — provider-neutral directions and route geometry.
  • SDK — @goway.to/sdk exposes maps, Places, search and routing without making consumer apps depend directly on MapLibre or an external geographic provider.

Places schema ownership

The Places schema is part of GoWay v1, not deferred. Its public/domain contracts live in packages/contracts; its canonical database schema, spatial indexes and migrations live in packages/backend using PostgreSQL + PostGIS. We intentionally do not create a separate public database-schema package because SDK consumers should depend on stable GoWay contracts, not on GoWay's internal tables or migrations.

GoWay hosts the map dataset itself — a planet PMTiles build in R2, at roughly $1.50 a month because R2 charges nothing for egress. The map infrastructure is still replaceable independently from GoWay-owned Places and ecosystem data: lib/map/provider.ts is the one module that names where cartography comes from, and moving the tiles from a third party to GoWay's own storage touched no component, no screen and no SDK contract.

Street 3D

Street 3D is GoWay's community-built future street-level layer. Instead of depending on proprietary panorama coverage, users can contribute ordinary geotagged photos or videos. GoWay temporarily stores useful source media, matches overlapping views, solves camera geometry and progressively builds versioned 3D Gaussian scenes.

The design is intentionally cost-conscious:

  • raw photos/videos are temporary reconstruction inputs, not a permanent archive;
  • default source retention is measured in weeks/months, with aggressive deduplication and early raw-video deletion after useful keyframes are extracted;
  • incomplete areas can become at risk when useful temporary captures are nearing expiry, allowing the community to contribute missing viewpoints before the opportunity is lost;
  • published 3D scenes remain durable even after their raw source images are deleted;
  • compact derived metadata, manifests and published splat/LOD assets are kept instead of indefinite raw media;
  • AWS provides the control plane, S3/object storage and queueing during the bootstrap stage;
  • expensive reconstruction can run on external workers through packages/reconstruction-worker, initially an external GPU worker consuming durable AWS SQS jobs;
  • the worker can be offline without breaking GoWay and can later scale to multiple owned/cloud GPUs using the same contract;
  • privacy preprocessing is required before captures become reconstruction inputs, including face/license-plate and dynamic-object handling;
  • Street 3D is streamed by versioned scene/LOD manifests and integrated back into GoWay Places rather than embedding business metadata into 3D assets.

Street 3D is not required for the first 2D GoWay release, but it is an explicit GoWay roadmap track rather than an undefined future Street View add-on.

Roadmap issues

Mapping platform

  • #1 repository/packages/Oxy foundation
  • #2 MapLibre + OpenFreeMap rendering
  • #3 @goway.to/sdk
  • #4 GoWay Places + PostgreSQL/PostGIS
  • #5 search/geocoding
  • #6 routing
  • #7 GoWay Bloom map experience
  • #8 FairCoin merchant-discovery reference integration

Street 3D

  • #16 Street 3D epic / implementation order / cost-quality gates
  • #9 geotagged photo/video contribution pipeline
  • #10 temporary storage, retention, deduplication and cost budgets
  • #11 geospatial capture graph + automated 3D Gaussian reconstruction
  • #12 distributed GPU reconstruction worker + external GPU worker/AWS SQS
  • #13 privacy-safe capture/reconstruction pipeline
  • #14 streamed Street 3D viewer + map integration
  • #15 coverage health, expiry risk and community rescue UX

License

The Breathe License 1.0 (LicenseRef-Breathe-1.0) — the same licence as the rest of the Oxy ecosystem. The full text is in LICENSE, with NOTICE for attribution. Commercial terms: https://github.com/OxyHQ/.github/blob/main/LICENSE-COMMERCIAL.md.

Attribution under Section 3.1 is required of everyone, including paying commercial licensees, and cannot be waived.

@goway.to/sdk ships its own copies of LICENSE and NOTICE in the published tarball, because a consumer who installs the package never sees this repository. Its release smoke test asserts both are present.

Geographic data served through GoWay carries its own upstream licences and is not relicensed by this file — OpenStreetMap-derived data is ODbL.

About

No description, website, or topics provided.

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages