Offline-First PWA

Progressive Web App architecture with service worker caching, IndexedDB storage, background sync, and conflict resolution for field operations without connectivity.

Last updated: 2025-02-18

Offline-First PWA Architecture

Certexi is a fully offline-capable Progressive Web App. Customs operations continue seamlessly without internet connectivity — events are queued locally and synced automatically when the connection is restored.

Architecture

Loading diagram…

Offline Status Indicator

<div className="flex flex-col gap-3 w-72">
  <Card>
    <CardContent className="p-3 flex items-center gap-3">
      <div className="w-2.5 h-2.5 rounded-full bg-green-500 animate-pulse" />
      <div className="flex-1">
        <div className="text-sm font-medium">Online</div>
        <div className="text-xs text-muted-foreground">All systems connected</div>
      </div>
      <Badge variant="outline" className="text-green-500">Synced</Badge>
    </CardContent>
  </Card>
  <Card>
    <CardContent className="p-3 flex items-center gap-3">
      <div className="w-2.5 h-2.5 rounded-full bg-amber-500" />
      <div className="flex-1">
        <div className="text-sm font-medium">Offline</div>
        <div className="text-xs text-muted-foreground">3 events queued</div>
      </div>
      <Badge variant="outline" className="text-amber-500">Pending</Badge>
    </CardContent>
  </Card>
  <Card>
    <CardContent className="p-3 space-y-2">
      <div className="flex items-center justify-between text-xs">
        <span className="text-muted-foreground">Sync queue</span>
        <span>3 / 3 events</span>
      </div>
      <Progress value={0} className="h-1.5" />
      <Button size="sm" variant="outline" className="w-full text-xs">Force Sync</Button>
    </CardContent>
  </Card>
</div>

Core Components

Service Worker (/public/sw.js)

The service worker manages all caching and background sync:

  • Caches static assets (HTML, CSS, JS, images, fonts)
  • Implements network-first and cache-first strategies
  • Handles background sync for queued events
  • Provides offline fallback pages for unmatched routes

IndexedDB Storage (/lib/offline/storage.ts)

Three object stores power the offline experience:

StorePurposeKey
eventsQueued operations waiting to syncAuto-generated ID
unitsCached transport units for offline accessUnit ID
responsesCached API responses with TTLRequest URL

Offline Indicator (/components/offline-indicator.tsx)

Visual feedback for users showing real-time online/offline status, pending sync count, and manual sync trigger.

Complete Offline Workflow

Users can perform all critical operations while offline:

  • Scan NFC tags and QR codes
  • Capture photos and GPS location
  • Record inspection observations
  • View cached transport units
  • Progress through all workflow stages

Events are queued in IndexedDB with the following structure:

interface QueuedEvent {
  id: string;               // "offline-{timestamp}-{random}"
  eventType: string;        // inspection, entry, exit, scale, etc.
  data: any;                // Full event payload
  timestamp: number;        // When queued (ms)
  retries: number;          // Sync retry count
  synced: boolean;          // Sync status
}

Caching Strategies

Static Assets (Cache-First)

HTML, CSS, JavaScript, images, and fonts are cached indefinitely and updated when the service worker version changes.

API Requests (Network-First)

API calls always try the network first, falling back to cached responses when offline. Responses are cached for 5 minutes (configurable).

Pages (Network-First)

Dynamic content is always fresh when online. Cached pages are available offline with an offline fallback for unmatched routes.

Background Sync

When the connection is restored:

  1. Service worker triggers background sync
  2. Queued events are processed sequentially
  3. Each event is sent to its appropriate endpoint
  4. Failed events are retried with exponential backoff (max 5 retries)
  5. Successfully synced events are removed from the queue
ℹ️

Browser Support

Background Sync API is supported in Chrome 49+ and Edge 79+. For Safari and Firefox, Certexi falls back to manual sync on the online event.

Sync Endpoint Mapping

Event TypeEndpoint
inspection/api/uma/attestation
entry/api/workflow/entry
exit/api/workflow/exit
scale/api/workflow/scale
Default/api/events

Data Integrity

Offline operations are protected by multiple layers:

  • Cryptographic hashing for all events
  • Merkle tree verification on sync
  • Timestamp-based replay attack prevention
  • Server-side duplicate detection
⚠️

Re-authentication

After an extended offline period, users may need to re-authenticate. Offline operations use cached credentials that expire based on the JWT token lifetime.

Configuration

Cache Version

Increment CACHE_VERSION in /public/sw.js to force a full cache refresh on the next visit.

Cache TTL

Default API response cache: 5 minutes (300,000ms). Configurable per-request.

Retry Limit

Maximum 5 sync retries before an event is discarded. Failed events are logged for manual recovery.

Browser Support

FeatureChromeFirefoxSafariEdge
Service Worker40+44+11.1+17+
IndexedDB24+16+10+12+
Background Sync49+FlagNo79+
Cache API40+41+11.1+17+