Subsystem 02.2 · 2023 — present
SmartCity
White-label civic platform — mobile apps and CMS
A municipality’s own app — news, services, transport, emergency numbers, citizen reports — run entirely from a CMS, so a new city is configuration, not a new codebase.
01What it is
SmartCity gives a municipality its own branded mobile app. Residents read municipal news, find institutions and services, check bus lines and taxis, call emergency services, browse a map of what’s nearby and report problems — litter, broken infrastructure, corruption — with photos and GPS.
City staff run all of it from an admin panel: content, menus, which tabs appear on the home screen and every interface string in every language. The first production instance serves the Municipality of Skenderaj in Kosovo.
02The problem
Small municipalities have no single digital channel to their residents and no structured way to receive reports from them. A bespoke app per city is too expensive to build and too expensive to keep alive.
So the app is server-driven: tabs, menus, content and translations are data. SmartCity 2.0 takes the idea to its conclusion — a multi-tenant platform where one deployment hosts many cities, each with its own domain, branding and store identity.
03What I built
- The Kosova Smart backend and admin (2023–2025): ASP.NET Core API, Angular admin, its own OpenIddict login server, per-tenant builds and Firebase push to web, iOS and Android.
- The Skenderaj app on Expo and React Native’s new architecture: server-driven home tabs, a map with zoom-aware nearby search, citizen reporting with photos, GPS and reverse geocoding, push notifications that deep-link into articles, and languages loaded from the CMS at runtime.
- SmartCity 2.0 (2026): a multi-tenant rebuild as an ASP.NET Core 10 modular monolith on PostgreSQL/PostGIS, Redis, Meilisearch and S3-compatible storage, with an Angular 21 admin.
- Builds, signing and store submission through EAS for both platforms.
04Architecture
05Technology
- Mobile
- ExpoReact Native (New Architecture)expo-routerFirebase Messagingreact-native-maps
- Admin
- Angular 17 → 21TailwindTanStack Table
- Services
- ASP.NET Core 7 → 10EF CoreOpenIddictSerilog
- Data
- SQL Server + NetTopologySuitePostgreSQL 17 + PostGISRedisMeilisearchS3 / MinIO
- Delivery
- EAS Build & SubmitDocker
06Engineering notes
- 01
A city is a hostname
SmartCity 2.0 resolves the tenant from a verified domain, cached in Redis, before anything else runs. A tenant can only go live after a readiness gate passes: a template version applied, a verified primary domain, exactly one default language and consistent single-city or multi-city rules.
- 02
Knowing when a translation is stale
Every translation records the version of the source text it was written and reviewed against, which gives five honest states — not started, in progress, needs review, outdated, complete. Publishing is blocked until every language marked as required is complete; menus are checked item by item.
- 03
Editorial workflow with a memory
Content moves through eight states, and every transition snapshots a restorable version. Slugs already used by published versions are protected, and a background job publishes and unpublishes on schedule.
- 04
Maps that stay fast
Places, institutions and stops are PostGIS points with spatial indexes, and one discovery index serves pages, places, events, transport and announcements with radius filters and distance sorting. On the phone, the nearby-search radius shrinks as you zoom in and every map cell is fetched once.
- 05
Media that is only public when published
Uploads run through sessions with incremental SHA-256 hashing; images get WebP variants, videos get FFmpeg thumbnails from a sidecar container and presigned stream links sized to their length. A file is served publicly only while published content uses it.
- 06
Push that lands where it should
A notification tapped on a cold start is held until the router is ready, retried on Android, re-shown as a local notification in the foreground and deep-linked to its article.
07Results
- 109
- tables in 2.0
- ~358
- API routes in 2.0
- 57
- permission codes
- 15
- feature modules
- 22
- app screens
- 142
- commits — all mine
- Skenderaj | SmartCity is live for residents on iOS and Android.
- One mobile codebase, driven entirely by the CMS — tabs, menus and languages included.
- A multi-tenant 2.0 platform built to host many cities from one deployment.