
South Africa’s first community accessibility-mapping platform, disabled people discover and rate accessible spaces; businesses get SANS 10400-S compliance scoring. One codebase for iOS, Android and Web.
View the product pageThe problem
4.2M South Africans live with disability and can’t safely survey a venue before an expensive, unreliable trip. SANS 10400-S mandates accessible buildings, but no platform aggregates real-world SA accessibility data.
.jpg&w=3840&q=75)
Architecture
A container-level view of the real system, the boundaries, the trust edges, and where intelligence and money actually flow.
Supabase + PostGIS is the source of truth for locations; an Edge Function publishes verified locations to Mapbox as visual tiles. Payment truth arrives via an HMAC-SHA512-verified Paystack webhook. A 50/50 co-venture.
.jpg&w=3840&q=75)
By the numbers
1
codebase, 3 platforms
0–100
SANS score · 17 checks
6
Deno Edge Functions
19
tables · 13 RPCs
Stack
Intelligence
No AI in production, listed only on the future roadmap, so it is never claimed as a current capability.
Data model
19 tables · PostGIS · 8 migrations · 13 RPCs
Key decisions
Architecture Decision Records, the trade-offs made deliberately, in the open.
.jpg&w=3840&q=75)
The hard parts, solved
Neither npm Mapbox path survives Metro (mapbox-gl uses import(variable), which Metro’s web transform rejects; react-map-gl drags it in). So Metro file-extension resolution picks MapView.native.tsx (@rnmapbox/maps v10) or MapView.web.tsx, CDN-loaded Mapbox GL JS v3.8.0 driven imperatively, pins painted on a canvas, both mirroring one cluster spec, behind a single MapViewProps contract.
An "admin can read all" policy tested admin-ness with EXISTS(SELECT FROM profiles…) inside a profiles policy → Postgres 42P17 recursion on every authenticated read; and handle_new_user lacked SET search_path, so 100% of signups returned 500. Both closed with SECURITY DEFINER is_admin()/is_staff() helpers, caught only by testing live as each real role.
The "own row" update policy let any user set their own role=admin. Because authenticated held table-level UPDATE, a bare REVOKE UPDATE(role) was a no-op, so the grant was dropped and re-granted on every column except role, forcing all role changes through guarded RPCs.
Sourced from TrueAccApp, .claude/ARCHITECTURE, SCHEMA, BUSINESS, BUILD_PHASES, PRODUCTION_READINESS, supabase/migrations 001–008.
.jpg&w=3840&q=75)
Keep exploring