Skip to content

Repository files navigation

AccessCity

AccessCity is an accessibility-focused urban routing system. It combines a .NET 9 geospatial API, PostGIS-backed OSM ingestion, Redis/Kafka worker paths, route graph preprocessing, hazard/risk scoring, vector tile support, and an Expo React Native client.

The project is built for safe, reproducible route decisions: AI assistance is limited to text normalization, report summarization, and explanations. It does not generate routes or dynamically change routing edge costs.

Status

AccessCity is an active pre-production system. The local and Kubernetes deployment paths are wired for API replicas, background workers, Kafka, Redis, PostGIS/PgBouncer, observability, SLO alerts, and route graph artifact profiling, but it is not an emergency service and has not yet been proven at global production scale.

Current verified baseline:

  • Distributed k6 run: 6 API pods, 2 worker pods, 3 Postgres instances, 3 PgBouncer pods, 3 Kafka brokers, Redis; 145,334 requests, 440 req/s, 0 HTTP failures, safe-path p95 167.56 ms in the checked-in scenario.
  • City graph profile: Birmingham OSM extract, 661,852 nodes, 1,428,512 directed edges, 1,419 shards, 225.7 MB packed source shard artifacts.
  • Full backend test suite: 264 xUnit tests passing in Release mode on the current branch.

See K6 load results, route graph preprocessing, and distributed load testing for methodology and limits.

What It Does

  • Accessibility-aware routing for standard, manual wheelchair, power wheelchair, and stroller profiles.
  • OSM import into a routable pedestrian graph with accessibility tags such as surface, width, incline, kerb, and crossing metadata where available.
  • Safe-path scoring using deterministic route costs, nearby hazards, infrastructure signals, lighting/environmental inputs, and profile-specific penalties.
  • Hazard reporting, moderation-oriented admin workflows, and live hazard alert broadcasting.
  • Map overlays, POI queries, vector tile endpoints, and offline map bundle support.
  • Route graph preprocessing with packed artifacts, source shard manifests, ALT landmarks, optional contraction hierarchy support, distributed cache coalescing, and profile quality gates.

Architecture

AccessCity architecture

AccessCity is a modular monolith with independently scalable deployment roles:

  • API: authentication, HTTP endpoints, WebSocket hazard alerts, cache lookups, job submission, and fast read paths.
  • Workers: route jobs, OSM import jobs, tile warming, graph artifact warmup, and CPU-heavy background work.
  • PostGIS: source of truth for users, hazards, OSM graph data, imports, and spatial read models.
  • Redis: distributed cache for route results, risk summaries, tile data, job status, and packed graph artifacts.
  • Kafka: route/import job handoff and multi-replica worker coordination.
  • Kubernetes: KEDA-driven API/worker scaling, pod disruption budgets, topology spread, probes, and separate migration jobs.

Module boundaries are documented in Modular Architecture. Scaling guardrails are documented in SLO and Scaling.

Prerequisites

  • .NET 9 SDK
  • Node.js 20 LTS
  • Docker Desktop or Docker Engine with Compose
  • Optional: kubectl, kustomize, and a Kubernetes cluster for distributed validation

Quick Start

Run the API, PostGIS, Redis, Kafka, and the routing/import worker:

docker compose --profile worker up -d --build

The API is exposed through the local gateway at:

http://localhost:8080

Useful local endpoints:

  • GET /health
  • GET /health/ready
  • Development OpenAPI: http://localhost:8080/openapi/v1.json
  • Development Scalar UI: http://localhost:8080/scalar/v1

Stop the stack:

docker compose --profile worker down

Run the API on the Host

Keep only the backing services in Docker, then run the API locally:

docker compose up -d db redis kafka

cd AccessCity.API
export ASPNETCORE_ENVIRONMENT=Development
export ConnectionStrings__DefaultConnection="Host=localhost;Port=5432;Database=accesscitydb;Username=accesscity;Password=accesscity123"
export ConnectionStrings__Redis="localhost:6379"
export Messaging__UseKafka=false
export Routing__DispatchJobsToWorker=false
dotnet run

The default development launch settings expose HTTP on port 5005 unless overridden.

Run the Expo Client

cd AccessCity.App
npm ci
npm run web

For simulator, device, and QR workflows:

npx expo start

The client API base URL is resolved in AccessCity.App/services/apiConfig.ts. Override it with EXPO_PUBLIC_API_URL, EXPO_PUBLIC_API_HOST, or EXPO_PUBLIC_API_PORT.

Configuration

Most runtime settings use ASP.NET configuration keys and can be supplied through environment variables with __ separators.

High-value settings:

Setting Purpose
ConnectionStrings__DefaultConnection PostGIS primary connection string.
READONLY_DATABASE_URL Optional read-only Postgres/PgBouncer path for hot geospatial reads.
ConnectionStrings__Redis Redis L2 cache and distributed job status path.
Messaging__UseKafka Enables Kafka-backed job publishing and worker consumption.
Kafka__BootstrapServers Kafka broker list.
Jwt__Key / Jwt__PreviousKeys Current and previous JWT signing keys for rotation.
Routing__AsyncFirstForCacheMiss Returns 202 Accepted for cold route misses instead of blocking API pods.
Routing__DispatchJobsToWorker Sends route jobs to workers rather than computing in the API process.
Routing__RouteGraphPackedArtifactsEnabled Uses compact binary graph artifacts for cache/storage.
Routing__RouteGraphMaxDistributedSnapshotBytes Prevents oversized merged route graph bundles from being written to Redis/L2.
Routing__RouteGraphCorridorSlicingEnabled / Routing__RouteGraphCorridorPaddingMetres Loads a route corridor of reusable graph cells instead of the whole padded rectangle for city-scale routes.
Routing__RouteGraphAdaptiveCorridorWideningEnabled Retries a wider route graph corridor when the first slice lacks endpoint coverage or a connected accessible path.
Routing__RouteGraphProfileFailOnQualityGate Fails graph profiling when configured artifact/latency budgets are exceeded.

