SiteMap

An offline-first mobile app for facility inspection and equipment mapping, powered by PowerSync for real-time bidirectional sync between a local SQLite database and PostgreSQL.

What is SiteMap?

SiteMap is an offline-first React Native app designed for facility teams who need to document, reference, and manage equipment locations across large industrial spaces — factory floors, warehouses, and plant facilities where connectivity is unreliable or nonexistent.

Every interaction writes to a local encrypted SQLite database first. PowerSync handles bidirectional sync with PostgreSQL in the background — the user never waits for a network request. Data created offline syncs automatically when connectivity returns, and changes from other team members stream down in real time.

The Problem

Factory shutdowns, facility audits, and infrastructure inspections require teams to document where equipment is located and who's responsible for it. These environments are exactly where traditional cloud-dependent apps fail:

  • Concrete walls, basements, and remote areas with no cell signal
  • Paper checklists and hand-drawn diagrams that can't be shared
  • Printed floor plans with handwritten notes that get lost
  • Fragmented spreadsheets and email threads with no single source of truth
  • Cloud-only apps that are unusable the moment connectivity drops

SiteMap solves this by putting the database on the device. PowerSync ensures every write is local-first and every read comes from SQLite — the network is used for sync, never as a dependency for core functionality.

Offline-First Architecture (PowerSync)

PowerSync is the backbone of SiteMap's offline-first architecture. It provides bidirectional sync between a local SQLite database on each mobile device and a central PostgreSQL server — so the app works at full speed with zero connectivity, and syncs transparently when a connection is available.

Sync Architecture

├─ Mobile App ──→ Writes to local SQLite (SQLCipher encrypted)

├─ PowerSync SDK ──→ Queues changes as CRUD transactions

├─ Upload path ──→ /api/sync/:table ──→ Upsert into PostgreSQL

├─ Download path ──→ PostgreSQL WAL ──→ PowerSync Server ──→ SQLite

└─ File uploads ──→ Queued separately ──→ Background upload to RustFS (S3)

Self-Hosted PowerSync Server
Docker-based sync engine

The PowerSync sync server runs as a Docker container connecting to PostgreSQL via logical replication. All configuration is committed to the repo:

  • Edition 3 sync streams with priority ordering
  • WAL-based logical replication (slot + publication)
  • JWT authentication (HS256, 23-hour token lifetime)
  • Auto-cleanup of stale replication slots on restart
  • Health checks and liveness probes
Sync Streams
Prioritized data delivery

Data is organized into sync streams with priority levels so critical data arrives first:

  • Priority 1: User profile, facilities, projects, team relationships
  • Priority 2: Maps, markers, comments, paths, checklists, service requests
  • User-scoped streams for profile and teammate data
  • Global streams for shared facility and project data
  • 18 tables synced across 6 streams
Local-First Writes
Every write goes to SQLite first

The mobile app never waits for a network request to complete a user action:

  • All writes go directly to local SQLite (encrypted with SQLCipher)
  • UI updates optimistically — no loading spinners for data operations
  • Changes queue as CRUD transactions for upload
  • Server upserts are idempotent (ON CONFLICT DO UPDATE)
  • Pending changes survive app restarts and crashes
Automatic Reconnection
Resilient sync with smart retry

The PowerSync client handles connectivity changes automatically:

  • Exponential backoff on connection failures (1s to 30s max)
  • Network state listener auto-reconnects on connectivity change
  • Token refresh before expiry (23-hour JWT lifetime)
  • File upload queue resumes on reconnect with retry logic
  • Database auto-recovery from SQLite BUSY errors

Core Features

Map Upload
Reference layer for your project
Upload a photo of your facility floor plan, overhead diagram, or blueprint. This becomes your reference layer for the entire project.
Custom Icon Keys
Define icons per equipment type

Define custom icons and metadata for different equipment types:

  • Server cabinets
  • Network infrastructure
  • HVAC systems
  • Electrical panels
  • Any other asset you need to track

Each icon can have a name, color, and associated metadata.

Location Marking
Pin equipment on your map

Tap on your uploaded map to place markers at exact locations. Each marker is associated with one of your custom icons, making it easy to see at a glance what equipment is where.

Markers follow a status workflow — active, flagged, or resolved — so teams can track inspection progress across the facility.

Detailed Records
Rich metadata per marker

Select any marker to add:

  • Descriptions and notes
  • Photos of the equipment
  • Responsibility assignments
  • Inspection dates
  • Any other contextual information
Offline-First via PowerSync
Works without connectivity

Every read and write targets a local SQLite database (encrypted with SQLCipher). PowerSync handles bidirectional sync with PostgreSQL in the background:

  • All 18 data tables synced via prioritized sync streams
  • Background file upload queue with automatic retry
  • Local media cache for offline photo viewing
  • Automatic reconnection with exponential backoff
Push Notifications
Real-time alerts via APNs & FCM

Custom self-hosted push notification server built with Go and Gorush:

  • iOS notifications via Apple Push Notification service
  • Android notifications via Firebase Cloud Messaging
  • Automatic device registration on login
  • Batch notifications to multiple users

Collaboration

SiteMap includes a full collaboration layer built directly into the map viewer. Teams can discuss issues, track progress, and coordinate work without leaving the map.

Comment Threads
Spatial discussions on maps

Pin comments to exact locations on a map. Each comment supports a full thread with nested replies, so discussions stay attached to the relevant spot on the floor plan.

  • Comments placed at x/y coordinates on the map
  • Nested reply threads per comment
  • Photo attachments on comments and replies
Reactions & Resolution
Track acknowledgment and progress

React to comments with emoji and mark issues as resolved when they're addressed:

  • Emoji reactions (thumbs up/down, heart, fire, eyes)
  • Resolve and reopen comments with tracking
  • See who resolved each issue and when

Annotations & Checklists

Beyond markers, SiteMap supports freehand path drawing and location-based checklists for structured inspection workflows.

Path Drawing
Annotate maps with freehand paths

Draw paths directly on your map to mark cable runs, routes, zones, or any spatial annotation:

  • Freehand drawing with smooth path rendering
  • Custom colors and stroke widths
  • Labels per path for quick identification
  • Edit or delete paths after creation
Location Checklists
Structured inspection tracking

Create named checklists on maps with items pinned to specific locations:

  • Items pinned to x/y coordinates on the map
  • Status tracking: pending, in progress, completed
  • Photo attachments per checklist item
  • Filter items by completion status

Service Requests

Operators on the plant floor can create IT service requests directly from the map, pinpointing exactly where the issue is and attaching photo evidence.

Issue Reporting
Location-based service requests

Create service requests with a pin on the map showing the exact location of the problem:

  • 9 predefined categories (network, hardware, software, etc.)
  • Custom category entry for edge cases
  • Description and multiple photo attachments
  • Camera capture or photo library
Status Workflow
Track issues to resolution

Each service request follows a lifecycle from report to resolution:

  • Open — newly reported issue
  • In Progress — technician is working on it
  • Resolved — fix applied
  • Closed — verified and complete

User Roles

SiteMap uses role-based access to match how plant teams actually work. Each role has capabilities tailored to their day-to-day responsibilities.

Technician
IT infrastructure & support

Technicians manage IT infrastructure across the plant. They use SiteMap to document and maintain equipment locations, run audits, and respond to operator requests.

  • Map and manage IT infrastructure (servers, network gear, panels)
  • Perform facility audits and inspections
  • Handle password resets and IT help requests
  • Create projects, upload floor plans, and define icon keys
  • Respond to issues logged by operators
Operator
Machine operators & floor staff

Operators are the people on the plant floor manning the machines. They use SiteMap to communicate issues to IT and document problems in real time.

  • Log machine breakdowns and equipment issues
  • Send photos of problems directly to IT
  • Request password resets and IT assistance
  • Point to their location on the map for faster response
  • Track the status of their reported issues
Admin
System administration

Admins manage the SiteMap platform itself. They control who has access, assign roles, and oversee all projects and data across the organization.

  • Invite and manage users
  • Assign and change user roles
  • Activate or deactivate accounts
  • Full access to all projects, maps, and data
  • View dashboards and audit trails

Use Cases

Factory Infrastructure Audits
Document server cabinet locations, cable runs, and network infrastructure during facility shutdowns or renovations.
Facility Maintenance
Mark HVAC units, electrical panels, fire suppression systems, and other equipment for regular inspection and maintenance cycles.
Warehouse Operations
Track equipment locations, storage zones, and responsibility assignments across large warehouse spaces.
Safety & Compliance
Maintain up-to-date diagrams showing emergency equipment, fire safety infrastructure, and evacuation routes.

Dashboard & Administration

The web dashboard provides a centralized view of all facility data, recent activity, and platform administration.

Dashboard Overview
Stats and recent activity

At-a-glance metrics and a live activity feed:

  • Total projects, markers, and maps
  • Marker counts by status (active, flagged, resolved)
  • Recent marker updates from mobile with timestamps
  • Filterable activity feed by status
Admin Panel
Platform management

Admin-only controls for managing the platform:

  • User management table with role assignment
  • Activate or deactivate user accounts
  • PowerSync health monitoring with refresh
  • Object storage credentials and console access

Facility & Team Management

Organize work by facility and manage team relationships directly from the app. Users can be assigned to specific facilities and collaborate with teammates across roles.

Facilities
Organize by location

Create and manage physical facilities:

  • Name, address, and description
  • Assign users to specific facilities
  • Filter maps by facility
  • Facility cards with map counts
Teammates
Team collaboration

Invite and manage your team:

  • Invite teammates by email
  • Assign roles: team member or manager
  • Update or remove teammates
  • Available on both web and mobile
Profile
User settings

Manage your account and preferences:

  • Upload and update profile avatar
  • Edit first and last name
  • View assigned facilities
  • Manage teammate connections

Push Notifications

SiteMap includes a fully self-hosted push notification system — no third-party push services required beyond Apple and Google's native gateways. This serves as a reference implementation for anyone building custom push notifications in an open-source project.

Mobile App

├─ Register device token ──→ Web API ──→ PostgreSQL (push_devices)

└─ Receive notification ◀── APNs / FCM ◀── Gorush ◀── Go Server ◀── Web App

Go Microservice
Lightweight notification server
A minimal Go HTTP server that queries the database for device tokens and forwards notifications through Gorush. Supports single-user and batch sends with a simple REST API.
Gorush Gateway
APNs & FCM push proxy
Open-source push gateway that handles the protocol details for Apple Push Notification service (token-based .p8 auth) and Firebase Cloud Messaging. Runs as a Docker sidecar.
Device Registration
Automatic token management
The mobile app automatically registers its push token on login and deactivates it on logout. Tokens are stored in PostgreSQL with platform, environment, and device metadata.
Docker Compose
One command to run
The entire notification stack (Go server + Gorush) is defined in a single Docker Compose file. Just add your APNs key and run docker compose up.

Object Storage (RustFS)

SiteMap uses a self-hosted S3-compatible object storage server (RustFS) for all file uploads — no third-party cloud storage required. All map images, marker photos, comment attachments, and checklist item photos are stored here.

RustFS Server
S3-compatible file storage

A Rust-based object storage server with full S3 API compatibility:

  • Auto-creates the default media bucket on startup
  • Access key and secret key authentication
  • Built-in web console for browsing stored files
  • Runs as a Docker container with persistent volumes
File Handling
Upload, proxy, and cache

Files flow through a consistent pipeline across both apps:

  • Web uploads via server functions with signed URLs
  • Mobile saves files locally first, then queues for upload
  • Authenticated file proxy for serving stored files
  • Local media cache on mobile for offline access

Authentication & Security

SiteMap uses a self-hosted authentication system — no third-party auth providers required. The system supports email/password login, TOTP two-factor authentication, and WebAuthn passkeys (Face ID, Touch ID, Windows Hello) for passwordless sign-in.

Auth Flow

├─ Email + Password ──→ better-auth ──→ Session (cookie)

├─ Passkey (WebAuthn) ──→ SimpleWebAuthn ──→ JWT ──→ Session

└─ 2FA (TOTP) ──→ Authenticator App ──→ Verify code ──→ Session

better-auth
Self-hosted auth framework
Handles email/password sign-in/sign-up, session management, and TOTP two-factor authentication. Runs entirely on your own server with a PostgreSQL backend via Drizzle ORM.
WebAuthn Passkeys
Passwordless authentication
Custom implementation using SimpleWebAuthn. Supports Face ID, Touch ID, and Windows Hello on both web and mobile. Uses signed JWT challenge tokens for cross-platform compatibility.
Role-Based Access
Admin, Operator, Technician
Three user roles selected during signup. Admins manage users and settings, Operators handle field operations, and Technicians view and update maps and markers.
Multi-Step Signup
Guided account creation
Three-step signup flow: role selection, credentials entry, and 2FA setup. Passkeys are promoted as the primary 2FA method when the device supports them, with TOTP authenticator apps always available as an alternative.

iOS Deployment (Fastlane)

SiteMap includes a fully transparent iOS deployment pipeline built with Fastlane. All build scripts, configuration, and automation logic are committed to the repo so contributors can see exactly how builds are created and shipped to TestFlight.

Auto Version Bumping
Hands-free version management
A Node.js script automatically increments the marketing version and build number in both a central ios-version.json file and the Xcode project before every TestFlight upload. Supports patch, minor, and major bumps.
Build Logging
Full logs on every deploy
Every deployment captures the complete xcodebuild and Fastlane output into a timestamped log file. If a build fails, the full log is immediately available under build-logs/ for debugging.
Auto Log Cleanup
Keeps only the last 10 builds
Before each deploy, old build logs are automatically pruned to keep only the 10 most recent. Empty date directories are removed and freed disk space is reported.
One-Command Deploy
Build, sign, and upload
Run pnpm run deploy:ios:beta to bump the version, archive with xcodebuild, export a signed IPA, and upload to TestFlight in a single step. All configuration is driven by environment variables.

Architecture

React Native + Re.PackTanStack StartTanStack DBPostgreSQLPowerSyncRustFSGorushGoDockerbetter-authSimpleWebAuthnreact-native-skiaSQLCipher
  • Mobile: React Native + Re.Pack (iOS/Android)
  • Web: React 19 + TanStack Start + TanStack DB + Drizzle ORM
  • Database: PostgreSQL (server) + SQLite/SQLCipher (mobile)
  • Sync: PowerSync (offline-first with cloud bridge)
  • File Storage: RustFS (self-hosted S3-compatible)
  • Push Notifications: Go microservice + Gorush (APNs/FCM gateway)
  • Canvas/Interaction: react-native-skia (pan/zoom/markers)
  • Infrastructure: Docker Compose for all services

Database

SiteMap uses PostgreSQL as its primary database, with a local SQLite replica on mobile devices for offline-first operation. A shared Zod schema package ensures type safety and consistency across all apps.

Shared Schema
Single source of truth

All entity definitions live in a shared package using Zod schemas. Both apps import from here to stay in sync:

  • Zod schemas for runtime validation
  • TypeScript types inferred from schemas
  • Table and column name constants
  • Shared enums (roles, statuses, shapes)
Dual ORM Layer
PostgreSQL + SQLite

Each app maintains its own ORM schema that references the shared constants:

  • Web: Drizzle ORM with PostgreSQL (UUIDs, timestamps, foreign keys)
  • Mobile: PowerSync with op-sqlite (text/integer/real columns)
  • Both use the same table names and column names from the shared package
Core Tables
18 data tables + auth tables
  • users — accounts with roles (admin, operator, technician)
  • facilities — physical locations (plants, buildings)
  • projects — site groupings with maps
  • maps — uploaded floor plans (PDF/image) with dimensions
  • map_keys — legend items with icons and colors
  • map_markers — pinned locations on maps
  • marker_photos — photos attached to markers
  • map_comments / comment_replies / comment_reactions / comment_photos — collaboration layer
  • map_paths — drawn annotations with color and width
  • map_lists / map_list_items / list_item_photos — checklists
  • service_requests / service_request_photos — IT issue tracking
  • user_facilities / teammates — team relationships

Auth tables (sessions, accounts, verifications, passkey_credentials) are web-only and not synced to mobile.

TanStack DB
Client-side reactive collections

The web app uses TanStack DB for client-side reactive state. Server function data is loaded into local-only collections that enable:

  • Live queries with automatic re-rendering
  • Client-side filtering and sorting
  • Optimistic updates before server confirmation
  • Decoupled data layer from UI components

This mirrors how PowerSync works on mobile — both apps use a local data layer that syncs with PostgreSQL, keeping the architecture consistent across platforms.

Sync via PowerSync
Bidirectional real-time replication

PostgreSQL and mobile SQLite stay in sync through a self-hosted PowerSync server:

  • Download: WAL-based logical replication streams changes to devices
  • Upload: CRUD transactions sent to /api/sync/:table endpoints
  • Edition 3 sync streams with user-scoped and global buckets
  • Priority ordering ensures critical data (profile, facilities) arrives first
  • Cascade deletes and schema changes propagate through sync

Migrations

Schema changes follow a structured workflow to keep PostgreSQL, Drizzle, PowerSync, and the shared type definitions in sync. Every migration touches multiple layers of the stack.

Migration Flow

1. Update Zod schema in shared package

2. Update Drizzle table in web app

3. Generate or write SQL migration

4. Apply migration to PostgreSQL

5. Update PowerSync table in mobile app

6. Update sync config + publication (new tables only)

Drizzle Kit
Development migrations

Drizzle Kit compares your TypeScript schema against the database and generates SQL migrations automatically:

  • drizzle-kit generate — generate migration SQL from schema diff
  • drizzle-kit push — push schema directly to dev DB
  • drizzle-kit check — compare schema against DB
  • drizzle-kit studio — visual DB browser
Raw SQL
Production migrations

For production deployments, numbered SQL migration files are stored in db/migrations/:

  • Sequential numbering (001, 002, ...)
  • Includes ALTER TABLE, new indexes, and triggers
  • PowerSync publication updates for new tables
  • Applied directly via psql
Shared Schema First
Types drive everything
Every schema change starts in the shared Zod package. Add the field to the Zod schema and column constants first, then update each app's ORM schema. TypeScript will error in both apps until the ORM schemas match, preventing drift.
PowerSync Sync
Keep mobile in sync

After applying a migration to PostgreSQL:

  • Add new columns to the PowerSync table definition
  • Existing columns picked up automatically via SELECT *
  • New tables need a sync stream entry and publication update

Roadmap

Phase 1 (MVP)
Core map, icons, markers, offline storage, PowerSync sync
Phase 2
Collaboration (comments, reactions, threads), path drawing, checklists, service requests, facility & team management
Phase 3
Document attachment (PDFs, manuals, schematics per equipment), audit reporting, advanced analytics

Privacy & Data

All data is stored locally on your device first. Sync to external services is optional and configurable. You maintain full control over what data leaves your device and when.

Getting Started

  1. Create a new project
  2. Upload a floor plan photo
  3. Define custom icons for your equipment types
  4. Start marking locations on your map
  5. Add details and photos to each marker
  6. Configure PowerSync to sync data when ready

Credits

This app was built with the help of the following tools:

  • shadcn/ui — component library and MCP server for AI-assisted UI development
  • PowerSync — offline-first sync engine and AI agent skills for guided development
  • better-auth — self-hosted authentication framework with 2FA support
  • SimpleWebAuthn — WebAuthn/passkey implementation for passwordless authentication
  • Re.Pack — Webpack/Rspack-based toolkit for React Native
  • Rock.js — server framework

Built for teams who work in the field, in areas with unreliable connectivity, and need to maintain accurate facility documentation without cloud dependency.