Secret rotation is documented in Secret Rotation. Do not use the checked-in development JWT placeholder in production.

Testing and Quality Gates

Backend format, build, and tests:

dotnet format CodeConquerors.sln --verify-no-changes --verbosity minimal
dotnet build CodeConquerors.sln --configuration Release
dotnet test AccessCity.Tests/AccessCity.Tests.csproj --configuration Release --no-build --verbosity normal

Frontend lint and tests:

cd AccessCity.App
npm ci
npm run lint
npm run test:ci

Dependency vulnerability checks:

dotnet list CodeConquerors.sln package --vulnerable --include-transitive

cd AccessCity.App
npm audit --audit-level=high --registry=https://registry.npmjs.org

Container and Kubernetes config checks:

docker compose config --quiet
docker compose --profile worker config --quiet
kubectl kustomize deploy/kubernetes >/tmp/accesscity-kustomize.yaml

CI gates are documented in CI/CD. Test architecture is documented in Test Architecture.

Route Graph Profiling

Profile a real city OSM extract through the offline graph preprocessing path:

tools/profile-city-route-graph.sh

The script downloads a Birmingham extract by default, builds source shards, packs graph artifacts, profiles warmup routes, and writes:

data/route-graph-artifacts/profile-report.json

Use fail mode in release validation:

Routing__RouteGraphProfileFailOnQualityGate=true \
Routing__RouteGraphProfileMaxRedisPayloadBytes=8388608 \
Routing__RouteGraphProfileMaxArtifactUnpackMilliseconds=150 \
tools/profile-city-route-graph.sh

The profile report is intentionally strict. A failed quality gate does not mean the app is broken; it means a city graph or route bundle exceeds the configured production budget and should be split, cached differently, or preprocessed more aggressively before rollout.

Kubernetes Deployment

Render manifests:

kubectl kustomize deploy/kubernetes

Apply the base stack:

kubectl apply -k deploy/kubernetes

The Kubernetes manifests include:

  • API and worker deployments
  • migration job
  • service and ingress
  • KEDA scaled objects
  • pod disruption budgets
  • topology spread constraints
  • secret examples and ExternalSecret examples
  • k6 distributed load and soak test jobs

Before production, replace example secrets, configure a managed Postgres/PostGIS or CloudNativePG path, point READONLY_DATABASE_URL at a read pooler/replica, and run the distributed k6 job in Distributed Load Testing.

Observability

The local observability stack is under deploy/observability:

  • OpenTelemetry Collector
  • Prometheus
  • Grafana provisioning
  • API performance alerts
  • SLO burn-rate alerts

SLOs include safe-path p95, API 5xx rate, route computation saturation, external dependency fallback rate, and shared cache hit ratio. See SLO and Scaling.

Security Notes

  • Production JWT keys must come from a secret manager or Kubernetes Secret/ExternalSecret.
  • JWT key rotation supports Jwt__PreviousKeys.
  • External APIs are guarded by timeout, bulkhead, circuit-breaker, and fallback paths.
  • Public Overpass, OSRM, Police, weather, and environmental APIs should not sit on hot production request paths; use background enrichment and cached signals.
  • Security reporting and deployment hardening expectations are documented in Security.

Known Limits

  • The verified load tests prove the checked-in distributed path, not unlimited linear scale.
  • Long cross-city route bundles can exceed the Redis/L2 payload budget; these are now flagged and kept out of distributed cache.
  • City-scale routing still needs stronger graph preprocessing for the next tier: route-level slicing, boundary overlays, CCH/CRP-style customization, and persistent versioned graph releases.
  • Accessibility quality is bounded by OSM/source data completeness. Missing width, kerb, surface, incline, curb ramp, and obstruction tags reduce route confidence.
  • AccessCity remains pre-production and should not be represented as an emergency service or globally proven production system.

Project Layout

AccessCity.API/             .NET 9 API, modules, workers, routing, geospatial services
AccessCity.App/             Expo React Native client
AccessCity.Tests/           xUnit integration, routing, architecture, stress, and benchmark tests
AccessCity.SoakTestRunner/  standalone soak/allocation harness
deploy/kubernetes/          Kubernetes production manifests and load/soak jobs
deploy/observability/       OpenTelemetry, Prometheus, Grafana, and alerts
docs/                       architecture, operations, scaling, security, and test reports
tools/                      city route graph profiling tooling
data/                       local OSM extracts and generated graph artifacts

Documentation Index

Contributing

Open an issue or pull request with a clear problem statement, reproduction steps, and the validation commands you ran. For routing changes, include profile-specific route quality tests or benchmark fixtures whenever behavior changes.

See Contributing and Code of Conduct.

License

AccessCity is licensed under the MIT License.

About

Accessibility-aware urban routing with .NET 9, PostGIS, Redis/Kafka workers, graph preprocessing, hazard scoring, and an Expo mobile client.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages